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 | finvoke | assign | operation | arglist); export type finvoke = rune; 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 arglist = []str; export type tokentype = enum { START, VARNAME, FINVOKE, OPERAND, OPERATOR, OPERATION, LITERAL, ASSIGN, ARGLIST, }; 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::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) = { // parsetoken() //}; export type parser = struct { h: io::handle, state: tokentype, unreadbuf: [2]rune, unreadcount: size, currentline: size, end: bool, }; 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) { 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 match (bufio::read_rune(p.h)?) { case let r: rune => if (r == '\n') { p.currentline += 1; }; yield r; case io::EOF => p.end = true; yield io::EOF; }; }; }; 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, ) (str | io::EOF | error) = { let m = memio::dynamic(); defer io::close(&m)!; return for (let r => read_rune(p)?) { if (r == '\n') { continue; }; 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, p.currentline, c.err): error; }; }; } else { if (runecount == 0) { return io::EOF; }; return (runecount, p.currentline, "Syntax error: variable not assigned or used"): error; }; }; fn parsevarname(p: *parser, runecount: size) (varname | error) = { let m = memio::dynamic(); defer io::close(&m)!; return for (let r => read_rune(p)?) { runecount += 1; if (r == '=' || 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, p.currentline, "Character is not alphanumeric"): error; }; } else { return (runecount, p.currentline, "Syntax error: variable not assigned or used"): error; }; }; fn parsearglist(p: *parser, runecount: size) (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)?; let arg = match (arg) { case io::EOF => return (runecount, p.currentline, "Unexpected EOF"): error; case let a: str => yield a; }; // reread unread " read_rune(p)?; append(arglist, arg)?; case '\'' => let arg = parsetil(p, '\'', void, 0z)?; let arg = match (arg) { case io::EOF => return (runecount, p.currentline, "Unexpected EOF"): error; case let a: str => yield a; }; // reread unread ' read_rune(p)?; append(arglist, arg)?; case ',', ' ' => continue; case ')' => break; case => return (runecount, p.currentline, "Argument literals must be wrapped with \" or '"): error; }; }; return arglist; }; fn parseoperation(p: *parser, runecount: size) (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, p.currentline, 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) (ast | done | error) = { if (p.end) { fmt::errorln("newdone")!; return done; }; let stop = false; //let t = parsetoken(p, linenb); //return parse(p); let tree: []ast = []; let root: ast = @undefined; for (!stop) { let t = parsetoken(p)?; match (t) { case io::EOF => return done; case let t: varname => let node: ast = ast { value = t, children = [], }; append(tree, node)?; case let t: finvoke => root.value = t; case let t: assign => root.value = t; case let t: operation => let node: ast = ast { value = t, children = [], }; append(tree, node)?; stop = true; case let t: arglist => let node: ast = ast { value = t, children = [], }; append(tree, node)?; stop = true; }; }; root.children = tree; return root; }; fn isalnum(c: rune) bool = ascii::isalnum(c); export fn parsetoken(p: *parser) (token | io::EOF | error) = { static let buf: [256]u8 = @undefined; let runecount = 0z; return switch (p.state) { case tokentype::START => // VARNAME //let t = parsevarname(p, 0z)?; const cond = validrunecond { f = &isalnum: validrunecondfn, err = "Character is not alphanumeric", }; let t = parsetil(p, ['=', '('], cond, 0z)?; yield match (t) { case io::EOF => return io::EOF; case let t: str => 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 '=': assign; } else if (r == '(') { p.state = tokentype::FINVOKE; yield '(': finvoke; } else { const msg = fmt::bsprintf(buf, "Missing '=' or '(', found '{}' instead", r)?; yield (runecount, p.currentline, msg): error; }; case io::EOF => yield (runecount, p.currentline, "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 fmt::errorln("parseoperation")!; let t = parseoperation(p, 0z)?; p.state = tokentype::START; yield t; case => yield (runecount, p.currentline, "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; //}; };