madlisp/mad/calc.mad
2020-12-06 11:04:04 +07:00

59 lines
1.8 KiB
Plaintext

;;
;; 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
(defn mainLoop ()
(let (inp (readline "[calc] "))
(do (readline-add inp)
(readline-save readlineFile)
(print (str (parseInput inp) EOL))
true)))
;; Run it
(loop mainLoop)