commit 84bfbcb
shrub
·
2026-10-15 21:03:34 +0000 UTC
parent b34769b
handle variables
4 files changed,
+115,
-13
+27,
-5
1@@ -5,6 +5,15 @@ staload "parse.sats"
2
3 exception ParseErr of ()
4
5+fun parts_free(p: List0_vt(stringpart)): void =
6+ case+ p of
7+ | ~nil_vt () => ()
8+ | ~cons_vt(part, rest) => (
9+ (case+ part of
10+ | ~Literal (text) => strptr_free(text)
11+ | ~Variable(text) => strptr_free(text) );
12+ parts_free(rest) )
13+
14 fun bindings_free(b: List0_vt(binding)): void =
15 case+ b of
16 (* tilde means destroy the list wrapper*)
17@@ -16,7 +25,7 @@ fun bindings_free(b: List0_vt(binding)): void =
18 val @{key = k, value = v} = entry
19 in
20 strptr_free (k);
21- strptr_free (v);
22+ parts_free (v);
23 bindings_free(rest)
24 end
25
26@@ -61,7 +70,7 @@ in
27 bindings_free(v)
28 end
29
30-fun binding_val(s: &scanner): Strptr1 =
31+fun binding_val(s: &scanner): List0_vt(stringpart) =
32 let
33 val () = skipspace(s)
34 val () = scanchar(s, char2int0('='))
35@@ -71,7 +80,7 @@ in
36 next(s);
37 text)
38 else(
39- strptr_free(text);
40+ parts_free(text);
41 prerrln!("expected newline after value at", s.line,":",s.col );
42 $raise ParseErr )
43 end
44@@ -104,7 +113,7 @@ fun hascommand(bindings: !List0_vt(binding)): bool =
45 | nil_vt() => false
46 | @cons_vt(entry, rest) =>
47 let
48- val found = if strptr_length(entry.value) >0 && entry.key = "command" then true
49+ val found = if list_vt_length(entry.value) > 0 && entry.key = "command" then true
50 else hascommand(rest)
51 in
52 fold@(bindings);
53@@ -219,11 +228,24 @@ fun hasrule(rules: !List0_vt(rule), name: !Strptr1): bool =
54 found
55 end
56
57+fun printparts(parts: !List0_vt(stringpart)): void =
58+ case+ parts of
59+ | nil_vt() => ()
60+ | @cons_vt(part, rest) => (
61+ (case+ part of
62+ | @Literal(text) => (print!(text); fold@(part))
63+ | @Variable(name) => (print!("${", name, "}"); fold@(part)));
64+ printparts(rest);
65+ fold@(parts)
66+ )
67+
68 fun printbindings(bindings: !List0_vt(binding)): void =
69 case+ bindings of
70 | nil_vt() => ()
71 | @cons_vt(entry, rest) => (
72- println!(" binding: ", entry.key," = ",entry.value);
73+ print!(" binding: ", entry.key," = ");
74+ printparts(entry.value);
75+ println!();
76 printbindings(rest);
77 fold@(bindings) )
78
+1,
-1
1@@ -2,7 +2,7 @@ staload "scan.sats"
2
3 vtypedef binding = @{
4 key = Strptr1,
5- value = Strptr1
6+ value = List0_vt(stringpart)
7 }
8
9 vtypedef rule = @{
+86,
-6
1@@ -20,6 +20,10 @@ fun isnamec(c: int): bool =
2 fun isvaluec(c: int): bool =
3 (c != char2int0('\n'))
4
5+fun isliteralc(c: int): bool =
6+ (c != char2int0('\n')) &&
7+ (c != char2int0 ('$'))
8+
9 fun ispathc(c: int): bool =
10 (c != char2int0('\n')) &&
11 (c != char2int0 (' ')) &&
12@@ -52,7 +56,87 @@ fun read(s: &scanner, allowed: (int) -<fun1> bool): void =
13 end
14 else ()
15
16+fun readliteral(s: &scanner): Strptr1 =
17+let
18+ val () = read(s, isliteralc)
19+in
20+ stringbuf_takeout_all(s.buf)
21+end
22
23+fun isvarc(c: int): bool =
24+ isnamec(c) &&
25+ c != char2int0('.')
26+
27+fun dollar(s: &scanner): stringpart =
28+let
29+ (* assume the scanner is sitting on $ *)
30+ val () = next(s)
31+in
32+ if s.cur < 0 then (
33+ prerrln!("unexpected EOF after '$'");
34+ $raise ScanErr()
35+ ) else
36+ case+ int2char0(s.cur) of
37+ | '$' => (
38+ let val inserted = stringbuf_insert_char(s.buf, '$')
39+ in if inserted = 1 then (next(s); Literal(stringbuf_takeout_all(s.buf)) ) else $raise ScanErr
40+ end )
41+ | ':' => (
42+ let val inserted = stringbuf_insert_char(s.buf, ':')
43+ in if inserted = 1 then (next(s); Literal(stringbuf_takeout_all(s.buf)) ) else $raise ScanErr
44+ end )
45+ | '\{' =>
46+ let
47+ val () = next(s)
48+ val () = read(s, isnamec)
49+ in
50+ if stringbuf_get_size(s.buf) = 0 then (
51+ prerrln!("expected variable name");
52+ $raise ScanErr()
53+ ) else (
54+ if s.cur = char2int0('}') then (
55+ next(s);
56+ Variable(stringbuf_takeout_all(s.buf))
57+ ) else (
58+ prerrln!("unclosed variable brace");
59+ $raise ScanErr() )
60+ )
61+ end
62+ | _ =>
63+ let
64+ val () = read(s, isvarc)
65+ in
66+ if stringbuf_get_size(s.buf) = 0 then (
67+ prerrln!("expected variable name");
68+ $raise ScanErr
69+ ) else
70+ Variable(stringbuf_takeout_all(s.buf))
71+ end
72+
73+end
74+
75+fun parts (s: &scanner, collected: List0_vt(stringpart)): List0_vt(stringpart) =
76+ if s.cur < 0 then
77+ list_vt_reverse(collected)
78+ else
79+ case+ int2char0(s.cur) of
80+ | '\n' => list_vt_reverse(collected)
81+ | '$' =>
82+ let
83+ val newpart = dollar(s);
84+ val more = cons_vt(newpart, collected)
85+ in
86+ parts(s, more)
87+ end
88+ | _ =>
89+ let
90+ val newpart = readliteral(s)
91+ val litpart = Literal(newpart)
92+ val more = cons_vt(litpart, collected)
93+ in
94+ parts(s, more)
95+ end
96+
97 implement scanname(s) (*Strptr1*)=
98 let
99 val () = read(s, isnamec)
100@@ -118,12 +202,8 @@ implement scankeyword(s) =
101 else (prerrln!("expected name at ",s.line, ":", s.col);
102 $raise ScanErr())
103
104-implement scanvalue(s) (*Strptr1*) =
105-let
106- val () = read(s, isvaluec)
107-in
108- stringbuf_takeout_all(s.buf)
109-end
110+implement scanvalue(s) (*List0_vt(stringpart)*) =
111+ parts(s, nil_vt())
112
113 implement scanpath(s) =
114 let
+1,
-1
1@@ -28,6 +28,6 @@ fun mktok (text: Strptr1,
2 fun scanchar (s: &scanner, wanted: int): void
3 fun scanindent (s: &scanner): bool
4 fun scankeyword(s: &scanner): tok
5-fun scanvalue (s: &scanner): Strptr1
6+fun scanvalue (s: &scanner): List0_vt(stringpart)
7 fun scanpath (s: &scanner): Strptr1
8