-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
180 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pub struct Token(kind, value) | ||
|
||
pub struct BinaryExpr(left, op, right) | ||
|
||
pub struct LiteralExpr(value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub struct ParseError(message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using ./ast | ||
|
||
pub fn evaluate(ast) { | ||
next(ast) | ||
} | ||
|
||
fn next(expr) { | ||
return nextBinary(expr) if expr | isType(BinaryExpr) | ||
return nextLiteral(expr) if expr | isType(LiteralExpr) | ||
} | ||
|
||
fn nextBinary(expr) { | ||
let left = expr->left | next | ||
let right = expr->right | next | ||
|
||
return left + right if expr->op == "+" | ||
return left - right if expr->op == "-" | ||
return left * right if expr->op == "*" | ||
return left / right if expr->op == "/" | ||
} | ||
|
||
fn nextLiteral(expr) { | ||
expr->value->value | into::float | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using ./errors | ||
using ./ast | ||
|
||
let chars = "" | ||
let index = 0 | ||
|
||
pub fn lex(input) { | ||
chars = input | ||
|
||
let tokens = [] | ||
while index < len(chars) { | ||
tokens | push(next) | ||
} | ||
|
||
chars = "" | ||
index = 0 | ||
|
||
tokens | ||
} | ||
|
||
fn current() { | ||
chars | iter::at(index) | ||
} | ||
|
||
fn next() { | ||
let c = current() | ||
while c | str::isWhitespace { | ||
c = advance | ||
} | ||
|
||
return nil if c == nil | ||
|
||
if c in ["+", "-", "*", "/"] { | ||
advance | ||
return new Token(c, c) | ||
} | ||
|
||
return nextNumber() if c | str::isDigit | ||
|
||
throw new ParseError("Unexpected character: ${c}") | ||
} | ||
|
||
fn nextNumber() { | ||
let digits = [] | ||
while (str::isDigit(current) or current() == ".") { | ||
digits | push(current) | ||
advance() | ||
} | ||
|
||
let numberString = digits | join | ||
|
||
return new Token("number", into::float(numberString)) if "." in digits | ||
return new Token("number", into::int(numberString)) | ||
} | ||
|
||
fn advance() { | ||
index += 1 | ||
|
||
current() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
with ./parser | ||
with ./evaluator | ||
|
||
fn main() { | ||
if (len(getArgv) > 1) { | ||
getArgv | ||
| join " " | ||
| evaluate | ||
| println | ||
|
||
return | ||
} | ||
|
||
while true { | ||
let input = ">> " | ansi::color blue | io::interactiveInput | ||
continue if not input | ||
input | evaluate | println | ||
} | ||
} | ||
|
||
fn evaluate(input) { | ||
parser::parse(input) | ||
| evaluator::evaluate | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using ./errors | ||
using ./ast | ||
with ./lexer | ||
|
||
let tokens = [] | ||
let index = 0 | ||
|
||
pub fn parse(input) { | ||
tokens = lexer::lex(input) | ||
index = 0 | ||
|
||
expr() | ||
} | ||
|
||
fn current() { | ||
tokens | iter::at(index) | ||
} | ||
|
||
fn advance() { | ||
let token = current() | ||
index += 1 | ||
|
||
token | ||
} | ||
|
||
fn match(kinds...) { | ||
return false if not current() | ||
kinds | iter::anyOf => kind { kind == current()->kind } | ||
} | ||
|
||
fn expr() { | ||
additive() | ||
} | ||
|
||
fn additive() { | ||
let left = multiplicative() | ||
while match("+", "-") { | ||
let op = advance()->kind | ||
let right = multiplicative() | ||
|
||
left = new BinaryExpr(left, op, right) | ||
} | ||
|
||
left | ||
} | ||
|
||
fn multiplicative() { | ||
let left = primary() | ||
while match("*", "/") { | ||
let op = advance()->kind | ||
let right = primary() | ||
|
||
left = new BinaryExpr(left, op, right) | ||
} | ||
|
||
left | ||
} | ||
|
||
fn primary() { | ||
if match("number") { | ||
return new LiteralExpr(advance()) | ||
} | ||
|
||
throw new ParseError("Unexpected end of expression") if current() != nil | ||
throw new ParseError("Unexpected token: '${current()->kind}'") | ||
} |