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; //}; };