Command line parsing of anon args.
[goals.git] / src / main.ml
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 open Printf
21
22 open Utils
23
24 (* See also "let id" in [lexer.mll]. *)
25 let var_regexp = Str.regexp "\\([a-zA-Z_][a-zA-Z0-9_]*\\)=\\(.*\\)"
26
27 let usage =
28   "\
29 goals: Build software.
30
31  goals [-f Goalfile] ['var=value' ...] ['target' ...]
32
33 For detailed help see goals(1).
34
35 Options:"
36
37 let main () =
38   (* Command line arguments. *)
39   let filename = ref "Goalfile" in
40
41   let argspec = [
42     "-f",        Arg.Set_string filename,
43                  "filename Set name of Goalfile";
44     "--file",    Arg.Set_string filename,
45                  "filename Set name of Goalfile";
46   ] in
47   let argspec = Arg.align argspec in
48   let args = ref [] in
49   let anon_fun s = args := s :: !args in
50   Arg.parse argspec anon_fun usage;
51
52   (*let args = List.rev !args in*)
53   let filename = !filename in
54
55   (* Parse the input file. *)
56   let file = Parse.parse_goalfile filename in
57
58   (* Parse the command line anon args.  Each parameter has the
59    * form "name=<expr>" to assign a value to a variable, or
60    * "<expr>" to indicate a target to build.
61    *)
62   let assignments = ref [] in
63   let targets = ref [] in
64   List.iter (
65     fun arg ->
66       if Str.string_match var_regexp arg 0 then ( (* assignment *)
67         let name = Str.matched_group 1 arg in
68         let expr = Parse.parse_cli_expr (Str.matched_group 2 arg) in
69         assignments := Ast.Let (name, expr) :: !assignments
70       )
71       else ( (* target *)
72         let expr = Parse.parse_cli_expr arg in
73         targets := expr :: !targets
74       )
75   ) !args;
76
77   (* If no target was set on the command line, find
78    * the first goal in the file.
79    *)
80   if !targets = [] then (
81     try
82       let first_goal =
83         List.find (function Ast.Goal _ -> true | _ -> false) file in
84       match first_goal with
85       | Ast.Goal (name, [], _, _, _) ->
86          targets := [Ast.ECall (name, [])]
87       | Ast.Goal (name, _, _, _, _) ->
88          failwithf "%s: first target ā€˜%sā€™ has parameters and so cannot be used as the default target"
89            filename name
90       | _ -> assert false
91     with
92       (* Actually this is fine.  If there are no goals we'll do nothing. *)
93       Not_found -> ()
94   );
95
96   let targets = List.rev !targets in
97
98   (* Assignments are simply treated as statements added to the end of
99    * the file (so they override earlier assignments to the same variable,
100    * if any).
101    *)
102   let file = file @ List.rev !assignments in
103
104   (* We start with an empty symbol table. *)
105   let vars = Hashtbl.create 13 in
106
107   (* Evaluate the target expressions in turn. *)
108   Eval.evaluate file vars targets
109
110 let () = main ()