main parse.dats
  1#include "share/atspre_staload.hats"
  2#define ATS_DYNLOADFLAG 0
  3staload "scan.sats"
  4staload "parse.sats"
  5
  6exception ParseErr of ()
  7
  8fun parts_free(p: List0_vt(stringpart)): void =
  9    case+ p of
 10    | ~nil_vt ()           => ()
 11    | ~cons_vt(part, rest) => (
 12        (case+ part of
 13        | ~Literal (text) => strptr_free(text)
 14        | ~Variable(text) => strptr_free(text) );
 15        parts_free(rest) )
 16
 17fun bindings_free(b: List0_vt(binding)): void =
 18    case+ b of 
 19    (* tilde means destroy the list wrapper*)
 20    | ~nil_vt()             => ()
 21    | ~cons_vt(entry, rest) =>
 22    let
 23        (* construct new local variables(k,v) from 
 24         * the entry record *)
 25        val @{key = k, value = v} = entry
 26    in
 27        strptr_free  (k);
 28        parts_free   (v);
 29        bindings_free(rest)
 30    end
 31
 32fun rule_free(r: List0_vt(rule)): void =
 33    case+ r of
 34    | ~nil_vt() => ()
 35    | ~cons_vt(rule, rest) =>
 36    let 
 37        val @{name = n, bindings = b} = rule
 38    in
 39        strptr_free  (n);
 40        bindings_free(b);
 41        rule_free    (rest)
 42    end
 43
 44fun paths_free(p: List0_vt(Strptr1)): void = 
 45    case+ p of 
 46    | ~nil_vt() => ()
 47    | ~cons_vt(path, rest) => (
 48    strptr_free(path);
 49    paths_free(rest)) 
 50
 51fun edge_free(e: List0_vt(edge)): void = 
 52    case+ e of 
 53    | ~nil_vt() => ()
 54    | ~cons_vt(edge, rest) =>
 55    let 
 56        val @{output = o, rule = r, input = i, bindings = b} = edge
 57    in
 58        paths_free(o);
 59        strptr_free(r);
 60        paths_free(i);
 61        bindings_free(b);
 62        edge_free(rest)
 63end
 64
 65implement manifest_free(m) =
 66let
 67    val @{rules = r, edges = e, variables = v} = m
 68in
 69    rule_free(r);
 70    edge_free(e);
 71    bindings_free(v)
 72end
 73
 74fun binding_val(s: &scanner): List0_vt(stringpart) = 
 75let
 76    val ()   = skipspace(s)
 77    val ()   = scanchar(s, char2int0('='))
 78    val text = scanvalue(s) 
 79in
 80    if s.cur = char2int0('\n') then (
 81        next(s);
 82        text)
 83    else(
 84        parts_free(text);
 85        prerrln!("expected newline after value at", s.line,":",s.col );
 86        $raise ParseErr )
 87end
 88
 89(* creates records if it sees indentation and adds them to a list, if it stops
 90 * seeing indentation, return the list *) 
 91fun parsebindings(s: &scanner, b: List0_vt(binding)): List0_vt(binding) = 
 92    if scanindent(s) then 
 93        let
 94            val key   = scanname(s)
 95            val value = binding_val(s)
 96            val entry: binding = @{key = key, value = value}
 97            val more = cons_vt(entry, b)
 98        in
 99            parsebindings(s, more)
