commit 9a29a949ccbb897b060cafbb8c3f07de744d3731
parent de964f635d782f9c6ad51eb14c87d1f4e54e3f76
Author: thing1 <thing1@seacrossedlovers.xyz>
Date: Tue, 5 May 2026 17:07:43 +0100
init parser
Diffstat:
5 files changed, 142 insertions(+), 45 deletions(-)
diff --git a/zpy/lex/+test.ha b/zpy/lex/+test.ha
@@ -18,55 +18,35 @@ fn check(lex: *lexer, expect: [](types | invalid)) void = {
@test
fn NoArgExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("(foo)")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("(foo)")));
check(lex, [types::OBRACE, types::NAME, types::CBRACE, types::EOF]);
};
@test
fn HasArgsExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("(foo 1 2 3)")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("(foo 1 2 3)")));
check(lex, [types::OBRACE, types::NAME, types::NUM, types::NUM, types::NUM, types::CBRACE, types::EOF]);
};
@test
fn NestedArgsExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("(foo (bar 1 2) 3)")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("(foo (bar 1 2) 3)")));
check(lex, [types::OBRACE, types::NAME, types::OBRACE, types::NAME, types::NUM, types::NUM, types::CBRACE, types::NUM, types::CBRACE, types::EOF]);
};
@test
fn NameNumExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("(foo bar 1)")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("(foo bar 1)")));
check(lex, [types::OBRACE, types::NAME, types::NAME, types::NUM, types::CBRACE, types::EOF]);
};
@test
fn NumNameExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("(foo 1 bar)")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("(foo 1 bar)")));
check(lex, [types::OBRACE, types::NAME, types::NUM, types::NAME, types::CBRACE, types::EOF]);
};
@@ -76,44 +56,28 @@ fn NumNameExpr() void = {
@test
fn EmptyExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("")));
check(lex, [invalid]);
};
@test
fn SingleOBraceExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("(")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("(")));
check(lex, [types::OBRACE, invalid]);
};
@test
fn SingleCBraceExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8(")")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8(")")));
check(lex, [invalid]);
};
@test
fn SingleNameExpr() void = {
- let lex = &lexer{
- in = &memio::fixed(strings::toutf8("foo")),
- nexts = [types::OBRACE],
- items = items
- };
+ let lex = &new(&memio::fixed(strings::toutf8("foo")));
check(lex, [invalid]);
};
diff --git a/zpy/parse/+test.ha b/zpy/parse/+test.ha
@@ -0,0 +1,51 @@
+use zpy::lex;
+use memio;
+use strings;
+
+@test
+fn BinaryExpr() void = {
+ let lex = &lex::new(&memio::fixed(strings::toutf8("(foo 1 2)")));
+
+ assert(((try(lex, [&parseSexpr]) as node) as sexpr).func == "foo");
+ assert(len(((try(lex, [&parseSexpr]) as node) as sexpr).args) == 2);
+ assert((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[0]) as lit) == 1);
+ assert((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[1]) as lit) == 2);
+};
+
+@test
+fn NoArgExpr() void = {
+ let lex = &lex::new(&memio::fixed(strings::toutf8("(foo)")));
+
+ assert(((try(lex, [&parseSexpr]) as node) as sexpr).func == "foo");
+ assert(len(((try(lex, [&parseSexpr]) as node) as sexpr).args) == 0);
+};
+
+@test
+fn ExprWithName() void = {
+ let lex = &lex::new(&memio::fixed(strings::toutf8("(foo bar)")));
+
+ assert(((try(lex, [&parseSexpr]) as node) as sexpr).func == "foo");
+ assert(len(((try(lex, [&parseSexpr]) as node) as sexpr).args) == 1);
+ assert((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[0]) as name) == "bar");
+};
+
+@test
+fn ExprWithNameAndNum() void = {
+ let lex = &lex::new(&memio::fixed(strings::toutf8("(foo bar 1)")));
+
+ assert(((try(lex, [&parseSexpr]) as node) as sexpr).func == "foo");
+ assert(len(((try(lex, [&parseSexpr]) as node) as sexpr).args) == 2);
+ assert((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[0]) as name) == "bar");
+ assert((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[1]) as lit) == 1);
+};
+
+@test
+fn NestedExpr() void = {
+ let lex = &lex::new(&memio::fixed(strings::toutf8("(foo (bar 1))")));
+
+ assert(((try(lex, [&parseSexpr]) as node) as sexpr).func == "foo");
+ assert(len(((try(lex, [&parseSexpr]) as node) as sexpr).args) == 1);
+ assert((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[0]) as sexpr).func == "bar");
+ assert(len((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[0]) as sexpr).args) == 1);
+ assert((*((*(((try(lex, [&parseSexpr]) as node) as sexpr).args[0]) as sexpr).args[0]) as lit) == 1);
+};
diff --git a/zpy/parse/ast.ha b/zpy/parse/ast.ha
@@ -0,0 +1,10 @@
+export type node = (sexpr | lit | name);
+
+export type sexpr = struct {
+ func: str,
+ args: []*node
+};
+
+export type lit = u64;
+
+export type name = str;
diff --git a/zpy/parse/help.ha b/zpy/parse/help.ha
@@ -0,0 +1,9 @@
+use zpy::lex;
+
+fn want(l: *lex::lexer, want: []lex::types) (lex::token | lex::error | error) = {
+ let n = lex::next(l)?;
+ for (let ty .. want) {
+ if (n.ty == ty) return n;
+ };
+ abort();
+};
diff --git a/zpy/parse/parse.ha b/zpy/parse/parse.ha
@@ -0,0 +1,63 @@
+use zpy::lex;
+use io;
+use strconv;
+
+export type error = !int;
+
+export type parserfn = fn(_: *lex::lexer) (node | error);
+
+export fn try(l: *lex::lexer, fs: []*parserfn) (node | error) = {
+ let errors: []error = [];
+
+ for (let f .. fs) {
+ let save = io::tell(l.in)!;
+
+ return match (f(l)) {
+ case let n: node => return n;
+ case let e: error =>
+ append(errors, e)!;
+ io::seek(l.in, save, io::whence::SET)!;
+ continue;
+ };
+ };
+
+ abort();
+};
+
+export fn parseSexpr(l: *lex::lexer) (node | error) = {
+ match (want(l, [lex::types::OBRACE])) {
+ case lex::token => yield;
+ case => abort();
+ };
+
+ let name = try(l, [&parseName])?;
+
+ let args: []*node = [];
+
+ for (true) {
+ match (try(l, [&parseName, &parseLit, &parseSexpr])) {
+ case let n: node => append(args, &n)!;
+ case let e: error =>
+ match (want(l, [lex::types::CBRACE])) {
+ case lex::token => return sexpr{func = name: str, args = args};
+ case => return e;
+ };
+ };
+ };
+};
+
+export fn parseName(l: *lex::lexer) (node | error) = {
+ match (want(l, [lex::types::NAME])) {
+ case let t: lex::token => return t.data;
+ case => abort();
+ };
+};
+
+export fn parseLit(l: *lex::lexer) (node | error) = {
+ match (want(l, [lex::types::NUM])) {
+ case let t: lex::token => return strconv::stou64(t.data)!;
+ case => abort();
+ };
+};
+
+