summaryrefslogtreecommitdiff
path: root/internal/parser/parser.ha
diff options
context:
space:
mode:
Diffstat (limited to 'internal/parser/parser.ha')
-rw-r--r--internal/parser/parser.ha123
1 files changed, 116 insertions, 7 deletions
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)?;