X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Fparser.mly;h=57a0c5cdb59b912d342b61097e385db49cb6ea69;hb=e23952e03c37201fe805ad3d1635e7d2d41e5908;hp=add72bb74106d6553d6a55a2f075e7b80c679760;hpb=8e1d9182f7573112cb36c7d0ff0c8612f34ffd57;p=goals.git diff --git a/src/parser.mly b/src/parser.mly index add72bb..57a0c5c 100644 --- a/src/parser.mly +++ b/src/parser.mly @@ -18,29 +18,76 @@ *) %{ +open Utils open Printf + +(* 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 + else ( + let rec loop = function + | [] -> filename + | inc :: incs -> + let path = inc // filename in + if Sys.file_exists path then path else loop incs + in + loop (Cmdline.includes ()) + ) + +let do_include env loc filename optflag file = + 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 lexer_read = + match !lexer_read with None -> assert false | Some r -> r in + let env' = file lexer_read lexbuf in + close_in fp; + Ast.Env.merge env env' + ) %} (* Tokens. *) -%token CODE +%token CODE %token COLON %token COMMA -%token ID %token EQUALS %token EOF +%token EXPRESSION +%token FUNCTION %token GOAL +%token ID +%token INCLUDE %token LEFT_ARRAY %token LEFT_PAREN %token LET +%token OPTINCLUDE +%token PURE +%token RETURNING %token RIGHT_ARRAY %token RIGHT_PAREN +%token SEMICOLON %token STRING - -%type stmt +%token STRING_KEYWORD +%token STRINGS +%token TACTIC +%token TACTIC_KEYWORD (* Start nonterminals. *) -%start file -%start expr +%start file +%start expr_only %% file: @@ -48,41 +95,65 @@ file: ; stmts: - | list(stmt) { $1 } + | (* none *) { Ast.Env.empty } + | stmts INCLUDE STRING option(SEMICOLON) + { do_include $1 $loc $3 false file } + | stmts OPTINCLUDE STRING option(SEMICOLON) + { do_include $1 $loc $3 true file } + | stmts stmt option(SEMICOLON) + { let name, expr = $2 in Ast.Env.add name expr $1 } ; + stmt: | option(goal_stmt) patterns COLON barelist option(CODE) { let name, params = match $1 with | None -> - let pos = $startpos in - sprintf "_goal@%d" pos.pos_lnum, [] + sprintf "_goal@%d" $startpos.pos_lnum, [] | Some x -> x in - Ast.Goal (name, params, $2, $4, $5) + name, Ast.EGoalDefn ($loc, (params, $2, $4, $5)) } | goal_stmt CODE { let name, params = $1 in - Ast.Goal (name, params, [], [], Some $2) + name, Ast.EGoalDefn ($loc, (params, [], [], Some $2)) + } + | GOAL ID + { + $2, Ast.EGoalDefn ($loc, ([], [], [], None)) } - | LET ID EQUALS expr { Ast.Let ($2, $4) } + | 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 + { + $2, Ast.ETacticDefn ($loc, ($3, $5)) + } + | LET ID EQUALS expr { $2, $4 } ; goal_stmt: - | GOAL ID option(param_decl) EQUALS + | GOAL ID option(params_decl) EQUALS { $2, match $3 with None -> [] | Some ps -> ps } ; -param_decl: - | LEFT_PAREN separated_list(COMMA, ID) RIGHT_PAREN { $2 } +params_decl: + | LEFT_PAREN separated_list(COMMA, param_decl) RIGHT_PAREN { $2 } ; +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 ("file", [$1]) } - | ID pattern_params { Ast.PTactic ($1, $2) } - | ID { Ast.PVarSubst $1 } + | STRING { Ast.PTactic ($loc, "*file", [$1]) } + | TACTIC pattern_params { Ast.PTactic ($loc, $1, $2) } ; pattern_params: | LEFT_PAREN separated_list(COMMA, pattern_param) RIGHT_PAREN { $2 } @@ -92,14 +163,28 @@ pattern_param: ; expr: - | ID params { Ast.ECall ($1, $2) } - | ID { Ast.EVar $1 (* This might be replaced with ECall later. *) } - | STRING { Ast.EString $1 } - | LEFT_ARRAY barelist RIGHT_ARRAY { Ast.EList $2 } + | ID params { Ast.ECall ($loc, $1, $2) } + | ID { Ast.EVar ($loc, $1) } + | TACTIC params { Ast.ETacticCtor ($loc, $1, $2) } + | STRING { Ast.ESubsts ($loc, $1) } + | 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 }