Abbreviations, simple: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|JavaScript}}: (Added some comments to the validation lines))
Line 287: Line 287:
)
)
);
);
// -> RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
// -> RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
};
};


Line 293: Line 293:
const validation = trie => s => {
const validation = trie => s => {
const go = (trees, cs) =>
const go = (trees, cs) =>
isNull(cs) ? Just(
isNull(cs) ? Just( // Completed input matches any expansions ?
concatMap(
concatMap(
x => {
x => {
Line 304: Line 304:
)
)
) : bindMay(
) : bindMay(
find(
find( // Any trie branch with a matching root ?
tree => isPrefixOf(head(cs), tree.root),
tree => isPrefixOf(head(cs), tree.root),
trees
trees
),
),
tree => {
tree => {
const
const // Hyphen splits minimum length from rest.
parts = tree.root.split('-'),
parts = tree.root.split('-'),
k = chars(fst(parts)),
k = chars(fst(parts));

lng = k.length;
return 1 < parts.length ? (
return 1 < parts.length ? ( // Hyphenated string in trie ?

isPrefixOf(k, cs) ? (
isPrefixOf(
isPrefixOf(k, cs) ? ( // A match up to min length ?

drop(lng, cs),
isPrefixOf( // Rest of input matches ?
drop(k.length, cs),
chars(snd(parts))
chars(snd(parts))

) ? (
) ? ( // No clash beyond min length. Expandable.
Just(parts.join(''))
Just(parts.join(''))

) : Nothing()
) : Nothing()
) : Nothing() // Match failure after min length.

) : isPrefixOf(k, cs) ? (
bindMay(
) : Nothing() // Match failure before min length.

go(tree.nest, drop(lng, cs)),
) : isPrefixOf(k, cs) ? ( // If trie string is a prefix,
bindMay( // then further recursion down the trie ...
go(tree.nest, drop(k.length, cs)),
xs => Just(k.concat(xs).join(''))
xs => Just(k.concat(xs).join(''))
)
)
) : Nothing();
) : Nothing(); // No match between trie string and input.
}
}
);
);

Revision as of 15:20, 12 August 2018

Abbreviations, simple is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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


Related tasks



Forth

Works with any ANS Forth

Needs the FMS-SI (single inheritance) library code located here: http://soton.mpeforth.com/flag/fms/index.html <lang forth>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

</lang>

Go

Translation of: Kotlin

<lang go>package main

import(

   "fmt"
   "strconv"
   "strings"

)

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 "

func minOf(x, y int) int {

   if x < y {
       return x
   }
   return y

}

func validate(commands, words []string, minLens []int) []string {

   results := make([]string, 0)
   if len(words) == 0 {
       return results
   }
   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 main() {

   table = strings.TrimSpace(table)
   splits := strings.Fields(table)
   slen := len(splits)
   commands := make([]string, 0, slen / 2)  // capacity approximate
   minLens  := make([]int, 0, slen / 2)     // ditto
   i := 0
   for {
       commands = append(commands, splits[i])
       len := len(splits[i])
       if i == slen - 1 {
           minLens = append(minLens, len)
           break
       }
       i++
       num, err := strconv.Atoi(splits[i])
       if err == nil {
           minLens = append(minLens, minOf(num, len))
           i++
           if i == slen {
               break
           }
       } else {
           minLens = append(minLens, len)
       }
   }
   sentence :=  "riG   rePEAT copies  put mo   rest    types   fup.    6       poweRin"
   words := strings.Fields(sentence)
   results := validate(commands, words, minLens)
   fmt.Print("user words:  ")
   for j := 0; j < len(words); j++ {
       fmt.Printf("%-*s ", len(results[j]), words[j])
   }
   fmt.Print("\nfull words:  ")
   fmt.Println(strings.Join(results, " "))

}</lang>

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

JavaScript

Constructing a prefix tree (or trie) for validation and expansion: <lang javascript>(() => {

   const main = () => {
       const strTable = `
  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`;
       // https://en.wikipedia.org/wiki/Trie
       const trie = prefixTree(strTable);
       return unwords(
           map(w => {
                   const maybe = validation(trie)(w);
                   return maybe.Nothing ? (
                       '*error*'
                   ) : maybe.Just;
               },
               map(toUpper,
                   words(
                       'riG   rePEAT copies  put mo   rest    ' +
                       'types   fup.    6       poweRin'
                   )
               )
           )
       );
       // -> RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT
   };
   // validation :: Tree String -> String -> Maybe String
   const validation = trie => s => {
       const go = (trees, cs) =>
           isNull(cs) ? Just( // Completed input matches any expansions ?
               concatMap(
                   x => {
                       const k = x.root;
                       return isPrefixOf('-', k) ? (
                           [k.slice(1)]
                       ) : [];
                   },
                   trees
               )
           ) : bindMay(
               find( // Any trie branch with a matching root ?
                   tree => isPrefixOf(head(cs), tree.root),
                   trees
               ),
               tree => {
                   const // Hyphen splits minimum length from rest.
                       parts = tree.root.split('-'),
                       k = chars(fst(parts));
                   return 1 < parts.length ? ( // Hyphenated string in trie ?
                       isPrefixOf(k, cs) ? ( // A match up to min length ?
                           isPrefixOf( // Rest of input matches ?
                               drop(k.length, cs),
                               chars(snd(parts))
                           ) ? ( // No clash beyond min length. Expandable.
                               Just(parts.join())
                           ) : Nothing() // Match failure after min length.
                       ) : Nothing() // Match failure before min length.
                   ) : isPrefixOf(k, cs) ? ( // If trie string is a prefix,
                       bindMay( // then further recursion down the trie ...
                           go(tree.nest, drop(k.length, cs)),
                           xs => Just(k.concat(xs).join())
                       )
                   ) : Nothing(); // No match between trie string and input.
               }
           );
       return go(trie, s.split());
   };
   // prefixTree :: String :: Tree Char
   const prefixTree = strTable =>
       trieFromStrings(
           sort(fst(foldl(
               (a, x) => {
                   const
                       n = isNaN(x) ? 0 : parseInt(x, 10),
                       k = snd(a);
                   return 0 < n ? (
                       Tuple(
                           fst(a).concat(
                               take(n, k) + '-' + drop(n, k)
                           ), 
                       )
                   ) : Tuple(
                       isNull(k) ? (
                           fst(a)
                       ) : fst(a).concat(k), x
                   )
               }, Tuple([], ),
               map(toUpper, words(strTable))
           )))
       );
   // GENERIC STRING TRIES -------------------------------
   // https://en.wikipedia.org/wiki/Trie
   // trieFromStrings :: [String] -> [Tree String]
   const trieFromStrings = xs => {
       const go = tree => {
           const
               root = tree.root,
               nest = tree.nest;
           return 1 === nest.length ? (() => {
               const child = nest[0];
               return go(Node(
                   root + child.root,
                   child.nest.map(go)
               ));
           })() : Node(
               root,
               map(go, nest)
           );
       };
       return charTrie(xs).map(go);
   };
   // charTrie = [String] -> [Tree String]
   const charTrie = xs => {
       const go = ks => concatMap(
           gp => 0 < gp.length ? (() => {
               const rest = concatMap(
                   x => 0 < x.length ? (
                       [x.slice(1)]
                   ) : [],
                   gp
               );
               return 1 < rest.length ? (
                   Node(
                       gp[0][0].toUpperCase(),
                       go(rest)
                   )
               ) : Node(gp[0].toUpperCase(), []);
           })() : gp,
           1 < ks.length ? (
               groupBy(on(eq, head), ks)
           ) : [ks]
       );
       return go(xs);
   };
   // GENERIC FUNCTIONS ----------------------------------
   // Just :: a -> Just a
   const Just = x => ({
       type: 'Maybe',
       Nothing: false,
       Just: x
   });
   // Node :: a -> [Tree a] -> Tree a
   const Node = (v, xs) => ({
       type: 'Node',
       root: v,
       nest: xs || []
   });
   // Nothing :: () -> Nothing
   const Nothing = () => ({
       type: 'Maybe',
       Nothing: true,
   });
   // Tuple (,) :: a -> b -> (a, b)
   const Tuple = (a, b) => ({
       type: 'Tuple',
       '0': a,
       '1': b,
       length: 2
   });
   // bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
   const bindMay = (mb, mf) =>
       mb.Nothing ? mb : mf(mb.Just);
   // chars :: String -> [Char]
   const chars = s => s.split();
   // concatMap :: (a -> [b]) -> [a] -> [b]
   const concatMap = (f, xs) =>
       0 < xs.length ? (() => {
           const unit = 'string' !== typeof xs ? (
               []
           ) : ;
           return unit.concat.apply(unit, xs.map(f))
       })() : [];
   // drop :: Int -> [a] -> [a]
   // drop :: Int -> String -> String
   const drop = (n, xs) => xs.slice(n);
   // enumFromThenToInt :: Int -> Int -> Int -> [Int]
   const enumFromThenToInt = (x1, x2, y) => {
       const d = x2 - x1;
       return Array.from({
           length: Math.floor(y - x2) / d + 2
       }, (_, i) => x1 + (d * i));
   };
   // eq (==) :: Eq a => a -> a -> Bool
   const eq = (a, b) => {
       const t = typeof a;
       return t !== typeof b ? (
           false
       ) : 'object' !== t ? (
           'function' !== t ? (
               a === b
           ) : a.toString() === b.toString()
       ) : (() => {
           const aks = Object.keys(a);
           return aks.length !== Object.keys(b).length ? (
               false
           ) : aks.every(k => eq(a[k], b[k]));
       })();
   };
   // find :: (a -> Bool) -> [a] -> Maybe a
   const find = (p, xs) => {
       for (let i = 0, lng = xs.length; i < lng; i++) {
           let x = xs[i];
           if (p(x)) return Just(x);
       }
       return Nothing();
   };
   // foldl :: (a -> b -> a) -> a -> [b] -> a
   const foldl = (f, a, xs) => xs.reduce(f, a);
   // fst :: (a, b) -> a
   const fst = tpl => tpl[0];
   // Typical usage: groupBy(on(eq, f), xs)
   // groupBy :: (a -> a -> Bool) -> [a] -> a
   const groupBy = (f, xs) => {
       const tpl = xs.slice(1)
           .reduce((a, x) => {
               const h = a[1].length > 0 ? a[1][0] : undefined;
               return (undefined !== h) && f(h, x) ? (
                   Tuple(a[0], a[1].concat([x]))
               ) : Tuple(a[0].concat([a[1]]), [x]);
           }, Tuple([], 0 < xs.length ? [xs[0]] : []));
       return tpl[0].concat([tpl[1]]);
   };
   // head :: [a] -> a
   const head = xs => xs.length ? xs[0] : undefined;
   // isNull :: [a] -> Bool
   // isNull :: String -> Bool
   const isNull = xs =>
       Array.isArray(xs) || ('string' === typeof xs) ? (
           1 > xs.length
       ) : undefined;
   // 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 pfx = (xs, ys) => {
           const intX = xs.length;
           return 0 < intX ? (
               ys.length >= intX ? xs[0] === ys[0] && pfx(
                   xs.slice(1), ys.slice(1)
               ) : false
           ) : true;
       };
       return 'string' !== typeof xs ? (
           pfx(xs, ys)
       ) : ys.startsWith(xs);
   };
   // map :: (a -> b) -> [a] -> [b]
   const map = (f, xs) => xs.map(f);
   // e.g. sortBy(on(compare,length), xs)
   // on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
   const on = (f, g) => (a, b) => f(g(a), g(b));
   // snd :: (a, b) -> b
   const snd = tpl => tpl[1];
   // sort :: Ord a => [a] -> [a]
   const sort = xs => xs.slice()
       .sort((a, b) => a < b ? -1 : (a > b ? 1 : 0));
   // take :: Int -> [a] -> [a]
   const take = (n, xs) => xs.slice(0, n);
   // toUpper :: String -> String
   const toUpper = s => s.toUpperCase();
   // unwords :: [String] -> String
   const unwords = xs => xs.join(' ');
   // words :: String -> [String]
   const words = s => s.split(/\s+/);
   // MAIN ---
   return main();

})();</lang>

Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT

Kotlin

<lang scala>// version 1.1.4-3

val r = Regex("[ ]+")

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 " 

fun validate(commands: List<String>, minLens: List<Int>, words: List<String>): List<String> {

   if (words.isEmpty()) return emptyList<String>()
   val results = mutableListOf<String>()
   for (word in words) {
       var matchFound = false
       for ((i, command) in commands.withIndex()) {
           if (minLens[i] == 0 || word.length !in minLens[i] .. command.length) continue 
           if (command.startsWith(word, true)) {
               results.add(command.toUpperCase())
               matchFound = true
               break
           }
       }
       if (!matchFound) results.add("*error*")
   }
   return results

}

fun main(args: Array<String>) {

   val splits = table.trimEnd().split(r)
   val commands = mutableListOf<String>()
   val minLens = mutableListOf<Int>()
   var i = 0
   while (true) {
       commands.add(splits[i])
       val len = splits[i].length
       if (i == splits.size - 1) {
           minLens.add(len)
           break
       }       
       val num = splits[++i].toIntOrNull()
       if (num != null) {
           minLens.add(minOf(num, len))
           if (++i == splits.size) break
       }
       else minLens.add(len)        
   }
   val sentence = "riG   rePEAT copies  put mo   rest    types   fup.    6       poweRin"
   val words = sentence.trim().split(r)
   val results = validate(commands, minLens, words)  
   print("user words:  ")
   for (j in 0 until words.size) print("${words[j].padEnd(results[j].length)} ")
   print("\nfull words:  ")
   for (j in 0 until results.size) print("${results[j]} ")
   println()

}</lang>

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 

Perl 6

Works with: Rakudo version 2017.08

Demonstrate that inputting an empty string returns an empty string in addition to the required test input.

<lang perl6>< 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.fc} // '*error*' }

  1. Testing

for 'riG rePEAT copies put mo rest types fup. 6 poweRin', -> $str {

   put ' Input: ', $str;
   put 'Output: ', join ' ', $str.words.map: *.&abbr-simple;

}</lang>

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: 

Python

Works with: Python version 3.6

<lang python>

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) </lang>

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"

Racket

<lang 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 "") ""))</lang>
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"

REXX

<lang 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

@= '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 @; arg x; upper @ /*ARG capitalizes all the X words. */

         $=                                     /*initialize the return string to null.*/
            do j=1  to words(x);   _=word(x, j) /*obtain a word from the     X  list.  */
              do k=1  to words(@); a=word(@, k) /*get a legitmate command name from  @.*/
              L=word(@, k+1)                    /*··· and maybe get it's abbrev length.*/
              if datatype(L, 'W')  then k=k + 1       /*yuppers, it's an abbrev length.*/
                                   else L=length(a)   /*nope,  it can't be abbreviated.*/
              if abbrev(a, _, L)   then do; $=$ a;  iterate j;  end  /*is valid abbrev?*/
              end   /*k*/
            $=$ '*error*'                       /*processed the whole list, not valid. */
            end     /*j*/
         return strip($)                        /*elide the superfluous leading blank. */</lang>
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

zkl

<lang zkl>commands:=Data(0,String, // "add\01\0alter\0..."

  1. <<<

"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());

  1. <<<

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();</lang>

Output:
RIGHT REPEAT *error* PUT MOVE RESTORE *error* *error* *error* POWERINPUT