c3148cae191b50a466fa4cf09ab48d2e7037c45d
[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 (* This is initialized with Lexer.read once the program
25  * starts.  Doing this avoids a circular dependency caused
26  * by include files.
27  *)
28 let lexer_read = ref None
29
30 let find_on_include_path filename =
31   if not (Filename.is_implicit filename) then filename
32   else (
33     let rec loop = function
34       | [] -> filename
35       | inc :: incs ->
36          let path = inc // filename in
37          if Sys.file_exists path then path else loop incs
38     in
39     loop Cmdline.includes
40   )
41
42 let do_include env loc filename optflag file =
43   let filename = Eval.substitute env loc filename in
44   let filename = find_on_include_path filename in
45   if optflag && not (Sys.file_exists filename) then env
46   else (
47     let fp = open_in filename in
48     let lexbuf = Lexing.from_channel fp in
49     lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = filename };
50     let reader =
51       match !lexer_read with None -> assert false | Some r -> r in
52     let env' = file reader lexbuf in
53     close_in fp;
54     Ast.Env.merge env env'
55   )
56 %}
57
58 (* Tokens. *)
59 %token <Ast.substs> CODE
60 %token COLON
61 %token COMMA
62 %token EQUALS
63 %token EOF
64 %token FUNCTION
65 %token GOAL
66 %token <string> ID
67 %token INCLUDE
68 %token LEFT_ARRAY
69 %token LEFT_PAREN
70 %token LET
71 %token OPTINCLUDE
72 %token RIGHT_ARRAY
73 %token RIGHT_PAREN
74 %token SEMICOLON
75 %token <Ast.substs> STRING
76 %token <string> TACTIC
77 %token TACTIC_KEYWORD
78
79 (* Start nonterminals. *)
80 %start <Ast.expr Ast.Env.t> file
81 %start <Ast.expr> expr
82 %%
83
84 file:
85     | stmts EOF  { $1 }
86     ;
87
88 stmts:
89     | (* none *) { Ast.Env.empty }
90     | stmts INCLUDE STRING option(SEMICOLON)
91     { do_include $1 $loc $3 false file }
92     | stmts OPTINCLUDE STRING option(SEMICOLON)
93     { do_include $1 $loc $3 true file }
94     | stmts stmt option(SEMICOLON)
95     { let name, expr = $2 in Ast.Env.add name expr $1 }
96     ;
97
98 stmt:
99     | option(goal_stmt) patterns COLON barelist option(CODE)
100     { let name, params =
101         match $1 with
102         | None ->
103            sprintf "_goal@%d" $startpos.pos_lnum, []
104         | Some x -> x in
105       name, Ast.EGoalDefn ($loc, (params, $2, $4, $5))
106     }
107     | goal_stmt CODE
108     {
109       let name, params = $1 in
110       name, Ast.EGoalDefn ($loc, (params, [], [], Some $2))
111     }
112     | FUNCTION ID params_decl EQUALS CODE
113     {
114       $2, Ast.EFuncDefn ($loc, ($3, $5))
115     }
116     | TACTIC_KEYWORD TACTIC params_decl EQUALS CODE
117     {
118       $2, Ast.ETacticDefn ($loc, ($3, $5))
119     }
120     | LET ID EQUALS expr { $2, $4 }
121     ;
122
123 goal_stmt:
124     | GOAL ID option(params_decl) EQUALS
125     { $2, match $3 with None -> [] | Some ps -> ps }
126     ;
127 params_decl:
128     | LEFT_PAREN separated_list(COMMA, param_decl) RIGHT_PAREN { $2 }
129     ;
130 param_decl:
131     | ID         { $1 }
132
133 patterns:
134     | separated_list(COMMA, pattern) { $1 }
135     ;
136 pattern:
137     | STRING     { Ast.PTactic ($loc, "*file", [$1]) }
138     | ID pattern_params { Ast.PTactic ($loc, $1, $2) }
139     ;
140 pattern_params:
141     | LEFT_PAREN separated_list(COMMA, pattern_param) RIGHT_PAREN { $2 }
142     ;
143 pattern_param:
144     | STRING     { $1 }
145     ;
146
147 expr:
148     | ID params  { Ast.ECall ($loc, $1, $2) }
149     | ID         { Ast.EVar ($loc, $1) }
150     | TACTIC params { Ast.ETacticCtor ($loc, $1, $2) }
151     | STRING     { Ast.ESubsts ($loc, $1) }
152     | LEFT_ARRAY barelist RIGHT_ARRAY { Ast.EList ($loc, $2) }
153     ;
154 barelist:
155     | separated_list(COMMA, expr) { $1 }
156     ;
157 params:
158     | LEFT_PAREN separated_list(COMMA, expr) RIGHT_PAREN { $2 }
159     ;