// Minimal recursive-descent parser for Ny v0 producing JSON IR v0 (MapBox) include("./apps/ny-parser-nyash/tokenizer.nyash") static box ParserV0 { init { tokens, pos } parse_program(input) { me.tokens = Tokenizer.tokenize(input) // Error passthrough if me.tokens.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return me.tokens } me.pos = 0 local stmt = me.parse_stmt() if stmt.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return stmt } local body = new ArrayBox(); body.push(stmt) local prog = new MapBox(); prog.set("version", 0); prog.set("kind", "Program"); prog.set("body", body) return prog } parse_stmt() { local tok = me.peek() if tok.get("type") == "RETURN" { me.next() local expr = me.parse_expr() if expr.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return expr } local ret = new MapBox(); ret.set("type", "Return"); ret.set("expr", expr) return ret } return me.err("Expected 'return'") } parse_expr() { local left = me.parse_term() loop(true) { local t = me.peek(); local ty = t.get("type") if ty == "+" || ty == "-" { me.next(); local right = me.parse_term() left = me.bin(ty, left, right) } else { break } } return left } parse_term() { local left = me.parse_factor() loop(true) { local t = me.peek(); local ty = t.get("type") if ty == "*" || ty == "/" { me.next(); local right = me.parse_factor() left = me.bin(ty, left, right) } else { break } } return left } parse_factor() { local t = me.peek(); local ty = t.get("type") if ty == "INT" { me.next(); local node = new MapBox(); node.set("type", "Int"); node.set("value", t.get("value")) return node } if ty == "(" { me.next(); local e = me.parse_expr() local r = me.peek(); if r.get("type") != ")" { return me.err(") expected") } else { me.next() } return e } return me.err("factor expected") } // helpers peek() { return me.tokens.get(me.pos) } next() { me.pos = me.pos + 1; return me.tokens.get(me.pos-1) } bin(op, lhs, rhs) { local m = new MapBox(); m.set("type", "Binary"); m.set("op", op); m.set("lhs", lhs); m.set("rhs", rhs); return m } err(msg) { local err = new MapBox(); err.set("version", 0); err.set("kind", "Error") local e = new MapBox(); e.set("message", msg) local sp = new MapBox(); sp.set("start", me.pos); sp.set("end", me.pos) e.set("span", sp); err.set("error", e) return err } }