use crate::environment::Environment;
use crate::environment::TypeValues;
use crate::eval::call_stack::CallStack;
use crate::eval::compr::eval_one_dimensional_comprehension;
use crate::eval::def::Def;
use crate::eval::def::ParameterCompiled;
use crate::eval::expr::AssignTargetExprCompiled;
use crate::eval::expr::AstAssignTargetExprCompiled;
use crate::eval::expr::AstAugmentedAssignTargetExprCompiled;
use crate::eval::expr::AstExprCompiled;
use crate::eval::expr::AugmentedAssignTargetExprCompiled;
use crate::eval::expr::ExprCompiled;
use crate::eval::expr::ExprLocal;
use crate::eval::module::Module;
use crate::eval::stmt::AstStatementCompiled;
use crate::eval::stmt::BlockCompiled;
use crate::eval::stmt::StatementCompiled;
use crate::syntax::ast::BinOp;
use crate::syntax::ast::UnOp;
use crate::syntax::ast::*;
use crate::syntax::dialect::Dialect;
use crate::syntax::errors::SyntaxError;
use crate::syntax::lexer::{LexerIntoIter, LexerItem};
use crate::syntax::parser::{parse, parse_file, parse_lexer};
use crate::values::context::EvaluationContext;
use crate::values::context::EvaluationContextEnvironment;
use crate::values::context::EvaluationContextEnvironmentLocal;
use crate::values::context::EvaluationContextEnvironmentModule;
use crate::values::context::IndexedGlobals;
use crate::values::context::IndexedLocals;
use crate::values::dict::Dictionary;
use crate::values::error::UnsupportedOperation;
use crate::values::error::ValueError;
use crate::values::function::FunctionParameter;
use crate::values::function::FunctionSignature;
use crate::values::function::WrappedMethod;
use crate::values::none::NoneType;
use crate::values::string::rc::RcString;
use crate::values::*;
use codemap::{CodeMap, Span, Spanned};
use codemap_diagnostic::{Diagnostic, Level, SpanLabel, SpanStyle};
use linked_hash_map::LinkedHashMap;
use std::cmp::Ordering;
use std::sync::{Arc, Mutex};
fn eval_vector<E: EvaluationContextEnvironment>(
v: &[AstExprCompiled],
ctx: &mut EvaluationContext<E>,
) -> Result<Vec<Value>, EvalException> {
v.into_iter().map(|s| eval_expr(s, ctx)).collect()
}
#[doc(hidden)]
pub const BREAK_ERROR_CODE: &str = "CE00";
#[doc(hidden)]
pub const CONTINUE_ERROR_CODE: &str = "CE01";
#[doc(hidden)]
pub const RETURN_ERROR_CODE: &str = "CE02";
#[doc(hidden)]
pub const INCORRECT_LEFT_VALUE_ERROR_CODE: &str = "CE03";
#[doc(hidden)]
pub const INCORRECT_UNPACK_ERROR_CODE: &str = "CE04";
#[doc(hidden)]
pub const RECURSION_ERROR_CODE: &str = "CE05";
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum EvalException {
Break(Span),
Continue(Span),
Return(Span, Value),
DiagnosedError(Diagnostic),
IncorrectLeftValue(Span),
IncorrectNumberOfValueToUnpack(Span, i64, i64),
Recursion(Span, String, CallStack),
}
impl From<Diagnostic> for EvalException {
fn from(diagnostic: Diagnostic) -> Self {
EvalException::DiagnosedError(diagnostic)
}
}
type EvalResult = Result<Value, EvalException>;
trait AsSpan {
fn as_span(&self) -> Span;
}
impl AsSpan for Span {
fn as_span(&self) -> Span {
*self
}
}
impl<T> AsSpan for Spanned<T> {
fn as_span(&self) -> Span {
self.span
}
}
impl<T> AsSpan for Box<Spanned<T>> {
fn as_span(&self) -> Span {
self.span
}
}
fn t<T, E: SyntaxError, S: AsSpan>(r: Result<T, E>, spanned: &S) -> Result<T, EvalException> {
match r {
Ok(v) => Ok(v),
Err(e) => Err(EvalException::DiagnosedError(
e.to_diagnostic(spanned.as_span()),
)),
}
}
impl Into<Diagnostic> for EvalException {
fn into(self) -> Diagnostic {
match self {
EvalException::DiagnosedError(e) => e,
EvalException::Break(s) => Diagnostic {
level: Level::Error,
message: "Break statement used outside of a loop".to_owned(),
code: Some(BREAK_ERROR_CODE.to_owned()),
spans: vec![SpanLabel {
span: s,
style: SpanStyle::Primary,
label: None,
}],
},
EvalException::Continue(s) => Diagnostic {
level: Level::Error,
message: "Continue statement used outside of a loop".to_owned(),
code: Some(CONTINUE_ERROR_CODE.to_owned()),
spans: vec![SpanLabel {
span: s,
style: SpanStyle::Primary,
label: None,
}],
},
EvalException::Return(s, ..) => Diagnostic {
level: Level::Error,
message: "Return statement used outside of a function call".to_owned(),
code: Some(RETURN_ERROR_CODE.to_owned()),
spans: vec![SpanLabel {
span: s,
style: SpanStyle::Primary,
label: None,
}],
},
EvalException::IncorrectLeftValue(s) => Diagnostic {
level: Level::Error,
message: "Incorrect expression as left value".to_owned(),
code: Some(INCORRECT_LEFT_VALUE_ERROR_CODE.to_owned()),
spans: vec![SpanLabel {
span: s,
style: SpanStyle::Primary,
label: None,
}],
},
EvalException::IncorrectNumberOfValueToUnpack(s, expected, got) => Diagnostic {
level: Level::Error,
message: format!("Unpacked {} values but expected {}", got, expected),
code: Some(INCORRECT_UNPACK_ERROR_CODE.to_owned()),
spans: vec![SpanLabel {
span: s,
style: SpanStyle::Primary,
label: None,
}],
},
EvalException::Recursion(s, f, stack) => Diagnostic {
level: Level::Error,
message: format!(
"Function {} recursed, call stack:{}",
f,
stack.print_with_newline_before()
),
code: Some(RECURSION_ERROR_CODE.to_owned()),
spans: vec![SpanLabel {
span: s,
style: SpanStyle::Primary,
label: Some("Recursive call".to_owned()),
}],
},
}
}
}
pub trait FileLoader {
fn load(&self, path: &str, type_values: &TypeValues) -> Result<Environment, EvalException>;
}
fn eval_un_op(op: UnOp, v: Value) -> Result<Value, ValueError> {
match op {
UnOp::Plus => v.plus(),
UnOp::Minus => v.minus(),
}
}
fn eval_bin_op(op: BinOp, l: Value, r: Value) -> Result<Value, ValueError> {
match op {
BinOp::EqualsTo => l.equals(&r).map(Value::new),
BinOp::Different => l.equals(&r).map(|b| Value::new(!b)),
BinOp::LowerThan => l.compare(&r).map(|c| Value::new(c == Ordering::Less)),
BinOp::GreaterThan => l.compare(&r).map(|c| Value::new(c == Ordering::Greater)),
BinOp::LowerOrEqual => l.compare(&r).map(|c| Value::new(c != Ordering::Greater)),
BinOp::GreaterOrEqual => l.compare(&r).map(|c| Value::new(c != Ordering::Less)),
BinOp::In => r.contains(&l).map(Value::new),
BinOp::NotIn => r.contains(&l).map(|r| Value::new(!r)),
BinOp::Substraction => l.sub(r),
BinOp::Addition => l.add(r),
BinOp::Multiplication => l.mul(r),
BinOp::Percent => l.percent(r),
BinOp::Division => {
return Err(ValueError::OperationNotSupported {
op: UnsupportedOperation::Div,
left: l.get_type().to_string(),
right: Some(r.get_type().to_string()),
});
}
BinOp::FloorDivision => l.floor_div(r),
BinOp::Pipe => l.pipe(r),
}
}
fn eval_bin_op_expr<E: EvaluationContextEnvironment>(
expr: &AstExprCompiled,
op: BinOp,
l: &AstExprCompiled,
r: &AstExprCompiled,
context: &mut EvaluationContext<E>,
) -> EvalResult {
let l = eval_expr(l, context)?;
let r = eval_expr(r, context)?;
t(eval_bin_op(op, l, r), expr)
}
fn eval_slice<E: EvaluationContextEnvironment>(
this: &AstExprCompiled,
a: &AstExprCompiled,
start: &Option<AstExprCompiled>,
stop: &Option<AstExprCompiled>,
stride: &Option<AstExprCompiled>,
context: &mut EvaluationContext<E>,
) -> EvalResult {
let a = eval_expr(a, context)?;
let start = match start {
Some(ref e) => Some(eval_expr(e, context)?),
None => None,
};
let stop = match stop {
Some(ref e) => Some(eval_expr(e, context)?),
None => None,
};
let stride = match stride {
Some(ref e) => Some(eval_expr(e, context)?),
None => None,
};
t(a.slice(start, stop, stride), this)
}
fn eval_call<E: EvaluationContextEnvironment>(
this: &AstExprCompiled,
e: &AstExprCompiled,
pos: &[AstExprCompiled],
named: &[(AstString, AstExprCompiled)],
args: &Option<AstExprCompiled>,
kwargs: &Option<AstExprCompiled>,
context: &mut EvaluationContext<E>,
) -> EvalResult {
let npos = eval_vector(pos, context)?;
let mut nnamed = LinkedHashMap::new();
for &(ref k, ref v) in named.iter() {
nnamed.insert(k.node.clone(), eval_expr(v, context)?);
}
let nargs = if let Some(ref x) = args {
Some(eval_expr(x, context)?)
} else {
None
};
let nkwargs = if let Some(ref x) = kwargs {
Some(eval_expr(x, context)?)
} else {
None
};
let f = eval_expr(e, context)?;
if context.call_stack.contains(f.function_id()) {
let mut new_stack = context.call_stack.clone();
new_stack.push(f.clone(), context.map.clone(), this.span.low());
Err(EvalException::Recursion(this.span, f.to_repr(), new_stack))
} else {
context
.call_stack
.push(f.clone(), context.map.clone(), this.span.low());
let r = t(
eval_expr(e, context)?.call(
context.call_stack,
context.type_values,
npos,
nnamed,
nargs,
nkwargs,
),
this,
);
context.call_stack.pop();
r
}
}
fn eval_dot<E: EvaluationContextEnvironment>(
this: &AstExprCompiled,
e: &AstExprCompiled,
s: &AstString,
context: &mut EvaluationContext<E>,
) -> EvalResult {
let left = eval_expr(e, context)?;
if let Some(v) = context.type_values.get_type_value(&left, &s.node) {
if v.get_type() == "function" {
Ok(WrappedMethod::new(left, v))
} else {
Ok(v)
}
} else {
t(left.get_attr(&s.node), this)
}
}
enum TransformedExpr {
Dot(Value, RcString, Span),
ArrayIndirection(Value, Value, Span),
Slot(usize, AstString),
}
fn set_transformed<E: EvaluationContextEnvironment>(
transformed: &TransformedExpr,
context: &mut EvaluationContext<E>,
new_value: Value,
) -> EvalResult {
let ok = Ok(Value::new(NoneType::None));
match transformed {
TransformedExpr::Dot(ref e, ref s, ref span) => {
t(e.clone().set_attr(&s, new_value), span)?;
ok
}
TransformedExpr::ArrayIndirection(ref e, ref idx, ref span) => {
t(e.clone().set_at(idx.clone(), new_value), span)?;
ok
}
TransformedExpr::Slot(slot, ident) => {
context.env.set_local(*slot, &ident.node, new_value);
ok
}
}
}
fn eval_transformed<E: EvaluationContextEnvironment>(
transformed: &TransformedExpr,
context: &mut EvaluationContext<E>,
) -> EvalResult {
match transformed {
TransformedExpr::Dot(ref left, ref s, ref span) => {
if let Some(v) = context.type_values.get_type_value(left, &s) {
if v.get_type() == "function" {
Ok(WrappedMethod::new(left.clone(), v))
} else {
Ok(v)
}
} else {
t(left.get_attr(&s), span)
}
}
TransformedExpr::ArrayIndirection(ref e, ref idx, ref span) => t(e.at(idx.clone()), span),
TransformedExpr::Slot(slot, ident) => t(context.env.get_local(*slot, &ident.node), ident),
}
}
fn make_set<E: EvaluationContextEnvironment>(
values: Vec<Value>,
context: &EvaluationContext<E>,
span: Span,
) -> EvalResult {
context
.env
.env()
.make_set(values)
.map_err(|err| EvalException::DiagnosedError(err.to_diagnostic(span)))
}
fn transform<E: EvaluationContextEnvironment>(
expr: &AstAugmentedAssignTargetExprCompiled,
context: &mut EvaluationContext<E>,
) -> Result<TransformedExpr, EvalException> {
match &expr.node {
AugmentedAssignTargetExprCompiled::Dot(ref e, ref s) => Ok(TransformedExpr::Dot(
eval_expr(e, context)?,
s.node.clone(),
expr.span,
)),
AugmentedAssignTargetExprCompiled::ArrayIndirection(ref e, ref idx) => {
Ok(TransformedExpr::ArrayIndirection(
eval_expr(e, context)?,
eval_expr(idx, context)?,
expr.span,
))
}
AugmentedAssignTargetExprCompiled::Slot(index, ref ident) => {
Ok(TransformedExpr::Slot(*index, ident.clone()))
}
}
}
fn eval_expr_local<E: EvaluationContextEnvironment>(
local: &ExprLocal,
context: &mut EvaluationContext<E>,
) -> EvalResult {
let mut ctx = EvaluationContext {
call_stack: context.call_stack,
env: EvaluationContextEnvironmentLocal {
globals: IndexedGlobals::new(&local.globals, context.env.env().clone()),
locals: IndexedLocals::new(&local.locals),
},
type_values: context.type_values,
map: context.map.clone(),
};
eval_expr(&local.expr, &mut ctx)
}
fn eval_expr<E: EvaluationContextEnvironment>(
expr: &AstExprCompiled,
context: &mut EvaluationContext<E>,
) -> EvalResult {
match expr.node {
ExprCompiled::Tuple(ref v) => {
let r = eval_vector(v, context)?;
Ok(Value::new(tuple::Tuple::new(r)))
}
ExprCompiled::Dot(ref e, ref s) => eval_dot(expr, e, s, context),
ExprCompiled::Call(ref e, ref pos, ref named, ref args, ref kwargs) => {
eval_call(expr, e, pos, named, args, kwargs, context)
}
ExprCompiled::ArrayIndirection(ref e, ref idx) => {
let idx = eval_expr(idx, context)?;
t(eval_expr(e, context)?.at(idx), expr)
}
ExprCompiled::Slice(ref a, ref start, ref stop, ref stride) => {
eval_slice(expr, a, start, stop, stride, context)
}
ExprCompiled::Name(ref name) => t(context.env.get(&name.node), name),
ExprCompiled::Value(ref v) => Ok(v.clone().into()),
ExprCompiled::Not(ref s) => Ok(Value::new(!eval_expr(s, context)?.to_bool())),
ExprCompiled::UnOp(op, ref s) => {
let v = eval_expr(s, context)?;
t(eval_un_op(op, v), expr)
}
ExprCompiled::Or(ref l, ref r) => {
let l = eval_expr(l, context)?;
Ok(if l.to_bool() {
l
} else {
eval_expr(r, context)?
})
}
ExprCompiled::And(ref l, ref r) => {
let l = eval_expr(l, context)?;
Ok(if !l.to_bool() {
l
} else {
eval_expr(r, context)?
})
}
ExprCompiled::BinOp(op, ref l, ref r) => eval_bin_op_expr(expr, op, l, r, context),
ExprCompiled::If(ref cond, ref v1, ref v2) => {
if eval_expr(cond, context)?.to_bool() {
eval_expr(v1, context)
} else {
eval_expr(v2, context)
}
}
ExprCompiled::List(ref v) => {
let r = eval_vector(v, context)?;
Ok(Value::from(r))
}
ExprCompiled::Dict(ref v) => {
let r = dict::Dictionary::new();
for s in v.iter() {
t(
r.borrow_mut()
.set_at(eval_expr(&s.0, context)?, eval_expr(&s.1, context)?),
expr,
)?
}
Ok(r.into())
}
ExprCompiled::Set(ref v) => {
let mut values = Vec::with_capacity(v.len());
for s in v {
values.push(eval_expr(s, context)?);
}
make_set(values, context, expr.span)
}
ExprCompiled::ListComprehension(ref expr, ref clauses) => {
let mut list = Vec::new();
eval_one_dimensional_comprehension(
&mut |context| {
list.push(eval_expr(expr, context)?);
Ok(())
},
clauses,
context,
)?;
Ok(Value::from(list))
}
ExprCompiled::SetComprehension(ref expr, ref clauses) => {
let mut values = Vec::new();
eval_one_dimensional_comprehension(
&mut |context| {
values.push(eval_expr(expr, context)?);
Ok(())
},
clauses,
context,
)?;
make_set(values, context, expr.span)
}
ExprCompiled::DictComprehension((ref k, ref v), ref clauses) => {
let mut dict = Dictionary::new_typed();
eval_one_dimensional_comprehension(
&mut |context| {
t(
dict.insert(eval_expr(k, context)?, eval_expr(v, context)?),
&expr.span,
)
},
clauses,
context,
)?;
Ok(Value::new(dict))
}
ExprCompiled::Local(ref local) => eval_expr_local(&local, context),
}
}
fn set_expr<E: EvaluationContextEnvironment>(
expr: &AstAssignTargetExprCompiled,
context: &mut EvaluationContext<E>,
new_value: Value,
) -> EvalResult {
let ok = Ok(Value::new(NoneType::None));
match expr.node {
AssignTargetExprCompiled::Subtargets(ref v) => {
let new_values: Vec<Value> = t(new_value.iter(), expr)?.iter().collect();
let l = v.len();
if new_values.len() != l {
Err(EvalException::IncorrectNumberOfValueToUnpack(
expr.span,
l as i64,
new_values.len() as i64,
))
} else {
let mut it1 = v.iter();
let mut it2 = new_values.into_iter();
for _ in 0..l {
set_expr(it1.next().unwrap(), context, it2.next().unwrap())?;
}
ok
}
}
AssignTargetExprCompiled::Dot(ref e, ref s) => {
t(eval_expr(e, context)?.set_attr(&(s.node), new_value), expr)?;
ok
}
AssignTargetExprCompiled::Name(ref name) => {
t(context.env.set(&name.node, new_value), expr)?;
ok
}
AssignTargetExprCompiled::ArrayIndirection(ref e, ref idx) => {
t(
eval_expr(e, context)?.set_at(eval_expr(idx, context)?, new_value),
expr,
)?;
ok
}
}
}
fn eval_assign_modify<E: EvaluationContextEnvironment>(
stmt: &AstStatementCompiled,
lhs: &AstAugmentedAssignTargetExprCompiled,
rhs: &AstExprCompiled,
context: &mut EvaluationContext<E>,
op: AugmentedAssignOp,
) -> EvalResult
where
{
let op = match op {
AugmentedAssignOp::Increment => Value::add,
AugmentedAssignOp::Decrement => Value::sub,
AugmentedAssignOp::Multiplier => Value::mul,
AugmentedAssignOp::Divider => Value::div,
AugmentedAssignOp::FloorDivider => Value::floor_div,
AugmentedAssignOp::Percent => Value::percent,
};
let lhs = transform(lhs, context)?;
let l = eval_transformed(&lhs, context)?;
let r = eval_expr(rhs, context)?;
set_transformed(&lhs, context, t(op(&l, r), stmt)?)
}
fn eval_stmt<E: EvaluationContextEnvironment>(
stmt: &AstStatementCompiled,
context: &mut EvaluationContext<E>,
) -> EvalResult {
match stmt.node {
StatementCompiled::Break => Err(EvalException::Break(stmt.span)),
StatementCompiled::Continue => Err(EvalException::Continue(stmt.span)),
StatementCompiled::Return(ref e) => {
Err(EvalException::Return(stmt.span, eval_expr(e, context)?))
}
StatementCompiled::Expression(ref e) => eval_expr(e, context),
StatementCompiled::Assign(ref lhs, ref rhs) => {
let rhs = eval_expr(rhs, context)?;
set_expr(lhs, context, rhs)
}
StatementCompiled::AugmentedAssign(ref lhs, op, ref rhs) => {
eval_assign_modify(stmt, lhs, rhs, context, op)
}
StatementCompiled::IfElse(ref cond, ref st1, ref st2) => {
if eval_expr(cond, context)?.to_bool() {
eval_block(st1, context)
} else {
eval_block(st2, context)
}
}
StatementCompiled::For(ref e1, ref e2, ref st) => {
let iterable = eval_expr(e2, context)?;
let mut result = Ok(Value::new(NoneType::None));
for v in &t(iterable.iter(), &e2.span)? {
set_expr(e1, context, v)?;
match eval_block(st, context) {
Err(EvalException::Break(..)) => break,
Err(EvalException::Continue(..)) => (),
Err(x) => {
result = Err(x);
break;
}
_ => (),
}
}
result
}
StatementCompiled::Def(ref stmt) => {
let mut p = Vec::new();
for x in &stmt.params {
p.push(match x.node {
ParameterCompiled::Normal(ref n) => FunctionParameter::Normal(n.node.clone()),
ParameterCompiled::WithDefaultValue(ref n, ref v) => {
FunctionParameter::WithDefaultValue(n.node.clone(), eval_expr(v, context)?)
}
ParameterCompiled::Args(ref n) => FunctionParameter::ArgsArray(n.node.clone()),
ParameterCompiled::KWArgs(ref n) => {
FunctionParameter::KWArgsDict(n.node.clone())
}
})
}
let f = Def::new(
context.env.assert_module_env().env.name(),
FunctionSignature::new(p, 0),
stmt.clone(),
context.map.clone(),
context.env.assert_module_env().env.clone(),
);
t(
context
.env
.set_global(stmt.slot, &stmt.name.node, f.clone().into()),
&stmt.name,
)?;
Ok(f.into())
}
StatementCompiled::Load(ref name, ref v) => {
let loadenv = context
.env
.assert_module_env()
.loader
.load(name, context.type_values)?;
loadenv.freeze();
for &(ref new_name, ref orig_name) in v.iter() {
t(
context.env.assert_module_env().env.import_symbol(
&loadenv,
&orig_name.node,
&new_name.node,
),
&new_name.span.merge(orig_name.span),
)?
}
Ok(Value::new(NoneType::None))
}
}
}
fn eval_block<E: EvaluationContextEnvironment>(
block: &BlockCompiled,
context: &mut EvaluationContext<E>,
) -> EvalResult {
let mut r = Value::new(NoneType::None);
for stmt in &block.0 {
r = eval_stmt(stmt, context)?;
}
Ok(r)
}
fn eval_module(
module: &Module,
env: &mut Environment,
type_values: &TypeValues,
map: Arc<Mutex<CodeMap>>,
loader: &dyn FileLoader,
) -> EvalResult {
let mut call_stack = CallStack::default();
let mut context = EvaluationContext {
env: EvaluationContextEnvironmentModule {
env: env.clone(),
globals: IndexedGlobals::new(&module.globals, env.clone()),
loader,
},
type_values,
call_stack: &mut call_stack,
map,
};
eval_block(&module.block, &mut context)
}
pub fn eval_lexer<T1: Iterator<Item = LexerItem>, T2: LexerIntoIter<T1>>(
map: &Arc<Mutex<CodeMap>>,
filename: &str,
content: &str,
dialect: Dialect,
lexer: T2,
env: &mut Environment,
type_values: &TypeValues,
file_loader: &dyn FileLoader,
) -> Result<Value, Diagnostic> {
match eval_module(
&parse_lexer(map, filename, content, dialect, lexer)?,
env,
type_values,
map.clone(),
file_loader,
) {
Ok(v) => Ok(v),
Err(p) => Err(p.into()),
}
}
pub fn eval(
map: &Arc<Mutex<CodeMap>>,
path: &str,
content: &str,
build: Dialect,
env: &mut Environment,
type_values: &TypeValues,
file_loader: &dyn FileLoader,
) -> Result<Value, Diagnostic> {
match eval_module(
&parse(map, path, content, build)?,
env,
type_values,
map.clone(),
file_loader,
) {
Ok(v) => Ok(v),
Err(p) => Err(p.into()),
}
}
pub fn eval_file(
map: &Arc<Mutex<CodeMap>>,
path: &str,
build: Dialect,
env: &mut Environment,
type_values: &TypeValues,
file_loader: &dyn FileLoader,
) -> Result<Value, Diagnostic> {
match eval_module(
&parse_file(map, path, build)?,
env,
type_values,
map.clone(),
file_loader,
) {
Ok(v) => Ok(v),
Err(p) => Err(p.into()),
}
}
pub mod interactive;
pub mod noload;
pub mod simple;
pub mod call_stack;
#[cfg(test)]
mod tests;
pub(crate) mod compiler;
pub(crate) mod compr;
pub(crate) mod def;
pub(crate) mod expr;
pub(crate) mod globals;
pub(crate) mod locals;
pub mod module;
pub mod stmt;