basic eval
This commit is contained in:
parent
ced1c64a96
commit
43bfff002f
1 changed files with 140 additions and 67 deletions
207
src/main.rs
207
src/main.rs
|
@ -1,5 +1,11 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
// TODO:
|
||||
// make "rusty" instead of directly ported from js
|
||||
// use enums instead of strings!
|
||||
// modularize into separate files?
|
||||
// do proper error handling instead of panics everywhere
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::{Regex, RegexBuilder};
|
||||
|
||||
|
@ -43,7 +49,7 @@ enum Tree {
|
|||
Function { ident: Box<Tree>, terms: Vec<Tree> },
|
||||
Index { array: Box<Tree>, index: Box<Tree> },
|
||||
Array { terms: Vec<Tree> },
|
||||
Value { term: String },
|
||||
Value { term: String, kind: String },
|
||||
}
|
||||
|
||||
fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(String, &str)>>, min_bp: usize) -> Tree {
|
||||
|
@ -78,10 +84,10 @@ fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(String, &str)>>, mi
|
|||
}
|
||||
tokens.next();
|
||||
Tree::Array { terms: cons }
|
||||
},
|
||||
},
|
||||
token @ (symb, _) if symb == "symb" => panic!("unexpected token {}", token.1),
|
||||
token @ (_, "in") => panic!("unexpected token {}", token.1),
|
||||
token @ (_, _) => Tree::Value { term: token.1.to_owned() },
|
||||
token @ (kind, _) => Tree::Value { term: token.1.to_owned(), kind: kind.clone() },
|
||||
};
|
||||
|
||||
loop {
|
||||
|
@ -168,71 +174,135 @@ fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(String, &str)>>, mi
|
|||
}
|
||||
}
|
||||
|
||||
fn expr(tree: Tree) {
|
||||
// match Tree {
|
||||
// Tree::Unary { op, term }
|
||||
// }
|
||||
#[derive(Debug)]
|
||||
enum Value {
|
||||
Number(f64),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
Array { kind: Box<Value>, values: Vec<Value> },
|
||||
}
|
||||
|
||||
// function expr(tree, ctx: { [name: string]: any } = {}): any {
|
||||
// switch (tree.type) {
|
||||
// case "int": return parseInt(tree.val);
|
||||
// case "float": return parseFloat(tree.val);
|
||||
// case "str": return tree.val.slice(1, -1);
|
||||
// case "ident":
|
||||
// switch (tree.val) {
|
||||
// case "true": return true;
|
||||
// case "false": return false;
|
||||
// case "null": return null;
|
||||
// default: return Object.hasOwn(ctx, tree.val) && ctx[tree.val];
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (tree.op) {
|
||||
// if (tree.op.val === ".") {
|
||||
// if (tree.cons[1].type !== "ident") throw "expected ident";
|
||||
// const obj = expr(tree.cons[0], ctx);
|
||||
// const idx = tree.cons[1].val;
|
||||
// if (!Object.hasOwn(obj, idx)) throw `${idx} doesn't exist on object`;
|
||||
// return obj[idx];
|
||||
// }
|
||||
// const op = ({
|
||||
// "**": (a, b) => a ** b,
|
||||
// "*": (a, b) => a * b,
|
||||
// "/": (a, b) => a / b,
|
||||
// "%": (a, b) => a % b,
|
||||
// "+": (a, b) => a + b,
|
||||
// "-": (a, b) => b === undefined ? -a : a - b,
|
||||
// "==": (a, b) => a == b,
|
||||
// "!=": (a, b) => a != b,
|
||||
// ">=": (a, b) => a >= b,
|
||||
// "<=": (a, b) => a <= b,
|
||||
// ">": (a, b) => a > b,
|
||||
// "<": (a, b) => a < b,
|
||||
// "in": (a, b) => {
|
||||
// if (!Array.isArray(b)) throw "can only use `in` on arrays";
|
||||
// return b.includes(a);
|
||||
// },
|
||||
// "&&": (a, b) => a && b,
|
||||
// "||": (a, b) => a || b,
|
||||
// "?": (cond, a, b) => cond ? a : b,
|
||||
// ",": (a, b) => (a, b),
|
||||
// })[tree.op.val];
|
||||
// if (!op) throw `invalid operator ${op}`;
|
||||
// return op(...tree.cons.map(i => expr(i, ctx)));
|
||||
// }
|
||||
|
||||
// if (tree.array) return tree.array.map(i => expr(i, ctx));
|
||||
// if (tree.func) return expr(tree.func, ctx)(...tree.cons.map(i => expr(i, ctx)));
|
||||
|
||||
// if (tree.index) {
|
||||
// const obj = expr(tree.index, ctx);
|
||||
// const idx = expr(tree.with, ctx);
|
||||
// if (!Object.hasOwn(obj, idx)) throw `${idx} doesn't exist on object`;
|
||||
// return obj[idx];
|
||||
// }
|
||||
// }
|
||||
|
||||
fn expr(tree: &Tree) -> Value {
|
||||
match tree {
|
||||
Tree::Value { term, kind } => {
|
||||
match kind.as_str() {
|
||||
"float" => Value::Number(term.parse().expect("invalid number")),
|
||||
"integer" => Value::Number(term.parse().expect("invalid number")),
|
||||
"str" => Value::String(term.clone()),
|
||||
"ident" => match term.as_str() {
|
||||
"true" => Value::Boolean(true),
|
||||
"false" => Value::Boolean(false),
|
||||
_ => todo!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
Tree::Unary { op, term } => {
|
||||
let term = expr(term.as_ref());
|
||||
match op.as_str() {
|
||||
"-" => match term {
|
||||
Value::Number(n) => Value::Number(-n),
|
||||
_ => panic!("cannot negate non number"),
|
||||
},
|
||||
"!" => match term {
|
||||
Value::Boolean(b) => Value::Boolean(!b),
|
||||
_ => panic!("cannot invert non boolean"),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
Tree::Binary { op, left, right } => {
|
||||
let left = expr(left.as_ref());
|
||||
let right = expr(right.as_ref());
|
||||
match op.as_str() {
|
||||
// note to self: i should make a macro for this (or find out how to do this properly lol)
|
||||
"+" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left + right),
|
||||
(Value::String(left), Value::String(right)) => Value::String(left + right.as_str()),
|
||||
_ => panic!("cannot negate add non numbers or non strings"),
|
||||
},
|
||||
"-" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left - right),
|
||||
_ => panic!("cannot subtract non number"),
|
||||
},
|
||||
"*" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left * right),
|
||||
_ => panic!("cannot multiply non number"),
|
||||
},
|
||||
"/" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left / right),
|
||||
_ => panic!("cannot divide non number"),
|
||||
},
|
||||
"%" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left % right),
|
||||
_ => panic!("cannot modulo non number"),
|
||||
},
|
||||
"**" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left.powf(right)),
|
||||
_ => panic!("cannot exponentiate non number"),
|
||||
},
|
||||
"==" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left == right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left == right),
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left == right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"!=" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left != right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left != right),
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left != right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
">" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left > right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left > right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"<" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left < right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left < right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
">=" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left <= right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left <= right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"<=" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left <= right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left <= right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"&&" => match (left, right) {
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left && right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"||" => match (left, right) {
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left || right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
},
|
||||
Tree::Ternary { op, left, middle, right } => {
|
||||
let left = expr(left.as_ref());
|
||||
match op.as_str() {
|
||||
"?" => {
|
||||
let Value::Boolean(cond) = left else {
|
||||
panic!("cannot use non boolean as boolean");
|
||||
};
|
||||
if cond {
|
||||
expr(middle.as_ref())
|
||||
} else {
|
||||
expr(right.as_ref())
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn test(code: &str) {
|
||||
let tokens = lex(code);
|
||||
|
@ -241,10 +311,12 @@ fn test(code: &str) {
|
|||
.filter(|(t, _)| t != "space")
|
||||
.collect();
|
||||
let tree = parse(&mut tokens.iter().peekable(), 0);
|
||||
dbg!(tree);
|
||||
let result = expr(&tree);
|
||||
dbg!(result);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test("123");
|
||||
// test("-123");
|
||||
// test("2 + 3 * 4");
|
||||
// test("(2 + 3) * 4");
|
||||
|
@ -252,6 +324,7 @@ fn main() {
|
|||
// test("math.sin(10 * math.pi)");
|
||||
// test("\"foo\" in [\"fooo\", \"bar\", \"baz\"]");
|
||||
// test("\"foo\" ? 1 : 4");
|
||||
// test("true ? 3 4");
|
||||
// test("\"hello\" + \", \" + \"world\"");
|
||||
// test("\"hello\".length");
|
||||
// test("[1, 2, 3].length");
|
||||
|
|
Loading…
Reference in a new issue