λ Support functions! #5
3
build.rs
3
build.rs
@@ -77,7 +77,8 @@ fn generate_tests(f: &mut File, path_so_far: PathBuf) -> std::io::Result<()> {
|
|||||||
f,
|
f,
|
||||||
" let ir = syntax.type_infer().expect(\"example is typed correctly\");"
|
" let ir = syntax.type_infer().expect(\"example is typed correctly\");"
|
||||||
)?;
|
)?;
|
||||||
writeln!(f, " let ir_result = ir.eval();")?;
|
writeln!(f, " let ir_evaluator = crate::ir::Evaluator::default();")?;
|
||||||
|
writeln!(f, " let ir_result = ir_evaluator.eval(ir.clone());")?;
|
||||||
writeln!(f, " match (&syntax_result, &ir_result) {{")?;
|
writeln!(f, " match (&syntax_result, &ir_result) {{")?;
|
||||||
writeln!(f, " (Err(e1), Err(e2)) => assert_eq!(e1, e2),")?;
|
writeln!(f, " (Err(e1), Err(e2)) => assert_eq!(e1, e2),")?;
|
||||||
writeln!(f, " (Ok((v1, o1)), Ok((v2, o2))) => {{")?;
|
writeln!(f, " (Ok((v1, o1)), Ok((v2, o2))) => {{")?;
|
||||||
|
|||||||
150
examples/basic/generated0004.ngr
Normal file
150
examples/basic/generated0004.ngr
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
d3347 = {
|
||||||
|
s3349 = v3348 = 17175152522826808410u64;
|
||||||
|
print s3349;
|
||||||
|
j3350 = -6926831316600240717i64;
|
||||||
|
g3351 = b3352 = print s3349;
|
||||||
|
g3351;
|
||||||
|
u3353 = v3348;
|
||||||
|
c3460 = p3354 = {
|
||||||
|
w3441 = u3355 = k3356 = v3357 = {
|
||||||
|
j3364 = b3358 = b3359 = {
|
||||||
|
e3363 = o3360 = a3361 = n3362 = v3348 * v3348;
|
||||||
|
print e3363;
|
||||||
|
a3361
|
||||||
|
};
|
||||||
|
v3365 = j3364;
|
||||||
|
o3376 = {
|
||||||
|
p3366 = 62081u16;
|
||||||
|
t3369 = {
|
||||||
|
k3367 = 3742184609455079849u64;
|
||||||
|
y3368 = print g3351;
|
||||||
|
k3367
|
||||||
|
};
|
||||||
|
e3371 = o3370 = p3366;
|
||||||
|
print s3349;
|
||||||
|
l3372 = 50u8;
|
||||||
|
g3373 = 1086766998u32;
|
||||||
|
u3374 = <u64>g3373;
|
||||||
|
p3375 = 13826883074707422152u64
|
||||||
|
};
|
||||||
|
h3379 = v3377 = h3378 = 1513207896u32;
|
||||||
|
x3382 = f3380 = i3381 = <i32>-72i8;
|
||||||
|
a3383 = g3351;
|
||||||
|
q3440 = q3384 = {
|
||||||
|
r3385 = v3365;
|
||||||
|
z3437 = n3386 = {
|
||||||
|
o3387 = -1428233008i32;
|
||||||
|
c3388 = s3349;
|
||||||
|
b3352;
|
||||||
|
c3389 = c3388;
|
||||||
|
a3383;
|
||||||
|
c3390 = 1056u16;
|
||||||
|
l3433 = {
|
||||||
|
b3392 = f3391 = -881200191i32;
|
||||||
|
print o3376;
|
||||||
|
print v3377;
|
||||||
|
h3395 = y3393 = j3394 = -2456592492064497053i64;
|
||||||
|
c3396 = c3388;
|
||||||
|
f3397 = 2442824079u32;
|
||||||
|
d3428 = {
|
||||||
|
n3400 = h3398 = m3399 = v3365;
|
||||||
|
j3401 = v3348;
|
||||||
|
t3402 = -10i8;
|
||||||
|
e3403 = g3404 = print r3385;
|
||||||
|
e3403;
|
||||||
|
d3425 = {
|
||||||
|
u3405 = 51313u16;
|
||||||
|
l3406 = 235u8;
|
||||||
|
l3407 = 7030u16;
|
||||||
|
i3413 = {
|
||||||
|
b3352;
|
||||||
|
v3408 = o3387;
|
||||||
|
i3409 = 42u8;
|
||||||
|
q3411 = {
|
||||||
|
u3410 = -70i8;
|
||||||
|
n3400
|
||||||
|
};
|
||||||
|
q3412 = print i3409;
|
||||||
|
x3382
|
||||||
|
};
|
||||||
|
z3414 = b3392;
|
||||||
|
j3417 = s3415 = o3416 = 7220082853233268797u64;
|
||||||
|
print i3381;
|
||||||
|
n3419 = a3418 = -1270109327i32;
|
||||||
|
o3420 = r3385;
|
||||||
|
o3421 = x3382;
|
||||||
|
j3422 = print i3381;
|
||||||
|
q3423 = -4497i16;
|
||||||
|
x3424 = -98995788i32;
|
||||||
|
f3391
|
||||||
|
};
|
||||||
|
{
|
||||||
|
x3426 = r3385 - 13032422114254415490u64;
|
||||||
|
e3427 = -51i8;
|
||||||
|
j3401
|
||||||
|
}
|
||||||
|
};
|
||||||
|
k3429 = <u64>c3396;
|
||||||
|
print j3350;
|
||||||
|
g3351;
|
||||||
|
f3430 = -12293i16;
|
||||||
|
v3431 = 4016608549u32;
|
||||||
|
t3432 = f3397;
|
||||||
|
f3391 - -716040069i32
|
||||||
|
};
|
||||||
|
r3434 = -12984i16;
|
||||||
|
s3435 = 293908485953501586u64;
|
||||||
|
{
|
||||||
|
print j3364;
|
||||||
|
h3436 = <u64>7732092399687242928u64
|
||||||
|
}
|
||||||
|
};
|
||||||
|
u3438 = v3377 * 3168795739u32;
|
||||||
|
p3439 = <i32>-12313i16;
|
||||||
|
u3438
|
||||||
|
};
|
||||||
|
v3348
|
||||||
|
};
|
||||||
|
x3442 = -5279281110772475785i64;
|
||||||
|
y3443 = v3348;
|
||||||
|
b3444 = 2732851783u32;
|
||||||
|
l3456 = l3445 = {
|
||||||
|
v3446 = -27499i16;
|
||||||
|
i3447 = 3517560837u32;
|
||||||
|
z3448 = u3353 * u3355;
|
||||||
|
u3449 = x3442;
|
||||||
|
t3450 = u3353;
|
||||||
|
m3452 = n3451 = u3355;
|
||||||
|
b3453 = m3452;
|
||||||
|
x3455 = t3454 = 2134443760u32;
|
||||||
|
print b3444;
|
||||||
|
b3444
|
||||||
|
};
|
||||||
|
c3457 = l3456;
|
||||||
|
g3351;
|
||||||
|
m3458 = 1822439673528019141u64;
|
||||||
|
d3459 = l3456
|
||||||
|
};
|
||||||
|
k3462 = f3461 = <i32>13u8;
|
||||||
|
g3351;
|
||||||
|
u3463 = -10083i16;
|
||||||
|
v3348
|
||||||
|
};
|
||||||
|
y3464 = {
|
||||||
|
y3465 = 163u8;
|
||||||
|
y3466 = -7760i16;
|
||||||
|
e3467 = d3347;
|
||||||
|
q3468 = 58708u16;
|
||||||
|
-426970972827051249i64
|
||||||
|
};
|
||||||
|
t3469 = 524885465u32;
|
||||||
|
function b3470 (y3471,c3472,u3473) <u32>1606677228u32;
|
||||||
|
y3464;
|
||||||
|
-240502590i32;
|
||||||
|
v3474 = z3475 = {
|
||||||
|
t3476 = y3464;
|
||||||
|
p3477 = t3469;
|
||||||
|
i3478 = p3477;
|
||||||
|
a3480 = e3479 = t3476;
|
||||||
|
p3477
|
||||||
|
};
|
||||||
@@ -120,15 +120,6 @@ proptest::proptest! {
|
|||||||
#[test]
|
#[test]
|
||||||
fn static_backend(program in Program::arbitrary()) {
|
fn static_backend(program in Program::arbitrary()) {
|
||||||
use crate::eval::PrimOpError;
|
use crate::eval::PrimOpError;
|
||||||
|
|
||||||
let basic_result = program.eval().map(|(_,x)| x);
|
|
||||||
|
|
||||||
// windows `printf` is going to terminate lines with "\r\n", so we need to adjust
|
|
||||||
// our test result here.
|
|
||||||
#[cfg(target_family="windows")]
|
|
||||||
let basic_result = basic_result.map(|x| x.replace('\n', "\r\n"));
|
|
||||||
|
|
||||||
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
|
||||||
use pretty::DocAllocator;
|
use pretty::DocAllocator;
|
||||||
let allocator = pretty::Arena::new();
|
let allocator = pretty::Arena::new();
|
||||||
let result = allocator.text("-------------")
|
let result = allocator.text("-------------")
|
||||||
@@ -138,6 +129,28 @@ proptest::proptest! {
|
|||||||
result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout()))
|
result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout()))
|
||||||
.expect("rendering works");
|
.expect("rendering works");
|
||||||
|
|
||||||
|
|
||||||
|
eprintln!("BEFORE EVAL");
|
||||||
|
let ir_evaluator = crate::ir::Evaluator::default();
|
||||||
|
let basic_result = ir_evaluator.eval(program.clone()).map(|(_,o)| o);
|
||||||
|
eprintln!("AFTER EVAL");
|
||||||
|
|
||||||
|
// windows `printf` is going to terminate lines with "\r\n", so we need to adjust
|
||||||
|
// our test result here.
|
||||||
|
#[cfg(target_family="windows")]
|
||||||
|
let basic_result = basic_result.map(|x| x.replace('\n', "\r\n"));
|
||||||
|
|
||||||
|
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
||||||
|
//use pretty::DocAllocator;
|
||||||
|
//let allocator = pretty::Arena::new();
|
||||||
|
//let result = allocator.text("-------------")
|
||||||
|
// .append(allocator.line())
|
||||||
|
// .append(program.pretty(&allocator))
|
||||||
|
// .append(allocator.line());
|
||||||
|
//result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout()))
|
||||||
|
// .expect("rendering works");
|
||||||
|
|
||||||
|
eprintln!("BEFORE OM EVAL");
|
||||||
let compiled_result = Backend::<ObjectModule>::eval(program);
|
let compiled_result = Backend::<ObjectModule>::eval(program);
|
||||||
proptest::prop_assert_eq!(basic_result, compiled_result);
|
proptest::prop_assert_eq!(basic_result, compiled_result);
|
||||||
}
|
}
|
||||||
@@ -159,7 +172,8 @@ proptest::proptest! {
|
|||||||
// .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto))
|
// .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto))
|
||||||
// .expect("rendering works");
|
// .expect("rendering works");
|
||||||
|
|
||||||
let basic_result = program.eval().map(|(_,x)| x);
|
let ir_evaluator = crate::ir::Evaluator::default();
|
||||||
|
let basic_result = ir_evaluator.eval(program.clone()).map(|(_,o)| o);
|
||||||
|
|
||||||
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) {
|
||||||
let compiled_result = Backend::<JITModule>::eval(program);
|
let compiled_result = Backend::<JITModule>::eval(program);
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ impl<M: Module> Backend<M> {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
match *function {
|
match function {
|
||||||
ValueOrRef::Value(_, _, _) => {
|
ValueOrRef::Value(_, _, _) => {
|
||||||
panic!("Can't use a value for a function")
|
panic!("Can't use a value for a function")
|
||||||
}
|
}
|
||||||
@@ -568,10 +568,7 @@ impl<M: Module> Backend<M> {
|
|||||||
|
|
||||||
Primitive::Negate => {
|
Primitive::Negate => {
|
||||||
assert_eq!(1, arguments.len());
|
assert_eq!(1, arguments.len());
|
||||||
Ok((
|
Ok((builder.ins().ineg(arguments[0]), argument_types[0]))
|
||||||
builder.ins().ineg(arguments[0]),
|
|
||||||
argument_types[0],
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Primitive::Print => {
|
Primitive::Print => {
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if cli.interpreter == Interpreter::IR {
|
if cli.interpreter == Interpreter::IR {
|
||||||
match ir.eval() {
|
let evaluator = ngr::ir::Evaluator::default();
|
||||||
|
match evaluator.eval(ir) {
|
||||||
Err(e) => tracing::error!(error = %e, "Evaluation error"),
|
Err(e) => tracing::error!(error = %e, "Evaluation error"),
|
||||||
Ok(v) => print_result(v),
|
Ok(v) => print_result(v),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ impl PrimitiveType {
|
|||||||
&[("+", 2), ("-", 2), ("*", 2), ("/", 2)]
|
&[("+", 2), ("-", 2), ("*", 2), ("/", 2)]
|
||||||
}
|
}
|
||||||
PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 => {
|
PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 => {
|
||||||
&[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)]
|
&[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,3 +21,4 @@ mod strings;
|
|||||||
mod top_level;
|
mod top_level;
|
||||||
|
|
||||||
pub use ast::*;
|
pub use ast::*;
|
||||||
|
pub use eval::Evaluator;
|
||||||
|
|||||||
@@ -359,11 +359,7 @@ fn generate_random_expression(
|
|||||||
Expression::Call(
|
Expression::Call(
|
||||||
Location::manufactured(),
|
Location::manufactured(),
|
||||||
out_type,
|
out_type,
|
||||||
Box::new(ValueOrRef::Primitive(
|
ValueOrRef::Primitive(Location::manufactured(), primtype, primop),
|
||||||
Location::manufactured(),
|
|
||||||
primtype,
|
|
||||||
primop,
|
|
||||||
)),
|
|
||||||
args,
|
args,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -393,11 +389,7 @@ fn generate_random_expression(
|
|||||||
Expression::Call(
|
Expression::Call(
|
||||||
Location::manufactured(),
|
Location::manufactured(),
|
||||||
Type::void(),
|
Type::void(),
|
||||||
Box::new(ValueOrRef::Primitive(
|
ValueOrRef::Primitive(Location::manufactured(), Type::void(), Primitive::Print),
|
||||||
Location::manufactured(),
|
|
||||||
Type::void(),
|
|
||||||
Primitive::Print,
|
|
||||||
)),
|
|
||||||
vec![ValueOrRef::Ref(
|
vec![ValueOrRef::Ref(
|
||||||
Location::manufactured(),
|
Location::manufactured(),
|
||||||
var_type.clone(),
|
var_type.clone(),
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ pub enum Expression<Type> {
|
|||||||
),
|
),
|
||||||
FieldRef(Location, Type, Type, ValueOrRef<Type>, ArcIntern<String>),
|
FieldRef(Location, Type, Type, ValueOrRef<Type>, ArcIntern<String>),
|
||||||
Block(Location, Type, Vec<Expression<Type>>),
|
Block(Location, Type, Vec<Expression<Type>>),
|
||||||
Call(Location, Type, Box<ValueOrRef<Type>>, Vec<ValueOrRef<Type>>),
|
Call(Location, Type, ValueOrRef<Type>, Vec<ValueOrRef<Type>>),
|
||||||
Bind(Location, Variable, Type, Box<Expression<Type>>),
|
Bind(Location, Variable, Type, Box<Expression<Type>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ impl<Type: Clone + TypeWithVoid> Expression<Type> {
|
|||||||
Expression::FieldRef(_, t, _, _, _) => t.clone(),
|
Expression::FieldRef(_, t, _, _, _) => t.clone(),
|
||||||
Expression::Block(_, t, _) => t.clone(),
|
Expression::Block(_, t, _) => t.clone(),
|
||||||
Expression::Call(_, t, _, _) => t.clone(),
|
Expression::Call(_, t, _, _) => t.clone(),
|
||||||
Expression::Bind(_, _, _, _) => Type::void(),
|
Expression::Bind(_, _, t, _) => t.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
138
src/ir/eval.rs
138
src/ir/eval.rs
@@ -3,60 +3,74 @@ use crate::eval::{EvalError, Value};
|
|||||||
use crate::ir::{Expression, Program, TopLevel, Variable};
|
use crate::ir::{Expression, Program, TopLevel, Variable};
|
||||||
use crate::util::scoped_map::ScopedMap;
|
use crate::util::scoped_map::ScopedMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
type IRValue<T> = Value<Expression<T>>;
|
type IRValue<T> = Value<Expression<T>>;
|
||||||
type IREvalError<T> = EvalError<Expression<T>>;
|
type IREvalError<T> = EvalError<Expression<T>>;
|
||||||
|
|
||||||
impl<T: Clone + Into<Type>> Program<T> {
|
pub struct Evaluator<T> {
|
||||||
|
env: ScopedMap<Variable, IRValue<T>>,
|
||||||
|
stdout: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Evaluator<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Evaluator {
|
||||||
|
env: ScopedMap::new(),
|
||||||
|
stdout: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Evaluator<T>
|
||||||
|
where
|
||||||
|
T: Clone + Into<Type>,
|
||||||
|
Expression<T>: Display,
|
||||||
|
{
|
||||||
/// Evaluate the program, returning either an error or the result of the final
|
/// Evaluate the program, returning either an error or the result of the final
|
||||||
/// statement and the complete contents of the console output.
|
/// statement and the complete contents of the console output.
|
||||||
///
|
///
|
||||||
/// The print outs will be newline separated, with one print out per line.
|
/// The print outs will be newline separated, with one print out per line.
|
||||||
pub fn eval(&self) -> Result<(IRValue<T>, String), IREvalError<T>> {
|
pub fn eval(mut self, program: Program<T>) -> Result<(IRValue<T>, String), IREvalError<T>> {
|
||||||
let mut env: ScopedMap<Variable, IRValue<T>> = ScopedMap::new();
|
|
||||||
let mut stdout = String::new();
|
|
||||||
let mut last_value = Value::Void;
|
let mut last_value = Value::Void;
|
||||||
|
|
||||||
for stmt in self.items.iter() {
|
for stmt in program.items.into_iter() {
|
||||||
match stmt {
|
match stmt {
|
||||||
TopLevel::Function(name, args, _, body) => {
|
TopLevel::Function(name, args, _, body) => {
|
||||||
let closure = Value::Closure(
|
let closure = Value::Closure(
|
||||||
Some(name.clone()),
|
Some(name.clone()),
|
||||||
env.clone(),
|
self.env.clone(),
|
||||||
args.iter().map(|(x, _)| x.clone()).collect(),
|
args.iter().map(|(x, _)| x.clone()).collect(),
|
||||||
body.clone(),
|
body.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
env.insert(name.clone(), closure.clone());
|
self.env.insert(name.clone(), closure.clone());
|
||||||
|
|
||||||
last_value = closure;
|
last_value = closure;
|
||||||
}
|
}
|
||||||
|
|
||||||
TopLevel::Statement(expr) => {
|
TopLevel::Statement(expr) => {
|
||||||
last_value = expr.eval(&mut env, &mut stdout)?;
|
last_value = self.eval_expr(expr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((last_value, stdout))
|
Ok((last_value, self.stdout))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Expression<T>
|
/// Get the current output of the evaluated program.
|
||||||
where
|
pub fn stdout(self) -> String {
|
||||||
T: Clone + Into<Type>,
|
self.stdout
|
||||||
{
|
}
|
||||||
fn eval(
|
|
||||||
&self,
|
fn eval_expr(&mut self, expr: Expression<T>) -> Result<IRValue<T>, IREvalError<T>> {
|
||||||
env: &mut ScopedMap<Variable, IRValue<T>>,
|
println!("evaluating {}", expr);
|
||||||
stdout: &mut String,
|
match expr {
|
||||||
) -> Result<IRValue<T>, IREvalError<T>> {
|
Expression::Atomic(x) => self.eval_atomic(x),
|
||||||
match self {
|
|
||||||
Expression::Atomic(x) => x.eval(env),
|
|
||||||
|
|
||||||
Expression::Cast(_, t, valref) => {
|
Expression::Cast(_, t, valref) => {
|
||||||
let value = valref.eval(env)?;
|
let value = self.eval_atomic(valref)?;
|
||||||
let ty = t.clone().into();
|
let ty = t.into();
|
||||||
|
|
||||||
match ty {
|
match ty {
|
||||||
Type::Primitive(pt) => Ok(pt.safe_cast(&value)?),
|
Type::Primitive(pt) => Ok(pt.safe_cast(&value)?),
|
||||||
@@ -68,15 +82,15 @@ where
|
|||||||
Expression::Construct(_, _, name, fields) => {
|
Expression::Construct(_, _, name, fields) => {
|
||||||
let mut result_fields = HashMap::with_capacity(fields.len());
|
let mut result_fields = HashMap::with_capacity(fields.len());
|
||||||
|
|
||||||
for (name, subexpr) in fields.iter() {
|
for (name, subexpr) in fields.into_iter() {
|
||||||
result_fields.insert(name.clone(), subexpr.eval(env)?);
|
result_fields.insert(name.clone(), self.eval_atomic(subexpr)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Structure(Some(name.clone()), result_fields))
|
Ok(Value::Structure(Some(name.clone()), result_fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression::FieldRef(loc, _, _, valref, field) => match valref.eval(env)? {
|
Expression::FieldRef(loc, _, _, valref, field) => match self.eval_atomic(valref)? {
|
||||||
Value::Structure(oname, mut fields) => match fields.remove(field) {
|
Value::Structure(oname, mut fields) => match fields.remove(&field) {
|
||||||
None => Err(EvalError::NoFieldForValue(
|
None => Err(EvalError::NoFieldForValue(
|
||||||
loc.clone(),
|
loc.clone(),
|
||||||
Value::Structure(oname, fields),
|
Value::Structure(oname, fields),
|
||||||
@@ -91,21 +105,21 @@ where
|
|||||||
Expression::Block(_, _, stmts) => {
|
Expression::Block(_, _, stmts) => {
|
||||||
let mut result = Value::Void;
|
let mut result = Value::Void;
|
||||||
|
|
||||||
for stmt in stmts.iter() {
|
for stmt in stmts.into_iter() {
|
||||||
result = stmt.eval(env, stdout)?;
|
result = self.eval_expr(stmt)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression::Bind(_, name, _, value) => {
|
Expression::Bind(_, name, _, value) => {
|
||||||
let value = value.eval(env, stdout)?;
|
let value = self.eval_expr(*value)?;
|
||||||
env.insert(name.clone(), value.clone());
|
self.env.insert(name.clone(), value.clone());
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression::Call(loc, _, fun, args) => {
|
Expression::Call(loc, _, fun, args) => {
|
||||||
let function = fun.eval(env)?;
|
let function = self.eval_atomic(fun)?;
|
||||||
|
|
||||||
match function {
|
match function {
|
||||||
Value::Closure(name, mut closure_env, arguments, body) => {
|
Value::Closure(name, mut closure_env, arguments, body) => {
|
||||||
@@ -120,20 +134,29 @@ where
|
|||||||
|
|
||||||
closure_env.new_scope();
|
closure_env.new_scope();
|
||||||
for (name, value) in arguments.into_iter().zip(args) {
|
for (name, value) in arguments.into_iter().zip(args) {
|
||||||
let value = value.eval(env)?;
|
let value = self.eval_atomic(value)?;
|
||||||
closure_env.insert(name, value);
|
closure_env.insert(name, value);
|
||||||
}
|
}
|
||||||
let result = body.eval(&mut closure_env, stdout)?;
|
|
||||||
|
let temp_ref = &mut closure_env;
|
||||||
|
|
||||||
|
std::mem::swap(&mut self.env, temp_ref);
|
||||||
|
let result = self.eval_expr(body);
|
||||||
|
std::mem::swap(&mut self.env, temp_ref);
|
||||||
closure_env.release_scope();
|
closure_env.release_scope();
|
||||||
Ok(result)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Primitive(name) if name == "print" => {
|
Value::Primitive(name) if name == "print" => {
|
||||||
if let [ValueOrRef::Ref(loc, ty, name)] = &args[..] {
|
if let [ValueOrRef::Ref(loc, ty, name)] = &args[..] {
|
||||||
let value = ValueOrRef::Ref(loc.clone(), ty.clone(), name.clone()).eval(env)?;
|
let value = self.eval_atomic(ValueOrRef::Ref(
|
||||||
|
loc.clone(),
|
||||||
|
ty.clone(),
|
||||||
|
name.clone(),
|
||||||
|
))?;
|
||||||
let addendum = format!("{} = {}\n", name, value);
|
let addendum = format!("{} = {}\n", name, value);
|
||||||
|
|
||||||
stdout.push_str(&addendum);
|
self.stdout.push_str(&addendum);
|
||||||
Ok(Value::Void)
|
Ok(Value::Void)
|
||||||
} else {
|
} else {
|
||||||
panic!("Non-reference/non-singleton argument to 'print'");
|
panic!("Non-reference/non-singleton argument to 'print'");
|
||||||
@@ -142,8 +165,8 @@ where
|
|||||||
|
|
||||||
Value::Primitive(name) => {
|
Value::Primitive(name) => {
|
||||||
let values = args
|
let values = args
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|x| x.eval(env))
|
.map(|x| self.eval_atomic(x))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
println!("primitive {}: args {:?}", name, values);
|
println!("primitive {}: args {:?}", name, values);
|
||||||
Value::calculate(name.as_str(), values).map_err(Into::into)
|
Value::calculate(name.as_str(), values).map_err(Into::into)
|
||||||
@@ -154,25 +177,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> ValueOrRef<T> {
|
fn eval_atomic(&self, value: ValueOrRef<T>) -> Result<IRValue<T>, IREvalError<T>> {
|
||||||
fn eval(&self, env: &ScopedMap<Variable, IRValue<T>>) -> Result<IRValue<T>, IREvalError<T>> {
|
match value {
|
||||||
match self {
|
|
||||||
ValueOrRef::Value(_, _, v) => match v {
|
ValueOrRef::Value(_, _, v) => match v {
|
||||||
super::Value::I8(_, v) => Ok(Value::I8(*v)),
|
super::Value::I8(_, v) => Ok(Value::I8(v)),
|
||||||
super::Value::I16(_, v) => Ok(Value::I16(*v)),
|
super::Value::I16(_, v) => Ok(Value::I16(v)),
|
||||||
super::Value::I32(_, v) => Ok(Value::I32(*v)),
|
super::Value::I32(_, v) => Ok(Value::I32(v)),
|
||||||
super::Value::I64(_, v) => Ok(Value::I64(*v)),
|
super::Value::I64(_, v) => Ok(Value::I64(v)),
|
||||||
super::Value::U8(_, v) => Ok(Value::U8(*v)),
|
super::Value::U8(_, v) => Ok(Value::U8(v)),
|
||||||
super::Value::U16(_, v) => Ok(Value::U16(*v)),
|
super::Value::U16(_, v) => Ok(Value::U16(v)),
|
||||||
super::Value::U32(_, v) => Ok(Value::U32(*v)),
|
super::Value::U32(_, v) => Ok(Value::U32(v)),
|
||||||
super::Value::U64(_, v) => Ok(Value::U64(*v)),
|
super::Value::U64(_, v) => Ok(Value::U64(v)),
|
||||||
super::Value::Void => Ok(Value::Void),
|
super::Value::Void => Ok(Value::Void),
|
||||||
},
|
},
|
||||||
|
|
||||||
ValueOrRef::Ref(loc, _, n) => env
|
ValueOrRef::Ref(loc, _, n) => self
|
||||||
.get(n)
|
.env
|
||||||
|
.get(&n)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string())),
|
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string())),
|
||||||
|
|
||||||
@@ -185,8 +207,9 @@ impl<T: Clone> ValueOrRef<T> {
|
|||||||
fn two_plus_three() {
|
fn two_plus_three() {
|
||||||
let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works");
|
let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works");
|
||||||
let ir = input.type_infer().expect("test should be type-valid");
|
let ir = input.type_infer().expect("test should be type-valid");
|
||||||
let (_, output) = ir.eval().expect("runs successfully");
|
let evaluator = Evaluator::default();
|
||||||
assert_eq!("x = 5u64\n", &output);
|
let (_, result) = evaluator.eval(ir).expect("runs successfully");
|
||||||
|
assert_eq!("x = 5u64\n", &result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -194,6 +217,7 @@ fn lotsa_math() {
|
|||||||
let input =
|
let input =
|
||||||
crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works");
|
crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works");
|
||||||
let ir = input.type_infer().expect("test should be type-valid");
|
let ir = input.type_infer().expect("test should be type-valid");
|
||||||
let (_, output) = ir.eval().expect("runs successfully");
|
let evaluator = Evaluator::default();
|
||||||
assert_eq!("x = 7u64\n", &output);
|
let (_, result) = evaluator.eval(ir).expect("runs successfully");
|
||||||
|
assert_eq!("x = 7u64\n", &result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,9 @@ pub use crate::syntax::ast::*;
|
|||||||
pub use crate::syntax::location::Location;
|
pub use crate::syntax::location::Location;
|
||||||
pub use crate::syntax::parser::{ProgramParser, TopLevelParser};
|
pub use crate::syntax::parser::{ProgramParser, TopLevelParser};
|
||||||
pub use crate::syntax::tokens::{LexerError, Token};
|
pub use crate::syntax::tokens::{LexerError, Token};
|
||||||
#[cfg(test)]
|
|
||||||
use ::pretty::Arena;
|
|
||||||
use lalrpop_util::ParseError;
|
use lalrpop_util::ParseError;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use proptest::{arbitrary::Arbitrary, prop_assert, prop_assert_eq};
|
use proptest::{arbitrary::Arbitrary, prop_assert};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -324,27 +322,6 @@ fn order_of_operations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
proptest::proptest! {
|
proptest::proptest! {
|
||||||
#[test]
|
|
||||||
fn random_render_parses_equal(program: Program) {
|
|
||||||
let mut file_database = SimpleFiles::new();
|
|
||||||
let writer = ::pretty::termcolor::StandardStream::stderr(::pretty::termcolor::ColorChoice::Auto);
|
|
||||||
let config = codespan_reporting::term::Config::default();
|
|
||||||
let allocator = Arena::<()>::new();
|
|
||||||
|
|
||||||
let mut out_vector = vec![];
|
|
||||||
prop_assert!(program.pretty(&allocator).render(80, &mut out_vector).is_ok());
|
|
||||||
let string = std::str::from_utf8(&out_vector).expect("emitted valid string");
|
|
||||||
let file_handle = file_database.add("test", string);
|
|
||||||
let file_db_info = file_database.get(file_handle).expect("find thing just inserted");
|
|
||||||
let parsed = Program::parse(file_handle, file_db_info.source());
|
|
||||||
|
|
||||||
if let Err(e) = &parsed {
|
|
||||||
eprintln!("failed to parse:\n{}", string);
|
|
||||||
codespan_reporting::term::emit(&mut writer.lock(), &config, &file_database, &e.into()).unwrap();
|
|
||||||
}
|
|
||||||
prop_assert_eq!(program, parsed.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn random_syntaxes_validate(program: Program) {
|
fn random_syntaxes_validate(program: Program) {
|
||||||
let (errors, _) = program.validate();
|
let (errors, _) = program.validate();
|
||||||
@@ -354,6 +331,8 @@ proptest::proptest! {
|
|||||||
#[test]
|
#[test]
|
||||||
fn generated_run_or_overflow(program in Program::arbitrary_with(GenerationEnvironment::new(false))) {
|
fn generated_run_or_overflow(program in Program::arbitrary_with(GenerationEnvironment::new(false))) {
|
||||||
use crate::eval::{EvalError, PrimOpError};
|
use crate::eval::{EvalError, PrimOpError};
|
||||||
|
println!("-----------\nprogram:\n{}\n", program);
|
||||||
|
println!("-----------\nresult:\n{:?}\n", program.eval());
|
||||||
prop_assert!(matches!(program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))));
|
prop_assert!(matches!(program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ impl ConstantType {
|
|||||||
match self {
|
match self {
|
||||||
ConstantType::Void => &[],
|
ConstantType::Void => &[],
|
||||||
ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => {
|
ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => {
|
||||||
&[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)]
|
&[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)]
|
||||||
}
|
}
|
||||||
ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => {
|
ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => {
|
||||||
&[("+", 2), ("-", 2), ("*", 2), ("/", 2)]
|
&[("+", 2), ("-", 2), ("*", 2), ("/", 2)]
|
||||||
|
|||||||
@@ -146,7 +146,8 @@ impl Expression {
|
|||||||
|
|
||||||
Value::Primitive(name) if name == "print" => {
|
Value::Primitive(name) if name == "print" => {
|
||||||
if let [Expression::Reference(_, name)] = &args[..] {
|
if let [Expression::Reference(_, name)] = &args[..] {
|
||||||
let value = Expression::Reference(loc.clone(), name.clone()).eval(stdout, env)?;
|
let value = Expression::Reference(loc.clone(), name.clone())
|
||||||
|
.eval(stdout, env)?;
|
||||||
let value = match value {
|
let value = match value {
|
||||||
Value::Number(x) => Value::U64(x),
|
Value::Number(x) => Value::U64(x),
|
||||||
x => x,
|
x => x,
|
||||||
@@ -156,7 +157,10 @@ impl Expression {
|
|||||||
stdout.push_str(&addendum);
|
stdout.push_str(&addendum);
|
||||||
Ok(Value::Void)
|
Ok(Value::Void)
|
||||||
} else {
|
} else {
|
||||||
panic!("Non-reference/non-singleton argument to 'print': {:?}", args);
|
panic!(
|
||||||
|
"Non-reference/non-singleton argument to 'print': {:?}",
|
||||||
|
args
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ proptest::proptest! {
|
|||||||
fn translation_maintains_semantics(input in syntax::Program::arbitrary_with(GenerationEnvironment::new(false))) {
|
fn translation_maintains_semantics(input in syntax::Program::arbitrary_with(GenerationEnvironment::new(false))) {
|
||||||
let syntax_result = input.eval().map(|(x,o)| (x.strip(), o));
|
let syntax_result = input.eval().map(|(x,o)| (x.strip(), o));
|
||||||
let ir = input.type_infer().expect("arbitrary should generate type-safe programs");
|
let ir = input.type_infer().expect("arbitrary should generate type-safe programs");
|
||||||
let ir_result = ir.eval().map(|(x,o)| (x.strip(), o));
|
let ir_evaluator = crate::ir::Evaluator::default();
|
||||||
|
let ir_result = ir_evaluator.eval(ir).map(|(x, o)| (x.strip(), o));
|
||||||
match (syntax_result, ir_result) {
|
match (syntax_result, ir_result) {
|
||||||
(Err(e1), Err(e2)) => proptest::prop_assert_eq!(e1, e2),
|
(Err(e1), Err(e2)) => proptest::prop_assert_eq!(e1, e2),
|
||||||
(Ok((v1, o1)), Ok((v2, o2))) => {
|
(Ok((v1, o1)), Ok((v2, o2))) => {
|
||||||
|
|||||||
@@ -360,10 +360,8 @@ fn convert_expression(
|
|||||||
let arg_type = ir::TypeOrVar::new_located(loc.clone());
|
let arg_type = ir::TypeOrVar::new_located(loc.clone());
|
||||||
constraint_db.push(Constraint::NumericType(loc.clone(), arg_type.clone()));
|
constraint_db.push(Constraint::NumericType(loc.clone(), arg_type.clone()));
|
||||||
constraint_db.push(Constraint::IsSigned(loc.clone(), arg_type.clone()));
|
constraint_db.push(Constraint::IsSigned(loc.clone(), arg_type.clone()));
|
||||||
let funtype = ir::TypeOrVar::Function(
|
let funtype =
|
||||||
vec![arg_type.clone()],
|
ir::TypeOrVar::Function(vec![arg_type.clone()], Box::new(arg_type));
|
||||||
Box::new(arg_type),
|
|
||||||
);
|
|
||||||
let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop);
|
let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop);
|
||||||
(ir::Expression::Atomic(result_value), funtype)
|
(ir::Expression::Atomic(result_value), funtype)
|
||||||
}
|
}
|
||||||
@@ -409,8 +407,7 @@ fn convert_expression(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let last_call =
|
let last_call = ir::Expression::Call(loc.clone(), return_type.clone(), fun, new_args);
|
||||||
ir::Expression::Call(loc.clone(), return_type.clone(), Box::new(fun), new_args);
|
|
||||||
|
|
||||||
(finalize_expressions(prereqs, last_call), return_type)
|
(finalize_expressions(prereqs, last_call), return_type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ fn finalize_expression(
|
|||||||
Expression::Call(loc, ty, fun, args) => Expression::Call(
|
Expression::Call(loc, ty, fun, args) => Expression::Call(
|
||||||
loc,
|
loc,
|
||||||
finalize_type(ty, resolutions),
|
finalize_type(ty, resolutions),
|
||||||
Box::new(finalize_val_or_ref(*fun, resolutions)),
|
finalize_val_or_ref(fun, resolutions),
|
||||||
args.into_iter()
|
args.into_iter()
|
||||||
.map(|x| finalize_val_or_ref(x, resolutions))
|
.map(|x| finalize_val_or_ref(x, resolutions))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|||||||
Reference in New Issue
Block a user