main scan.dats
  1#include "share/atspre_staload.hats"
  2#define ATS_DYNLOADFLAG 0
  3staload "libats/libc/SATS/stdio.sats"
  4staload "libats/SATS/stringbuf.sats"
  5staload "libats/DATS/stringbuf.dats"
  6staload "scan.sats"
  7
  8exception ScanErr of ()
  9
 10(*get one character from a file and return the corresponding int*)
 11fun char (manifest: FILEref): int =
 12    fgetc0(manifest)
 13
 14fun isnamec(c: int): bool = 
 15    isalnum(c)           || 
 16    (c = char2int0('_')) || 
 17    (c = char2int0('-')) ||
 18    (c = char2int0('.'))
 19
 20fun isvaluec(c: int): bool = 
 21    (c != char2int0('\n'))
 22
 23fun isliteralc(c: int): bool = 
 24    (c != char2int0('\n')) &&
 25    (c != char2int0 ('$'))
 26
 27fun ispathc(c: int): bool = 
 28    (c != char2int0('\n')) &&
 29    (c != char2int0 (' ')) &&
 30    (c != char2int0 (':'))
 31
 32implement next(s) (*void*) =
 33    ((if s.cur = char2int0('\n') then (
 34        s.line := s.line + 1;
 35        s.col  := 1 )
 36    else s.col := s.col +1);
 37    s.cur := char(s.file))
 38
 39fun read(s: &scanner, allowed: (int) -<fun1> bool): void =
 40    if (s.cur >= 0) && allowed(s.cur) then
 41        let
 42            val c = g1ofg0_char(int2char0(s.cur))
 43        in
 44            if c != '\000' then
 45                let 
 46                    val inserted = stringbuf_insert_char(s.buf, c);
 47                in
 48                    if inserted = 1 then (
 49                        next(s);
 50                        read(s, allowed) ) 
 51                    else $raise ScanErr
 52               end
 53            else (
 54                prerrln!("null at ", s.line,":",s.col);
 55                $raise ScanErr )
 56        end
 57    else ()
 58
 59fun readliteral(s: &scanner): Strptr1 = 
 60let
 61    val () = read(s, isliteralc)
 62in
 63    stringbuf_takeout_all(s.buf)
 64end
 65
 66fun isvarc(c: int): bool =
 67    isnamec(c) &&
 68    c != char2int0('.')
 69
 70(*we may or may not return a stringpart, its like haskell "maybe"*)
 71fun dollar(s: &scanner): Option_vt(stringpart) = 
 72let 
 73    (* assume the scanner is sitting on $ *)
 74    val () = next(s)
 75in  
 76    if s.cur < 0 then ( 
 77        prerrln!("unexpected EOF after '$'");
 78        $raise ScanErr()
 79    ) else 
 80        case+ int2char0(s.cur) of 
 81        | '$'  => (
 82            let val inserted = stringbuf_insert_char(s.buf, '$')
 83            in if inserted = 1 then (next(s); Some_vt(Literal(stringbuf_takeout_all(s.buf))) ) else $raise ScanErr 
 84            end )
 85        | ':'  => (
 86            let val inserted = stringbuf_insert_char(s.buf, ':')
 87            in if inserted = 1 then (next(s); Some_vt(Literal(stringbuf_takeout_all(s.buf))) ) else $raise ScanErr 
 88            end )
 89        | ' '  => (
 90            let val inserted = stringbuf_insert_char(s.buf, ' ')
 91            in if inserted = 1 then (next(s); Some_vt(Literal(stringbuf_takeout_all(s.buf))) ) else $raise ScanErr 
 92            end )
 93        | '\n' => 
 94            let 
 95                val () = next(s)
 96                val () = skipspace(s)
 97            in  None_vt()
 98            end
 99        | '\{' => 
100            let
101               val () = next(s)
102               val () = read(s, isnamec)
103            in
104                if stringbuf_get_size(s.buf) = 0 then (
105                    prerrln!("expected variable name");
106                    $raise ScanErr()
107                ) else (
108                    if s.cur = char2int0('}') then (
109                        next(s);
110                        Some_vt(Variable(stringbuf_takeout_all(s.buf)))
111                    ) else ( 
112                        prerrln!("unclosed variable brace");
113                        $raise ScanErr() )
114                )
115            end
116        | _    =>
117            let
118               val () = read(s, isvarc)
119            in
120                if stringbuf_get_size(s.buf) = 0 then (
121                    prerrln!("expected variable name");
122                    $raise ScanErr
123                ) else
124                    Some_vt(Variable(stringbuf_takeout_all(s.buf)))
125            end
126
127end
128
129fun parts (s: &scanner, collected: List0_vt(stringpart)): List0_vt(stringpart) = 
130    if s.cur < 0 then
131        list_vt_reverse(collected)
132    else
133        case+ int2char0(s.cur) of 
134        | '\n' => list_vt_reverse(collected)
135        | '$'  => ( 
136            case+ dollar(s) of 
137            | ~Some_vt(part) => parts(s, cons_vt(part, collected)) 
138            | ~None_vt()     => parts(s, collected) )
139        | _   => 
140            let
141               val newpart = readliteral(s)
142               val litpart = Literal(newpart)
143               val more    = cons_vt(litpart, collected)
144            in 
145               parts(s, more)
146            end
147    
148implement scanname(s) (*Strptr1*)= 
149let
150    val () = read(s, isnamec)
151in
152    if stringbuf_get_size(s.buf) = 0 then (
153        prerrln!("expected name");
154        $raise ScanErr 
155    ) else 
156        stringbuf_takeout_all(s.buf) 
157end
158
159implement skipspace(s) (*void*) =
160    if (s.cur = char2int0(' ')) then (
161        next(s); skipspace(s))
162    else ()
163
164fun skipcomment(s: &scanner): void =
165    if s.cur < 0 || s.cur = char2int0('\n') then ()
166    else (next(s); skipcomment(s))
167
168implement mktok(text, line, col) (*tok*) =
169    if text = "rule" then (
170        strptr_free(text);
171        TOKrule(line, col)
172    ) else if
173       text = "build" then (
174        strptr_free(text);
175        TOKbuild(line, col)
176    ) else TOKvar(text, line, col)
177
178(* check if the current charachter is the wanted charachter: 
179 * if it is, consume it, if it's not, raise an error *)
180implement scanchar(s, wanted) (*void*) = 
181    if s.cur = wanted then (
182        next(s);
183        skipspace(s)
184    ) else (prerrln!("unexpected token at ",s.line, ":", s.col);
185          $raise ScanErr())
186
187implement scanindent(s) (*bool*) =
188    if s.cur = char2int0(' ') then (
189        skipspace(s);
190        true
191    ) else (false)
192
193(*fun scankeyword(s: &scanner): tok = *)
194implement scankeyword(s) =
195    if s.cur < 0 then (TOKeof(s.line, s.col))
196    else if s.cur = char2int0('\n') then (
197        next(s);
198        scankeyword(s))
199    else if s.cur = char2int0('#')  then (
200        skipcomment(s);
201        scankeyword(s))
202    else if isnamec(s.cur) then
203        let
204            val sline = s.line
205            val scol  = s.col
206            val text  = scanname(s)
207        in
208            mktok(text, sline, scol)
209        end
210    else (prerrln!("expected name at ",s.line, ":", s.col);
211          $raise ScanErr())
212
213implement scanvalue(s) (*List0_vt(stringpart)*) = 
214    parts(s, nil_vt())
215
216implement scanpath(s) = 
217let
218    val () = read(s, ispathc) 
219in
220    stringbuf_takeout_all(s.buf)
221end