Files
doslang-mirror/fec/tests/exec/lexer/parse.fe
T
coolguy 63ad48cd8a 트랩은 검사가 쓰인 파일을 댄다
빌드 전체가 한 모듈이라 파일 이름도 하나였다. std.list 안에서 터진 경계 검사가
프로그램의 파일 이름을 대고 있었으니, 줄 번호는 맞는데 파일이 틀려서 엉뚱한 줄을
가리켰다 -- 이름을 안 대는 것보다 나쁘다.

모듈이 파일 표를 들고 트랩은 그 인덱스를 든다. 생성기는 파일마다 FE_FILE_n 을
한 번씩 찍는다.

  before: index out of bounds at main.fe:2
  after:  index out of bounds at pick.fe:6

그리고 Parser.on 이 구조체 리터럴 안에서 다시 try 를 쓴다. 앞서 그것이 깨졌던
것은 try 때문이 아니라 Parser 가 1 바이트로 자리잡았기 때문이었다.

224/224, 31/31.
2026-08-17 12:56:34 +09:00

255 lines
8.3 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,
};
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);
}
}