Update TODO.
[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.code> CODE
64 %token COLON
65 %token COMMA
66 %token EQUALS
67 %token EOF
68 %token EXPRESSION
69 %token FUNCTION
70 %token GOAL
71 %token <string> ID
72 %token INCLUDE
73 %token LEFT_ARRAY
74 %token LEFT_PAREN
75 %token LET
76 %token OPTINCLUDE
77 %token PURE
78 %token RETURNING
79 %token RIGHT_ARRAY
80 %token RIGHT_PAREN
81 %token SEMICOLON
82 %token <Ast.substs> STRING
83 %token STRING_KEYWORD
84 %token STRINGS
85 %token <string> TACTIC
86 %token TACTIC_KEYWORD
87
88 (* Start nonterminals. *)
89 %start <Ast.expr Ast.Env.t> file
90 %start <Ast.expr> expr
91 %%
92
93 file:
94     | stmts EOF  { $1 }
95     ;
96
97 stmts:
98     | (* none *) { Ast.Env.empty }
99     | stmts INCLUDE STRING option(SEMICOLON)
100     { do_include $1 $loc $3 false file }
101     | stmts OPTINCLUDE STRING option(SEMICOLON)
102     { do_include $1 $loc $3 true file }
103     | stmts stmt option(SEMICOLON)
104     { let name, expr = $2 in Ast.Env.add name expr $1 }
105     ;
106
107 stmt:
108     | option(goal_stmt) patterns COLON barelist option(CODE)
109     { let name, params =
110         match $1 with
111         | None ->
112            sprintf "_goal@%d" $startpos.pos_lnum, []
113         | Some x -> x in
114       name, Ast.EGoalDefn ($loc, (params, $2, $4, $5))
115     }
116     | goal_stmt CODE
117     {
118       let name, params = $1 in
119       name, Ast.EGoalDefn ($loc, (params, [], [], Some $2))
120     }
121     | option(PURE) FUNCTION ID params_decl return_decl EQUALS CODE
122     {
123       $3, Ast.EFuncDefn ($loc, ($4, $5, $1 <> None, $7))
124     }
125     | TACTIC_KEYWORD TACTIC params_decl EQUALS CODE
126     {
127       $2, Ast.ETacticDefn ($loc, ($3, $5))
128     }
129     | LET ID EQUALS expr { $2, $4 }
130     ;
131
132 goal_stmt:
133     | GOAL ID option(params_decl) EQUALS
134     { $2, match $3 with None -> [] | Some ps -> ps }
135     ;
136 params_decl:
137     | LEFT_PAREN separated_list(COMMA, param_decl) RIGHT_PAREN { $2 }
138     ;
139 param_decl:
140     | ID         { $1 }
141 return_decl:
142     |            { RetExpr }
143     | RETURNING EXPRESSION { RetExpr }
144     | RETURNING STRINGS { RetStrings }
145     | RETURNING STRING_KEYWORD { RetString }
146
147 patterns:
148     | separated_list(COMMA, pattern) { $1 }
149     ;
150 pattern:
151     | STRING     { Ast.PTactic ($loc, "*file", [$1]) }
152     | TACTIC pattern_params { Ast.PTactic ($loc, $1, $2) }
153     ;
154 pattern_params:
155     | LEFT_PAREN separated_list(COMMA, pattern_param) RIGHT_PAREN { $2 }
156     ;
157 pattern_param:
158     | STRING     { $1 }
159     ;
160
161 expr:
162     | ID params  { Ast.ECall ($loc, $1, $2) }
163     | ID         { Ast.EVar ($loc, $1) }
164     | TACTIC params { Ast.ETacticCtor ($loc, $1, $2) }
165     | STRING     { Ast.ESubsts ($loc, $1) }
166     | LEFT_ARRAY barelist RIGHT_ARRAY { Ast.EList ($loc, $2) }
167     ;
168 barelist:
169     | right_flexible_list(COMMA, expr) { $1 }
170     ;
171 params:
172     | LEFT_PAREN separated_list(COMMA, expr) RIGHT_PAREN { $2 }
173     ;
174
175 (* http://gallium.inria.fr/blog/lr-lists/ *)
176 right_flexible_list(delim, X):
177     | (* nothing *) { [] }
178     | x = X { [x] }
179     | x = X delim xs = right_flexible_list(delim, X) { x :: xs }