basic eval

This commit is contained in:
tezlm 2023-02-14 11:21:55 -08:00
parent ced1c64a96
commit 43bfff002f
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA

View file

@ -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");