Ok, now we can parse the individual statements. Let’s start with easier ones: incr and decr. What we ant is a hash-map which looks like this:
1 2 3 4 |
(parse-statement "incr i") -> {:incr "i"} (parse-statement "decr i") -> {:incr "i"} (parse-statement "while j /= 0 do") -> {:while "j"} (parse-statement "end") -> {:end nil} |
What we want to do is pattern-matching. I will some regex for each one which works fine here for individual statements. The variable names can consist of digits, lower and uppercase letters and underscores.
Here’s the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
(def statement-patterns [#"(incr) (\w+)" #"(decr) (\w+)" #"(while) (\w+) /= 0 do" #"(end)"]) (defn parse-statement "Parses a given statement and returns a hashmap with optional variable" [string] (let [matches (map #(re-matches % string) statement-patterns) [_ statement & var] (first (filter identity matches))] (if (not (nil? statement)) (hash-map (keyword statement) (first var)) nil))) (deftest test-parse (is (= (parse-statement "incr foo") {:incr "foo"})) (is (= (parse-statement "decr bar42") {:decr "bar42"})) (is (= (parse-statement "while 4foo /= 0 do") {:while "4foo"})) (is (= (parse-statement "end") {:end nil})) (is (= (parse-statement "foobaz i") nil))) |
The next thing I want to do is to create variables dynamically. I integrate this into the function decr and incr first, just because it’s easier. The creation of variables int he while statement is a special case because automatically it won’t do the stuff in it because the variable is bound to 0 intially.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
(defn dec-limited "Decreases a variable by one but only down to 0" [x] (if (= x 0) 0 (dec x))) ; partial function for do-var (def var-manipulation (fn [state varname fun] (assoc state varname (if (contains? state varname) (fun (state varname)) (fun 0))))) (defn do-var "Takes a state, varname and command and returns a new modified state" [state varname command] (cond (= command :incr) (var-manipulation state varname inc) (= command :decr) (var-manipulation state varname dec-limited))) (deftest test-do-var (def state {"A" 0 "B" 2}) (is (= (do-var state "A" :decr) {"A" 0 "B" 2})) (is (= (do-var state "B" :incr) {"A" 0 "B" 3})) (is (= (do-var state "C" :decr) {"A" 0 "B" 2 "C" 0})) (is (= (do-var state "C" :incr) {"A" 0 "B" 2 "C" 1}))) |
Last part tomorrow