'OCaml map not updating before the next step in a sequence
I am trying to implement a basic parser, scanner, and a minimal language in OCaml. I believe the issue is that I'm trying to maintain a map between variables in this toy language and their values, and the language should be able to handle an expression like a=2;a and return 2. The name seems to successfully store the number 2, but by the time the program moves on to evaluating the second expression, it does not find the name in the map. And I can't understand why.
Below is the abstract syntax tree.
Ast.ml
type operator = Add (* for now just one operator *)
type expr =
Binop of expr * operator * expr
| Lit of int (* a number *)
| Seq of expr * expr (* a sequence, to behave like ";" *)
| Asn of string * expr (* assignment, to behave like "=" in an imperative language *)
| Var of string (* a variable *)
Here are the parser and scanner.
parser.mly
%{
open Ast
%}
%token SEQ PLUS ASSIGN EOF
%token <int> LITERAL
%token <string> VARIABLE
%left SEQ PLUS
%start expr
%type <Ast.expr> expr
%%
expr:
| expr SEQ expr { Seq($1, $3) }
| expr PLUS expr { Binop($1, Add, $3) }
| LITERAL { Lit($1) }
| VARIABLE { Var($1) }
| VARIABLE ASSIGN expr { Asn($1, $3) }
scanner.mll
{
open Parser
}
rule tokenize = parse
[' ' '\t' '\r' '\n'] { tokenize lexbuf }
| '+' { PLUS }
| ['0'-'9']+ as lit { LITERAL(int_of_string lit) }
| ['a'-'z']+ as id { VARIABLE(id) }
| '=' { ASSIGN }
| ';' { SEQ }
| eof { EOF }
And here's where I tried to implement a sort of name-space in a basic calculator.
calc.ml
open Ast
module StringMap = Map.Make(String)
let namespace = ref StringMap.empty
let rec eval exp = match exp with
| Lit(n) -> n
| Binop(e1, op, e2) ->
let v1 = eval e1 in
let v2 = eval e2 in
v1+v2
| Asn (name, e) ->
let v = eval e in
(namespace := StringMap.add name v !namespace; v)
| Var(name) -> StringMap.find name !namespace
| Seq(e1, e2) ->
(let _ = eval e1 in
eval e2)
let _ =
let lexbuf = Lexing.from_channel stdin in
let expr = Parser.expr Scanner.tokenize lexbuf in
let result = eval expr in
print_endline (string_of_int result)
To test it out I compile, and it compiles successfully, then run $ ./calc in a terminal, and enter a=2;a then press Ctrl+D. It should print 2 but it gives a Not found exception. Presumably this is coming from the StringMap.find line, and it's not finding the name in the namespace. I've tried throwing print lines around, and I think I can confirm that the sequence is being correctly processed in the terminal and that the first evaluation is happening successfully, with the name and value getting entered into the string map. But for some reason it seems not to be there when the program moves on to processing the second expression in the sequence.
I'd appreciate any clarification, thanks.
Solution 1:[1]
I cannot reproduce your error.
Feeding the AST directly to eval
let () =
let ast = Seq(Asn ("a", Lit 2),Var "a") in
let result = eval ast in
print_endline (string_of_int result)
prints 2 as expected.
After fixing your parser to recognize the end of the stream:
entry:
| expr EOF { $1 }
using it in
let () =
let s = Lexing.from_string "a=2;a\n" in
let ast = Parser.entry Scanner.tokenize s in
let result = eval ast in
print_endline (string_of_int result)
prints 2 as expected. And without this fix, your code fails with a syntax error.
EDIT:
Rather than using a makefile, I will advise to use dune, with the following simple dune file:
(menhir (modules parser))
(ocamllex scanner)
(executable (name calc))
it will at least solve your compilation troubles.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
