From 474c14c92de165516f4302685e3fa8acc3a64f45 Mon Sep 17 00:00:00 2001 From: Julian Hurst Date: Sat, 28 Mar 2026 19:17:16 +0100 Subject: Add finvoke parsing and interpreting --- cmd/hs/.hs.ha.swp | Bin 12288 -> 0 bytes cmd/hs/hs.ha | 9 +++ internal/interpreter/.interpreter.ha.swp | Bin 12288 -> 0 bytes internal/interpreter/interpreter.ha | 32 +++++++- internal/parser/.parser.ha.swp | Bin 20480 -> 0 bytes internal/parser/parser.ha | 123 +++++++++++++++++++++++++++++-- spec/variable-declaration.md | 2 +- test.ra | 1 + 8 files changed, 158 insertions(+), 9 deletions(-) delete mode 100644 cmd/hs/.hs.ha.swp delete mode 100644 internal/interpreter/.interpreter.ha.swp delete mode 100644 internal/parser/.parser.ha.swp diff --git a/cmd/hs/.hs.ha.swp b/cmd/hs/.hs.ha.swp deleted file mode 100644 index 7e6d875..0000000 Binary files a/cmd/hs/.hs.ha.swp and /dev/null differ diff --git a/cmd/hs/hs.ha b/cmd/hs/hs.ha index 758ad62..c0ad72f 100644 --- a/cmd/hs/hs.ha +++ b/cmd/hs/hs.ha @@ -40,6 +40,15 @@ export fn main() void = { fmt::printfln("{}: {}", var.0, var.1)!; }; + let ast = parser::parse(&p); + let ast = match (ast) { + case let a: parser::ast => + yield a; + case let e: parser::error => + fmt::fatal(parser::strerror(e)); + }; + interpreter::interpret(&it, ast); + //let tk = parsetoken(&p); //fmt::printfln("tk value: {}\ntk type: {}", tk.value, parser::strtktype(tk.tktype))!; //tk = parsetoken(&p); diff --git a/internal/interpreter/.interpreter.ha.swp b/internal/interpreter/.interpreter.ha.swp deleted file mode 100644 index 76a76c9..0000000 Binary files a/internal/interpreter/.interpreter.ha.swp and /dev/null differ diff --git a/internal/interpreter/interpreter.ha b/internal/interpreter/interpreter.ha index 5e92461..f4ff180 100644 --- a/internal/interpreter/interpreter.ha +++ b/internal/interpreter/interpreter.ha @@ -1,22 +1,52 @@ +use fmt; use internal::parser; use strconv; use strings; +use os::exec; export type variable = (str, str); export type interpreter = struct { vars: []variable, }; -export type error = !(interperror | nomem); +export type error = !(interperror | exec::error | nomem); export type interperror = !str; export fn interpret(it: *interpreter, tree: parser::ast) void = { match (tree.value) { case parser::assign => assign(it, tree.children)!; + case parser::finvoke => + match (invoke(it, tree.children)) { + case let e: exec::error => + fmt::errorln(exec::strerror(e))!; + case void => void; + case let e: interperror => + fmt::fatal(e); + case nomem => + fmt::fatal("No memory left"); + }; }; }; +fn printinvoke(it: *interpreter, nodes: []parser::ast) (void | error) = { + const fname = nodes[0].value as parser::varname; + const arglist = nodes[1].value as parser::arglist; + + let args = strings::join(", ", arglist...)!; + defer free(args); + fmt::errorfln("{}: {}", fname, args)!; +}; + +fn invoke(it: *interpreter, nodes: []parser::ast) (void | error) = { + const fname = nodes[0].value as parser::varname; + const arglist = nodes[1].value as parser::arglist; + + let cmd = exec::cmd(fname, arglist...)?; + let proc = exec::start(&cmd)?; + let status = exec::wait(&proc)?; +}; + fn assign(it: *interpreter, nodes: []parser::ast) (void | error) = { const varname = nodes[0].value as parser::varname; const operation = nodes[1].value as parser::operation; diff --git a/internal/parser/.parser.ha.swp b/internal/parser/.parser.ha.swp deleted file mode 100644 index e2d9062..0000000 Binary files a/internal/parser/.parser.ha.swp and /dev/null differ diff --git a/internal/parser/parser.ha b/internal/parser/parser.ha index 682cb35..39b9abc 100644 --- a/internal/parser/parser.ha +++ b/internal/parser/parser.ha @@ -12,8 +12,9 @@ use fmt; // value: str, //}; -export type token = (varname | assign | operation); +export type token = (varname | finvoke | assign | operation | arglist); +export type finvoke = rune; export type assign = rune; export type varname = str; @@ -29,14 +30,18 @@ export type operation = struct { stack: []argument, }; +export type arglist = []str; + export type tokentype = enum { START, VARNAME, + FINVOKE, OPERAND, OPERATOR, OPERATION, LITERAL, ASSIGN, + ARGLIST, }; export type parseerror = !(size, size, str); @@ -46,11 +51,13 @@ export type error = !(parseerror | io::error | utf8::invalid | strconv::invalid export fn strtktype(tktype: tokentype) str = switch (tktype) { case tokentype::START => yield "START"; case tokentype::VARNAME => yield "VARNAME"; +case tokentype::FINVOKE => yield "FINVOKE"; case tokentype::OPERAND => yield "OPERAND"; case tokentype::OPERATOR => yield "OPERATOR"; case tokentype::OPERATION => yield "OPERATION"; case tokentype::LITERAL => yield "LITERAL"; case tokentype::ASSIGN => yield "ASSIGN"; +case tokentype::ARGLIST => yield "ARGLIST"; }; //export fn parseast(h: io::handle) (void | io::error) = { @@ -64,6 +71,14 @@ export type parser = struct { unreadcount: size, }; +type validrunecondfn = *fn(c: rune) bool; + +type validrunecond = struct { + f: validrunecondfn, + err: const str, +}; + + export fn strerror(e: error) str = { static let b: [256]u8 = @undefined; return match (e) { @@ -94,13 +109,62 @@ fn read_rune(p: *parser) (rune | io::EOF | error) = { }; }; +fn runeisin(r: rune, rs: []rune) bool = { + for (let i = 0z; i < len(rs); i += 1) { + if (r == rs[i]) { + return true; + }; + } else { + return false; + }; +}; + +fn parsetil( + p: *parser, + til: (rune | []rune), + cond: (void | validrunecond), + runecount: size, + linenb: size = 0, +) +(str | error) = { + let m = memio::dynamic(); + defer io::close(&m)!; + + return for (let r => read_rune(p)?) { + runecount += 1; + const ismatch = match (til) { + case let ru: rune => + yield r == ru; + case let ru: []rune => + yield runeisin(r, ru); + }; + if (ismatch) { + unreadrune(p, r); + let s = strings::trim(memio::string(&m)?); + break strings::dup(s)?; + }; + match (cond) { + case void => + memio::appendrune(&m, r)?; + case let c: validrunecond => + if (c.f(r)) { + memio::appendrune(&m, r)?; + } else { + break (runecount, linenb, c.err): error; + }; + }; + } else { + return (runecount, linenb, "Syntax error: variable not assigned or used"): error; + }; +}; + fn parsevarname(p: *parser, runecount: size, linenb: size = 0) (varname | error) = { let m = memio::dynamic(); defer io::close(&m)!; return for (let r => read_rune(p)?) { runecount += 1; - if (r == '=') { + if (r == '=' || r == '(') { unreadrune(p, r); let s = strings::trim(memio::string(&m)?); break strings::dup(s)?: varname; @@ -115,6 +179,36 @@ fn parsevarname(p: *parser, runecount: size, linenb: size = 0) (varname | error) }; }; +fn parsearglist(p: *parser, runecount: size, linenb: size = 0) (arglist | error) = { + static let buf: [256]u8 = @undefined; + let arglist: arglist = []; + + let m = memio::dynamic(); + defer io::close(&m)!; + + for (let r => read_rune(p)?) { + switch (r) { + case '"' => + let arg = parsetil(p, '"', void, 0z)?; + // reread unread " + read_rune(p)?; + append(arglist, arg)?; + case '\'' => + let arg = parsetil(p, '\'', void, 0z)?; + // reread unread ' + read_rune(p)?; + append(arglist, arg)?; + case ',', ' ' => + continue; + case ')' => + break; + case => + return (runecount, linenb, "Argument literals must be wrapped with \" or '"): error; + }; + }; + return arglist; +}; + fn parseoperation(p: *parser, runecount: size, linenb: size = 0) (operation | error) = { static let buf: [256]u8 = @undefined; let stack: []argument = []; @@ -214,9 +308,9 @@ export fn parse(p: *parser, linenb: size = 0) (ast | error) = { children = [], }; append(tree, node)?; - case assign => + case (assign | finvoke) => root.value = t; - case operation => + case (operation | arglist) => let node: ast = ast { value = t, children = [], @@ -229,6 +323,8 @@ export fn parse(p: *parser, linenb: size = 0) (ast | error) = { return root; }; +fn isalnum(c: rune) bool = ascii::isalnum(c); + export fn parsetoken(p: *parser, linenb: size = 0) (token | error) = { static let buf: [256]u8 = @undefined; let runecount = 0z; @@ -236,7 +332,12 @@ export fn parsetoken(p: *parser, linenb: size = 0) (token | error) = { return switch (p.state) { case tokentype::START => // VARNAME - let t = parsevarname(p, 0z)?; + //let t = parsevarname(p, 0z)?; + const cond = validrunecond { + f = &isalnum: validrunecondfn, + err = "Character is not alphanumeric", + }; + let t = parsetil(p, ['=', '('], cond, 0z)?; p.state = tokentype::VARNAME; yield t; case tokentype::VARNAME => @@ -246,14 +347,22 @@ export fn parsetoken(p: *parser, linenb: size = 0) (token | error) = { case let r: rune => yield if (r == '=') { p.state = tokentype::ASSIGN; - yield '='; + yield '=': assign; + } else if (r == '(') { + p.state = tokentype::FINVOKE; + yield '(': finvoke; } else { - const msg = fmt::bsprintf(buf, "Missing '=', found '{}' instead", r)?; + const msg = fmt::bsprintf(buf, "Missing '=' or '(', found '{}' instead", r)?; yield (runecount, linenb, msg): error; }; case io::EOF => yield (runecount, linenb, "Unexpected EOF"): error; }; + case tokentype::FINVOKE => + // look for FINVOKE + let t = parsearglist(p, 0z)?; + p.state = tokentype::START; + yield t; case tokentype::ASSIGN => // look for OPERATION let t = parseoperation(p, 0z)?; diff --git a/spec/variable-declaration.md b/spec/variable-declaration.md index 964671a..0f96c4d 100644 --- a/spec/variable-declaration.md +++ b/spec/variable-declaration.md @@ -71,7 +71,7 @@ a=15 Declares a variable `b` with the string value "a string". ``` -s=a string +s="a string" ``` Declares the variable `o` that is an operation resulting in the number value 6. diff --git a/test.ra b/test.ra index 93c6ebd..de49f3d 100644 --- a/test.ra +++ b/test.ra @@ -1,2 +1,3 @@ a=2 1 + 6 * b=3 5 * +ls() -- cgit v1.2.3