parser: Fix longstanding bug where "()" was required after CLI targets.
[goals.git] / src / parser.mly
index c3148ca..57a0c5c 100644 (file)
 open Utils
 open Printf
 
-(* This is initialized with Lexer.read once the program
- * starts.  Doing this avoids a circular dependency caused
- * by include files.
+(* There are several circular dependencies between the lexer
+ * (caused by includes) and eval.  These references break
+ * the circular dependencies.  They are initialized when
+ * the program starts, hence are never really None.
  *)
 let lexer_read = ref None
+let eval_substitute = ref None
 
 let find_on_include_path filename =
   if not (Filename.is_implicit filename) then filename
@@ -36,31 +38,34 @@ let find_on_include_path filename =
          let path = inc // filename in
          if Sys.file_exists path then path else loop incs
     in
-    loop Cmdline.includes
+    loop (Cmdline.includes ())
   )
 
 let do_include env loc filename optflag file =
-  let filename = Eval.substitute env loc filename in
+  let eval_substitute =
+    match !eval_substitute with None -> assert false | Some f -> f in
+  let filename = eval_substitute env loc filename in
   let filename = find_on_include_path filename in
   if optflag && not (Sys.file_exists filename) then env
   else (
     let fp = open_in filename in
     let lexbuf = Lexing.from_channel fp in
     lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = filename };
-    let reader =
+    let lexer_read =
       match !lexer_read with None -> assert false | Some r -> r in
-    let env' = file reader lexbuf in
+    let env' = file lexer_read lexbuf in
     close_in fp;
     Ast.Env.merge env env'
   )
 %}
 
 (* Tokens. *)
-%token <Ast.substs> CODE
+%token <Ast.code> CODE
 %token COLON
 %token COMMA
 %token EQUALS
 %token EOF
+%token EXPRESSION
 %token FUNCTION
 %token GOAL
 %token <string> ID
@@ -69,16 +74,20 @@ let do_include env loc filename optflag file =
 %token LEFT_PAREN
 %token LET
 %token OPTINCLUDE
+%token PURE
+%token RETURNING
 %token RIGHT_ARRAY
 %token RIGHT_PAREN
 %token SEMICOLON
 %token <Ast.substs> STRING
+%token STRING_KEYWORD
+%token STRINGS
 %token <string> TACTIC
 %token TACTIC_KEYWORD
 
 (* Start nonterminals. *)
 %start <Ast.expr Ast.Env.t> file
-%start <Ast.expr> expr
+%start <Ast.expr> expr_only
 %%
 
 file:
@@ -109,9 +118,13 @@ stmt:
       let name, params = $1 in
       name, Ast.EGoalDefn ($loc, (params, [], [], Some $2))
     }
-    | FUNCTION ID params_decl EQUALS CODE
+    | GOAL ID
     {
-      $2, Ast.EFuncDefn ($loc, ($3, $5))
+      $2, Ast.EGoalDefn ($loc, ([], [], [], None))
+    }
+    | option(PURE) FUNCTION ID params_decl return_decl EQUALS CODE
+    {
+      $3, Ast.EFuncDefn ($loc, ($4, $5, $1 <> None, $7))
     }
     | TACTIC_KEYWORD TACTIC params_decl EQUALS CODE
     {
@@ -129,13 +142,18 @@ params_decl:
     ;
 param_decl:
     | ID         { $1 }
+return_decl:
+    |            { RetExpr }
+    | RETURNING EXPRESSION { RetExpr }
+    | RETURNING STRINGS { RetStrings }
+    | RETURNING STRING_KEYWORD { RetString }
 
 patterns:
     | separated_list(COMMA, pattern) { $1 }
     ;
 pattern:
     | STRING     { Ast.PTactic ($loc, "*file", [$1]) }
-    | ID pattern_params { Ast.PTactic ($loc, $1, $2) }
+    | TACTIC pattern_params { Ast.PTactic ($loc, $1, $2) }
     ;
 pattern_params:
     | LEFT_PAREN separated_list(COMMA, pattern_param) RIGHT_PAREN { $2 }
@@ -152,8 +170,21 @@ expr:
     | LEFT_ARRAY barelist RIGHT_ARRAY { Ast.EList ($loc, $2) }
     ;
 barelist:
-    | separated_list(COMMA, expr) { $1 }
+    | right_flexible_list(COMMA, expr) { $1 }
     ;
 params:
     | LEFT_PAREN separated_list(COMMA, expr) RIGHT_PAREN { $2 }
     ;
+
+(* This is used by Parse.parse_expr where we have to parse
+ * a standalone string (eg. from the command line).
+ *)
+expr_only:
+    | expr EOF   { $1 }
+    ;
+
+(* http://gallium.inria.fr/blog/lr-lists/ *)
+right_flexible_list(delim, X):
+    | (* nothing *) { [] }
+    | x = X { [x] }
+    | x = X delim xs = right_flexible_list(delim, X) { x :: xs }