summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorJulian Hurst <ark@mansus.space>2026-03-28 19:17:16 +0100
committerJulian Hurst <ark@mansus.space>2026-03-28 19:17:16 +0100
commit474c14c92de165516f4302685e3fa8acc3a64f45 (patch)
tree6c1315dd6ea19c35f8a15f4ac6179e286e57bce7 /internal
parent4f7aa6ba55ace8e81dde90183ed2e17d05bce8e5 (diff)
downloadrabbitscript-474c14c92de165516f4302685e3fa8acc3a64f45.tar.gz
Add finvoke parsing and interpreting
Diffstat (limited to 'internal')
-rw-r--r--internal/interpreter/.interpreter.ha.swpbin12288 -> 0 bytes
-rw-r--r--internal/interpreter/interpreter.ha32
-rw-r--r--internal/parser/.parser.ha.swpbin20480 -> 0 bytes
-rw-r--r--internal/parser/parser.ha123
4 files changed, 147 insertions, 8 deletions
diff --git a/internal/interpreter/.interpreter.ha.swp b/internal/interpreter/.interpreter.ha.swp
deleted file mode 100644
index 76a76c9..0000000
--- a/internal/interpreter/.interpreter.ha.swp
+++ /dev/null
Binary files 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
--- a/internal/parser/.parser.ha.swp
+++ /dev/null
Binary files 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)?;