Abbreviations, simple
You are encouraged to solve this task according to the task description, using any language you may know.
The use of abbreviations (also sometimes called synonyms, nicknames, AKAs, or aliases) can be an
easy way to add flexibility when specifying or using commands, sub─commands, options, etc.
For this task, the following command table will be used:
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
Notes concerning the above command table:
- it can be thought of as one long literal string (with blanks at end-of-lines)
- it may have superfluous blanks
- it may be in any case (lower/upper/mixed)
- the order of the words in the command table must be preserved as shown
- the user input(s) may be in any case (upper/lower/mixed)
- commands will be restricted to the Latin alphabet (A ──► Z, a ──► z)
- a command is followed by an optional number, which indicates the minimum abbreviation
- A valid abbreviation is a word that has:
- at least the minimum length of the word's minimum number in the command table
- compares equal (regardless of case) to the leading characters of the word in the command table
- a length not longer than the word in the command table
- ALT, aLt, ALTE, and ALTER are all abbreviations of ALTER 3
- AL, ALF, ALTERS, TER, and A aren't valid abbreviations of ALTER 3
- The 3 indicates that any abbreviation for ALTER must be at least three characters
- Any word longer than five characters can't be an abbreviation for ALTER
- o, ov, oVe, over, overL, overla are all acceptable abbreviations for overlay 1
- if there isn't a number after the command, then there isn't an abbreviation permitted
- Task
-
- The command table needn't be verified/validated.
- Write a function to validate if the user "words" (given as input) are valid (in the command table).
- If the word is valid, then return the full uppercase version of that "word".
- If the word isn't valid, then return the lowercase string: *error* (7 characters).
- A blank input (or a null input) should return a null string.
- Show all output here.
- An example test case to be used for this task
For a user string of:
riG rePEAT copies put mo rest types fup. 6 poweRin
the computer program should return the string:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
- Metrics
- Counting
- Word frequency
- Letter frequency
- Jewels and stones
- I before E except after C
- Bioinformatics/base count
- Count occurrences of a substring
- Count how many vowels and consonants occur in a string
- Remove/replace
- XXXX redacted
- Conjugate a Latin verb
- Remove vowels from a string
- String interpolation (included)
- Strip block comments
- Strip comments from a string
- Strip a set of characters from a string
- Strip whitespace from a string -- top and tail
- Strip control codes and extended characters from a string
- Anagrams/Derangements/shuffling
- Word wheel
- ABC problem
- Sattolo cycle
- Knuth shuffle
- Ordered words
- Superpermutation minimisation
- Textonyms (using a phone text pad)
- Anagrams
- Anagrams/Deranged anagrams
- Permutations/Derangements
- Find/Search/Determine
- ABC words
- Odd words
- Word ladder
- Semordnilap
- Word search
- Wordiff (game)
- String matching
- Tea cup rim text
- Alternade words
- Changeable words
- State name puzzle
- String comparison
- Unique characters
- Unique characters in each string
- Extract file extension
- Levenshtein distance
- Palindrome detection
- Common list elements
- Longest common suffix
- Longest common prefix
- Compare a list of strings
- Longest common substring
- Find common directory path
- Words from neighbour ones
- Change e letters to i in words
- Non-continuous subsequences
- Longest common subsequence
- Longest palindromic substrings
- Longest increasing subsequence
- Words containing "the" substring
- Sum of the digits of n is substring of n
- Determine if a string is numeric
- Determine if a string is collapsible
- Determine if a string is squeezable
- Determine if a string has all unique characters
- Determine if a string has all the same characters
- Longest substrings without repeating characters
- Find words which contains all the vowels
- Find words which contain the most consonants
- Find words which contains more than 3 vowels
- Find words whose first and last three letters are equal
- Find words with alternating vowels and consonants
- Formatting
- Substring
- Rep-string
- Word wrap
- String case
- Align columns
- Literals/String
- Repeat a string
- Brace expansion
- Brace expansion using ranges
- Reverse a string
- Phrase reversals
- Comma quibbling
- Special characters
- String concatenation
- Substring/Top and tail
- Commatizing numbers
- Reverse words in a string
- Suffixation of decimal numbers
- Long literals, with continuations
- Numerical and alphabetical suffixes
- Abbreviations, easy
- Abbreviations, simple
- Abbreviations, automatic
- Song lyrics/poems/Mad Libs/phrases
- Mad Libs
- Magic 8-ball
- 99 bottles of beer
- The Name Game (a song)
- The Old lady swallowed a fly
- The Twelve Days of Christmas
- Tokenize
- Text between
- Tokenize a string
- Word break problem
- Tokenize a string with escaping
- Split a character string based on change of character
- Sequences
11l
V command_table_text =
|‘add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1’
V user_words = ‘riG rePEAT copies put mo rest types fup. 6 poweRin’
F find_abbreviations_length(command_table_text)
‘ find the minimal abbreviation length for each word.
a word that does not have minimum abbreviation length specified
gets it's full lengths as the minimum.
’
[String = Int] command_table
V input_list = command_table_text.split((‘ ’, "\n"), group_delimiters' 1B)
V i = 0
V word = ‘’
L i < input_list.len | word != ‘’
I word == ‘’
word = input_list[i++]
V abbr_len = I i < input_list.len {input_list[i++]} E String(word.len)
X.try
command_table[word] = Int(abbr_len)
word = ‘’
X.catch ValueError
command_table[word] = word.len
word = abbr_len
R command_table
F find_abbreviations(command_table)
‘ for each command insert all possible abbreviations’
[String = String] abbreviations
L(command, min_abbr_len) command_table
L(l) min_abbr_len .. command.len
V abbr = command[0 .< l].lowercase()
abbreviations[abbr] = command.uppercase()
R abbreviations
F parse_user_string(user_string, abbreviations)
V user_words = user_string.split(‘ ’, group_delimiters' 1B).map(word -> word.lowercase())
V commands = user_words.map(user_word -> @abbreviations.get(user_word, ‘*error*’))
R commands.join(‘ ’)
V command_table = find_abbreviations_length(command_table_text)
V abbreviations_table = find_abbreviations(command_table)
V full_words = parse_user_string(user_words, abbreviations_table)
print(‘user words: ’user_words)
print(‘full words: ’full_words)
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
AArch64 Assembly
/* ARM assembly AARCH64 Raspberry PI 3B */
/* program abbrSimple64.s */
/* store list of command in a file commandSimple.txt */
/* and run the program abbrSimple64 commandSimple.txt */
/*******************************************/
/* Constantes file */
/*******************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeConstantesARM64.inc"
.equ BUFFERSIZE, 1000
.equ NBMAXIELEMENTS, 100
/*******************************************/
/* Structures */
/********************************************/
/* command structure */
.struct 0
command_name_address: // name
.struct command_name_address + 8
command_min: // minimum letters
.struct command_min + 8
command_end:
/*********************************/
/* Initialized data */
/*********************************/
.data
szMessTitre: .asciz "Nom du fichier : "
szCarriageReturn: .asciz "\n"
szMessErreur: .asciz "Error detected.\n"
szMessErrBuffer: .asciz "buffer size too less !!"
szMessCtrlCom: .asciz "Command : @ minimum : @ \n"
szMessErrorAbr: .asciz "*error*"
szMessInput: .asciz "Enter command (or <ctrl-c> to stop) : "
/*********************************/
/* UnInitialized data */
/*********************************/
.bss
.align 4
sZoneConv: .skip 24
qAdrFicName: .skip 8
iTabAdrCmd: .skip command_end * NBMAXIELEMENTS
sBufferCmd: .skip BUFFERSIZE
sBuffer: .skip BUFFERSIZE
/*********************************/
/* code section */
/*********************************/
.text
.global main
main: // INFO: main
mov x0,sp // stack address for load parameter
bl traitFic // read file and store value in array
cmp x0,#-1
beq 100f // error ?
ldr x0,qAdriTabAdrCmd
bl controlLoad
1:
ldr x0,qAdrszMessInput // display input message
bl affichageMess
mov x0,#STDIN // Linux input console
ldr x1,qAdrsBuffer // buffer address
mov x2,#BUFFERSIZE // buffer size
mov x8, #READ // request to read datas
svc 0 // call system
sub x0,x0,#1
mov x2,#0
strb w2,[x1,x0] // replace character 0xA by zéro final
cbnz x0,2f // null string ?
mov x0,x1
b 3f
2:
ldr x0,qAdrsBuffer
ldr x1,qAdriTabAdrCmd
bl controlCommand // control text command
3:
mov x2,x0 // display result
bl affichageMess
ldr x0,qAdrszCarriageReturn
bl affichageMess
b 1b // loop
99:
ldr x0,qAdrszMessErrBuffer
bl affichageMess
100: // standard end of the program
mov x0, #0 // return code
mov x8, #EXIT // request to exit program
svc #0 // perform the system call
qAdrszCarriageReturn: .quad szCarriageReturn
qAdrszMessErrBuffer: .quad szMessErrBuffer
qAdrsZoneConv: .quad sZoneConv
qAdrszMessInput: .quad szMessInput
/******************************************************************/
/* control abbrevation command */
/******************************************************************/
/* x0 contains string input command */
/* x1 contains address table string command */
controlCommand: // INFO: controlCommand
stp x1,lr,[sp,-16]! // save registres
stp x2,x3,[sp,-16]! // save registres
stp x4,x5,[sp,-16]! // save registres
stp x6,x7,[sp,-16]! // save registres
stp x8,x9,[sp,-16]! // save registres
mov x8,x0
mov x9,x1
mov x10,#command_end // length item
bl computeLength // length input command
mov x4,x0 // save length input
mov x2,#0 // indice
mov x3,#0 // find counter
1:
mov x0,x8
madd x6,x2,x10,x9 // compute address
ldr x1,[x6,#command_name_address] // load a item
cbz x1,5f // end ?
bl comparStringSpe //
cbz x0,4f // no found other search
ldr x5,[x6,#command_min]
cmp x5,#0 // minimum = zéro ?
ble 2f
cmp x4,x5 // input < command capital letters
blt 4f // no correct
add x3,x3,#1 // else increment counter
mov x7,x1 // and save address command
b 4f
2:
mov x0,x1
bl computeLength // length table command
cmp x0,x4 // length input commant <> lenght table command
bne 4f // no correct
add x3,x3,#1 // else increment counter
mov x7,x1 // and save address command
4:
add x2,x2,#1 // increment indice
b 1b // and loop
5:
cmp x3,#1 // no find or multiple find ?
bne 99f // error
// one find
mov x0,x7 // length command table
bl computeLength
cmp x4,x0 // length input > command ?
bgt 99f // error
mov x4,#0x20 // 5 bit to 1
mov x2,#0
6:
ldrb w3,[x7,x2]
cbz w3,7f
bic x3,x3,x4 // convert to capital letter
strb w3,[x8,x2]
add x2,x2,#1
b 6b
7:
strb w3,[x8,x2]
mov x0,x8 // return string input address
b 100f
99:
ldr x0,qAdrszMessErrorAbr // return string "error"
100:
ldp x8,x9,[sp],16 // restaur des 2 registres
ldp x6,x7,[sp],16 // restaur des 2 registres
ldp x4,x5,[sp],16 // restaur des 2 registres
ldp x2,x3,[sp],16 // restaur des 2 registres
ldp x1,lr,[sp],16 // restaur des 2 registres
ret
qAdrszMessErreur: .quad szMessErreur
qAdrszMessErrorAbr: .quad szMessErrorAbr
/******************************************************************/
/* comparaison first letters String */
/******************************************************************/
/* x0 contains first String */
/* x1 contains second string */
/* x0 return 0 if not find else returns number letters OK */
comparStringSpe:
stp x1,lr,[sp,-16]! // save registres
stp x2,x3,[sp,-16]! // save registres
stp x4,x5,[sp,-16]! // save registres
stp x6,x7,[sp,-16]! // save registres
mov x2,#0
1:
ldrb w3,[x0,x2] // input
orr w4,w3,#0x20 // convert capital letter
ldrb w5,[x1,x2] // table
orr w6,w5,#0x20 // convert capital letter
cmp w4,w6
bne 2f
cbz w3,3f // end strings ?
add x2,x2,#1
b 1b
2:
cbz w3,3f // fist letters Ok
mov x0,#0 // no ok
b 100f
3:
mov x0,x2
100:
ldp x6,x7,[sp],16 // restaur des 2 registres
ldp x4,x5,[sp],16 // restaur des 2 registres
ldp x2,x3,[sp],16 // restaur des 2 registres
ldp x1,lr,[sp],16 // restaur des 2 registres
ret
/******************************************************************/
/* compute length String */
/******************************************************************/
/* x0 contains String */
/* x0 return length */
computeLength: // INFO: functionFN
stp x1,lr,[sp,-16]! // save registres
stp x2,x3,[sp,-16]! // save registres
mov x1,#0
1:
ldrb w2,[x0,x1]
cbz w2,2f // end ?
add x1,x1,#1
b 1b
2:
mov x0,x1 // return length in x0
100:
ldp x2,x3,[sp],16 // restaur des 2 registres
ldp x1,lr,[sp],16 // restaur des 2 registres
ret
/******************************************************************/
/* read file */
/******************************************************************/
/* x0 contains address stack begin */
traitFic: // INFO: traitFic
stp x1,lr,[sp,-16]! // save registres
stp x2,x3,[sp,-16]! // save registres
stp x4,x5,[sp,-16]! // save registres
stp x6,x7,[sp,-16]! // save registres
stp x8,fp,[sp,-16]! // save registres
mov fp,x0 // fp <- start address
ldr x4,[fp] // number of Command line arguments
cmp x4,#1
ble 99f
add x5,fp,#16 // second parameter address
ldr x5,[x5]
ldr x0,qAdrqAdrFicName
str x5,[x0]
ldr x0,qAdrszMessTitre
bl affichageMess // display string
mov x0,x5
bl affichageMess
ldr x0,qAdrszCarriageReturn
bl affichageMess // display carriage return
mov x0,AT_FDCWD
mov x1,x5 // file name
mov x2,#O_RDWR // flags
mov x3,#0 // mode
mov x8, #OPEN // call system OPEN
svc 0
cmp x0,#0 // error ?
ble 99f
mov x7,x0 // File Descriptor
ldr x1,qAdrsBufferCmd // buffer address
mov x2,#BUFFERSIZE // buffer size
mov x8,#READ // read file
svc #0
cmp x0,#0 // error ?
blt 99f
// extraction datas
ldr x1,qAdrsBufferCmd // buffer address
add x1,x1,x0
mov x0,#0 // store zéro final
strb w0,[x1]
ldr x0,qAdriTabAdrCmd // key string command table
ldr x1,qAdrsBufferCmd // buffer address
bl extracDatas
// close file
mov x0,x7
mov x8, #CLOSE
svc 0
mov x0,#0
b 100f
99: // error
ldr x0,qAdrszMessErreur // error message
bl affichageMess
mov x0,#-1
100:
ldp x8,fp,[sp],16 // restaur des 2 registres
ldp x6,x7,[sp],16 // restaur des 2 registres
ldp x4,x5,[sp],16 // restaur des 2 registres
ldp x2,x3,[sp],16 // restaur des 2 registres
ldp x1,lr,[sp],16 // restaur des 2 registres
ret
qAdrqAdrFicName: .quad qAdrFicName
qAdrszMessTitre: .quad szMessTitre
qAdrsBuffer: .quad sBuffer
qAdrsBufferCmd: .quad sBufferCmd
qAdriTabAdrCmd: .quad iTabAdrCmd
/******************************************************************/
/* extrac digit file buffer */
/******************************************************************/
/* x0 contains strings address */
/* x1 contains buffer address */
extracDatas: // INFO: extracDatas
stp x1,lr,[sp,-16]! // save registres
stp x2,x3,[sp,-16]! // save registres
stp x4,x5,[sp,-16]! // save registres
stp x6,x7,[sp,-16]! // save registres
stp x8,fp,[sp,-16]! // save registres
mov x7,x0
mov x6,x1
mov x8,#0 // top command name
mov x2,#0 // string buffer indice
mov x4,x1 // start string
mov x5,#0 // string index
1:
ldrb w3,[x6,x2]
cbz w3,4f // end
cmp x3,#0xA
beq 2f
cmp x3,#' ' // end string
beq 3f
add x2,x2,#1
b 1b
2:
mov x3,#0
strb w3,[x6,x2]
ldrb w3,[x6,x2]
cmp w3,#0xD
bne 21f
add x2,x2,#2
b 4f
21:
add x2,x2,#1
b 4f
3:
mov x3,#0
strb w3,[x6,x2]
add x2,x2,#1
4:
mov x0,x4
ldrb w1,[x0] // load first byte
cmp w1,#'0' // it is à digit ?
blt 5f
cmp w1,#'9'
bgt 5f
mov x1,#command_end
madd x1,x5,x1,x7 // compute address to store
mov x0,x4
bl conversionAtoD // conversion ascii digit
str x0,[x1,#command_min] // and store in minimum
mov x8,#0 // line command ok
add x5,x5,#1 // increment indice
b 7f
5:
cmp x8,#0 // other name ?
beq 6f
mov x0,#0 // yes store zéro in minimum in prec
mov x1,#command_end
madd x1,x5,x1,x7
add x1,x1,#command_min
str x0,[x1]
add x5,x5,#1 // and increment indice
6:
mov x8,#1 // load name
mov x1,#command_end
madd x1,x5,x1,x7 // store name in table
str x4,[x1,#command_name_address]
7: // loop suppress spaces
ldrb w3,[x6,x2]
cmp w3,#0
beq 100f
cmp w3,#' '
cinc x2,x2,eq
beq 7b
add x4,x6,x2 // new start address
b 1b
100:
ldp x8,fp,[sp],16 // restaur des 2 registres
ldp x6,x7,[sp],16 // restaur des 2 registres
ldp x4,x5,[sp],16 // restaur des 2 registres
ldp x2,x3,[sp],16 // restaur des 2 registres
ldp x1,lr,[sp],16 // restaur des 2 registres
ret
/******************************************************************/
/* control load */
/******************************************************************/
/* x0 contains string table */
controlLoad:
stp x1,lr,[sp,-16]! // save registres
stp x2,x3,[sp,-16]! // save registres
stp x4,x5,[sp,-16]! // save registres
stp x6,x7,[sp,-16]! // save registres
mov x5,x0
mov x6,#0
mov x2,#command_end
1:
madd x3,x6,x2,x5 // compute item address
ldr x1,[x3,#command_name_address]
cbz x1,100f
ldr x0,qAdrszMessCtrlCom
bl strInsertAtCharInc
mov x4,x0
ldr x0,[x3,#command_min]
ldr x1,qAdrsZoneConv
bl conversion10 // call decimal conversion
mov x0,x4
ldr x1,qAdrsZoneConv // insert conversion in message
bl strInsertAtCharInc
bl affichageMess // display message
add x6,x6,#1
b 1b
100:
ldp x6,x7,[sp],16 // restaur des 2 registres
ldp x4,x5,[sp],16 // restaur des 2 registres
ldp x2,x3,[sp],16 // restaur des 2 registres
ldp x1,lr,[sp],16 // restaur des 2 registres
ret
qAdrszMessCtrlCom: .quad szMessCtrlCom
/********************************************************/
/* File Include fonctions */
/********************************************************/
/* for this file see task include a file in language AArch64 assembly */
.include "../includeARM64.inc"
Enter command (or <ctrl-c> to stop) : riG RIGHT Enter command (or <ctrl-c> to stop) : rePEAT REPEAT Enter command (or <ctrl-c> to stop) : copies *error* Enter command (or <ctrl-c> to stop) : put PUT Enter command (or <ctrl-c> to stop) : mo MOVE Enter command (or <ctrl-c> to stop) : rest RESTORE Enter command (or <ctrl-c> to stop) : types *error* Enter command (or <ctrl-c> to stop) : fup. *error* Enter command (or <ctrl-c> to stop) : 6 *error* Enter command (or <ctrl-c> to stop) : poweRin POWERINPUT Enter command (or <ctrl-c> to stop) : ^C pi@debian-buster-64:~/asm64/rosetta/asm9 $
Ada
with Ada.Characters.Handling;
with Ada.Containers.Vectors;
with Ada.Strings.Fixed;
with Ada.Strings.Maps.Constants;
with Ada.Strings.Unbounded;
with Ada.Text_IO;
procedure Abbreviations_Simple is
use Ada.Strings.Unbounded;
subtype Ustring is Unbounded_String;
type Word_Entry is record
Word : Ustring;
Min : Natural;
end record;
package Command_Vectors
is new Ada.Containers.Vectors (Index_Type => Positive,
Element_Type => Word_Entry);
Commands : Command_Vectors.Vector;
Last_Word : Ustring;
Last_Was_Word : Boolean := False;
procedure Append (Word_List : String) is
use Ada.Strings;
function Is_Word (Item : String) return Boolean
is (Fixed.Count (Item, Maps.Constants.Letter_Set) /= 0);
procedure Process (Token : String) is
begin
if Is_Word (Token) then
if Last_Was_Word then
Commands.Append ((Word => Last_Word,
Min => Length (Last_Word)));
end if;
Last_Word := To_Unbounded_String (Token);
Last_Was_Word := True;
else -- Token is expected to be decimal
Commands.Append ((Word => Last_Word,
Min => Natural'Value (Token)));
Last_Was_Word := False;
end if;
end Process;
Token_First : Positive := Word_List'First;
Token_Last : Natural;
begin
while Token_First in Word_List'Range loop
Fixed.Find_Token (Word_List, Maps.Constants.Alphanumeric_Set,
Token_First, Inside,
Token_First, Token_Last);
exit when Token_Last = 0;
Process (Word_List (Token_First .. Token_Last));
Token_First := Token_Last + 1;
end loop;
end Append;
function Match (Word : String) return String is
use Ada.Characters.Handling;
use Ada.Strings.Fixed;
Result : Ustring := To_Unbounded_String ("*error*");
Min : Natural := 0;
Upper_Word : constant String := To_Upper (Word);
begin
if Upper_Word = "" then
return "";
end if;
for Candidate of Commands loop
declare
Upper_Cand : constant String := To_Upper (To_String (Candidate.Word));
Length : constant Natural := Natural'Max (Candidate.Min,
Upper_Word'Length);
Upper_Abbrev_Cand : constant String := Head (Upper_Cand, Length);
Upper_Abbrev_Word : constant String := Head (Upper_Word, Length);
begin
if Upper_Word = Upper_Cand
and then Upper_Word'Length > Min
then
Result := To_Unbounded_String (Upper_Cand);
Min := Upper_Word'Length;
elsif Upper_Abbrev_Word = Upper_Abbrev_Cand
and then Upper_Abbrev_Word'Length > Min
then
Result := To_Unbounded_String (Upper_Cand);
Min := Upper_Abbrev_Word'Length;
end if;
end;
end loop;
return To_String (Result);
end Match;
procedure Put_Match (To : String) is
use Ada.Text_IO;
begin
Put ("Match to '"); Put (To);
Put ("' is '"); Put (Match (To));
Put_Line ("'");
end Put_Match;
procedure A (Item : String) renames Append;
begin
A ("add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3");
A ("compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate");
A ("3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2");
A ("forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load");
A ("locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2");
A ("msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3");
A ("refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left");
A ("2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1");
Put_Match ("riG");
Put_Match ("rePEAT");
Put_Match ("copies");
Put_Match ("put");
Put_Match ("mo");
Put_Match ("rest");
Put_Match ("types");
Put_Match ("fup.");
Put_Match ("6");
Put_Match ("poweRin");
Put_Match ("");
end Abbreviations_Simple;
- Output:
Match to 'riG' is 'RIGHT' Match to 'rePEAT' is 'REPEAT' Match to 'copies' is '*error*' Match to 'put' is 'PUT' Match to 'mo' is 'MOVE' Match to 'rest' is 'RESTORE' Match to 'types' is '*error*' Match to 'fup.' is '*error*' Match to '6' is '*error*' Match to 'poweRin' is 'POWERINPUT' Match to '' is ''
ALGOL 68
# "Simple" abbreviations #
# returns the next word from text, updating pos #
PRIO NEXTWORD = 1;
OP NEXTWORD = ( REF INT pos, STRING text )STRING:
BEGIN
# skip spaces #
WHILE IF pos > UPB text THEN FALSE ELSE text[ pos ] = " " FI DO pos +:= 1 OD;
# get the word #
STRING word := "";
WHILE IF pos > UPB text THEN FALSE ELSE text[ pos ] /= " " FI DO
word +:= text[ pos ];
pos +:= 1
OD;
word
END # NEXTWORD # ;
# returns text converted to upper case #
OP TOUPPER = ( STRING text )STRING:
BEGIN
STRING result := text;
FOR ch pos FROM LWB result TO UPB result DO
IF is lower( result[ ch pos ] ) THEN result[ ch pos ] := to upper( result[ ch pos ] ) FI
OD;
result
END # TOUPPER # ;
# returns text converted to an INT or -1 if text is not a number #
OP TOINT = ( STRING text )INT:
BEGIN
INT result := 0;
BOOL is numeric := TRUE;
FOR ch pos FROM UPB text BY -1 TO LWB text WHILE is numeric DO
CHAR c = text[ ch pos ];
is numeric := ( c >= "0" AND c <= "9" );
IF is numeric THEN ( result *:= 10 ) +:= ABS c - ABS "0" FI
OD;
IF is numeric THEN result ELSE -1 FI
END # TOINT # ;
# returns the length of word #
OP LENGTH = ( STRING word )INT: 1 + ( UPB word - LWB word );
# counts the number of commands in commands #
PROC count commands = ( STRING commands )INT:
BEGIN
INT result := 0;
INT pos := LWB commands;
WHILE STRING command := pos NEXTWORD commands; command /= "" DO
IF TOINT command < 0 THEN
# not an abbreviation length #
result +:= 1
FI
OD;
result
END # count commands # ;
# build a table of the commands and their minimum lengths #
# list of "commands" - the words are optionally followed by the minimum #
# length of abbreviation - if there isn't a number #
# the command can only appear in full #
STRING command list
= "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 "
+ "compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate "
+ "3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 "
+ "forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load "
+ "locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 "
+ "msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 "
+ "refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left "
+ "2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
;
PROC load commands = ( STRING commands )VOID:
BEGIN
INT cmd pos := 0;
INT pos := LWB command table;
WHILE STRING command := pos NEXTWORD commands; command /= "" DO
INT len := TOINT command;
IF len >= 0 THEN
# have an abbreviation length #
IF cmd pos > 0 THEN min abbreviation[ cmd pos ] := len FI
ELSE
# new command #
cmd pos +:= 1;
command table[ cmd pos ] := TOUPPER command;
min abbreviation[ cmd pos ] := LENGTH command
FI
OD
END # load commands # ;
# searches for word in command table and returns the full command #
# matching the possible abbreviation or *error* if there is no match #
OP EXPAND = ( STRING word )STRING:
IF word = ""
THEN # empty word #
""
ELSE # non-empty word #
INT word len = LENGTH word;
STRING upper word := TOUPPER word;
STRING result := "*error*";
FOR cmd pos FROM LWB command table TO UPB command table
WHILE STRING command := command table[ cmd pos ];
IF word len < min abbreviation[ cmd pos ] OR word len > LENGTH command
THEN # word is too short or too long - try the next command #
TRUE
ELIF upper word = command[ LWB command : ( LWB command - 1 ) + word len ]
THEN # found the command #
result := command;
FALSE
ELSE # word doexn't match - try the next command #
TRUE
FI
DO SKIP OD;
result
FI # EXPAND # ;
# tests the EXPAND operator #
PROC test expand = ( STRING words )VOID:
BEGIN
STRING results := "", separator := "";
INT pos := LWB words;
WHILE STRING word = pos NEXTWORD words; word /= "" DO
results +:= separator + EXPAND word;
separator := " "
OD;
print( ( "Input: ", words, newline ) );
print( ( "Output: ", results, newline ) )
END # test expand # ;
# build the command table #
[ 1 : count commands( command list ) ]STRING command table;
[ 1 : UPB command table ]INT min abbreviation;
load commands( commandlist );
# task test cases #
test expand( "riG rePEAT copies put mo rest types fup. 6 poweRin" )
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
ARM Assembly
Ok correction le 17/11/2020 16H
/* ARM assembly Raspberry PI */
/* program abbrSimple.s */
/* store list of command in a file */
/* and run the program abbrSimple command.file */
/* REMARK 1 : this program use routines in a include file
see task Include a file language arm assembly
for the routine affichageMess conversion10
see at end of this program the instruction include */
/* for constantes see task include a file in arm assembly */
/************************************/
/* Constantes */
/************************************/
.include "../constantes.inc"
.equ STDIN, 0 @ Linux input console
.equ STDOUT, 1 @ Linux output console
.equ EXIT, 1 @ Linux syscall
.equ READ, 3 @ Linux syscall
.equ WRITE, 4 @ Linux syscall
.equ OPEN, 5 @ Linux syscall
.equ CLOSE, 6 @ Linux syscall
.equ O_RDWR, 0x0002 @ open for reading and writing
.equ BUFFERSIZE, 1000
.equ NBMAXIELEMENTS, 100
/*******************************************/
/* Structures */
/********************************************/
/* command structure */
.struct 0
command_name_address: @ name
.struct command_name_address + 4
command_min: @ minimum letters
.struct command_min + 4
command_end:
/*********************************/
/* Initialized data */
/*********************************/
.data
szMessTitre: .asciz "Nom du fichier : "
szCarriageReturn: .asciz "\n"
szMessErreur: .asciz "Error detected.\n"
szMessErrBuffer: .asciz "buffer size too less !!"
szMessCtrlCom: .asciz "Command : @ minimum : @ \n"
szMessErrorAbr: .asciz "*error*"
szMessInput: .asciz "Enter command (or <ctrl-c> to stop) : "
/*********************************/
/* UnInitialized data */
/*********************************/
.bss
.align 4
sZoneConv: .skip 24
iAdrFicName: .skip 4
iTabAdrCmd: .skip command_end * NBMAXIELEMENTS
sBufferCmd: .skip BUFFERSIZE
sBuffer: .skip BUFFERSIZE
/*********************************/
/* code section */
/*********************************/
.text
.global main
main: @ INFO: main
mov r0,sp @ stack address for load parameter
bl traitFic @ read file and store value in array
cmp r0,#-1
beq 100f @ error ?
ldr r0,iAdriTabAdrCmd
bl controlLoad
1:
ldr r0,iAdrszMessInput @ display input message
bl affichageMess
mov r0,#STDIN @ Linux input console
ldr r1,iAdrsBuffer @ buffer address
mov r2,#BUFFERSIZE @ buffer size
mov r7, #READ @ request to read datas
svc 0 @ call system
sub r0,r0,#1
mov r2,#0
strb r2,[r1,r0] @ replace character 0xA by zéro final
cmp r0,#0 @ null string ?
moveq r0,r1
beq 2f
ldr r0,iAdrsBuffer
ldr r1,iAdriTabAdrCmd
bl controlCommand @ control text command
2:
mov r2,r0 @ display result
bl affichageMess
ldr r0,iAdrszCarriageReturn
bl affichageMess
b 1b @ loop
99:
ldr r0,iAdrszMessErrBuffer
bl affichageMess
100: @ standard end of the program
mov r0, #0 @ return code
mov r7, #EXIT @ request to exit program
svc #0 @ perform the system call
iAdrszCarriageReturn: .int szCarriageReturn
iAdrszMessErrBuffer: .int szMessErrBuffer
iAdrsZoneConv: .int sZoneConv
iAdrszMessInput: .int szMessInput
/******************************************************************/
/* control abbrevation command */
/******************************************************************/
/* r0 contains string input command */
/* r1 contains address table string command */
controlCommand: @ INFO: controlCommand
push {r1-r8,lr} @ save registers
mov r8,r0
mov r9,r1
mov r10,#command_end @ length item
bl computeLength @ length input command
mov r4,r0 @ save length input
mov r2,#0 @ indice
mov r3,#0 @ find counter
1:
mov r0,r8
mla r6,r2,r10,r9 @ compute address
ldr r1,[r6,#command_name_address] @ load a item
cmp r1,#0 @ end ?
beq 5f
bl comparStringSpe @
cmp r0,#0 @ no found other search
beq 4f
ldr r5,[r6,#command_min]
cmp r5,#0 @ minimum = zéro ?
ble 2f
cmp r4,r5 @ input < command minimum letters
blt 4f @ no correct
add r3,r3,#1 @ else increment counter
mov r7,r1 @ and save address command
b 4f
2:
mov r0,r1
bl computeLength @ length input command
cmp r4,r0 @ length command input = length command
bne 4f @ no correct
add r3,r3,#1 @ else increment counter
mov r7,r1 @ and save address command
4:
add r2,r2,#1 @ increment indice
b 1b @ and loop
5:
cmp r3,#1 @ no find or multiple find ?
bne 99f @ error
@ one find
mov r0,r7 @ length command table
bl computeLength
cmp r4,r0 @ length input > command ?
bgt 99f @ error
mov r4,#0x20 @ 5 bit to 1
mov r2,#0
6:
ldrb r3,[r7,r2]
cmp r3,#0
beq 7f
bic r3,r3,r4 @ convert to capital letter
strb r3,[r8,r2]
add r2,r2,#1
b 6b
7:
strb r3,[r8,r2]
mov r0,r8 @ return string input address
b 100f
99:
ldr r0,iAdrszMessErrorAbr @ return string "error"
100:
pop {r1-r8,lr} @ restaur registers
bx lr @return
iAdrszMessErreur: .int szMessErreur
iAdrszMessErrorAbr: .int szMessErrorAbr
/******************************************************************/
/* comparaison first letters String */
/******************************************************************/
/* r0 contains first String */
/* r1 contains second string */
/* r0 return 0 if not find else returns number letters OK */
comparStringSpe:
push {r1-r6,lr} @ save register
mov r2,#0
1:
ldrb r3,[r0,r2] @ input
orr r4,r3,#0x20 @ convert capital letter
ldrb r5,[r1,r2] @ table
orr r6,r5,#0x20 @ convert capital letter
cmp r4,r6
bne 2f
cmp r3,#0
beq 3f
add r2,r2,#1
b 1b
2:
cmp r3,#0 @ fist letters Ok
beq 3f
mov r0,#0 @ no ok
b 100f
3:
mov r0,r2
100:
pop {r1-r6,lr} @ restaur registers
bx lr @return
/******************************************************************/
/* compute length String */
/******************************************************************/
/* r0 contains String */
/* r0 return length */
computeLength: @ INFO: functionFN
push {r1-r2,lr} @ save register
mov r1,#0
1:
ldrb r2,[r0,r1]
cmp r2,#0 @ end ?
moveq r0,r1 @ return length in r0
beq 100f
add r1,r1,#1
b 1b
100:
pop {r1-r2,lr} @ restaur registers
bx lr @return
/******************************************************************/
/* read file */
/******************************************************************/
/* r0 contains address stack begin */
traitFic: @ INFO: traitFic
push {r1-r8,fp,lr} @ save registers
mov fp,r0 @ fp <- start address
ldr r4,[fp] @ number of Command line arguments
cmp r4,#1
movle r0,#-1
ble 99f
add r5,fp,#8 @ second parameter address
ldr r5,[r5]
ldr r0,iAdriAdrFicName
str r5,[r0]
ldr r0,iAdrszMessTitre
bl affichageMess @ display string
mov r0,r5
bl affichageMess
ldr r0,iAdrszCarriageReturn
bl affichageMess @ display carriage return
mov r0,r5 @ file name
mov r1,#O_RDWR @ flags
mov r2,#0 @ mode
mov r7, #OPEN @ call system OPEN
svc 0
cmp r0,#0 @ error ?
ble 99f
mov r8,r0 @ File Descriptor
ldr r1,iAdrsBufferCmd @ buffer address
mov r2,#BUFFERSIZE @ buffer size
mov r7,#READ @ read file
svc #0
cmp r0,#0 @ error ?
blt 99f
@ extraction datas
ldr r1,iAdrsBufferCmd @ buffer address
add r1,r0
mov r0,#0 @ store zéro final
strb r0,[r1]
ldr r0,iAdriTabAdrCmd @ key string command table
ldr r1,iAdrsBufferCmd @ buffer address
bl extracDatas
@ close file
mov r0,r8
mov r7, #CLOSE
svc 0
mov r0,#0
b 100f
99: @ error
ldr r1,iAdrszMessErreur @ error message
bl displayError
mov r0,#-1
100:
pop {r1-r8,fp,lr} @ restaur registers
bx lr @return
iAdriAdrFicName: .int iAdrFicName
iAdrszMessTitre: .int szMessTitre
iAdrsBuffer: .int sBuffer
iAdrsBufferCmd: .int sBufferCmd
iAdriTabAdrCmd: .int iTabAdrCmd
/******************************************************************/
/* extrac digit file buffer */
/******************************************************************/
/* r0 contains strings address */
/* r1 contains buffer address */
extracDatas: @ INFO: extracDatas
push {r1-r8,lr} @ save registers
mov r7,r0
mov r6,r1
mov r8,#0 @ top command name
mov r2,#0 @ string buffer indice
mov r4,r1 @ start string
mov r5,#0 @ string index
1:
ldrb r3,[r6,r2]
cmp r3,#0
beq 4f @ end
cmp r3,#0xA
beq 2f
cmp r3,#' ' @ end string
beq 3f
add r2,#1
b 1b
2:
mov r3,#0
strb r3,[r6,r2]
ldrb r3,[r6,r2]
cmp r3,#0xD
addeq r2,#2
addne r2,#1
b 4f
3:
mov r3,#0
strb r3,[r6,r2]
add r2,#1
4:
mov r0,r4
ldrb r1,[r0] @ load first byte
cmp r1,#'0' @ it is à digit ?
blt 5f
cmp r1,#'9'
bgt 5f
mov r0,r1
mov r1,#command_end
mla r1,r5,r1,r7 @ compute address to store
mov r0,r4
bl conversionAtoD @ conversion ascii digit
str r0,[r1,#command_min] @ and store in minimum
mov r8,#0 @ line command ok
add r5,#1 @ increment indice
b 7f
5:
cmp r8,#0 @ other name ?
beq 6f
mov r0,#0 @ yes store zéro in minimum in prec
mov r1,#command_end
mla r1,r5,r1,r7
add r1,r1,#command_min
str r0,[r1]
add r5,#1 @ and increment indice
6:
mov r8,#1 @ load name
mov r1,#command_end
mla r1,r5,r1,r7 @ store name in table
str r4,[r1,#command_name_address]
7: @ loop suppress spaces
ldrb r3,[r6,r2]
cmp r3,#0
beq 100f
cmp r3,#' '
addeq r2,r2,#1
beq 7b
add r4,r6,r2 @ new start address
b 1b
100:
pop {r1-r8,lr} @ restaur registers
bx lr @return
/******************************************************************/
/* control load */
/******************************************************************/
/* r0 contains string table */
controlLoad:
push {r1-r6,lr} @ save registers
mov r5,r0
mov r6,#0
mov r2,#command_end
1:
mla r3,r6,r2,r5 @ compute item address
ldr r1,[r3,#command_name_address]
cmp r1,#0
beq 100f
ldr r0,iAdrszMessCtrlCom
bl strInsertAtCharInc
mov r4,r0
ldr r0,[r3,#command_min]
ldr r1,iAdrsZoneConv
bl conversion10 @ call decimal conversion
mov r0,r4
ldr r1,iAdrsZoneConv @ insert conversion in message
bl strInsertAtCharInc
bl affichageMess @ display message
add r6,r6,#1
b 1b
100:
pop {r1-r6,lr} @ restaur registers
bx lr @return
iAdrszMessCtrlCom: .int szMessCtrlCom
/***************************************************/
/* ROUTINES INCLUDE */
/***************************************************/
.include "../affichage.inc"
Enter command (or <ctrl-c> to stop) : riG RIGHT Enter command (or <ctrl-c> to stop) : rePEAT REPEAT Enter command (or <ctrl-c> to stop) : copies *error* Enter command (or <ctrl-c> to stop) : put PUT Enter command (or <ctrl-c> to stop) : mo MOVE Enter command (or <ctrl-c> to stop) : rest RESTORE Enter command (or <ctrl-c> to stop) : types *error* Enter command (or <ctrl-c> to stop) : fup. *error* Enter command (or <ctrl-c> to stop) : 6 *error* Enter command (or <ctrl-c> to stop) : poweRin POWERINPUT Enter command (or <ctrl-c> to stop) : ^C
AutoHotkey
table := "
(
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
)"
MsgBox % result := Abbreviations("riG rePEAT copies put mo rest types fup. 6 poweRin", table)
return
Abbreviations(str, table){
Words := [], Expanded := []
while pos := RegExMatch(table, "i)([a-z]+)(?:\s+(\d+))?", m, A_Index=1?1:pos+StrLen(m))
Words[m1] := m2?m2:StrLen(m1)
for i, abb in StrSplit(RegExReplace(str, " +", " "), " ")
{
for word, count in Words
if (word ~= "i)^" abb) && (StrLen(abb) >= count)
{
StringUpper, word, word
result .= word " "
Expanded[abb] := true
break
}
if !Expanded[abb]
result .= "*error* "
}
return Trim(result, " ")
}
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
C
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* command_table =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 "
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate "
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 "
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load "
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 "
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 "
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left "
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1";
typedef struct command_tag {
char* cmd;
size_t length;
size_t min_len;
struct command_tag* next;
} command_t;
// str is assumed to be all uppercase
bool command_match(const command_t* command, const char* str) {
size_t olen = strlen(str);
return olen >= command->min_len && olen <= command->length
&& strncmp(str, command->cmd, olen) == 0;
}
// convert string to uppercase
char* uppercase(char* str, size_t n) {
for (size_t i = 0; i < n; ++i)
str[i] = toupper((unsigned char)str[i]);
return str;
}
void fatal(const char* message) {
fprintf(stderr, "%s\n", message);
exit(1);
}
void* xmalloc(size_t n) {
void* ptr = malloc(n);
if (ptr == NULL)
fatal("Out of memory");
return ptr;
}
void* xrealloc(void* p, size_t n) {
void* ptr = realloc(p, n);
if (ptr == NULL)
fatal("Out of memory");
return ptr;
}
char** split_into_words(const char* str, size_t* count) {
size_t size = 0;
size_t capacity = 16;
char** words = xmalloc(capacity * sizeof(char*));
size_t len = strlen(str);
for (size_t begin = 0; begin < len; ) {
size_t i = begin;
for (; i < len && isspace((unsigned char)str[i]); ++i) {}
begin = i;
for (; i < len && !isspace((unsigned char)str[i]); ++i) {}
size_t word_len = i - begin;
if (word_len == 0)
break;
char* word = xmalloc(word_len + 1);
memcpy(word, str + begin, word_len);
word[word_len] = 0;
begin += word_len;
if (capacity == size) {
capacity *= 2;
words = xrealloc(words, capacity * sizeof(char*));
}
words[size++] = word;
}
*count = size;
return words;
}
command_t* make_command_list(const char* table) {
command_t* cmd = NULL;
size_t count = 0;
char** words = split_into_words(table, &count);
for (size_t i = 0; i < count; ++i) {
char* word = words[i];
command_t* new_cmd = xmalloc(sizeof(command_t));
size_t word_len = strlen(word);
new_cmd->length = word_len;
new_cmd->min_len = word_len;
new_cmd->cmd = uppercase(word, word_len);
if (i + 1 < count) {
char* eptr = 0;
unsigned long min_len = strtoul(words[i + 1], &eptr, 10);
if (min_len > 0 && *eptr == 0) {
free(words[i + 1]);
new_cmd->min_len = min_len;
++i;
}
}
new_cmd->next = cmd;
cmd = new_cmd;
}
free(words);
return cmd;
}
void free_command_list(command_t* cmd) {
while (cmd != NULL) {
command_t* next = cmd->next;
free(cmd->cmd);
free(cmd);
cmd = next;
}
}
const command_t* find_command(const command_t* commands, const char* word) {
for (const command_t* cmd = commands; cmd != NULL; cmd = cmd->next) {
if (command_match(cmd, word))
return cmd;
}
return NULL;
}
void test(const command_t* commands, const char* input) {
printf(" input: %s\n", input);
printf("output:");
size_t count = 0;
char** words = split_into_words(input, &count);
for (size_t i = 0; i < count; ++i) {
char* word = words[i];
uppercase(word, strlen(word));
const command_t* cmd_ptr = find_command(commands, word);
printf(" %s", cmd_ptr ? cmd_ptr->cmd : "*error*");
free(word);
}
free(words);
printf("\n");
}
int main() {
command_t* commands = make_command_list(command_table);
const char* input = "riG rePEAT copies put mo rest types fup. 6 poweRin";
test(commands, input);
free_command_list(commands);
return 0;
}
- Output:
input: riG rePEAT copies put mo rest types fup. 6 poweRin output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
C++
#include <algorithm>
#include <cctype>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
const char* command_table =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 "
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate "
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 "
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load "
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 "
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 "
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left "
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1";
class command {
public:
command(const std::string&, size_t);
const std::string& cmd() const { return cmd_; }
size_t min_length() const { return min_len_; }
bool match(const std::string&) const;
private:
std::string cmd_;
size_t min_len_;
};
// cmd is assumed to be all uppercase
command::command(const std::string& cmd, size_t min_len)
: cmd_(cmd), min_len_(min_len) {}
// str is assumed to be all uppercase
bool command::match(const std::string& str) const {
size_t olen = str.length();
return olen >= min_len_ && olen <= cmd_.length()
&& cmd_.compare(0, olen, str) == 0;
}
bool parse_integer(const std::string& word, int& value) {
try {
size_t pos;
int i = std::stoi(word, &pos, 10);
if (pos < word.length())
return false;
value = i;
return true;
} catch (const std::exception& ex) {
return false;
}
}
// convert string to uppercase
void uppercase(std::string& str) {
std::transform(str.begin(), str.end(), str.begin(),
[](unsigned char c) -> unsigned char { return std::toupper(c); });
}
class command_list {
public:
explicit command_list(const char*);
const command* find_command(const std::string&) const;
private:
std::vector<command> commands_;
};
command_list::command_list(const char* table) {
std::istringstream is(table);
std::string word;
std::vector<std::string> words;
while (is >> word) {
uppercase(word);
words.push_back(word);
}
for (size_t i = 0, n = words.size(); i < n; ++i) {
word = words[i];
// if there's an integer following this word, it specifies the minimum
// length for the command, otherwise the minimum length is the length
// of the command string
int len = word.length();
if (i + 1 < n && parse_integer(words[i + 1], len))
++i;
commands_.push_back(command(word, len));
}
}
const command* command_list::find_command(const std::string& word) const {
auto iter = std::find_if(commands_.begin(), commands_.end(),
[&word](const command& cmd) { return cmd.match(word); });
return (iter != commands_.end()) ? &*iter : nullptr;
}
std::string test(const command_list& commands, const std::string& input) {
std::string output;
std::istringstream is(input);
std::string word;
while (is >> word) {
if (!output.empty())
output += ' ';
uppercase(word);
const command* cmd_ptr = commands.find_command(word);
if (cmd_ptr)
output += cmd_ptr->cmd();
else
output += "*error*";
}
return output;
}
int main() {
command_list commands(command_table);
std::string input("riG rePEAT copies put mo rest types fup. 6 poweRin");
std::string output(test(commands, input));
std::cout << " input: " << input << '\n';
std::cout << "output: " << output << '\n';
return 0;
}
- Output:
input: riG rePEAT copies put mo rest types fup. 6 poweRin output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Clojure
NOTE: Type Hints have been added here to indicate the types of function arguments and to make the program faster. They are not strictly necessary, since Clojure is a dynamically typed language (i.e., variable types are determined at runtime).
(defn words
"Split string into words"
[^String str]
(.split (.stripLeading str) "\\s+"))
(defn join-words
"Join words into a single string"
^String [strings]
(String/join " " strings))
;; SOURCE: https://www.stackoverflow.com/a/38947571/12947681
(defn starts-with-ignore-case
"Does string start with prefix (ignoring case)?"
^Boolean [^String string, ^String prefix]
(.regionMatches string true 0 prefix 0 (count prefix)))
(defrecord CommandWord [^String word, ^long min-abbr-size])
(defn parse-cmd-table
"Parse list of strings in command table into list of words and numbers
If number is missing for any word, then the word is not included"
([cmd-table]
(parse-cmd-table cmd-table 0 []))
([cmd-table i ans]
(let [cmd-count (count cmd-table)]
(if (= i cmd-count)
ans
(let [word (nth cmd-table i),
[i num] (try [(+ i 2)
(Integer/parseInt ^String (nth cmd-table (inc i)))]
(catch NumberFormatException _
[(inc i) 0]))]
(recur cmd-table i (conj ans (CommandWord. word num))))))))
;; cmd-table is a list of objects of type CommandWord (defined above)
(def cmd-table
(->
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
words
parse-cmd-table))
(defn abbr?
"Is abbr a valid abbreviation of this command?"
^Boolean [^String abbr, ^CommandWord cmd]
(let [{:keys [word min-abbr-size]} cmd]
(and (<= min-abbr-size (count abbr) (count word))
(starts-with-ignore-case word abbr))))
(defn solution
"Find word matching each abbreviation in input (or *error* if not found),
and join results into a string"
^String [^String str]
(join-words (for [abbr (words str)]
(if-let [{:keys [word]} (first (filter #(abbr? abbr %) cmd-table))]
(.toUpperCase ^String word)
"*error*"))))
;; Print solution for given test case
(println (solution "riG rePEAT copies put mo rest types fup. 6 poweRin"))
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Crystal
COMMAND_TABLE =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
def parse_command_table(input : String)
cmds = {} of String => String
table = input.strip.split
word = table[0].upcase
table.each do |item|
if /[0-9]+/.match(item)
abbreviation_length = item.to_i
(0..word.size-abbreviation_length).each do |i|
cmds[word[(0..abbreviation_length-1+i)]] = word
end
else
word = item.upcase
cmds[word] = word
end
end
return cmds
end
def parse_user_input(input : String?, commands)
output = ""
unless input.nil?
user_commands = input.strip.split
user_commands.each do |command|
command = command.upcase
if commands.has_key?(command)
output += commands[command]
else
output += "*error*"
end
output += " "
end
end
return output
end
cmds = parse_command_table(COMMAND_TABLE)
puts "Input:"
user_input = gets
puts "Output:"
puts parse_user_input(user_input, cmds)
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
D
class Abbreviations
{
import std.array: split, join;
import std.uni:toUpper;
string[string] replaces;
this(string table)
{
import std.ascii;
import std.conv:parse;
string saved;
auto add = (string word){ if(word.length) replaces[word] = word; };
foreach(word; table.toUpper.split)
if(isDigit(word[0]))
{
for(int length=parse!int(word); length<=saved.length; ++length)
replaces[saved[0..length]] = saved;
} else
{
add(saved);
saved = word;
}
add(saved);
}
string expand(string input) // pre-filled hashtable is used
{
import std.algorithm: map;
return input.toUpper.split.map!(word => word in replaces ? replaces[word] : "*error*").join(" ");
}
}
string table = "
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1";
string input = "riG rePEAT copies put mo rest types fup. 6 poweRin";
string expected = "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT";
unittest // 'dmd -unittest ...' to activate it
{
auto expander = new Abbreviations(table);
assert(expander.expand(input) == expected);
assert(expander.expand("") == "");
assert(expander.expand("addadd") == "*error*");
}
void main()
{
import std.stdio:writeln;
writeln("Input : ", input);
writeln("Output: ", new Abbreviations(table).expand(input));
}
- Output:
Input : riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Delphi
program Abraviation_simple;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
TCommand = record
value: string;
len: integer;
end;
function ReadTable(table: string): TArray<TCommand>;
begin
var fields := table.Split([' '], TStringSplitOptions.ExcludeEmpty);
var i := 0;
var max := Length(fields);
while i < max do
begin
var cmd := fields[i];
var cmdLen := cmd.Length;
inc(i);
if i < max then
begin
var num: Integer;
if TryStrToInt(fields[i], num) and (1 <= num) and (num < cmdLen) then
begin
cmdLen := num;
inc(i);
end;
end;
SetLength(result, Length(result) + 1);
with result[High(result)] do
begin
value := cmd;
len := cmdLen;
end;
end;
end;
function ValidateCommands(Commands: TArray<TCommand>; Words: TArray<string>):
TArray<string>;
begin
SetLength(result, 0);
for var wd in Words do
begin
var matchFound := false;
var wLen := wd.Length;
for var i := 0 to High(Commands) do
begin
var command := Commands[i];
if (command.len = 0) or (wLen < command.len) or (wLen > command.value.Length) then
Continue;
var c := command.value.ToUpper;
var w := wd.ToUpper;
if c.StartsWith(w) then
begin
SetLength(result, Length(result) + 1);
result[High(result)] := c;
matchFound := true;
Break;
end;
end;
if not matchFound then
begin
SetLength(result, Length(result) + 1);
result[High(result)] := '*error*';
end;
end;
end;
procedure PrintResults(words, results: TArray<string>);
begin
Writeln('user words:');
for var w in words do
write(^I, w);
Writeln(#10, 'full words:'^I, string.join(^I, results));
end;
const
table = '' +
'add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 ' +
'compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate ' +
'3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 ' +
'forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load ' +
'locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 ' +
'msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 ' +
'refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left ' +
'2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 ';
const
SENTENCE = 'riG rePEAT copies put mo rest types fup. 6 poweRin';
begin
var Commands := ReadTable(table);
var Words := SENTENCE.Split([' '], TStringSplitOptions.ExcludeEmpty);
var results := ValidateCommands(Commands, Words);
PrintResults(Words, results);
Readln;
end.
Factor
USING: arrays assocs combinators formatting fry grouping.extras
kernel literals math math.parser multiline sequences
splitting.extras unicode ;
IN: rosetta-code.abbr-simple
CONSTANT: input $[
"riG rePEAT copies put mo rest types fup. 6 "
"poweRin" append
]
<< ! Make the following two words available at parse time.
: abbr-pair ( first second -- seq/f )
{
{ [ 2dup drop [ digit? ] all? ] [ 2drop f ] }
{
[ 2dup nip [ Letter? ] all? ]
[ drop >upper 0 2array ]
}
[ [ >upper ] [ string>number ] bi* 2array ]
} cond ;
: parse-commands ( seq -- seq )
" \n" split-harvest [ abbr-pair ] 2clump-map sift ;
>>
CONSTANT: commands $[
HEREDOC: END
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange
Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3
delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract
3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward
2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2
spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix
2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse
preserve 4 purge 3 put putD query 1 quit read recover 3 refresh
renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT
right 2 left 2 save set shift 2 si sort sos stack 3 status
4 top transfer 3 type 1 up 1
END
parse-commands
]
: valid-abbrevs ( assoc seq -- assoc )
dup '[ [ _ head? ] [ _ length <= ] bi* and ] assoc-filter ;
: find-command ( seq -- seq )
>upper [ commands ] dip valid-abbrevs
[ "*error*" ] [ first first ] if-empty ;
: (find-commands) ( seq -- seq )
" " split-harvest [ find-command ] map " " join ;
: find-commands ( seq -- seq )
dup empty? not [ (find-commands) ] when ;
: show-commands ( seq -- )
dup find-commands " Input: \"%s\"\nOutput: \"%s\"\n" printf
;
: main ( -- ) input "" [ show-commands ] bi@ ;
MAIN: main
- Output:
Input: "riG rePEAT copies put mo rest types fup. 6 poweRin" Output: "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT" Input: "" Output: ""
Forth
Works with any ANS Forth
Needs the FMS-SI (single inheritance) library code located here: http://soton.mpeforth.com/flag/fms/index.html
include FMS-SI.f
include FMS-SILib.f
${ add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 }
value list ' upper: list map:
: compare' { adr len $obj -- f }
len $obj size: > if false exit then
adr len $obj @: drop len compare 0= ;
: <= ( n1 n2 = f) 1+ swap > ;
: abbrev 0 0 { adr len obj1 obj2 -- }
list uneach:
list each: drop to obj1
begin
list each:
while
to obj2
obj2 @: >integer
if \ word followed by a number
len <= if adr len obj1 compare'
if obj1 p: exit then
then list each: if to obj1 else ." *error* " exit then
else \ word not followed by a number
adr len obj1 @: compare 0=
if obj1 p: exit then
obj2 to obj1
then
repeat ;
${ riG rePEAT copies put mo rest types fup. 6 poweRin }
value input-list ' upper: input-list map:
: valid-input ( adr len -- f)
over + swap do i c@ isalpha 0= if ." *error* " unloop false exit then loop true ;
: run
begin
input-list each:
while
dup @: valid-input
if @: abbrev space else drop then
repeat ;
run RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT ok
FreeBASIC
Function Token(cad As String, tokens() As String) As Integer
Dim As Integer n = 0
Dim As String temp = ""
Dim As Integer i
For i = 1 To Len(cad)
If Mid(cad, i, 1) <> " " Then
temp &= Mid(cad, i, 1)
Elseif temp <> "" Then
n += 1
Redim Preserve tokens(1 To n)
tokens(n) = temp
temp = ""
End If
Next
If temp <> "" Then
n += 1
Redim Preserve tokens(1 To n)
tokens(n) = temp
End If
Return n
End Function
Dim As String c = "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3" & _
" compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate" & _
" 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2" & _
" forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load" & _
" locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2" & _
" msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3" & _
" refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left" & _
" 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
c = Ucase(c)
Dim As String chunk()
Dim As Integer i, j, n, v
n = Token(c, chunk())
Dim As Integer lonc(1 To n)
For i = 1 To n - 1
v = Val(chunk(i + 1))
If v Then
lonc(i) = v
chunk(i + 1) = ""
Else
lonc(i) = Len(chunk(i))
End If
Next
Dim As String test = "riG rePEAT copies put mo rest types fup. 6 poweRin"
test = Ucase(test)
Dim As String w()
Dim As Integer x = Token(test, w())
Dim As Integer lonw(1 To x)
For i = 1 To x
lonw(i) = Len(w(i))
Next
Dim As String r = ""
For j = 1 To x
Dim As String p = ""
For i = 1 To n
Dim As String lc = Left(chunk(i), lonw(j))
Dim As String lw = w(j)
If (lw = lc) And (lonc(i) <= lonw(j)) Then
p = chunk(i)
Exit For
End If
Next
If p = "" Then p = "*error*"
r &= " " & p
Next
Print r
Sleep
- Output:
Similar to Yabasic entry.
FutureBasic
_window = 1
begin enum 1
_userStringFld
_validateBtn
_resultsStringFld
end enum
void local fn BuildWindow
window _window, @"Abbreviations, simple", (0,0,600,268)
WindowSetContentMinSize( _window, fn CGSizeMake( 200, 268 ) )
WindowSetContentMaxSize( _window, fn CGSizeMake( 10000, 268 ) )
textfield _userStringFld,, @"riG rePEAT copies put mo rest types fup. 6 poweRin", (20,152,560,96)
TextFieldSetPlaceholderString( _userStringFld, @"Enter commands" )
ViewSetAutoresizingMask( _userStringFld, NSViewWidthSizable )
button _validateBtn,,, @"Validate", (259,117,83,32)
ViewSetAutoresizingMask( _validateBtn, NSViewMinXMargin + NSViewMaxXMargin )
textfield _resultsStringFld,,, (20,20,560,96)
TextFieldSetEditable( _resultsStringFld, NO )
TextFieldSetSelectable( _resultsStringFld, YES )
ViewSetAutoresizingMask( _resultsStringFld, NSViewWidthSizable )
end fn
local fn Commands as CFArrayRef
CFStringRef cmd, string
long abbrLen
CFMutableArrayRef commands = fn MutableArrayNew
ScannerRef scanner
string = @" add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3"
string = concat( string, @" compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate" )
string = concat( string, @" 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2" )
string = concat( string, @" forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load" )
string = concat( string, @" locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2" )
string = concat( string, @" msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3" )
string = concat( string, @" refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left" )
string = concat( string, @" 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1" )
scanner = fn ScannerWithString( string )
while ( fn ScannerIsAtEnd( scanner ) == NO )
if ( fn ScannerScanUpToCharactersFromSet( scanner, fn CharacterSetWhitespaceAndNewlineSet, @cmd ) )
abbrLen = 0
fn ScannerScanInteger( scanner, @abbrLen )
MutableArrayAddObject( commands, @{@"cmd":cmd,@"len":@(abbrLen)} )
end if
wend
end fn = commands
void local fn Validate
CFArrayRef commands, words
CFStringRef userString, result, wd, cmd
long wordCount, i, wordLen, abbrLen
CFMutableStringRef results
CFDictionaryRef dict
BOOL found
commands = fn Commands
userString = fn ControlStringValue( _userStringFld )
words = fn StringComponentsSeparatedByCharactersInSet( userString, fn CharacterSetWhitespaceAndNewlineSet )
results = fn MutableStringNew
wordCount = len( words )
for i = 0 to wordCount - 1
found = NO
result = @"*error* "
wd = words[i]
wordLen = len( wd )
if ( wordLen )
for dict in commands
cmd = dict[@"cmd"]
abbrLen = fn NumberIntegerValue(dict[@"len"])
if ( abbrLen != 0 and wordLen >= abbrLen )
found = fn StringHasPrefix( lcase( cmd ), lcase( wd ) )
else
found = fn StringIsEqual( lcase( cmd ), lcase( wd ) )
end if
if ( found )
result = fn StringWithFormat( @"%@ ",ucase( cmd ) )
break
end if
next
MutableStringAppendString( results, result )
end if
next
ControlSetStringValue( _resultsStringFld, results )
end fn
void local fn DoDialog( ev as long, tag as long )
select ( ev )
case _btnClick : fn Validate
end select
end fn
editmenu 1
fn BuildWindow
on dialog fn DoDialog
HandleEvents
Go
package main
import (
"io"
"os"
"strconv"
"strings"
"text/tabwriter"
)
func readTable(table string) ([]string, []int) {
fields := strings.Fields(table)
var commands []string
var minLens []int
for i, max := 0, len(fields); i < max; {
cmd := fields[i]
cmdLen := len(cmd)
i++
if i < max {
num, err := strconv.Atoi(fields[i])
if err == nil && 1 <= num && num < cmdLen {
cmdLen = num
i++
}
}
commands = append(commands, cmd)
minLens = append(minLens, cmdLen)
}
return commands, minLens
}
func validateCommands(commands []string, minLens []int, words []string) []string {
var results []string
for _, word := range words {
matchFound := false
wlen := len(word)
for i, command := range commands {
if minLens[i] == 0 || wlen < minLens[i] || wlen > len(command) {
continue
}
c := strings.ToUpper(command)
w := strings.ToUpper(word)
if strings.HasPrefix(c, w) {
results = append(results, c)
matchFound = true
break
}
}
if !matchFound {
results = append(results, "*error*")
}
}
return results
}
func printResults(words []string, results []string) {
wr := tabwriter.NewWriter(os.Stdout, 0, 1, 1, ' ', 0)
io.WriteString(wr, "user words:")
for _, word := range words {
io.WriteString(wr, "\t"+word)
}
io.WriteString(wr, "\n")
io.WriteString(wr, "full words:\t"+strings.Join(results, "\t")+"\n")
wr.Flush()
}
func main() {
const table = "" +
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " +
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " +
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " +
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " +
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " +
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " +
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " +
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
const sentence = "riG rePEAT copies put mo rest types fup. 6 poweRin"
commands, minLens := readTable(table)
words := strings.Fields(sentence)
results := validateCommands(commands, minLens, words)
printResults(words, results)
}
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Haskell
import Data.List (find, isPrefixOf)
import Data.Char (isDigit, toUpper)
import Data.Maybe (maybe)
withExpansions :: [(String, Int)] -> String -> String
withExpansions tbl s = unwords $ expanded tbl <$> words s
expanded :: [(String, Int)] -> String -> String
expanded tbl k = maybe "*error" fst (expand k)
where
expand [] = Just ([], 0)
expand s =
let u = toUpper <$> s
lng = length s
in find (\(w, n) -> lng >= n && isPrefixOf u w) tbl
cmdsFromString :: String -> [(String, Int)]
cmdsFromString s =
let go w@(x:_) (xs, n)
| isDigit x = (xs, read w :: Int)
| otherwise = ((toUpper <$> w, n) : xs, 0)
in fst $ foldr go ([], 0) (words s)
-- TESTS --------------------------------------------------
table :: [(String, Int)]
table =
cmdsFromString
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 \
\Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 \
\cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 \
\extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 \
\forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 \
\split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 \
\Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 \
\parse preserve 4 purge 3 put putD query 1 quit read recover 3 \
\refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 \
\rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 \
\status 4 top transfer 3 type 1 up 1"
main :: IO ()
main = do
let unAbbrev = withExpansions table
print $
unAbbrev
"riG rePEAT copies put mo rest types fup. 6 poweRin"
print $ unAbbrev ""
- Output:
"RIGHT REPEAT *error PUT MOVE RESTORE *error *error *error POWERINPUT" ""
J
ctable=:|:(({.;~{:@(_,".)@;@}.);.1~ _2<nc) cut {{)n
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
}} -.LF
findmatch=: {{
lens=. >{.ctable
for_ndx. I.y-:"1&tolower (#y){.&> list do. cmd=. ;(<1,ndx){ctable
if. (#y) >: ndx{lens do. toupper cmd return. end.
if. y -: cmd do. toupper cmd return. end.
end.
'*error*'
}}L:0
Task:
;:inv findmatch cut ' riG rePEAT copies put mo rest types fup. 6 poweRin'
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Java
import java.util.*;
public class Abbreviations {
public static void main(String[] args) {
CommandList commands = new CommandList(commandTable);
String input = "riG rePEAT copies put mo rest types fup. 6 poweRin";
System.out.println(" input: " + input);
System.out.println("output: " + test(commands, input));
}
private static String test(CommandList commands, String input) {
StringBuilder output = new StringBuilder();
Scanner scanner = new Scanner(input);
while (scanner.hasNext()) {
String word = scanner.next();
if (output.length() > 0)
output.append(' ');
Command cmd = commands.findCommand(word);
if (cmd != null)
output.append(cmd.cmd);
else
output.append("*error*");
}
return output.toString();
}
private static String commandTable =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " +
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " +
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " +
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " +
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " +
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " +
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " +
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1";
private static class Command {
private Command(String cmd, int minLength) {
this.cmd = cmd;
this.minLength = minLength;
}
private boolean match(String str) {
int olen = str.length();
return olen >= minLength && olen <= cmd.length()
&& cmd.regionMatches(true, 0, str, 0, olen);
}
private String cmd;
private int minLength;
}
private static Integer parseInteger(String word) {
try {
return Integer.valueOf(word);
} catch (NumberFormatException ex) {
return null;
}
}
private static class CommandList {
private CommandList(String table) {
Scanner scanner = new Scanner(table);
List<String> words = new ArrayList<>();
while (scanner.hasNext()) {
String word = scanner.next();
words.add(word.toUpperCase());
}
for (int i = 0, n = words.size(); i < n; ++i) {
String word = words.get(i);
// if there's an integer following this word, it specifies the minimum
// length for the command, otherwise the minimum length is the length
// of the command string
int len = word.length();
if (i + 1 < n) {
Integer number = parseInteger(words.get(i + 1));
if (number != null) {
len = number.intValue();
++i;
}
}
commands.add(new Command(word, len));
}
}
private Command findCommand(String word) {
for (Command command : commands) {
if (command.match(word))
return command;
}
return null;
}
private List<Command> commands = new ArrayList<>();
}
}
- Output:
input: riG rePEAT copies put mo rest types fup. 6 poweRin output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
JavaScript
(() => {
'use strict';
// withExpansions :: [(String, Int)] -> String -> String
const withExpansions = tbl => s =>
unwords(map(expanded(tbl), words(s)));
// expanded :: [(String, Int)] -> String -> String
const expanded = tbl => s => {
const
lng = s.length,
u = toUpper(s),
p = wn => {
const [w, n] = Array.from(wn);
return lng >= n && isPrefixOf(u, w);
}
return maybe(
'*error*',
fst,
0 < lng ? (
find(p, tbl)
) : Just(Tuple([], 0))
);
};
// cmdsFromString :: String -> [(String, Int)]
const cmdsFromString = s =>
fst(foldr(
(w, a) => {
const [xs, n] = Array.from(a);
return isDigit(head(w)) ? (
Tuple(xs, parseInt(w, 10))
) : Tuple(
[Tuple(toUpper(w), n)].concat(xs),
0
);
},
Tuple([], 0),
words(s)
));
// TEST -----------------------------------------------
const main = () => {
// table :: [(String, Int)]
const table = cmdsFromString(
`add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1
Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3
cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3
extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1
split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3
Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1
parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4
rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3
status 4 top transfer 3 type 1 up 1`
);
return fTable(
'Abbreviation tests:\n',
s => "'" + s + "'",
s => "\n\t'" + s + "'",
withExpansions(table),
[
'riG rePEAT copies put mo rest types fup. 6 poweRin',
''
]
);
};
// GENERIC FUNCTIONS ----------------------------------
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (f, g) => x => f(g(x));
// find :: (a -> Bool) -> [a] -> Maybe a
const find = (p, xs) => {
for (let i = 0, lng = xs.length; i < lng; i++) {
if (p(xs[i])) return Just(xs[i]);
}
return Nothing();
};
// flip :: (a -> b -> c) -> b -> a -> c
const flip = f =>
1 < f.length ? (
(a, b) => f(b, a)
) : (x => y => f(y)(x));
// foldl1 :: (a -> a -> a) -> [a] -> a
const foldl1 = (f, xs) =>
1 < xs.length ? xs.slice(1)
.reduce(f, xs[0]) : xs[0];
// foldr :: (a -> b -> b) -> b -> [a] -> b
const foldr = (f, a, xs) => xs.reduceRight(flip(f), a);
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
// fTable :: String -> (a -> String) ->
// (b -> String) -> (a -> b) -> [a] -> String
const fTable = (s, xShow, fxShow, f, xs) => {
// Heading -> x display function ->
// fx display function ->
// f -> values -> tabular string
const
ys = map(xShow, xs),
w = maximum(map(length, ys)),
rows = zipWith(
(a, b) => justifyRight(w, ' ', a) + ' -> ' + b,
ys,
map(compose(fxShow, f), xs)
);
return s + '\n' + unlines(rows);
};
// head :: [a] -> a
const head = xs => xs.length ? xs[0] : undefined;
// isDigit :: Char -> Bool
const isDigit = c => {
const n = ord(c);
return 48 <= n && 57 >= n;
};
// isPrefixOf takes two lists or strings and returns
// true iff the first is a prefix of the second.
// isPrefixOf :: [a] -> [a] -> Bool
// isPrefixOf :: String -> String -> Bool
const isPrefixOf = (xs, ys) => {
const go = (xs, ys) => {
const intX = xs.length;
return 0 < intX ? (
ys.length >= intX ? xs[0] === ys[0] && go(
xs.slice(1), ys.slice(1)
) : false
) : true;
};
return 'string' !== typeof xs ? (
go(xs, ys)
) : ys.startsWith(xs);
};
// justifyRight :: Int -> Char -> String -> String
const justifyRight = (n, cFiller, s) =>
n > s.length ? (
s.padStart(n, cFiller)
) : s;
// Returns Infinity over objects without finite length.
// This enables zip and zipWith to choose the shorter
// argument when one is non-finite, like cycle, repeat etc
// length :: [a] -> Int
const length = xs =>
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) =>
(Array.isArray(xs) ? (
xs
) : xs.split('')).map(f);
// maximum :: Ord a => [a] -> a
const maximum = xs =>
0 < xs.length ? (
foldl1((a, x) => x > a ? x : a, xs)
) : undefined;
// maybe :: b -> (a -> b) -> Maybe a -> b
const maybe = (v, f, m) =>
m.Nothing ? v : f(m.Just);
// ord :: Char -> Int
const ord = c => c.codePointAt(0);
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = (n, xs) =>
'GeneratorFunction' !== xs.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// toUpper :: String -> String
const toUpper = s => s.toLocaleUpperCase();
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// unwords :: [String] -> String
const unwords = xs => xs.join(' ');
// words :: String -> [String]
const words = s => s.split(/\s+/);
// Use of `take` and `length` here allows zipping with non-finite lists
// i.e. generators like cycle, repeat, iterate.
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = (f, xs, ys) => {
const
lng = Math.min(length(xs), length(ys)),
as = take(lng, xs),
bs = take(lng, ys);
return Array.from({
length: lng
}, (_, i) => f(as[i], bs[i], i));
};
// MAIN ---
return main();
})();
- Output:
Abbreviation tests: 'riG rePEAT copies put mo rest types fup. 6 poweRin' -> 'RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT' '' -> ''
jq
Also works with gojq, the Go implementation of jq, and with fq.
Adapted from Wren
def table:
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " +
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " +
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " +
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " +
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " +
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " +
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " +
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
;
# Input: {commands, minLens}
# Output: array of expansions or error markers corresponding to $words
def validate($words):
.commands as $commands
| .minLens as $minLens
| [ $words[] as $word
| ($word|length) as $wlen
| first( range(0; $commands|length) as $i
| $commands[$i]
| select($minLens[$i] != 0 and $wlen >= $minLens[$i] and $wlen <= length)
| ascii_upcase
| select(startswith(($word|ascii_upcase))) )
// "*error*" ];
# Output: {commands, minLens} corresponding to the $table string
def commands($table):
[$table|splits(" *")] as $split_table
| ($split_table|length) as $slen
| {commands:[], minLens:[], i:0}
| until(.found;
.commands += [ $split_table[.i] ]
| ($split_table[.i]|length) as $len
| if (.i == $slen - 1)
then .minLens += [$len]
| .found = true
else .
end
| .i += 1
| ($split_table[.i] | try (tonumber) // null) as $num
| if ($num != null)
then .minLens += [ if ($num < $len) then $num else $len end ]
| .i += 1
| if (.i == $slen) then .found = true else . end
else .minLens += [$len]
end );
def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;
def task($sentence):
[$sentence | splits(" *")] as $words
| commands(table)
| validate($words)
| $words, .
| map(lpad(10))
| join(" ") ;
task("riG rePEAT copies put mo rest types fup. 6 poweRin")
- Output:
Invocation jq -rnf abbreviations-simple.jq
riG rePEAT copies put mo rest types fup. 6 poweRin RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Julia
const commandtable = """
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"""
function makedict(tbl)
str = split(uppercase(tbl), r"\s+")
dict = Dict{String, String}()
for (i, s) in enumerate(str)
if (n = tryparse(Int, s)) != nothing
dict[str[i-1][1:n]] = str[i-1]
else
dict[s] = s
end
end
dict
end
function okabbrev(dict, abb)
for i in length(abb):-1:1
if haskey(dict, abb[1:i])
com = dict[abb[1:i]]
if length(abb) <= length(com) && abb == com[1:length(abb)]
return dict[abb[1:i]]
end
end
end
return "*error*"
end
formattedprint(arr, n) = (for s in arr print(rpad(s, n)) end; println())
function teststring(str)
d = makedict(commandtable)
commands = split(str, r"\s+")
formattedprint(commands, 9)
formattedprint([okabbrev(d, uppercase(s)) for s in commands], 9)
end
teststring("riG rePEAT copies put mo rest types fup. 6 poweRin")
- Output:
riG rePEAT copies put mo rest types fup. 6 poweRin RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Kotlin
import java.util.Locale
private const val table = "" +
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " +
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " +
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " +
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " +
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " +
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " +
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " +
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
private data class Command(val name: String, val minLen: Int)
private fun parse(commandList: String): List<Command> {
val commands = mutableListOf<Command>()
val fields = commandList.trim().split(" ")
var i = 0
while (i < fields.size) {
val name = fields[i++]
var minLen = name.length
if (i < fields.size) {
val num = fields[i].toIntOrNull()
if (num != null && num in 1..minLen) {
minLen = num
i++
}
}
commands.add(Command(name, minLen))
}
return commands
}
private fun get(commands: List<Command>, word: String): String? {
for ((name, minLen) in commands) {
if (word.length in minLen..name.length && name.startsWith(word, true)) {
return name.toUpperCase(Locale.ROOT)
}
}
return null
}
fun main(args: Array<String>) {
val commands = parse(table)
val sentence = "riG rePEAT copies put mo rest types fup. 6 poweRin"
val words = sentence.trim().split(" ")
val results = words.map { word -> get(commands, word) ?: "*error*" }
val paddedUserWords = words.mapIndexed { i, word -> word.padEnd(results[i].length) }
println("user words: ${paddedUserWords.joinToString(" ")}")
println("full words: ${results.joinToString(" ")}")
}
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Lua
abbr = {
define = function(self, cmdstr)
local cmd
self.hash = {}
for word in cmdstr:upper():gmatch("%S+") do
if cmd then
local n = tonumber(word)
for len = n or #cmd, #cmd do
self.hash[cmd:sub(1,len)] = cmd
end
cmd = n==nil and word or nil
else
cmd = word
end
end
end,
expand = function(self, input)
local output = {}
for word in input:upper():gmatch("%S+") do
table.insert(output, self.hash[word] or "*error*")
end
return table.concat(output, " ")
end
}
abbr:define[[
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
]]
local input = "riG rePEAT copies put mo rest types fup. 6 poweRin"
print("Input:", input)
print("Output:", abbr:expand(input))
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
M2000 Interpreter
Using a List Object
Object Queue is a list (using a hash table) which can hold same keys (strings or numbers or mix of them). Always the Exist(aQueuePointer, "word") set the internal index to the last key with name "word" if return true. We read the item in that index using Eval$(aQueuePointer) for string value, or Eval(aQueuePointer) for aritmetic value. We can't delete keys from anywhere, but only from the last entries using Drop statement to drop a number of keys. Here we didn't use drop, we have only a table of words.
Module Abbreviations_Simple {
Function Lex {
a$={add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
}
const crlftab$={
}+chr$(9)
Lex=Queue
Word$=""
dim part$()
part$()=piece$(trim$(filter$(a$, crlftab$)), " ")
for i=0 to len(part$())-1
if part$(i)<>"" then
k=val(part$(i))
if k=0 then
if Word$<>"" then Append Lex, Word$:=Word$
Word$=ucase$(part$(i))
else
for j=k to len(Word$)
Append Lex, left$(Word$,j):=Word$
next j
word$=""
end if
end if
next i
if Word$<>"" then Append Lex, Word$:=Word$
=Lex
}
Parse$=Lambda$ Lex=Lex() (a$) -> {
Dim part$()
Rep$=""
part$()=piece$(a$," ")
if len(part$())=0 then exit
for i=0 to len(part$())-1
if part$(i)<>"" then
if exist(Lex, ucase$(part$(i))) then
Rep$+=if$(Rep$=""->"", " ")+Eval$(lex)
else
Rep$+=if$(Rep$=""->"", " ")+"*error*"
end if
end if
next i
=Rep$
}
Print Parse$("riG rePEAT copies put mo rest types fup. 6 poweRin")
Print Parse$("riG macro copies macr")
Print Parse$("")=""
}
Abbreviations_Simple
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT RIGHT MACRO *error* *error* True
Using simple string
Here we use just a string to hold the words and the Instr() function to search for each abbreviation.
Module Abbreviations_Simple_2 {
Function Lex$ {
a$={add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
}
const crlftab$={
}+chr$(9)
Lex$=""
Word$=""
dim part$()
part$()=piece$(trim$(filter$(a$, crlftab$)), " ")
for i=0 to len(part$())-1
if part$(i)<>"" then
k=val(part$(i))
if k=0 then
if Word$<>"" then Lex$+="#"+Word$+" 0"
Word$=ucase$(part$(i))
else
Lex$+="#"+ Word$+str$(k)
word$=""
end if
end if
next i
if Word$<>"" then Lex$+="#"+Word$+" 0"
=Lex$
}
Parse$=Lambda$ Lex$=Lex$() (a$) -> {
Dim part$()
Rep$=""
part$()=piece$(a$," ")
if len(part$())=0 then exit
for i=0 to len(part$())-1
if part$(i)<>"" then
j=1
do
j=instr(Lex$, "#"+ucase$(part$(i)), j)
if j=0 then exit
q=instr(Lex$, " ", j+1)
if Val(Mid$(lex$, q,10))=0 then
if Mid$(Lex$, j+1, q-j)=ucase$(part$(i))+" " then exit
else.if len(part$(i))>=Val(Mid$(lex$, q,10)) then
exit
end if
j++
Always
if j>0 then
Rep$+=if$(Rep$=""->"", " ")+Mid$(Lex$, j+1, q-j-1)
else
Rep$+=if$(Rep$=""->"", " ")+"*error*"
end if
end if
next i
=Rep$
}
Print Parse$("riG rePEAT copies put mo rest types fup. 6 poweRin")
Print Parse$("riG macro copies macr")
Print Parse$("")=""
}
Abbreviations_Simple_2
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT RIGHT MACRO *error* *error* True
Mathematica /Wolfram Language
ClearAll[ct, FunctionMatchQ, ValidFunctionQ, ProcessString]
ct = "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1";
ct = FixedPoint[StringReplace[{"\n" -> "", Longest[" " ..] -> " "}], ct];
ct = StringSplit[ct, " "];
ct = SequenceReplace[ct, {x_, y : (Alternatives @@ (ToString /@ Range[1, 9]))} :> {x, ToExpression@y}];
ct = If[MatchQ[#, {_, _Integer}], #, {#, 1}] & /@ ct;
FunctionMatchQ[{func_String, min_Integer}, test_String] :=
Module[{max, l},
max = StringLength[func];
l = StringLength[test];
If[min <= l <= max,
If[StringStartsQ[func, test, IgnoreCase -> True],
True
,
False
]
,
False
]
]
ValidFunctionQ[test_String] := Module[{val},
val = SelectFirst[ct, FunctionMatchQ[#, test] &, Missing[]];
If[MissingQ[val], "*error*", ToUpperCase[First@val]]
]
ProcessString[test_String] := Module[{parts},
parts = StringSplit[test];
StringRiffle[ValidFunctionQ /@ parts, " "]
]
ProcessString["riG rePEAT copies put mo rest types fup. 6 poweRin"]
- Output:
"RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT"
MiniScript
c = "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3" +
" compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate" +
" 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2" +
" forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load" +
" locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2" +
" msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3" +
" refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left" +
" 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
minLen = {}
lastItem = ""
for item in c.split
if item == "" then continue
item = item.upper
if lastItem and item[0] >= "0" and item[0] <= "9" then
minLen[lastItem] = val(item)
lastItem = ""
else
minLen[item] = null
lastItem = item
end if
end for
check = function(word)
word = word.upper
for key in minLen.indexes
if key[:word.len] != word then continue
min = minLen[key]
if min and word.len < min then continue
return key
end for
return "*error*"
end function
input = "riG rePEAT copies put mo rest types fup. 6 poweRin"
output = []
for word in input.split
if word == "" then continue
output.push check(word)
end for
print output.join
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
newLISP
(define (foo text)
(let (command-table '() toks
(map upper-case (parse (trim "
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1
Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3
Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1
duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2
Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get
help 1 hexType 4 input 1 powerInput 3 join 1 split 2
spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase
3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1
overlay 1 parse preserve 4 purge 3 put putD query 1
quit read recover 3 refresh renum 3 repeat 3 replace 1
Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2
save set shift 2 si sort sos stack 3 status 4 top
transfer 3 type 1 up 1"))))
(local (tok amin word found? result)
(until (empty? toks)
(setq tok (pop toks))
(setq amin
(or (and (true? toks) (int (first toks)) (int (pop toks)))
99))
(push (list tok amin) command-table -1))
(dolist (word (parse (trim text)))
(setq word (upper-case word))
(setq found? nil)
(dolist (command-pair command-table found?)
(when (or (= word (command-pair 0))
(and (starts-with (command-pair 0) word)
(>= (length word) (command-pair 1))))
(setq found? (command-pair 0))))
(push (or found? "*error*") result -1))
(join result " "))))
(foo
"riG rePEAT copies put mo rest types fup. 6 poweRin"
)
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Nim
Adapted from Python version with several modifications.
import parseutils
import strutils
import tables
const Commands =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " &
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " &
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " &
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " &
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " &
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " &
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " &
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
#---------------------------------------------------------------------------------------------------
proc abbrevationLengths(commands: string): Table[string, int] =
## Find the minimal abbreviation length for each word.
## A word that does not have minimum abbreviation length specified
## gets it's full length as the minimum.
var word = ""
for item in commands.splitWhitespace():
var n: int
if item.parseInt(n) == 0:
# Not a number.
if word.len != 0:
# No minimal length specified for the word.
result[word] = word.len
word = item
else:
# Got an integer.
if word.len == 0:
raise newException(ValueError, "Invalid position for number: " & $n)
result[word] = n
word = ""
#---------------------------------------------------------------------------------------------------
proc abbreviations(commandTable: Table[string, int]): Table[string, string] =
## For each command insert all possible abbreviations.
for command, minlength in commandTable.pairs:
for length in minLength..command.len:
let abbr = command[0..<length].toLower
result[abbr] = command.toUpper
#---------------------------------------------------------------------------------------------------
proc parse(words: seq[string]; abbrevTable: Table[string, string]): seq[string] =
## Parse a list of words and return the list of full words (or *error*).
for word in words:
result.add(abbrevTable.getOrDefault(word.toLower, "*error*"))
#---------------------------------------------------------------------------------------------------
let commandTable = Commands.abbrevationLengths()
let abbrevTable = commandTable.abbreviations()
while true:
try:
stdout.write "Input? "
let userWords = stdin.readline().strip().splitWhitespace()
let fullWords = userWords.parse(abbrevTable)
stdout.write("\nUser words: ")
for i, word in userWords:
stdout.write(word.alignLeft(fullWords[i].len) & ' ')
stdout.write("\nFull words: ")
for word in fullWords:
stdout.write(word & ' ')
stdout.write("\n\n")
except EOFError:
echo ""
break
- Output:
User words: riG rePEAT copies put mo rest types fup. 6 poweRin Full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT Input?
OCaml
open String
let table_as_string =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 \
Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 \
Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 \
xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 \
Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 \
input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 \
Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 \
modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 \
put putD query 1 quit read recover 3 refresh renum 3 repeat 3 \
replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 \
save set shift 2 si sort sos stack 3 status 4 top \
transfer 3 type 1 up 1"
The interesting part below is the compare function. We'll use it when storing our entries ( {"add";1}, {"alter";3}, etc ) and when looking for a word, from a given abbreviation. For the former, a simple comparison function on strings is enough, but for the latter, we need to ensure that the word corresponds to the abbreviation (the abbreviation is a substring of the word and the abbreviation length is sufficient).
module Entry = struct
type t = { word : string ; min : int }
let compare e1 e2 =
let n1 = length e1.word in
if n1 < e2.min || n1 > length e2.word then
compare e1.word e2.word
else
compare (sub e1.word 0 n1) (sub e2.word 0 n1)
end
module Table = Set.Make(Entry)
The few functions below are used to build the table from the string at the beginning.
let clean_strings strs =
List.filter (fun w -> w <> "" && w <> " ") strs
let rec split = function
| [] -> Table.empty
| [w] ->
Table.singleton { word=w ; min=length w }
| w::x::t ->
try
let m = int_of_string x in
Table.add { word=uppercase_ascii w ; min=m } (split t)
with Failure _ ->
let m = length w in
Table.add { word=uppercase_ascii w ; min=m } (split (x::t))
let make_table table_as_string =
split_on_char ' ' table_as_string
|> clean_strings
|> split
Finally, here is the function looking for a word :
let abbrev (table:Table.t) (w:string) : string =
let w = uppercase_ascii w in
try
let e = Table.find { word=w ; min=length w } table in e.word
with Not_found -> "*error*"
let rec check (table:Table.t) (inputs:string list) : unit =
List.map (abbrev table) inputs
|> List.iter (Printf.printf "%s ");
print_newline ()
let main =
let table = make_table table_as_string in
let inputs = ["riG";"rePEAT";"copies";"put";"mo";"rest";"types";"fup.";"6";"poweRin"] in
check table inputs;
exit 0
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Perl
@c = (uc join ' ', qw<
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
>) =~ /([a-zA-Z]+(?:\s+\d+)?)(?=\s+[a-zA-Z]|$)/g;
my %abr = ('' => '', ' ' => '');
for (@c) {
($w,$sl) = split ' ', $_;
$ll = length($w);
$sl = $ll unless $sl;
$abr{substr($w,0,$sl)} = $w;
map { $abr{substr($w, 0, $_)} = $w } $sl .. $ll;
}
$fmt = "%-10s";
$inp = sprintf $fmt, 'Input:';
$out = sprintf $fmt, 'Output:';
for $str ('', qw<riG rePEAT copies put mo rest types fup. 6 poweRin>) {
$inp .= sprintf $fmt, $str;
$out .= sprintf $fmt, $abr{uc $str} // '*error*';
}
print "$inp\n$out\n";
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Phix
constant abbrtxt = """ add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 """, input = "riG rePEAT copies put mo rest types fup. 6 poweRin" function set_min_lengths(sequence a) -- -- set default lengths and apply min lengths if present, eg ..."macro","merge","2",... -- ==> {.."macro",5}} ==> {.."macro",5},{"merge",5}} ==> {.."macro",5},{"merge",2}} -- ie both macro and merge get a default min length of 5, but the min length of merge -- is overwritten when the "2" is processed. -- sequence res = {} for i=1 to length(a) do string ai = a[i] if length(ai)=1 and ai>="1" and ai<="9" then res[$][2] = ai[1]-'0' else res = append(res,{ai,length(ai)}) end if end for return res end function constant abbrevs = set_min_lengths(split(substitute(abbrtxt,"\n"," "),no_empty:=true)) constant inputs = split(input,no_empty:=true) for i=1 to length(inputs) do string ii = inputs[i], res = "*error*" for j=1 to length(abbrevs) do {string aj, integer l} = abbrevs[j] if length(ii)>=l and match(ii,aj,case_insensitive:=true)==1 then res = upper(aj) exit end if end for puts(1,res&" ") end for puts(1,"\n")
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Python
Procedural
command_table_text = """add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"""
user_words = "riG rePEAT copies put mo rest types fup. 6 poweRin"
def find_abbreviations_length(command_table_text):
""" find the minimal abbreviation length for each word.
a word that does not have minimum abbreviation length specified
gets it's full lengths as the minimum.
"""
command_table = dict()
input_iter = iter(command_table_text.split())
word = None
try:
while True:
if word is None:
word = next(input_iter)
abbr_len = next(input_iter, len(word))
try:
command_table[word] = int(abbr_len)
word = None
except ValueError:
command_table[word] = len(word)
word = abbr_len
except StopIteration:
pass
return command_table
def find_abbreviations(command_table):
""" for each command insert all possible abbreviations"""
abbreviations = dict()
for command, min_abbr_len in command_table.items():
for l in range(min_abbr_len, len(command)+1):
abbr = command[:l].lower()
abbreviations[abbr] = command.upper()
return abbreviations
def parse_user_string(user_string, abbreviations):
user_words = [word.lower() for word in user_string.split()]
commands = [abbreviations.get(user_word, "*error*") for user_word in user_words]
return " ".join(commands)
command_table = find_abbreviations_length(command_table_text)
abbreviations_table = find_abbreviations(command_table)
full_words = parse_user_string(user_words, abbreviations_table)
print("user words:", user_words)
print("full words:", full_words)
- Output:
input: "" output: "" input: "riG rePEAT copies put mo rest types fup. 6 poweRin" output: "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT"
Composition of pure functions
'''Simple abbreviations'''
from functools import reduce
import re
# withExpansions :: [(String, Int)] -> String -> String
def withExpansions(table):
'''A string in which all words are either
expanded (if they match abbreviations in
a supplied table), or replaced with an
'*error*' string.
'''
return lambda s: unwords(map(
expanded(table), words(s)
))
# expanded :: [(String, Int)] -> String -> String
def expanded(table):
'''An abbreviation (or error string) for
a given word, based on a table of full
strings and minimum abbreviation lengths.
'''
def expansion(k):
u = k.upper()
lng = len(k)
def p(wn):
w, n = wn
return w.startswith(u) and lng >= n
return find(p)(table) if k else Just(('', 0))
return lambda s: maybe('*error*')(fst)(expansion(s))
# cmdsFromString :: String -> [(String, Int)]
def cmdsFromString(s):
'''A simple rule-base consisting of a
list of tuples [(
upper case expansion string,
minimum character count of abbreviation
)],
obtained by a parse of single input string.
'''
def go(a, x):
xs, n = a
return (xs, int(x)) if x.isdigit() else (
([(x.upper(), n)] + xs, 0)
)
return fst(reduce(
go,
reversed(re.split(r'\s+', s)),
([], 0)
))
# TEST -------------------------------------------------
def main():
'''Tests of abbreviation expansions'''
# table :: [(String, Int)]
table = cmdsFromString(
'''add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1
Schange Cinsert 2 Clast 3 compress 4 copy 2 count 3 Coverlay 3
cursor 3 delete 3 Cdelete 2 down 1 duplicate 3 xEdit 1 expand 3
extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1
split 2 spltJOIN load locate 1 Clocate 2 lowerCase 3 upperCase 3
Lprefix 2 macro merge 2 modify 3 move 2 msg next 1 overlay 1
parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4
rgtLEFT right 2 left 2 save set shift 2 si sort sos stack 3
status 4 top transfer 3 type 1 up 1'''
)
# tests :: [String]
tests = [
'riG rePEAT copies put mo rest types fup. 6 poweRin',
''
]
print(
fTable(__doc__ + ':\n')(lambda s: "'" + s + "'")(
lambda s: "\n\t'" + s + "'"
)(withExpansions(table))(tests)
)
# GENERIC -------------------------------------------------
# compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
def compose(g):
'''Right to left function composition.'''
return lambda f: lambda x: g(f(x))
# Just :: a -> Maybe a
def Just(x):
'''Constructor for an inhabited Maybe (option type) value.'''
return {'type': 'Maybe', 'Nothing': False, 'Just': x}
# Nothing :: Maybe a
def Nothing():
'''Constructor for an empty Maybe (option type) value.'''
return {'type': 'Maybe', 'Nothing': True}
# find :: (a -> Bool) -> [a] -> Maybe a
def find(p):
'''Just the first element in the list that matches p,
or Nothing if no elements match.'''
def go(xs):
for x in xs:
if p(x):
return Just(x)
return Nothing()
return lambda xs: go(xs)
# fst :: (a, b) -> a
def fst(tpl):
'''First member of a pair.'''
return tpl[0]
# fTable :: String -> (a -> String)
# -> (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
'''Heading -> x display function ->
fx display function ->
f -> value list -> tabular string.'''
def go(xShow, fxShow, f, xs):
w = max(map(compose(len)(xShow), xs))
return s + '\n' + '\n'.join([
xShow(x).rjust(w, ' ') + (' -> ') + fxShow(f(x))
for x in xs
])
return lambda xShow: lambda fxShow: lambda f: lambda xs: go(
xShow, fxShow, f, xs
)
# maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
'''Either the default value v, if m is Nothing,
or the application of f to x,
where m is Just(x).'''
return lambda f: lambda m: v if m.get('Nothing') else (
f(m.get('Just'))
)
# unwords :: [String] -> String
def unwords(xs):
'''A space-separated string derived from
a list of words.'''
return ' '.join(xs)
# words :: String -> [String]
def words(s):
'''A list of words delimited by characters
representing white space.'''
return re.split(r'\s+', s)
# MAIN ---
if __name__ == '__main__':
main()
- Output:
Simple abbreviations: 'riG rePEAT copies put mo rest types fup. 6 poweRin' -> 'RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT' '' -> ''
Racket
#lang racket
(require srfi/13)
(define command-table #<<EOS
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
EOS
)
(define command/abbr-length-pairs
(let loop ((cmd-len-list (string-split command-table)) (acc (list)))
(match cmd-len-list
((list) (sort acc < #:key cdr))
((list-rest a (app string->number (and ad (not #f))) dd) (loop dd (cons (cons a ad) acc)))
((list-rest a d) (loop d (cons (cons a (string-length a)) acc))))))
(define (validate-word w)
(or (let ((w-len (string-length w)))
(for/first ((candidate command/abbr-length-pairs)
#:when (and (>= w-len (cdr candidate)) (string-prefix-ci? w (car candidate))))
(string-upcase (car candidate))))
"*error*"))
(define (validate-string s) (string-join (map validate-word (string-split s))))
(module+ main
(define (debug-i/o s) (printf "input: ~s~%output: ~s~%" s (validate-string s)))
(debug-i/o "")
(debug-i/o "riG rePEAT copies put mo rest types fup. 6 poweRin"))
(module+ test
(require rackunit)
(check-equal?
(validate-string "riG rePEAT copies put mo rest types fup. 6 poweRin")
"RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT")
(check-equal? (validate-string "") ""))
- Output:
input: "" output: "" input: "riG rePEAT copies put mo rest types fup. 6 poweRin" output: "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT"
Raku
(formerly Perl 6)
Demonstrate that inputting an empty string returns an empty string in addition to the required test input.
<
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
> ~~ m:g/ (<.alpha>+) \s* (\d*) /;
my %abr = '' => '', |$/.map: {
my $abbrv = .[0].Str.fc;
|map { $abbrv.substr( 0, $_ ) => $abbrv.uc },
+(.[1].Str || .[0].Str.chars) .. .[0].Str.chars;
};
sub abbr-simple ( $str ) { %abr{$str.trim.fc} // '*error*' }
# Testing
for 'riG rePEAT copies put mo rest types fup. 6 poweRin', '' -> $str {
put ' Input: ', $str;
put 'Output: ', join ' ', $str.words.map: &abbr-simple;
}
- Output:
Input: riG rePEAT copies put mo rest types fup. 6 poweRin Output: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT Input: Output:
REXX
/*REXX program validates a user "word" against a "command table" with abbreviations.*/
Parse Arg uw /*obtain optional arguments from the CL*/
If uw='' Then uw= 'riG rePEAT copies put mo rest types fup. 6 poweRin'
Say 'user words: ' uw
keyws= 'add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3',
'compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate',
'3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2',
'forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load',
'locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2',
'msg next 1 overlay 1 Parse preserve 4 purge 3 put putD query 1 quit read recover 3',
'refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left',
'2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1'
Say 'full words: ' validate(uw) /*display the result(s) to the terminal*/
Exit /*stick a fork in it, we're all done. */
/*--------------------------------------------------------------------------------------*/
validate: Procedure Expose keyws
keyws=translate(keyws)
Arg userwords /*ARG capitalizes all the userwords. */
res='' /*initialize the Return string to null.*/
Do j=1 to words(userwords) /* loop over userwords */
uword=word(userwords,j) /*obtain a word from the userword list.*/
Do k=1 to words(keyws) /* loop over keywords */
kw=word(keyws,k) /*get a legitmate command name from keyws.*/
L=word(keyws,k+1) /*··· and maybe get its abbrev length.*/
If datatype(L,'W') Then /* it's a number - an abbrev length. */
k=k + 1 /* skip it for next kw */
Else /* otherwise */
L=length(kw) /* it can't be abbreviated. */
If abbrev(kw,uword,L) Then Do /* is valid abbreviation */
res=res kw /* add to result string */
Iterate j /* proceed with next userword */
End
End
res=res '*error*' /*processed the whole list, not valid */
End
Return strip(res) /* elide superfluous leading blank. */
- output when using the default input:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Ruby
str = "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
RE = /(?<word1>[a-zA-Z]+)\s+(?<word2>[a-zA-Z]+)/
str = str.upcase
# add missing wordsizes
2.times{ str.gsub!(RE){ [ $~[:word1], $~[:word1].size, $~[:word2] ].join(" ")} }
table = Hash[*str.split].transform_values(&:to_i)
test = "riG rePEAT copies put mo rest types fup. 6 poweRin"
ar = test.split.map do |w|
(res = table.detect{|k,v| k.start_with?(w.upcase) && w.size >= v}) ? res[0] : "*error*"
end
puts ar.join(" ")
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Rust
cargo clippy and cargo fmt run against it
use std::collections::HashMap;
// The plan here is to build a hashmap of all the commands keyed on the minimum number of
// letters than can be provided in the input to match. For each known command it will appear
// in a list of possible commands for a given string lengths. A command can therefore appear a
// number of times. For example, the command 'recover' has a minimum abbreviation length of 3.
// In the hashmap 'recover' will be stored behind keys for 3, 4, 5, 6 & 7 as any abbreviation of
// 'recover' from 3 until 7 letters inclusive can match. This way, once the length of the input
// string is known a subset of possible matches can be retrieved immediately and then checked.
//
fn main() {
let command_table_string =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input_command 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1";
// Split up the command table string using the whitespace and set up an iterator
// to run through it. We need the iterator to be peekable so that we can look ahead at
// the next item.
let mut iter = command_table_string.split_whitespace().peekable();
let mut command_table = HashMap::new();
// Attempt to take two items at a time from the command table string. These two items will be
// the command string and the minimum length of the abbreviation. If there is no abbreviation length
// then there is no number provided. As the second item might not be a number, so we need to peek at
// it first. If it is a number we can use it as a key for the hashmap. If it is not a number then
// we use the length of the first item instead because no abbreviations are available for the
// word i.e. the whole word must be used. A while loop is used because we need to control iteration
// and look ahead.
//
while let Some(command_string) = iter.next() {
let command_string_length = command_string.len() as i32;
let min_letter_match = match iter.peek() {
Some(potential_number) => match potential_number.parse::<i32>() {
Ok(number) => {
iter.next();
number
}
Err(_) => command_string_length,
},
None => break,
};
// The word must be stored for every valid abbreviation length.
//
for i in min_letter_match..=command_string_length {
let cmd_list = command_table.entry(i).or_insert_with(Vec::new);
cmd_list.push(command_string.to_uppercase());
}
}
const ERROR_TEXT: &str = "*error*";
let test_input_text = "riG rePEAT copies put mo rest types fup. 6 poweRin";
let mut output_text = String::new();
let mut iter = test_input_text.split_whitespace().peekable();
// Run through each item in the input string, find the length of it
// and then use this to fetch a list of possible matches.
// A while loop is used because we need to look ahead in order to indentify
// the last item and avoid adding an unnecessary space.
//
while let Some(input_command) = iter.next() {
let input_command_length = input_command.len() as i32;
let command_list = match command_table.get(&input_command_length) {
Some(list) => list,
None => {
output_text.push_str(ERROR_TEXT);
continue;
}
};
let input_command_caps = input_command.to_uppercase();
let matched_commands: Vec<&String> = command_list
.iter()
.filter(|command| command.starts_with(&input_command_caps))
.collect();
// Should either be 0 or 1 command found
assert!(
matched_commands.len() < 2,
"Strange.. {:?}",
matched_commands
);
match matched_commands.first() {
Some(cmd) => output_text.push_str(cmd),
None => output_text.push_str(ERROR_TEXT),
}
if iter.peek().is_some() {
output_text.push(' ');
}
}
println!("Input was: {}", test_input_text);
println!("Output is: {}", output_text);
let correct_output = "RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT";
assert_eq!(output_text, correct_output)
}
- Output:
Input was: riG rePEAT copies put mo rest types fup. 6 poweRin Output is: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Scala
object Main extends App {
implicit class StrOps(i: String) {
def isAbbreviationOf(target: String, targetMinLength: Int): Boolean = {
@scala.annotation.tailrec
def checkPAsPrefixOfM(p: List[Char], m: List[Char]): Boolean = (p, m) match {
case (Nil, _) => true //prefix empty
case (_, Nil) => false //main string empty
case (ph :: pt, mh :: mt) if ph.toUpper == mh.toUpper => checkPAsPrefixOfM(pt, mt) //case insensitive match of head characters
case _ => false
}
i.length >= targetMinLength && checkPAsPrefixOfM(i.toList, target.toList)
}
}
val commands = """
add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1
""".stripMargin.replace("\n", " ").trim.split(" ")
val commandWithMinLengths = commands.sliding(2, 1)
.filter{ window =>
window.length > 1 && Try(window(0).toInt).toOption.isEmpty
}
.map{ w =>
(
w(0),
Try(w(1).toInt).toOption.getOrElse(0)
)
}
.toList
val input = "riG rePEAT copies put mo rest types fup. 6 poweRin".split(" ").filter(!_.isEmpty)
val resultLine = input.map{ i =>
commandWithMinLengths.find{case (c, l) => i.isAbbreviationOf(c, l)}.map(_._1.toUpperCase).getOrElse("*error*")
}.mkString(" ")
println(resultLine)
}
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
SNOBOL4
* Program: abbr_simple.sbl
* To run: sbl abbr_simple.sbl
* Description: Abbreviations, simple
* Comment: Tested using the Spitbol for Linux version of SNOBOL4
commands =
+ "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 "
+ "compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate "
+ "3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 "
+ "forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load "
+ "locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 "
+ "msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 "
+ "refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left "
+ "2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
commands = replace(commands,&lcase,&ucase)
numerals = '0123456789'
* Function filltable will fill the command abbreviations table
define("filltable(s,n)slen,i")
ct = table(300, ,"*error*") :f(errr);* command abbreviations table
:(filltable_end)
filltable
slen = size(s)
ct[s] = s
eq(n,slen) :s(filltable3)
i = n - 1
filltable2
i = lt(i,slen - 1) i + 1 :f(filltable3)
ct[substr(s,1,i)] = s
:(filltable2)
filltable3
filltable = ""
:(return)
filltable_end
x0
* Populate command abbreviations table
commands ? (span(' ') | "") breakx(&ucase) span(&ucase) . c
+ span(' ') (span(numerals) | "") . ablen = "" :f(x1)
ablen = ident(ablen) size(c)
ret = filltable(c,ablen)
:(x0)
x1
* Process user string
userstring = "riG rePEAT copies put mo rest types fup. 6 poweRin"
output = "Original user string:"
output = userstring
userstring = replace(userstring,&lcase,&ucase)
x2
userstring ? (span(' ') | "") (break(' ') | (len(1) rem)) . c = "" :f(x3)
user_commands = (gt(size(user_commands),0) user_commands ' ' ct[c], ct[c])
:(x2)
x3
output = ""
output = "User string with abbreviations expanded:"
output = user_commands
END
- Output:
Original user string: riG rePEAT copies put mo rest types fup. 6 poweRin User string with abbreviations expanded: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Tcl
proc appendCmd {word} {
# Procedure to append the correct command from the global list ::cmds
# for the word given as parameter to the global list ::result.
# If a matching word has been found and appended to ::result, this procedure
# behaves like a "continue" statement, causing the loop containing it to
# jump over the rest of the body.
set candidates [lsearch -inline -all -nocase -glob $::cmds "${word}*"]
foreach cand $candidates {
if {[string length $word] >= $::minLen($cand)} {
lappend ::result [string toupper $cand]
return -code continue
}
}
}
set cmds {Add ALTer BAckup Bottom CAppend Change SCHANGE CInsert CLAst COMPress COpy
COUnt COVerlay CURsor DELete CDelete Down DUPlicate Xedit EXPand EXTract Find
NFind NFINDUp NFUp CFind FINdup FUp FOrward GET Help HEXType Input POWerinput
Join SPlit SPLTJOIN LOAD Locate CLocate LOWercase UPPercase LPrefix MACRO
MErge MODify MOve MSG Next Overlay PARSE PREServe PURge PUT PUTD Query QUIT
READ RECover REFRESH RENum REPeat Replace CReplace RESet RESTore RGTLEFT
RIght LEft SAVE SET SHift SI SORT SOS STAck STATus TOP TRAnsfer Type Up}
# Find the minimum lengths necessary for each command.
foreach c $cmds {
regexp {^[A-Z]+} $c match
set minLen($c) [string length $match]
}
set words {riG rePEAT copies put mo rest types fup. 6 poweRin}
set result {}
foreach w $words {
appendCmd $w
lappend result *error*
}
puts "user words: $words"
puts $result
- Output:
./abbreviations_simple.tcluser words: riG rePEAT copies put mo rest types fup. 6 poweRin RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
VBA
Private Function ValidateUserWords(userstring As String) As String
Dim s As String
Dim user_words() As String
Dim command_table As Scripting.Dictionary
Set command_table = New Scripting.Dictionary
Dim abbreviations As Scripting.Dictionary
Set abbreviations = New Scripting.Dictionary
abbreviations.CompareMode = TextCompare
Dim commandtable() As String
Dim commands As String
s = s & "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 "
s = s & "compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate "
s = s & "3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 "
s = s & "forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load "
s = s & "locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 "
s = s & "msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 "
s = s & "refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left "
s = s & "2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
commandtable = Split(s, " ")
Dim i As Integer, word As Variant, number As Integer
For i = LBound(commandtable) To UBound(commandtable)
word = commandtable(i)
If Len(word) > 0 Then
i = i + 1
Do While Len(commandtable(i)) = 0: i = i + 1: Loop
number = Val(commandtable(i))
If number > 0 Then
command_table.Add Key:=word, Item:=number
Else
command_table.Add Key:=word, Item:=Len(word)
i = i - 1
End If
End If
Next i
For Each word In command_table
For i = command_table(word) To Len(word)
On Error Resume Next
abbreviations.Add Key:=Left(word, i), Item:=UCase(word)
Next i
Next word
user_words() = Split(userstring, " ")
For Each word In user_words
If Len(word) > 0 Then
If abbreviations.exists(word) Then
commands = commands & abbreviations(word) & " "
Else
commands = commands & "*error* "
End If
End If
Next word
ValidateUserWords = commands
End Function
Public Sub program()
Dim guserstring As String
guserstring = "riG rePEAT copies put mo rest types fup. 6 poweRin"
Debug.Print "user words:", guserstring
Debug.Print "full words:", ValidateUserWords(guserstring)
End Sub
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRinfull words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
VBScript
option explicit
function iif(c,t,f) if c then iif=t else iif=f end if: end function
function usrin(pr) wscript.stdout.write vbcrlf &pr &": ":usrin= wscript.stdin.readline:end function
sub do_abbrev
dim j, sm,n,n0,a
for each j in m
sm=trim(lcase(j.submatches(0)))
n0=iif (j.submatches(1)="",1, j.submatches(1))
n=1
do
a=left(sm,n)
if not d.exists(a) then
d.add a,sm
else
if len(a)=len(sm) then
d(a)=sm
elseif len(d(a))>len(a) then
d(a)=null
end if
end if
n=n+1
loop until n>len(sm)
next
end sub
'output sorted
sub display
dim d2,k,j,kk,mm
set d2=createobject("Scripting.dictionary")
for each k in d
kk=d(k)
if not isnull(kk) then
if not d2.exists(kk) then
d2.add kk,k
else
d2(kk)= d2(kk) & " " & k
end if
end if
next
for each j in m
mm=trim(lcase(j.submatches(0)))
wscript.echo left(mm&space(15),15),d2(mm)
next
wscript.echo
set d2=nothing
end sub
sub test
wscript.echo vbcrlf&"test:"
dim a,i,k,s1
do
s1= lcase(usrin("Command?"))
if trim(s1)="" then wscript.quit
a=split(trim(s1)," ")
for i=0 to ubound(a)
if a(i)<>"" then
wscript.stdout.write iif (d.exists(a(i)), ucase(d(a(i)))," **ERROR**")& " "
end if
next
loop
end sub
'main program
dim s:s=_
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 "&_
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate "&_
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 "&_
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load "&_
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 "&_
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 "&_
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left "&_
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
dim m,d
'use regexp to separate input
with new regexp
.pattern="([a-z]+?)\s+(\d?)"
.global=true
.ignorecase=true
set m=.execute(s)
end with
set d=createobject("Scripting.dictionary")
do_abbrev
'display
test
- Output:
Command?: riG rePEAT copies put mo rest types fup. 6 poweRin RIGHT REPEAT **ERROR** PUT RESTORE **ERROR** **ERROR** **ERROR** POWERINPUT
V (Vlang)
import encoding.utf8
import strconv
fn read_table(table string) ([]string, []int) {
fields := table.fields()
mut commands := []string{}
mut min_lens := []int{}
for i, max := 0, fields.len; i < max; {
cmd := fields[i]
mut cmd_len := cmd.len
i++
if i < max {
num := strconv.atoi(fields[i]) or {-1}
if 1 <= num && num < cmd_len {
cmd_len = num
i++
}
}
commands << cmd
min_lens << cmd_len
}
return commands, min_lens
}
fn validate_commands(commands []string, min_lens []int, words []string) []string {
mut results := []string{}
for word in words {
mut match_found := false
wlen := word.len
for i, command in commands {
if min_lens[i] == 0 || wlen < min_lens[i] || wlen > command.len {
continue
}
c := utf8.to_upper(command)
w := utf8.to_upper(word)
if c.index(w) or {-1} == 0 {
results << c
match_found = true
break
}
}
if !match_found {
results << "*error*"
}
}
return results
}
fn print_results(words []string, results []string) {
println("user words:\t${words.join("\t")}")
println("full words:\t${results.join("\t")}")
}
fn main() {
table := "" +
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " +
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " +
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " +
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " +
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " +
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " +
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " +
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1 "
sentence := "riG rePEAT copies put mo rest types fup. 6 poweRin"
commands, min_lens := read_table(table)
words := sentence.fields()
results := validate_commands(commands, min_lens, words)
print_results(words, results)
}
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Wren
Based on an older version of the Go entry.
import "./fmt" for Fmt
import "./str" for Str
var table =
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3 " +
"compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate " +
"3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2 " +
"forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load " +
"locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2 " +
"msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3 " +
"refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left " +
"2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
var validate = Fn.new { |commands, words, minLens|
var results = []
if (words.count == 0) return results
for (word in words) {
var matchFound = false
var wlen = word.count
for (i in 0...commands.count) {
var command = commands[i]
if (minLens[i] != 0 && wlen >= minLens[i] && wlen <= command.count) {
var c = Str.upper(command)
var w = Str.upper(word)
if (c.startsWith(w)) {
results.add(c)
matchFound = true
break
}
}
}
if (!matchFound) results.add("*error*")
}
return results
}
var splits = table.split(" ")
// get rid of empty entries
for (i in splits.count-1..0) if (splits[i] == "") splits.removeAt(i)
var slen = splits.count
var commands = []
var minLens = []
var i = 0
while (true) {
commands.add(splits[i])
var len = splits[i].count
if (i == slen - 1) {
minLens.add(len)
break
}
i = i + 1
var num = Num.fromString(splits[i])
if (num != null) {
minLens.add( (num < len) ? num : len )
i = i + 1
if (i == slen) break
} else minLens.add(len)
}
var sentence = "riG rePEAT copies put mo rest types fup. 6 poweRin"
var words = sentence.split(" ")
// get rid of empty entries
for (i in words.count-1..0) if (words[i] == "") words.removeAt(i)
var results = validate.call(commands, words, minLens)
System.write("user words: ")
for (j in 0...words.count) {
System.write("%(Fmt.s(-results[j].count, words[j])) ")
}
System.write("\nfull words: ")
System.print(results.join(" "))
- Output:
user words: riG rePEAT copies put mo rest types fup. 6 poweRin full words: RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
Yabasic
c$ = "add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3"
c$ = c$ + " compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate"
c$ = c$ + " 3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2"
c$ = c$ + " forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load"
c$ = c$ + " locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2"
c$ = c$ + " msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3"
c$ = c$ + " refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left"
c$ = c$ + " 2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
c$ = upper$(c$)
dim chunk$(1)
n = token(c$, chunk$())
dim lonc(n)
for i = 1 to n - 1
v = val(chunk$(i + 1))
if v then
lonc(i) = v
chunk$(i + 1) = ""
else
lonc(i) = len(chunk$(i))
end if
next
inp$ = "riG rePEAT copies put mo rest types fup. 6 poweRin"
inp$ = upper$(inp$)
dim w$(1)
x = token(inp$, w$())
dim lonw(x)
for i = 1 to x : lonw(i) = len(w$(i)) : next
for j = 1 to x
p$ = ""
for i = 1 to n
lc$ = left$(chunk$(i), lonw(j)) : lw$ = w$(j)
if (lw$ = lc$) and (lonc(i) <= lonw(j)) p$ = chunk$(i) : break
next
if p$ = "" p$ = "*error*"
r$ = r$ + " " + p$
next
print r$
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
zkl
commands:=Data(0,String, // "add\01\0alter\0..."
#<<<
"add 1 alter 3 backup 2 bottom 1 Cappend 2 change 1 Schange Cinsert 2 Clast 3
compress 4 copy 2 count 3 Coverlay 3 cursor 3 delete 3 Cdelete 2 down 1 duplicate
3 xEdit 1 expand 3 extract 3 find 1 Nfind 2 Nfindup 6 NfUP 3 Cfind 2 findUP 3 fUP 2
forward 2 get help 1 hexType 4 input 1 powerInput 3 join 1 split 2 spltJOIN load
locate 1 Clocate 2 lowerCase 3 upperCase 3 Lprefix 2 macro merge 2 modify 3 move 2
msg next 1 overlay 1 parse preserve 4 purge 3 put putD query 1 quit read recover 3
refresh renum 3 repeat 3 replace 1 Creplace 2 reset 3 restore 4 rgtLEFT right 2 left
2 save set shift 2 si sort sos stack 3 status 4 top transfer 3 type 1 up 1"
.toUpper().split());
#<<<
var szs=Dictionary(); // [<index>:<length> ...]
n:=0; while(n<commands.len()){
cmd,nc := commands.readString(n), n + cmd.len() + 1;
len:=commands.readString(nc);
if(len.matches("[0-9]*")){ szs[n]=len.toInt(); n=nc+len.len()+1 }
else { szs[n]=cmd.len(); n=nc; }
}
testText:="riG rePEAT copies put mo rest types "
" fup. 6 poweRin";
testText.split().apply('wrap(w){
w=w.toUpper(); n:=0;
while(True){ // check for length requirement and, if there, verify
n=commands.find(w,n);
if(Void==n) return("*error*"); // end of loop if no match
c:=commands.readString(n);
if(w.len()>=szs.find(n,99999)) return(c);
n+=c.len();
}
}).concat(" ").println();
- Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
- Programming Tasks
- Solutions by Programming Task
- 11l
- AArch64 Assembly
- Ada
- ALGOL 68
- ARM Assembly
- ARM Assembly examples needing attention
- Examples needing attention
- AutoHotkey
- C
- C++
- Clojure
- Crystal
- D
- Delphi
- System.SysUtils
- Factor
- Forth
- FreeBASIC
- FutureBasic
- Go
- Haskell
- J
- Java
- JavaScript
- Jq
- Julia
- Kotlin
- Lua
- M2000 Interpreter
- Mathematica
- Wolfram Language
- MiniScript
- NewLISP
- Nim
- OCaml
- Perl
- Phix
- Python
- Racket
- Raku
- REXX
- Ruby
- Rust
- Scala
- SNOBOL4
- Tcl
- VBA
- VBScript
- V (Vlang)
- Wren
- Wren-fmt
- Wren-str
- Yabasic
- Zkl