aca014eecb089e01177a04a1ec40eed56b40f388
[goals.git] / src / parser.mly
1 (* Goalfile parser
2  * Copyright (C) 2019 Richard W.M. Jones
3  * Copyright (C) 2019 Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  *)
19
20 %{
21 open Utils
22 open Printf
23
24 (* There are several circular dependencies between the lexer
25  * (caused by includes) and eval.  These references break
26  * the circular dependencies.  They are initialized when
27  * the program starts, hence are never really None.
28  *)
29 let lexer_read = ref None
30 let eval_substitute = ref None
31
32 let find_on_include_path filename =
33   if not (Filename.is_implicit filename) then filename
34   else (
35     let rec loop = function
36       | [] -> filename
37       | inc :: incs ->
38          let path = inc // filename in
39          if Sys.file_exists path then path else loop incs
40     in
41     loop Cmdline.includes
42   )
43
44 let do_include env loc filename optflag file =
45   let eval_substitute =
46     match !eval_substitute with None -> assert false | Some f -> f in
47   let filename = eval_substitute env loc filename in
48   let filename = find_on_include_path filename in
49   if optflag && not (Sys.file_exists filename) then env
50   else (
51     let fp = open_in filename in
52     let lexbuf = Lexing.from_channel fp in
53     lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = filename };
54     let lexer_read =
55       match !lexer_read with None -> assert false | Some r -> r in
56     let env' = file lexer_read lexbuf in
57     close_in fp;
58     Ast.Env.merge env env'
59   )
60 %}
61
62 (* Tokens. *)
63 %token <Ast.substs> CODE
64 %token COLON
65 %token COMMA
66 %token EQUALS
67 %token EOF
68 %token FUNCTION
69 %token GOAL
70 %token <string> ID
71 %token INCLUDE
72 %token LEFT_ARRAY
73 %token LEFT_PAREN
74 %token LET
75 %token OPTINCLUDE
76 %token RIGHT_ARRAY
77 %token RIGHT_PAREN
78 %token SEMICOLON
79 %token <Ast.substs> STRING
80 %token <string> TACTIC
81 %token TACTIC_KEYWORD
82
83 (* Start nonterminals. *)
84 %start <Ast.expr Ast.Env.t> file
85 %start <Ast.expr> expr
86 %%
87
88 file:
89     | stmts EOF  { $1 }
90     ;
91
92 stmts:
93     | (* none *) { Ast.Env.empty }
94     | stmts INCLUDE STRING option(SEMICOLON)
95     { do_include $1 $loc $3 false file }
96     | stmts OPTINCLUDE STRING option(SEMICOLON)
97     { do_include $1 $loc $3 true file }
98     | stmts stmt option(SEMICOLON)
99     { let name, expr = $2 in Ast.Env.add name expr $1 }
100     ;
101
102 stmt:
103     | option(goal_stmt) patterns COLON barelist option(CODE)
104     { let name, params =
105         match $1 with
106         | None ->
107            sprintf "_goal@%d" $startpos.pos_lnum, []
108         | Some x -> x in
109       name, Ast.EGoalDefn ($loc, (params, $2, $4, $5))
110     }
111     | goal_stmt CODE
112     {
113       let name, params = $1 in
114       name, Ast.EGoalDefn ($loc, (params, [], [], Some $2))
115     }
116     | FUNCTION ID params_decl EQUALS CODE
117     {
118       $2, Ast.EFuncDefn ($loc, ($3, $5))
119     }
120     | TACTIC_KEYWORD TACTIC params_decl EQUALS CODE
121     {
122       $2, Ast.ETacticDefn ($loc, ($3, $5))
123     }
124     | LET ID EQUALS expr { $2, $4 }
125     ;
126
127 goal_stmt:
128     | GOAL ID option(params_decl) EQUALS
129     { $2, match $3 with None -> [] | Some ps -> ps }
130     ;
131 params_decl:
132     | LEFT_PAREN separated_list(COMMA, param_decl) RIGHT_PAREN { $2 }
133     ;
134 param_decl:
135     | ID         { $1 }
136
137 patterns:
138     | separated_list(COMMA, pattern) { $1 }
139     ;
140 pattern:
141     | STRING     { Ast.PTactic ($loc, "*file", [$1]) }
142     | ID pattern_params { Ast.PTactic ($loc, $1, $2) }
143     ;
144 pattern_params:
145     | LEFT_PAREN separated_list(COMMA, pattern_param) RIGHT_PAREN { $2 }
146     ;
147 pattern_param:
148     | STRING     { $1 }
149     ;
150
151 expr:
152     | ID params  { Ast.ECall ($loc, $1, $2) }
153     | ID         { Ast.EVar ($loc, $1) }
154     | TACTIC params { Ast.ETacticCtor ($loc, $1, $2) }
155     | STRING     { Ast.ESubsts ($loc, $1) }
156     | LEFT_ARRAY barelist RIGHT_ARRAY { Ast.EList ($loc, $2) }
157     ;
158 barelist:
159     | right_flexible_list(COMMA, expr) { $1 }
160     ;
161 params:
162     | LEFT_PAREN separated_list(COMMA, expr) RIGHT_PAREN { $2 }
163     ;
164
165 (* http://gallium.inria.fr/blog/lr-lists/ *)
166 right_flexible_list(delim, X):
167     | (* nothing *) { [] }
168     | x = X { [x] }
169     | x = X delim xs = right_flexible_list(delim, X) { x :: xs }