ast.Node 가 이름이 무엇으로 해석됐는지 들고, Map 이 저장소를 유지한 채 키만 잊는다. 스코프가 끝날 때 표를 다음 스코프에 넘기는 것이 리졸버가 원하는 모양이다 -- 함수마다가 아니라 중첩 단계마다 표 하나. bind 는 Name 노드의 남는 a 필드를 재활용할 수도 있었지만 명시적인 쪽을 골랐다. 노드가 32 에서 36 바이트가 되는 값으로 그 자리가 무엇인지 이름이 말한다. clear 는 아무도 부르지 않는 채로 들어와 있었다. maps.fe 가 이제 부른다: 키가 사라지고, 방은 64 로 남고, 그 위에 다시 채워도 버퍼를 새로 잡지 않는다. cleared 0 room 64 gone 0 / refilled 3 / balanced GOAL.md 를 더했다. 외부 감사와, 그 항목들을 실제로 빌드해서 확인한 결과를 합친 P0~P4 다. 228/228, 32/32.
257 lines
8.4 KiB
Plaintext
257 lines
8.4 KiB
Plaintext
unit parse;
|
|
|
|
import std.list;
|
|
import std.str;
|
|
import tok;
|
|
import scan;
|
|
import ast;
|
|
|
|
// Recursive descent over the lexer's tokens.
|
|
//
|
|
// The source is not a field: R4 keeps borrows out of aggregate storage, so
|
|
// `src` is passed to every step, the same way the lexer passes it. What the
|
|
// parser does own is the node array, and every parent points at its children
|
|
// by index into it.
|
|
//
|
|
// One token of lookahead lives in `cur`. That is all this grammar needs.
|
|
|
|
pub struct Parser {
|
|
nodes: list.List(ast.Node),
|
|
at: usize,
|
|
line: usize,
|
|
cur: tok.Token,
|
|
pub errors: usize,
|
|
|
|
pub fn on(src: []u8) -> !Self {
|
|
var p: Self = Self{
|
|
nodes: try list.List(ast.Node).with_capacity(16),
|
|
at: 0,
|
|
line: 1,
|
|
cur: tok.Token{ kind: tok.Kind.End, from: 0, len: 0, line: 1 },
|
|
errors: 0,
|
|
};
|
|
p.bump(src);
|
|
return p;
|
|
}
|
|
|
|
fn bump(self: &mut Self, src: []u8) -> void {
|
|
self.cur = scan.next(src, &mut self.at, &mut self.line);
|
|
return;
|
|
}
|
|
|
|
fn done(self: &Self) -> bool { return self.cur.kind == tok.Kind.End; }
|
|
|
|
/// Is the token in hand this exact spelling?
|
|
fn is(self: &Self, src: []u8, want: []u8) -> bool {
|
|
return str.eq(tok.text(src, self.cur), want);
|
|
}
|
|
|
|
/// Take the token in hand if it is this spelling; say whether it was.
|
|
fn eat(self: &mut Self, src: []u8, want: []u8) -> bool {
|
|
if not self.is(src, want) { return false; }
|
|
self.bump(src);
|
|
return true;
|
|
}
|
|
|
|
/// Demand this spelling. A miss is counted and the token stays put, so the
|
|
/// caller decides how to get back on its feet.
|
|
fn want(self: &mut Self, src: []u8, w: []u8) -> bool {
|
|
if self.eat(src, w) { return true; }
|
|
self.errors = self.errors + 1;
|
|
return false;
|
|
}
|
|
|
|
fn add(self: &mut Self, s: ast.Shape, t: tok.Token, a: usize,
|
|
b: usize) -> !usize {
|
|
let n: ast.Node = ast.Node{
|
|
shape: s, from: t.from, len: t.len, line: t.line,
|
|
a: a, b: b, next: ast.NONE,
|
|
// The parser does not resolve names; the resolver writes this.
|
|
bind: ast.NONE,
|
|
};
|
|
let i: usize = self.nodes.count();
|
|
try self.nodes.push(n);
|
|
return i;
|
|
}
|
|
|
|
// -- reading the tree back ------------------------------------------
|
|
|
|
pub fn count(self: &Self) -> usize { return self.nodes.count(); }
|
|
|
|
pub fn node(self: &Self, i: usize) -> ast.Node {
|
|
return self.nodes.at(i);
|
|
}
|
|
|
|
|
|
// -- the grammar ----------------------------------------------------
|
|
//
|
|
// unit := "unit" NAME ";" item*
|
|
// item := "fn" NAME "(" ")" block
|
|
// block := "{" stmt* "}"
|
|
// stmt := "let" NAME "=" expr ";" | "return" expr? ";"
|
|
// expr := term (("+" | "-") term)*
|
|
// term := factor (("*" | "/") factor)*
|
|
// factor := NUMBER | NAME | TEXT | "(" expr ")"
|
|
|
|
pub fn unit_decl(self: &mut Self, src: []u8) -> !usize {
|
|
let ok: bool = self.want(src, "unit");
|
|
let name: tok.Token = self.cur;
|
|
if ok { self.bump(src); }
|
|
let semi: bool = self.want(src, ";");
|
|
let root: usize = try self.add(ast.Shape.Unit, name, ast.NONE,
|
|
ast.NONE);
|
|
var first: usize = ast.NONE;
|
|
var last: usize = ast.NONE;
|
|
while not self.done() {
|
|
let it: usize = try self.item(src);
|
|
if first == ast.NONE { first = it; }
|
|
else { self.link(last, it); }
|
|
last = it;
|
|
}
|
|
self.set_a(root, first);
|
|
return root;
|
|
}
|
|
|
|
/// Point one statement or item at the one after it.
|
|
fn link(self: &mut Self, from: usize, to: usize) -> void {
|
|
var n: ast.Node = self.nodes.at(from);
|
|
n.next = to;
|
|
self.nodes.set(from, n);
|
|
return;
|
|
}
|
|
|
|
fn set_a(self: &mut Self, at: usize, a: usize) -> void {
|
|
var n: ast.Node = self.nodes.at(at);
|
|
n.a = a;
|
|
self.nodes.set(at, n);
|
|
return;
|
|
}
|
|
|
|
fn item(self: &mut Self, src: []u8) -> !usize {
|
|
if not self.is(src, "fn") {
|
|
let bad: tok.Token = self.cur;
|
|
self.errors = self.errors + 1;
|
|
self.skip_stmt(src);
|
|
return try self.add(ast.Shape.Error, bad, ast.NONE, ast.NONE);
|
|
}
|
|
self.bump(src);
|
|
let name: tok.Token = self.cur;
|
|
self.bump(src);
|
|
let open: bool = self.want(src, "(");
|
|
let close: bool = self.want(src, ")");
|
|
let body: usize = try self.block(src);
|
|
return try self.add(ast.Shape.Fn, name, ast.NONE, body);
|
|
}
|
|
|
|
fn block(self: &mut Self, src: []u8) -> !usize {
|
|
let open: bool = self.want(src, "{");
|
|
var first: usize = ast.NONE;
|
|
var last: usize = ast.NONE;
|
|
while true {
|
|
if self.done() { break; }
|
|
if self.is(src, "}") { break; }
|
|
let s: usize = try self.stmt(src);
|
|
if first == ast.NONE { first = s; }
|
|
else { self.link(last, s); }
|
|
last = s;
|
|
}
|
|
let close: bool = self.want(src, "}");
|
|
return first;
|
|
}
|
|
|
|
fn stmt(self: &mut Self, src: []u8) -> !usize {
|
|
if self.is(src, "let") {
|
|
self.bump(src);
|
|
let name: tok.Token = self.cur;
|
|
self.bump(src);
|
|
let has_eq: bool = self.want(src, "=");
|
|
let value: usize = try self.expr(src);
|
|
let semi: bool = self.want(src, ";");
|
|
return try self.add(ast.Shape.Let, name, ast.NONE, value);
|
|
}
|
|
if self.is(src, "return") {
|
|
let head: tok.Token = self.cur;
|
|
self.bump(src);
|
|
if self.is(src, ";") {
|
|
self.bump(src);
|
|
return try self.add(ast.Shape.Return, head, ast.NONE,
|
|
ast.NONE);
|
|
}
|
|
let value: usize = try self.expr(src);
|
|
let semi: bool = self.want(src, ";");
|
|
return try self.add(ast.Shape.Return, head, value, ast.NONE);
|
|
}
|
|
let bad: tok.Token = self.cur;
|
|
self.errors = self.errors + 1;
|
|
self.skip_stmt(src);
|
|
return try self.add(ast.Shape.Error, bad, ast.NONE, ast.NONE);
|
|
}
|
|
|
|
/// Get back to a statement boundary after something unrecognised. Stopping
|
|
/// at `;` or `}` means one bad statement costs one diagnostic, not a run
|
|
/// of them.
|
|
fn skip_stmt(self: &mut Self, src: []u8) -> void {
|
|
while not self.done() {
|
|
if self.is(src, "}") { return; }
|
|
if self.is(src, ";") { self.bump(src); return; }
|
|
self.bump(src);
|
|
}
|
|
return;
|
|
}
|
|
|
|
fn expr(self: &mut Self, src: []u8) -> !usize {
|
|
var left: usize = try self.term(src);
|
|
while true {
|
|
if self.done() { break; }
|
|
let plus: bool = self.is(src, "+");
|
|
let minus: bool = self.is(src, "-");
|
|
if not plus and not minus { break; }
|
|
let op: tok.Token = self.cur;
|
|
self.bump(src);
|
|
let right: usize = try self.term(src);
|
|
left = try self.add(ast.Shape.Binary, op, left, right);
|
|
}
|
|
return left;
|
|
}
|
|
|
|
fn term(self: &mut Self, src: []u8) -> !usize {
|
|
var left: usize = try self.factor(src);
|
|
while true {
|
|
if self.done() { break; }
|
|
let star: bool = self.is(src, "*");
|
|
let slash: bool = self.is(src, "/");
|
|
if not star and not slash { break; }
|
|
let op: tok.Token = self.cur;
|
|
self.bump(src);
|
|
let right: usize = try self.factor(src);
|
|
left = try self.add(ast.Shape.Binary, op, left, right);
|
|
}
|
|
return left;
|
|
}
|
|
|
|
fn factor(self: &mut Self, src: []u8) -> !usize {
|
|
let t: tok.Token = self.cur;
|
|
if t.kind == tok.Kind.Number {
|
|
self.bump(src);
|
|
return try self.add(ast.Shape.Number, t, ast.NONE, ast.NONE);
|
|
}
|
|
if t.kind == tok.Kind.Name {
|
|
self.bump(src);
|
|
return try self.add(ast.Shape.Name, t, ast.NONE, ast.NONE);
|
|
}
|
|
if t.kind == tok.Kind.Text {
|
|
self.bump(src);
|
|
return try self.add(ast.Shape.Text, t, ast.NONE, ast.NONE);
|
|
}
|
|
if self.is(src, "(") {
|
|
self.bump(src);
|
|
let inner: usize = try self.expr(src);
|
|
let close: bool = self.want(src, ")");
|
|
return inner;
|
|
}
|
|
self.errors = self.errors + 1;
|
|
self.bump(src);
|
|
return try self.add(ast.Shape.Error, t, ast.NONE, ast.NONE);
|
|
}
|
|
}
|