100        end
101    else(list_vt_reverse(b))
102   
103fun variable(s: &scanner, name: Strptr1): binding = 
104let
105    val value = binding_val(s)
106in 
107    @{key = name, value = value}
108end
109
110
111(* does a list of bindings contain a "command" binding *)
112fun hascommand(bindings: !List0_vt(binding)): bool =
113    case+ bindings of 
114    | nil_vt() => false
115    | @cons_vt(entry, rest) =>
116    let 
117        val found = if list_vt_length(entry.value) > 0 && entry.key = "command" then true
118        else hascommand(rest)
119    in
120        fold@(bindings);
121        found
122    end
123 
124fun rule(s: &scanner): rule = 
125let
126        val ()       = skipspace(s)
127        val rulename = scanname (s)
128in 
129    skipspace(s);
130    if s.cur = char2int0('\n') then (
131        next(s);
132        let 
133            val bindings = parsebindings(s, nil_vt()) 
134        in
135            if hascommand(bindings) = true then
136                @{name = rulename, bindings = bindings} 
137            else (
138                prerrln!("rule is missing 'command' binding");
139                strptr_free(rulename);
140                bindings_free(bindings);
141                $raise ParseErr )
142        end
143    ) else (
144        strptr_free(rulename);
145        prerrln!("expected newline after rule name at", s.line, ":", s.col);
146        $raise ParseErr())
147end
148
149(* collect the outputs of a build stmt assuming the scanner already 
150 * consumed 'build' 
151 *
152 * The 'collected' list is list0 because it starts empty. the  returned
153 * list is list1 because it must contain atleast one thing. in ATS, list0 
154 * is allowed to be empty, but list1 isnt. *)
155fun outputs (s: &scanner, collected: List0_vt(Strptr1)): List1_vt(Strptr1) =
156let 
157     val () = skipspace(s)
158in 
159    if (s.cur < 0) || (s.cur = char2int0('\n')) then (
160        paths_free(collected);
161        $raise ParseErr 
162    ) else if (s.cur = char2int0(':')) then (
163        if list_vt_length(collected) > 0 then // we need to do this check so that the compiler knows that we
164            list_vt_reverse(collected)        // are satisfying List1_vt (atleast one thing in the list)
165        else (
166            paths_free(collected);
167            prerrln!("expected at least one output");
168            $raise ParseErr() ) )
169    else
170        let
171             val path = scanpath(s)
172             val more = cons_vt(path, collected)
173        in
174             outputs(s, more)
175        end
176end
177
178fun inputs (s: &scanner, collected: List0_vt(Strptr1)): List0_vt(Strptr1) =
179let 
180     val () = skipspace(s)
181in 
182    if (s.cur < 0) || (s.cur = char2int0('\n')) then
183        list_vt_reverse(collected)
184    else if s.cur = char2int0(':') then (
185        paths_free(collected);
186        prerrln!("unexpected ':' at", s.line, ":", s.col);
187        $raise ParseErr
188    ) else 
189        let
190             val path = scanpath(s)
191             val more = cons_vt(path, collected)
192        in
193             inputs(s, more)
194        end
195end
196
197fun build (s: &scanner): edge =
198let
199    (* build out: rule in *)
200    val o   = outputs (s, nil_vt())
201    val ()  = scanchar(s, char2int0(':'))
202    val r   = scanname(s)
203    val i   = inputs  (s, nil_vt()) 
204in
205    if (s.cur = char2int0('\n')) then (
206        next(s);
207        let val b = parsebindings(s, nil_vt())
208        in @{output = o, rule = r, input = i, bindings = b}
209        end
210    ) else (
211	paths_free (o);
212        paths_free (i);
213        strptr_free(r);
214        $raise ParseErr 
215    )
216end
217
218(* does the rule refrenced in a build edge exist?*)
219fun hasrule(rules: !List0_vt(rule), name: !Strptr1): bool =
220    case+ rules of 
221    | nil_vt() => false
222    (*here @ means we open the list without freeing it *)
223    | @cons_vt(entry, rest) => 
224    let 
225        val found = 
226            if compare_strptr_strptr(entry.name, name) = 0 then true
227            else hasrule(rest, name)
228    in
229        (* close the list *)
230        fold@(rules);
231        found
232    end
233
234fun printparts(parts: !List0_vt(stringpart)): void =
235    case+ parts of
236    | nil_vt()             => ()
237    | @cons_vt(part, rest) => (
238        (case+ part of
239         | @Literal(text) => (print!(text); fold@(part))
240         | @Variable(name) => (print!("${", name, "}"); fold@(part)));
241        printparts(rest);
242        fold@(parts)
243    )
244
245fun printbindings(bindings: !List0_vt(binding)): void =
246    case+ bindings of 
247    | nil_vt() => ()
248    | @cons_vt(entry, rest) => (
249    print!("  binding: ", entry.key," = ");
250    printparts(entry.value);
251    println!();
252    printbindings(rest);
253    fold@(bindings) )
254 
255implement printrule(rules) =
256     case+ rules of
257     | nil_vt() => ()
258     | @cons_vt(entry, rest) => (
259     println!("rule: ", entry.name);
260     printbindings(entry.bindings);
261     printrule(rest);
262     fold@(rules) )
263
264implement parse(s, rules, edges, variables) (*manifest*) = 
265let
266    val tok = scankeyword(s)
267in
268    (case+ tok of
269    | ~TOKrule (line,col)       => (let 
270                                        val r    = rule(s);
271                                        val more = cons_vt(r, rules)
272                                    in
273                                        parse(s, more, edges, variables)
274                                    end)
275    | ~TOKbuild(line,col)       => (println!("build at: ", line,":", col);
276                                    (let 
277                                        val b = build(s)
278                                    in
279                                        if hasrule(rules, b.rule) then (
280                                            let 
281                                                val more = cons_vt(b, edges)
282                                            in
283                                                parse(s, rules, more, variables)
284                                            end
285                                        ) else (
286                                            prerrln!("unknown rule ", b.rule, " at ", line,":",col);
287                                            edge_free(cons_vt(b, nil_vt()));
288                                            bindings_free(variables);
289                                            rule_free(rules);
290                                            edge_free(edges);
291                                            $raise ParseErr)
292                                    end))
293    | ~TOKvar  (text, line,col) => (println!("var ", text, " at ", line,":", col);
294                                    let 
295                                        val entry = variable(s, text);
296                                        val more  = cons_vt(entry, variables)
297                                    in
298                                        parse(s, rules, edges, more)
299                                    end)
300    | ~TOKeof(line,col)         => (println!("eof at: ", line,":", col);
301                                   @{
302                                       rules = list_vt_reverse(rules),
303                                       edges = list_vt_reverse(edges),
304                                       variables = list_vt_reverse(variables)
305                                   }))
306end
307