Implementing interpreters in Clojurescript

const : only one numerical constant (of integral type). addsub : additions and subtraction. addmult : additions, subtractions, multiplications and division, with priorities arith : same as addmult, with parenthesis. lang0 : any number of expressions that can be either arithmetic expressions for the arith language or assignment of the result of such an expression into a variable. Expressions can also use variables. The code returns the value of the last expression. lang1 : same as lang0 with arguments written as %0, %1,…

Const language

Parser

number= '-'? ('0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9')+

[:number "-" "1" "2" "3"]

NUMBER= #'-?[0-9]+'

(ns interpreters.core (:require [instaparse.core :as insta])) (def const-parser (insta/parser "PROG= NUMBER NUMBER= #'-?[0-9]+'")) (const-parser "-1243")

(const-parser " -123456 ")

Interpreter

(defn const-interpreter [ast] (instaparse.core/transform {:prog identity :number (fn[s](js/parseInt s))} ast)) (-> " -123" const-parser const-interpreter)

(defn dynamic-eval [interpreter] (fn[ast] (fn[] (instaparse.core/transform interpreter ast))))

(def const-interpreting {:prog identity :number (fn[s](js/parseInt s))}) (def const-eval (dynamic-eval const-interpreting))

(def const-eval-test (-> "-123 " const-parser const-eval))

(const-eval-test)

AddSub language

Parser

(addsub-parser " 1 +2-3-1")

Interpreter

(def addsub-interpreting (assoc const-interpreting :add + :sub -))

(def addsub-eval (dynamic-eval addsub-interpreting)) (def addsub-eval-test (-> "1+2-3-1" addsub-parser addsub-eval)) (addsub-eval-test)

AddMult language

Parser

(addmult-parser "1 + 3 * -2 -1")

Interpreter

(def addmult-interpreting (assoc addsub-interpreting :mult * :div /))

(def addmult-eval (dynamic-eval addmult-interpreting ))

(def addmult-eval-test (-> "1 + 3 * -2 -1" addmult-parser addmult-eval)) (addmult-eval-test)

Arith language

Parser

(arith-parser "(1 + 3) * -2 -1")

Interpreter

(def arith-eval-test (-> "(1 + 3) * -2 -1" arith-parser addmult-eval)) (arith-eval-test)

Lang0

Parser

a program as a sequence of expressions

an expression that can be either an assignment or an arithmetic expression

variables that can either be used on the left hand side of an assignment or as a read (varget) on the right hand side

factor that replaces term and also allows for varget or assignments (the value of the assignment expression is the assigned value)

(lang0-parser "a=1+1*3;b=a-2; a+b;")

Interpreter

(defn make-interpreting [make-instr-interpreting init-env] {:prog (fn [& instrs] (:_ret (reduce (fn[env instr] (instaparse.core/transform (make-instr-interpreting env) instr)) init-env instrs)))}) (defn make-lang0-instr-interpreting [env] { :assig (fn[{varname :_ret :as env1} {value :_ret :as env2}] (assoc (merge env1 env2) varname value :_ret value)) :add (fn[{v1 :_ret :as env1} {v2 :_ret :as env2}] (assoc (merge env1 env2) :_ret (+ v1 v2))) :sub (fn[{v1 :_ret :as env1} {v2 :_ret :as env2}] (assoc (merge env1 env2) :_ret (- v1 v2))) :mult (fn[{v1 :_ret :as env1} {v2 :_ret :as env2}] (assoc (merge env1 env2) :_ret (* v1 v2))) :div (fn[{v1 :_ret :as env1} {v2 :_ret :as env2}] (assoc (merge env1 env2) :_ret (quot v1 v2))) :number #(assoc env :_ret (js/parseInt %)) :varname #(assoc env :_ret (keyword %)) :varget (fn [{varname :_ret :as env1}] (assoc env1 :_ret (varname env1)))})

(def lang0-interpret (dynamic-eval (make-interpreting make-lang0-instr-interpreting {:_ret 0})))

(def lang0-interpret-test (->> "a=1+(2-1)*3;b=a-2; a+b;" lang0-parser lang0-interpret)) (lang0-interpret-test)

Lang1

Parser

(lang1-parser "a=%0;a + %1 *3;")

Interpreter

(defn args-to-env[args] (into {} (map-indexed #(vector (keyword (str "%" %1)) %2) args))) (defn dynamic-eval-args [make-interpreter] (fn[ast] (fn[& args] (instaparse.core/transform (make-interpreting make-interpreter (assoc (args-to-env args) :_ret 0)) ast))))

(defn make-lang1-instr-interpreting [env] (assoc (make-lang0-instr-interpreting env) :argument #(assoc env :_ret (keyword (str "%" %)))))

(def lang1-interpret (dynamic-eval-args make-lang1-instr-interpreting))