0

Very close to getting this to work but having trouble with StringMap from OCaml. Essentially I'm making a calculator which takes in from ocamllex the lexical stream ... so here commas are supposed to separate out our expressions while equal signs means that we will be assigning a value to a variable.

I realized that when assigning variables I'm not able to look them up because I get (Fatal error: exception Not_found) on the lines for not finding the key's I added in the Var case of the function. I don't know where to put the StringMap.empty or how to make it visible in this function... I was wondering why it can't find what I'm adding in the equals case?

Here's my code.

open Ast

module StringMap = Map.Make(String)

let varMap = StringMap.empty

let rec parser = function

  Lit(x) -> x
| Binop(e1, op, e2) -> (
      let v1 = parser e1 and v2 = parser e2 in
      match op with
            Add -> v1 + v2
            | Sub -> v1 - v2
            | Mul -> v1 * v2
            | Div -> v1 / v2
      )
| Var(v) -> StringMap.find v varMap
| Statements(e1, e2) ->   ignore(parser e1); parser e2
| Equals(v, e1) ->  StringMap.add v e1 varMap; parser e1

let _ =
  let LexingBuffer = Lexing.from_channel stdin in
  let expression = Parser.expression Scanner.token LexingBuffer in
  let result = parser expression in
  print_endline (string_of_int result)
  • Do you not get a warning form the compiler on `StringMap.add v e1 varMap;` saying you're just throwing the value away? – glennsl Mar 10 '19 at 18:44
  • @glennsl I did get "Warning 10: this expression should have type unit." on the line you're talking about but I thought that was b/c I'm not returning an int like the other cases and was ok... am I really using add wrong? In that line I'm only doing the add to map first and then returning the expression value after? – growingSmarter Mar 10 '19 at 18:52

2 Answers2

3

Map is an immutable data structure. Any function that changes the map will return a new modified instance, while leaving the previous one unaltered. Therefore, this expression

StringMap.add v e1 varMap; parser e1

will just throw the new map away, and return whatever the recursive call to parser returns instead. If you want to use Map you have to keep the new instance around. You can do so either by making varMap a ref cell that you update:

let varMap = ref StringMap.empty

...

varMap := StringMap.add v1 !varMap

or by adding a function argument to parser and pass it along:

let rec parser varMap = function

...

parser (StringMap.add v e1 varMap) e1

Another option is to use a Hashtbl instead, which is mutable and works very well with strings.

glennsl
  • 28,186
  • 12
  • 57
  • 75
1

Maps are immutable. The line

StringMap.add v e1 varMap; parser e1

is thus computing an updated map and then discard it immediately. In other words, the map varMap is always empty.

To avoid discarding the environment map, you should track it in the function parser itself by adding it as an argument of the function — which you might rename to eval:

  let rec eval env = function
  | ...
octachron
  • 17,178
  • 2
  • 16
  • 23
  • Is there a better / another way without changing the function prototype (what it's taking in... I tried moving the "let varMap = StringMap.empty" into the rec function but it didn't like that ... I see between you and @glennsl that I guess the map is getting discarded after I add value... I need to know how to make this map data struct in this function and I'm set I guess – growingSmarter Mar 10 '19 at 18:55
  • 1
    Hashmap can be used indeed, and it would be simpler since you don't have local definitions(scopes) in your language. Nevertheless, using an immutable environment might be a good exercice. – octachron Mar 10 '19 at 18:59