ref: 66a103f6afd50eb45e9ba62f167688edeb10063a
dir: /parse.myr/
use std use "types.use" use "util.use" use "opts.use" use "fsel.use" pkg bld = const load : (b : build#, path : byte[:] -> bool) ;; type parser = struct /* parse input */ data : byte[:] rest : byte[:] fname : byte[:] fdir : byte[:] /* directory relative to base */ line : int /* extracted data for further parsing */ subdirs : byte[:][:] ;; const load = {b, path -> loadall(b, path, "") } const loadall = {b, path, dir var p : parser# var subpath, subbld, ok p = mkparser(path, dir) ok = bld.parse(b, p, "") for s in p.subdirs subpath = std.pathcat(p.fdir, s) subbld = std.pathcat(subpath, "bldfile") loadall(b, subbld, subpath) std.slfree(subpath) std.slfree(subbld) ;; freeparser(p) -> ok } const mkparser = {path, dir var p p = std.zalloc() p.line = 1 p.fname = std.sldup(path) p.fdir = std.sldup(dir) match std.slurp(path) | `std.Ok d: p.data = d | `std.Fail _: std.fatal(1, "could not open '%s'\n", path) ;; p.rest = p.data -> p } const freeparser = {p std.slfree(p.fname) std.slfree(p.fdir) std.slfree(p.subdirs) std.slfree(p.data) std.free(p) } const failparse = {p, msg, args : ... var buf : byte[1024] var ap var sl ap = std.vastart(&args) sl = std.bfmtv(buf[:], msg, ap) std.fput(1, "%s:%i: %s", p.fname, p.line, sl) std.exit(1) } const parse = {b, p, path while true skipspace(p) if !target(b, p) break ;; ;; skipspace(p) if p.rest.len > 0 failparse(p, "junk in file near %s\n", p.rest[:std.min(p.rest.len, 10)]) -> false ;; -> true } const target = {b, p match word(p) | `std.Some "bin": bintarget(b, p) | `std.Some "test": testtarget(b, p) | `std.Some "lib": libtarget(b, p) | `std.Some "gen": gentarget(b, p) | `std.Some "man": mantarget(b, p) | `std.Some "sub": subtarget(b, p) | `std.Some targtype: failparse(p, "unknown targtype type %s\n", targtype) | `std.None: -> false ;; -> true } /* bintarget: myrtarget */ const bintarget = {b, p addtarg(b, p.fdir, `Bin myrtarget(p, "bin")) } /* testtarget: myrtarget */ const testtarget = {b, p addtarg(b, p.fdir, `Test myrtarget(p, "test")) } /* libtarget: myrtarget */ const libtarget = {b, p addtarg(b, p.fdir, `Lib myrtarget(p, "lib")) } /* subtarget : anontarget */ const subtarget = {b, p p.subdirs = std.sljoin(p.subdirs, anontarget(p, "sub")) } /* mantarget: anontarget */ const mantarget = {b, p addtarg(b, p.fdir, `Man anontarget(p, "man")) } /* gentarget: wordlist {attrs} = wordlist ;; */ const gentarget = {b, p var outlist, cmdlist var durable var attrs var gt match wordlist(p) | `std.None: failparse(p, "gen target missing output files\n") | `std.Some out: outlist = out ;; skipspace(p) if matchc(p, '{') match attrlist(p) | `std.Some al: attrs = al | `std.None: failparse(p, "invalid attr list for %s\n", outlist[0]) ;; else attrs = [][:] ;; skipspace(p) if !matchc(p, '=') failparse(p, "expected '=' after '%s %s'\n", cmdlist, outlist[outlist.len-1]) ;; match wordlist(p) | `std.None: failparse(p, "gen target missing command\n") | `std.Some cmd: cmdlist = cmd ;; if !matchc(p, ';') || !matchc(p, ';') failparse(p, "expected ';;' terminating genfile command, got %c\n", peekc(p)) ;; durable = false for elt in attrs match elt | ("durable", ""): durable = true | ("durable", val): failparse(p, "durable attr does not take argument\n") ;; ;; gt = std.mk([ .out=outlist, .durable=durable, .cmd=cmdlist ]) for o in outlist std.htput(b.gensrc, o, gt) ;; addtarg(b, p.fdir, `Gen gt) } /* myrtarget: name '=' inputlist ';;' | name attrlist = inputlist ';;' */ const myrtarget = {p, targ var ldscript, runtime, inst, incpath var name, inputs, libdeps, attrs var fsel match word(p) | `std.Some n: name = n | `std.None: failparse(p, "expected target name after '%s'\n", targ) ;; skipspace(p) if matchc(p, '{') match attrlist(p) | `std.Some al: attrs = al | `std.None: failparse(p, "invalid attr list for %s %s\n", targ, name) ;; else attrs = [][:] ;; skipspace(p) if !matchc(p, '=') failparse(p, "expected '=' after '%s %s'\n", targ, name) ;; match inputlist(p) | `std.Some (wl, libs): fsel = mkfsel() libdeps = libs for w in wl fseladd(fsel, w) ;; inputs = fselfin(fsel) std.slfree(wl) | `std.None: failparse(p, "expected list of file names after '%s %s'\n", targ, name) ;; skipspace(p) if !matchc(p, ';') || !matchc(p, ';') failparse(p, "expected ';;' terminating input list, got %c\n", peekc(p)) ;; inst = true ldscript = "" runtime = "" incpath = [][:] for elt in attrs match elt | ("ldscript", lds): ldscript = std.sldup(lds) | ("runtime", rt): runtime = std.sldup(rt) | ("inc", path): incpath = std.slpush(incpath, std.sldup(path)) | ("noinst", ""): inst = false | ("noinst", val): failparse(p, "noinst attr does not take argument\n") | (invalid, _): std.fatal(1, "%s: got invalid attr '%s'\n", targ, invalid) ;; ;; -> std.mk([ /* target */ .name=name, .inputs=inputs, .libdeps=libdeps, /* attrs */ .install=inst, .ldscript=ldscript, .runtime=runtime, .incpath=incpath, .built=false ]) } /* anontarget: '=' wordlist ';;' */ const anontarget = {p, targ var inputs inputs = [][:] skipspace(p) if !matchc(p, '=') failparse(p, "expected '=' after '%s' target\n", targ) ;; match wordlist(p) | `std.None: failparse(p, "expected list of file names after '%s' target\n", targ) | `std.Some wl: inputs = wl ;; skipspace(p) if !matchc(p, ';') || !matchc(p, ';') failparse(p, "expected ';;' terminating input list\n") ;; -> inputs } /* attrlist: attrs '}' attrs : EMPTY | attrs attr attr : name | name '=' name */ const attrlist = {p var al al = [][:] while true match word(p) | `std.Some k: skipspace(p) if matchc(p, '=') match word(p) | `std.Some v: al = std.slpush(al, (k, v)) | `std.None: failparse(p, "invalid attr in attribute list\n") ;; else al = std.slpush(al, (k, [][:])) ;; if !matchc(p, ',') break ;; | `std.None: break ;; ;; if !matchc(p, '}') failparse(p, "expected '}' at end of attr list, got '%c'\n", peekc(p)) ;; if al.len == 0 -> `std.None else -> `std.Some al ;; } /* inputlist: EMPTY | inputlist input input : word | "lib" word */ const inputlist = {p var wl, libs wl = [][:] libs = [][:] while true match word(p) | `std.Some "lib": match word(p) | `std.Some l: libs = std.slpush(libs, l) | `std.None: failparse(p, "expected lib name after 'lib'\n") ;; | `std.Some w: wl = std.slpush(wl, w) | `std.None: break ;; ;; if wl.len == 0 -> `std.None else -> `std.Some (wl, libs) ;; } /* wordlist: EMPTY | wordlist word */ const wordlist = {p var wl wl = [][:] while true match word(p) | `std.Some w: wl = std.slpush(wl, w) | `std.None: break ;; ;; if wl.len == 0 -> `std.None else -> `std.Some wl ;; } /* word: /wordchar*/ const word = {p var c, n var start skipspace(p) c = peekc(p) if c == '"' n = 0 getc(p) start = p.rest while p.rest.len > 0 c = peekc(p) if c == '"' getc(p) goto done elif c == '\\' c = getc(p) ;; getc(p) n += std.charlen(c) ;; failparse(p, "input ended within quoted word\n") else n = 0 start = p.rest while p.rest.len > 0 c = peekc(p) if wordchar(c) getc(p) n += std.charlen(c) else break ;; ;; ;; :done if n > 0 -> `std.Some std.sldup(start[:n]) else -> `std.None ;; } const wordchar = {c -> std.isalnum(c) || \ c == '.' || c == '_' || c == '$' || c == '-' || \ c == '/' || c == ':' || c == '!' || c == '~' || \ c == '+' } const skipspace = {p var c, r r = p.rest while r.len > 0 c = peekc(p) match c | ' ': getc(p) | '\t': getc(p) | '\n': getc(p) p.line++ | '#': while p.rest.len > 0 && peekc(p) != '\n' getc(p) ;; | _: break ;; ;; } const matchc = {p, c var chr, s if p.rest.len == 0 -> false ;; (chr, s) = std.striter(p.rest) if c == chr p.rest = s -> true else -> false ;; } const peekc = {p -> std.decode(p.rest) } const getc = {p var c, s (c, s) = std.striter(p.rest) p.rest = s -> c } const addtarg = {b, d, t var tl tl = std.htgetv(b.targs, d, [][:]) tl = std.slpush(tl, t) std.htput(b.targs, d, tl) }