;; ;; Simple command line calculator which demonstrates the following concepts: ;; - command parser with arguments ;; - readline support with history ;; - main loop ;; ;; This is a barebones example for creating interactive command line scripts. ;; This script should be executed directly, not inside the repl. ;; ;; Define commands (defn cmdAdd (args) (apply + args)) (defn cmdDiv (args) (apply / args)) (defn cmdMod (args) (apply % args)) (defn cmdMul (args) (apply * args)) (defn cmdSub (args) (apply - args)) (defn cmdHelp (args) (str "Available commands: " (apply join ", " (keys cmdMap)))) ;; And we define a hash-map for command lookups with minimum number of arguments (def cmdMap { "add": [cmdAdd 2] "div": [cmdDiv 2] "mod": [cmdMod 2] "mul": [cmdMul 2] "sub": [cmdSub 2] "help": [cmdHelp 0] }) ;; Find the first command which starts with the given name, or null (defn findCmd (name) (if (empty? name) null (let (matches (filterh (fn (v k) (prefix? k name)) cmdMap)) (if (empty? matches) null (get matches (first (keys matches))))))) ;; Split input by spaces, find command that matches the first word ;; and call it, giving the rest of the words as arguments. (defn parseInput (inp) (let (words (split " " inp) cname (first words) args (tail words) cmd (findCmd cname)) (if (null? cmd) (print "Unknown command, try 'help'.") (if (< (len args) (second cmd)) (print (str "Give at least 2 arguments to " cname ".")) ((first cmd) args))))) ;; Define a file for readline and load it (def readlineFile (str HOME "calc_history")) (readline-load readlineFile) ;; Main loop: Read input from user, add it to readline, and parse it (while true (let (inp (readline "[calc] ")) (readline-add inp) (readline-save readlineFile) (print (str "⇒ " (parseInput inp) EOL))))