(def flc-program
'((module Main ()
(begin)
(gets BatchCode)
(while (!= "" BatchCode)
(getn DimensionA)
(getn DimensionB)
(getn DimensionC)
(if (!= DimensionA (max DimensionA DimensionB DimensionC))
(report error to user "Invalid data")
(else)
(if (== "Y" (call check_Pythagoras DimensionA DimensionB DimensionC))
(show answer "Batch code: " BatchCode " is right angled")
(else)
(show answer "Batch code: " BatchCode " is not right angled")
(end if))
(end if))
(gets BatchCode)
(end while))
(end))
(module check_Pythagoras (ADim BDim CDim)
(begin)
(if (== (* ADim ADim)
(+ (* BDim BDim)
(* CDim CDim)))
(return "Y")
(else)
(return "N")
(end if))
(end))))
(defn run [program]
(let [env-stack (atom (list {}))
modules (into {} (map #(vector (-> % second str) %) program))
cur-module (atom "")
return-value (atom nil)
exec (fn exec [instr]
(let [[cmd & args] instr cur-env (first @env-stack)]
(cond
(seq? cmd)
(exec cmd)
(= cmd 'module)
(do (reset! cur-module (-> args first str))
(reset! return-value nil)
(loop [instrs (drop 3 instr)]
(when-not (or (empty? instrs) @return-value)
(exec (first instrs))
(recur (rest instrs)))))
(#{'begin 'end 'else} cmd) nil
(= (take 2 instr) '(show answer))
(println (apply str (map #(exec [%]) (drop 2 instr))))
(= (take 4 instr) '(report error to user))
(println (apply str (map #(exec [%]) (drop 4 instr))))
(#{'gets 'getn} cmd)
(let [var-name (str @cur-module "#" (first args))
var-value ((if (= cmd 'gets) identity #(Double. %)) (read-line))
new-env (assoc cur-env var-name var-value)]
(swap! env-stack #(cons new-env (rest %))))
(= cmd 'if)
(let [con (-> args first list exec)
tbody (rest (take-while #(not= % '(else)) args))
fbody (drop-while #(not= % '(else)) args)]
(if con
(doseq [i tbody] (exec i))
(doseq [i fbody] (exec i))))
(= cmd 'while)
(let [body (rest args)]
(loop [con (-> args first list exec)]
(when con
(doseq [i body] (exec i))
(recur (-> args first list exec)))))
(= cmd 'call)
(let [mdl-name (-> args first str)
mdl (modules mdl-name)
mdl-args (map #(exec [%]) (rest args))
mdl-arg-names (nth mdl 2)
caller @cur-module
new-env (into cur-env (map #(vector (str mdl-name "#" %1) %2)
mdl-arg-names mdl-args))]
(swap
! env
-stack
conj new
-env
) (exec mdl)
(reset! cur-module caller)
(swap! env-stack rest)
(let [ret @return-value]
(reset! return-value nil)
ret))
(= cmd 'return)
(reset! return-value (if args (-> args first list exec)))
(#{'< '> '<= '>= '+ '- '* '/ 'max} cmd)
(apply (eval cmd) (map #(exec [%]) args))
(#{'== '!=} cmd)
(apply ({'== = '!= not=} cmd) (map #(exec [%]) args))
(string? cmd) cmd
(number? cmd) (Double. (str cmd))
(and (symbol? cmd)
(contains? cur-env (str @cur-module "#" cmd)))
(cur-env (str @cur-module "#" cmd))
(symbol? cmd)
(println "undefined variable" cmd)
:else
(println "unknown instruction" instr))))]
(exec (modules "Main"))))
(run flc-program)