diff options
| author | Julian Hurst <ark@mansus.space> | 2026-03-28 19:17:16 +0100 |
|---|---|---|
| committer | Julian Hurst <ark@mansus.space> | 2026-03-28 19:17:16 +0100 |
| commit | 474c14c92de165516f4302685e3fa8acc3a64f45 (patch) | |
| tree | 6c1315dd6ea19c35f8a15f4ac6179e286e57bce7 | |
| parent | 4f7aa6ba55ace8e81dde90183ed2e17d05bce8e5 (diff) | |
| download | rabbitscript-474c14c92de165516f4302685e3fa8acc3a64f45.tar.gz | |
Add finvoke parsing and interpreting
| -rw-r--r-- | cmd/hs/.hs.ha.swp | bin | 12288 -> 0 bytes | |||
| -rw-r--r-- | cmd/hs/hs.ha | 9 | ||||
| -rw-r--r-- | internal/interpreter/.interpreter.ha.swp | bin | 12288 -> 0 bytes | |||
| -rw-r--r-- | internal/interpreter/interpreter.ha | 32 | ||||
| -rw-r--r-- | internal/parser/.parser.ha.swp | bin | 20480 -> 0 bytes | |||
| -rw-r--r-- | internal/parser/parser.ha | 123 | ||||
| -rw-r--r-- | spec/variable-declaration.md | 2 | ||||
| -rw-r--r-- | test.ra | 1 |
8 files changed, 158 insertions, 9 deletions
diff --git a/cmd/hs/.hs.ha.swp b/cmd/hs/.hs.ha.swp Binary files differdeleted file mode 100644 index 7e6d875..0000000 --- a/cmd/hs/.hs.ha.swp +++ /dev/null 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 Binary files differdeleted file mode 100644 index 76a76c9..0000000 --- a/internal/interpreter/.interpreter.ha.swp +++ /dev/null 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 Binary files differdeleted file mode 100644 index e2d9062..0000000 --- a/internal/parser/.parser.ha.swp +++ /dev/null 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. @@ -1,2 +1,3 @@ a=2 1 + 6 * b=3 5 * +ls() |
