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.ha293
1 files changed, 293 insertions, 0 deletions
diff --git a/internal/parser/parser.ha b/internal/parser/parser.ha
new file mode 100644
index 0000000..682cb35
--- /dev/null
+++ b/internal/parser/parser.ha
@@ -0,0 +1,293 @@
+use memio;
+use bufio;
+use encoding::utf8;
+use io;
+use strings;
+use strconv;
+use ascii;
+use fmt;
+
+//export type token = struct {
+// tktype: tokentype,
+// value: str,
+//};
+
+export type token = (varname | assign | operation);
+
+export type assign = rune;
+
+export type varname = str;
+
+export type argument = (int | operator);
+export type operator = enum {
+ ADD,
+ SUBTRACT,
+ MULTIPLY,
+ DIVIDE,
+};
+export type operation = struct {
+ stack: []argument,
+};
+
+export type tokentype = enum {
+ START,
+ VARNAME,
+ OPERAND,
+ OPERATOR,
+ OPERATION,
+ LITERAL,
+ ASSIGN,
+};
+
+export type parseerror = !(size, size, str);
+
+export type error = !(parseerror | io::error | utf8::invalid | strconv::invalid | strconv::overflow | nomem);
+
+export fn strtktype(tktype: tokentype) str = switch (tktype) {
+case tokentype::START => yield "START";
+case tokentype::VARNAME => yield "VARNAME";
+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";
+};
+
+//export fn parseast(h: io::handle) (void | io::error) = {
+// parsetoken()
+//};
+
+export type parser = struct {
+ h: io::handle,
+ state: tokentype,
+ unreadbuf: [2]rune,
+ unreadcount: size,
+};
+
+export fn strerror(e: error) str = {
+ static let b: [256]u8 = @undefined;
+ return match (e) {
+ case let e: parseerror =>
+ yield fmt::bsprintf(b, "Error on col {} line {}: {}", e.0, e.1, e.2)!;
+ case let e: io::error =>
+ yield io::strerror(e);
+ case let e: utf8::invalid =>
+ yield utf8::strerror(e);
+ case let e: (strconv::invalid | strconv::overflow) =>
+ yield strconv::strerror(e);
+ case nomem =>
+ yield "No memory left";
+ };
+};
+
+fn unreadrune(p: *parser, rn: rune) void = {
+ p.unreadbuf[p.unreadcount] = rn;
+ p.unreadcount += 1;
+};
+
+fn read_rune(p: *parser) (rune | io::EOF | error) = {
+ if (p.unreadcount > 0) {
+ p.unreadcount -= 1;
+ return p.unreadbuf[p.unreadcount];
+ } else {
+ return bufio::read_rune(p.h)?;
+ };
+};
+
+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 == '=') {
+ unreadrune(p, r);
+ let s = strings::trim(memio::string(&m)?);
+ break strings::dup(s)?: varname;
+ };
+ if (ascii::isalnum(r)) {
+ memio::appendrune(&m, r)?;
+ } else {
+ break (runecount, linenb, "Character is not alphanumeric"): error;
+ };
+ } else {
+ return (runecount, linenb, "Syntax error: variable not assigned or used"): error;
+ };
+};
+
+fn parseoperation(p: *parser, runecount: size, linenb: size = 0) (operation | error) = {
+ static let buf: [256]u8 = @undefined;
+ let stack: []argument = [];
+
+ let m = memio::dynamic();
+ defer io::close(&m)!;
+
+ let checkneg = false;
+
+ for (let r => read_rune(p)?) {
+ runecount += 1;
+ if (ascii::isdigit(r)) {
+ if (checkneg) {
+ memio::appendrune(&m, '-')?;
+ checkneg = false;
+ };
+ memio::appendrune(&m, r)?;
+ } else {
+ if (checkneg) {
+ append(stack, operator::SUBTRACT)?;
+ };
+ let s = memio::string(&m)?;
+ if (s != "") {
+ fmt::errorfln("Try parse num: {}", s)!;
+ let i = strconv::stoi(s)?;
+ append(stack, i)?;
+ memio::reset(&m);
+ fmt::errorfln("Parse num: {}", i)!;
+ };
+ switch (r) {
+ case '+' =>
+ append(stack, operator::ADD)?;
+ fmt::errorln("Parse add")!;
+ case '-' =>
+ // could be negative number or subtraction
+ checkneg = true;
+ case '*' =>
+ append(stack, operator::MULTIPLY)?;
+ fmt::errorln("Parse mul")!;
+ case '/' =>
+ append(stack, operator::DIVIDE)?;
+ fmt::errorln("Parse div")!;
+ case ' ' =>
+ fmt::errorln("Parse space")!;
+ case '\n' =>
+ fmt::errorln("Parse newline")!;
+ break;
+ case =>
+ const msg = fmt::bsprintf(buf, "Unexpected char '{}'", r)?;
+ return (runecount, linenb, msg): error;
+ };
+ };
+ };
+ for (let arg .. stack) {
+ match (arg) {
+ case let i: int =>
+ fmt::errorf("{} ", i)!;
+ case let o: operator =>
+ switch (o) {
+ case operator::ADD =>
+ fmt::error("+ ")!;
+ case operator::SUBTRACT =>
+ fmt::error("- ")!;
+ case operator::MULTIPLY =>
+ fmt::error("* ")!;
+ case operator::DIVIDE =>
+ fmt::error("/ ")!;
+ };
+ };
+ };
+ fmt::errorln()!;
+ return operation {
+ stack = stack,
+ };
+};
+
+export type ast = struct {
+ value: token,
+ children: []ast,
+};
+
+export fn parse(p: *parser, linenb: size = 0) (ast | error) = {
+ let stop = false;
+
+ //let t = parsetoken(p, linenb);
+ //return parse(p);
+
+ let tree: []ast = [];
+ let root: ast = @undefined;
+
+ for (!stop) {
+ let t = parsetoken(p, linenb)?;
+ match (t) {
+ case varname =>
+ let node: ast = ast {
+ value = t,
+ children = [],
+ };
+ append(tree, node)?;
+ case assign =>
+ root.value = t;
+ case operation =>
+ let node: ast = ast {
+ value = t,
+ children = [],
+ };
+ append(tree, node)?;
+ stop = true;
+ };
+ };
+ root.children = tree;
+ return root;
+};
+
+export fn parsetoken(p: *parser, linenb: size = 0) (token | error) = {
+ static let buf: [256]u8 = @undefined;
+ let runecount = 0z;
+
+ return switch (p.state) {
+ case tokentype::START =>
+ // VARNAME
+ let t = parsevarname(p, 0z)?;
+ p.state = tokentype::VARNAME;
+ yield t;
+ case tokentype::VARNAME =>
+ // look for ASSIGN
+ let r = read_rune(p)?;
+ yield match (r) {
+ case let r: rune =>
+ yield if (r == '=') {
+ p.state = tokentype::ASSIGN;
+ yield '=';
+ } else {
+ const msg = fmt::bsprintf(buf, "Missing '=', found '{}' instead", r)?;
+ yield (runecount, linenb, msg): error;
+ };
+ case io::EOF =>
+ yield (runecount, linenb, "Unexpected EOF"): error;
+ };
+ case tokentype::ASSIGN =>
+ // look for OPERATION
+ let t = parseoperation(p, 0z)?;
+ p.state = tokentype::START;
+ yield t;
+ case =>
+ yield (runecount, linenb, "Not implemented"): error;
+ };
+
+ //return for (let r => read_rune(p)?) {
+ // fmt::errorln(r)?;
+ // if (r == '=') {
+ // if (runecount > 0) {
+ // unreadrune(p, r);
+ // let s = memio::string(&m)?;
+ // break token {
+ // tktype = tokentype::VARNAME,
+ // value = strings::dup(s)?,
+ // };
+ // } else {
+ // break token {
+ // tktype = tokentype::ASSIGN,
+ // value = strings::dup("=")?,
+ // };
+ // };
+ // } else {
+ // if (ascii::isalnum(r)) {
+ // memio::appendrune(&m, r)?;
+ // } else {
+ // break (runecount, linenb, "Character is not alphanumeric"): error;
+ // };
+ // };
+ // runecount += 1;
+ //} else {
+ // return (runecount, linenb, "Syntax error"): error;
+ //};
+};