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