You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

391 lines
14 KiB
Rust

use crate::ast::tree::{FnBody, Node, Operator, Type};
use crate::error::Error;
use std::borrow::Borrow;
pub(crate) mod native;
pub(crate) mod obj;
use obj::Object;
pub(crate) mod val;
use crate::rt::obj::{KEY_DEPENDENCIES, KEY_SOURCES, KEY_TYPE};
use val::{Scope, Val, ValOps};
/// Execute a program.
/// The `root` node must be a `Node::File`.
pub fn exec(root: &Node) -> Result<Val, Error> {
match root {
Node::File(pos, f) => {
let mut root_scope = Scope::new_root(pos.file.clone());
let mut val = Val::new(());
for node in &f.content {
val = node.eval(&mut root_scope)?;
}
Ok(val)
}
node => Err(Error::runtime_error(
empty_pos(),
format!("cannot exec non-file node {}", node),
)),
}
}
impl Node {
fn eval(&self, scope: &mut Scope) -> Result<Val, Error> {
match self {
Node::Bool(_, b) => Ok(Val::new(*b)),
Node::Int(_, i) => Ok(Val::new(*i)),
Node::String(_, s) => Ok(Val::new(s.clone())),
Node::Ident(pos, name) => {
if let Some(sym) = scope.resolve(name) {
Ok(sym.clone())
} else {
Err(Error::runtime_error(
pos.clone(),
format!("Identifier \"{}\" not found in this scope", name),
))
}
}
Node::Array(_, elems) => {
let mut vals = Vec::new();
for e in elems {
vals.push(e.eval(scope)?);
}
Ok(Val::new(vals))
}
Node::UnaryExpr(_, expr) => expr.op.eval_unary(scope, expr.rhs.borrow()),
Node::BinaryExpr(_, expr) => {
expr.op
.eval_binary(scope, expr.lhs.borrow(), expr.rhs.borrow())
}
Node::IfStmt(_, expr) => exec_if(
scope,
expr.condition.borrow(),
expr.then_body.borrow(),
if let Some(els) = expr.else_body.borrow() {
Some(els)
} else {
None
},
),
Node::WhileStmt(_, stmt) => {
exec_while(scope, stmt.condition.borrow(), stmt.body.borrow())
}
Node::ForStmt(_, stmt) => exec_for(
scope,
if let Some(setup) = &stmt.setup {
Some(setup)
} else {
None
},
if let Some(condition) = &stmt.condition {
Some(condition)
} else {
None
},
if let Some(exec) = &stmt.exec {
Some(exec)
} else {
None
},
&stmt.body,
),
Node::CallExpr(_, expr) => {
// we don't execute the function within the callback because
// (1) that would block the mutex for way longer than necessary and
// (2) recursive function calls would cause a deadlock (ask me how i know)
let (func, mut capture) = expr.func.eval(scope)?.as_fn(|func, capture| {
(func.clone(), capture.clone())
})?;
exec_fn(scope, &mut capture, &func.body, &func.params, &expr.params)
},
Node::ArrayExpr(pos, expr) => expr.array.eval(scope)?.as_array(|array| {
expr.index.eval(scope)?.as_int(|index| {
if *index >= 0 && *index < array.len() as i128 {
Ok(array[*index as usize].clone())
} else {
Err(Error::runtime_error(
pos.clone(),
format!("Array index {} out of bounds", index),
))
}
})?
})?,
Node::Break(_) => {
let mut val = Val::new(());
val.set_break(true);
Ok(val)
}
Node::ReturnStmt(_, expr) => {
let mut val = expr.eval(scope)?;
val.set_return(true);
Ok(val)
}
Node::Block(_, nodes) => {
let mut child_scope = Scope::new(scope);
for n in nodes {
let val = n.eval(&mut child_scope)?;
if val.is_return() || val.is_break() {
return Ok(val);
}
}
Ok(Val::new(()))
}
Node::TargetStmt(_, stmt) => {
let name = stmt.name.assert_ident()?;
let target_obj = Object::target(name.clone());
scope.object().add_child(target_obj.clone())?;
let mut target_scope = Scope::new_with_obj(scope, target_obj);
stmt.val.eval(&mut target_scope)?.assert_void()?;
Ok(Val::new(()))
}
Node::SetStmt(_, stmt) => {
let name = stmt.name.assert_ident()?;
let val = stmt.val.eval(scope)?;
scope.object().set(name.clone(), val);
Ok(Val::new(()))
}
Node::Fn(_, func) => {
if let Some(name) = &func.name {
let name = name.assert_ident()?;
let fn_obj = Object::func(name.clone(), func.clone());
scope.object().add_child(fn_obj)?;
scope.define(name.clone(), Val::new((func.clone(), scope.clone())));
}
Ok(Val::new((func.clone(), scope.clone())))
}
Node::DepList(_, node) => append_list(scope, node, KEY_DEPENDENCIES),
Node::SourceList(_, node) => append_list(scope, node, KEY_SOURCES),
Node::TypeStmt(_, node) => {
let val = node.eval(scope)?;
scope.object().set(String::from(KEY_TYPE), val);
Ok(Val::new(()))
}
n => panic!("Unexpected node {}", n),
}
}
}
impl Operator {
pub fn eval_unary(&self, scope: &mut Scope, node: &Node) -> Result<Val, Error> {
match self {
Operator::Minus => node.eval(scope)?.as_int(|i| Ok(Val::new(-*i)))?,
Operator::Bang => node.eval(scope)?.as_bool(|b| Ok(Val::new(!*b)))?,
_ => panic!("eval_unary() called on non-unary operator {}", self),
}
}
pub fn eval_binary(&self, scope: &mut Scope, lhs: &Node, rhs: &Node) -> Result<Val, Error> {
match self {
Operator::Eq => match lhs {
Node::Ident(pos, name) => {
let val = rhs.eval(scope)?;
match val.typ() {
Type::Void => {
Err(Error::type_error(pos.clone(), Type::Unknown, Type::Void))
}
_ => {
scope.define(name.clone(), val);
Ok(Val::new(()))
}
}
}
_ => Err(Error::runtime_error(
empty_pos(),
String::from("Cannot assign"),
)),
},
Operator::Plus => {
let left = lhs.eval(scope)?;
if left.is_string() {
let left = left.as_string(|s| s.clone())?;
rhs.eval(scope)?.as_string(|s| Val::new(left + s))
} else {
let left = left.as_int(|i| *i)?;
rhs.eval(scope)?.as_int(|i| Val::new(left + *i))
}
}
Operator::Minus => exec_math(scope, lhs, rhs, |l, r| l - r),
Operator::Asterisk => exec_math(scope, lhs, rhs, |l, r| l * r),
Operator::Slash => exec_math(scope, lhs, rhs, |l, r| l / r),
Operator::Percent => exec_math(scope, lhs, rhs, |l, r| l % r),
Operator::Pipe => exec_math(scope, lhs, rhs, |l, r| l | r),
Operator::Caret => exec_math(scope, lhs, rhs, |l, r| l ^ r),
Operator::Amp => exec_math(scope, lhs, rhs, |l, r| l & r),
Operator::GtGt => exec_math(scope, lhs, rhs, |l, r| l >> r),
Operator::LtLt => exec_math(scope, lhs, rhs, |l, r| l << r),
Operator::EqEq => exec_cmp(scope, lhs, rhs, |l, r| l == r),
Operator::Gt => exec_cmp(scope, lhs, rhs, |l, r| l > r),
Operator::GtEq => exec_cmp(scope, lhs, rhs, |l, r| l >= r),
Operator::Lt => exec_cmp(scope, lhs, rhs, |l, r| l < r),
Operator::LtEq => exec_cmp(scope, lhs, rhs, |l, r| l <= r),
Operator::PipePipe => exec_bool_math(scope, lhs, rhs, |l, r| l || r),
Operator::AmpAmp => exec_bool_math(scope, lhs, rhs, |l, r| l && r),
op => {
if let Some(assignment_op) = op.get_assignment_op() {
// XXX we evaluate lhs twice here, that's probably Not Good
let result = assignment_op.eval_binary(scope, lhs, rhs)?;
lhs.eval(scope)?.update(&result);
Ok(Val::new(()))
} else {
panic!("eval_binary() called on non-binary operator {}", self)
}
}
}
}
}
fn exec_math<F>(scope: &mut Scope, lhs: &Node, rhs: &Node, op: F) -> Result<Val, Error>
where
F: FnOnce(i128, i128) -> i128,
{
let left = lhs.eval(scope)?.as_int(|i| *i)?;
let right = rhs.eval(scope)?.as_int(|i| *i)?;
Ok(Val::new(op(left, right)))
}
fn exec_bool_math<F>(scope: &mut Scope, lhs: &Node, rhs: &Node, op: F) -> Result<Val, Error>
where
F: FnOnce(bool, bool) -> bool,
{
let left = lhs.eval(scope)?.as_bool(|b| *b)?;
let right = rhs.eval(scope)?.as_bool(|b| *b)?;
Ok(Val::new(op(left, right)))
}
fn exec_cmp<F>(scope: &mut Scope, lhs: &Node, rhs: &Node, op: F) -> Result<Val, Error>
where
F: FnOnce(i128, i128) -> bool,
{
let left = lhs.eval(scope)?.as_int(|i| *i)?;
let right = rhs.eval(scope)?.as_int(|i| *i)?;
Ok(Val::new(op(left, right)))
}
fn exec_fn(
scope: &mut Scope,
capture: &mut Scope,
body: &FnBody,
param_names: &Vec<Node>,
param_exprs: &Vec<Node>,
) -> Result<Val, Error> {
let mut param_vals = Vec::new();
for expr in param_exprs {
param_vals.push(expr.eval(scope)?);
}
let param_vals = param_vals;
Ok(match body {
FnBody::Native(f) => f(param_vals)?,
FnBody::Sandbox(node) => {
let mut child_scope = Scope::new(capture);
for (i, n) in param_names.iter().enumerate() {
let param_name = n.assert_ident()?;
if i < param_vals.len() {
child_scope.define(param_name.clone(), param_vals[i].clone());
} else {
child_scope.define(param_name.clone(), Val::new(()));
}
}
let mut result = node.eval(&mut child_scope)?;
if result.is_return() {
result.set_return(false);
}
result
}
})
}
fn exec_if(
scope: &mut Scope,
condition: &Node,
then: &Node,
els: Option<&Node>,
) -> Result<Val, Error> {
let cond_result = condition.eval(scope)?.as_bool(|b| *b)?;
let mut child_scope = Scope::new(scope);
if cond_result {
then.eval(&mut child_scope)
} else if let Some(els) = els {
els.eval(&mut child_scope)
} else {
Ok(Val::new(()))
}
}
fn exec_while(scope: &mut Scope, condition: &Node, block: &Node) -> Result<Val, Error> {
loop {
let cond_result = condition.eval(scope)?.as_bool(|b| *b)?;
let mut child_scope = Scope::new(scope);
if cond_result {
let block_result = block.eval(&mut child_scope)?;
if block_result.is_break() {
break;
} else if block_result.is_return() {
return Ok(block_result);
}
} else {
break;
}
}
Ok(Val::new(()))
}
fn exec_for(
scope: &mut Scope,
setup: Option<&Node>,
condition: Option<&Node>,
exec: Option<&Node>,
block: &Node,
) -> Result<Val, Error> {
let mut for_scope = Scope::new(scope);
if let Some(setup) = setup {
setup.eval(&mut for_scope)?;
}
loop {
let cond_result = if let Some(condition) = condition {
condition.eval(&mut for_scope)?.as_bool(|b| *b)?
} else {
true
};
if cond_result {
let mut block_scope = Scope::new(&for_scope);
let block_result = block.eval(&mut block_scope)?;
if block_result.is_break() {
break;
} else if block_result.is_return() {
return Ok(block_result);
}
if let Some(exec) = exec {
exec.eval(&mut for_scope)?;
}
} else {
break;
}
}
Ok(Val::new(()))
}
fn append_list(scope: &mut Scope, node: &Node, key: &str) -> Result<Val, Error> {
let val = node.eval(scope)?;
match scope.object().get(key) {
Some(v) => v.as_array(|a| {
let result = val.as_array(|new_array| {
// XXX there are a *lot* of unnecessary copies here
let mut new_array = new_array.clone();
a.append(&mut new_array);
});
if result.is_err() {
a.push(val);
}
})?,
None => scope.object().set(String::from(key), val),
}
Ok(Val::new(()))
}
pub fn empty_pos() -> crate::lex::token::Position {
crate::lex::token::Position {
file: String::from("<unimplemented>"),
line: 0,
col: 0,
}
}