diff --git a/examples/basic/test0005.ngr b/examples/basic/test0005.ngr new file mode 100644 index 0000000..447c859 --- /dev/null +++ b/examples/basic/test0005.ngr @@ -0,0 +1,2 @@ +t = v = 5i64; +print t; \ No newline at end of file diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 82242bc..fce0da9 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -129,14 +129,14 @@ proptest::proptest! { 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"); + 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"); let compiled_result = Backend::::eval(program); proptest::prop_assert_eq!(basic_result, compiled_result); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 724153a..cfcd47d 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -556,7 +556,6 @@ impl Backend { let pointer = self.module.target_config().pointer_type(); let pointer_to = builder.ins().symbol_value(pointer, *global_value); builder.ins().store(MemFlags::new(), value, pointer_to, 0); - Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } Some(ReferenceBuilder::Argument(_, _)) => { @@ -571,9 +570,10 @@ impl Backend { builder.declare_var(variable, value_type); builder.def_var(variable, value); variables.insert(name, ReferenceBuilder::Local(value_type, variable)); - Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } } + + Ok((value, value_type)) } Expression::Call(_, _, function, args) => { diff --git a/src/ir/eval.rs b/src/ir/eval.rs index d7462ee..63748b0 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -126,8 +126,8 @@ where Expression::Bind(_, name, _, value) => { let value = value.eval(env, stdout)?; - env.insert(name.clone(), value); - Ok(Value::Void) + env.insert(name.clone(), value.clone()); + Ok(value) } Expression::Call(loc, _, fun, args) => { diff --git a/src/repl.rs b/src/repl.rs index 1fbd501..58225f7 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,5 +1,5 @@ use crate::backend::{Backend, BackendError}; -use crate::syntax::{ConstantType, Location, ParserError, Statement, TopLevel}; +use crate::syntax::{ConstantType, Expression, Location, ParserError, Statement, TopLevel}; use crate::type_infer::TypeInferenceResult; use crate::util::scoped_map::ScopedMap; use codespan_reporting::diagnostic::Diagnostic; @@ -130,7 +130,7 @@ impl REPL { let syntax = TopLevel::parse(entry, source)?; let program = match syntax { - TopLevel::Statement(Statement::Binding(loc, name, expr)) => { + TopLevel::Statement(Statement::Expression(Expression::Binding(loc, name, expr))) => { // if this is a variable binding, and we've never defined this variable before, // we should tell cranelift about it. this is optimistic; if we fail to compile, // then we won't use this definition until someone tries again. @@ -142,7 +142,11 @@ impl REPL { crate::syntax::Program { items: vec![ - TopLevel::Statement(Statement::Binding(loc.clone(), name.clone(), expr)), + TopLevel::Statement(Statement::Expression(Expression::Binding( + loc.clone(), + name.clone(), + expr, + ))), TopLevel::Statement(Statement::Print(loc, name)), ], } diff --git a/src/syntax.rs b/src/syntax.rs index 3f89769..751c36d 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -285,32 +285,34 @@ fn order_of_operations() { assert_eq!( Program::from_str(muladd1).unwrap(), Program { - items: vec![TopLevel::Statement(Statement::Binding( - Location::new(testfile, 0..1), - Name::manufactured("x"), - Expression::Primitive( - Location::new(testfile, 6..7), - "+".to_string(), - vec![ - Expression::Value( - Location::new(testfile, 4..5), - Value::Number(None, None, 1), - ), - Expression::Primitive( - Location::new(testfile, 10..11), - "*".to_string(), - vec![ - Expression::Value( - Location::new(testfile, 8..9), - Value::Number(None, None, 2), - ), - Expression::Value( - Location::new(testfile, 12..13), - Value::Number(None, None, 3), - ), - ] - ) - ] + items: vec![TopLevel::Statement(Statement::Expression( + Expression::Binding( + Location::new(testfile, 0..1), + Name::manufactured("x"), + Box::new(Expression::Primitive( + Location::new(testfile, 6..7), + "+".to_string(), + vec![ + Expression::Value( + Location::new(testfile, 4..5), + Value::Number(None, None, 1), + ), + Expression::Primitive( + Location::new(testfile, 10..11), + "*".to_string(), + vec![ + Expression::Value( + Location::new(testfile, 8..9), + Value::Number(None, None, 2), + ), + Expression::Value( + Location::new(testfile, 12..13), + Value::Number(None, None, 3), + ), + ] + ) + ] + )) ) ))], } diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index cbe03ad..9abfa1f 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -72,11 +72,11 @@ impl Arbitrary for Program { genenv.bindings.insert(psi.name.clone(), psi.binding_type); items.push( expr.prop_map(move |expr| { - TopLevel::Statement(Statement::Binding( + TopLevel::Statement(Statement::Expression(Expression::Binding( Location::manufactured(), psi.name.clone(), - expr, - )) + Box::new(expr), + ))) }) .boxed(), ); diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 2fff204..f128ecb 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -99,7 +99,6 @@ impl fmt::Display for Name { /// thing, not if they are the exact same statement. #[derive(Clone, Debug)] pub enum Statement { - Binding(Location, Name, Expression), Print(Location, Name), Expression(Expression), } @@ -107,10 +106,6 @@ pub enum Statement { impl PartialEq for Statement { fn eq(&self, other: &Self) -> bool { match self { - Statement::Binding(_, name1, expr1) => match other { - Statement::Binding(_, name2, expr2) => name1 == name2 && expr1 == expr2, - _ => false, - }, Statement::Print(_, name1) => match other { Statement::Print(_, name2) => name1 == name2, _ => false, @@ -139,6 +134,7 @@ pub enum Expression { Primitive(Location, String, Vec), Call(Location, Box, Vec), Block(Location, Vec), + Binding(Location, Name, Box), } impl PartialEq for Expression { @@ -176,6 +172,10 @@ impl PartialEq for Expression { Expression::Block(_, stmts2) => stmts1 == stmts2, _ => false, }, + Expression::Binding(_, name1, expr1) => match other { + Expression::Binding(_, name2, expr2) => name1 == name2 && expr1 == expr2, + _ => false, + }, } } } @@ -192,6 +192,7 @@ impl Expression { Expression::Primitive(loc, _, _) => loc, Expression::Call(loc, _, _) => loc, Expression::Block(loc, _) => loc, + Expression::Binding(loc, _, _) => loc, } } } diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 506ed13..7546392 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -59,12 +59,6 @@ impl Statement { env: &mut ScopedMap, Value>, ) -> Result, EvalError> { match self { - Statement::Binding(_, name, value) => { - let actual_value = value.eval(stdout, env)?; - env.insert(name.clone().intern(), actual_value); - Ok(Value::Void) - } - Statement::Print(loc, name) => { let value = env .get(&name.clone().intern()) @@ -197,6 +191,12 @@ impl Expression { Ok(result) } + + Expression::Binding(_, name, value) => { + let actual_value = value.eval(stdout, env)?; + env.insert(name.clone().intern(), actual_value.clone()); + Ok(actual_value) + } } } } diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 70534ea..3c37a92 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -148,16 +148,6 @@ Statements: Vec = { #[inline] Statement: Statement = { - // A statement can be a variable binding. Note, here, that we use this - // funny @L thing to get the source location before the variable, so that - // we can say that this statement spans across everything. - "> "=" => - Statement::Binding( - Location::new(file_idx, ls..le), - Name::new(v, Location::new(file_idx, ls..var_end)), - e, - ), - // A statement can just be a print statement. "print" "> => Statement::Print( @@ -194,13 +184,27 @@ Statement: Statement = { // parse something like "1 + 2 * 3", for example, versus "1 + 2 + 3" or // "1 * 2 + 3", and hopefully that'll help. Expression: Expression = { + BindingExpression, +} + +BindingExpression: Expression = { + // An expression can be a variable binding. Note, here, that we use this + // funny @L thing to get the source location before the variable, so that + // we can say that this statement spans across everything. + "> "=" => + Expression::Binding( + Location::new(file_idx, ls..le), + Name::new(v, Location::new(file_idx, ls..var_end)), + Box::new(e), + ), + ConstructorExpression, - AdditiveExpression, } ConstructorExpression: Expression = { "{" "}" => Expression::Constructor(Location::new(file_idx, s..e), name, fields), + AdditiveExpression, } FieldSetter: (Name, Expression) = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 10fa97c..249c571 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -90,12 +90,6 @@ impl TopLevel { impl Statement { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { - Statement::Binding(_, var, expr) => allocator - .text(var.to_string()) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(expr.pretty(allocator)), Statement::Print(_, var) => allocator .text("print") .append(allocator.space()) @@ -178,6 +172,12 @@ impl Expression { .append(allocator.text("}")) } }, + Expression::Binding(_, var, expr) => allocator + .text(var.to_string()) + .append(allocator.space()) + .append(allocator.text("=")) + .append(allocator.space()) + .append(expr.pretty(allocator)), } } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index d751578..3c868d0 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -156,25 +156,6 @@ impl Statement { let mut warnings = vec![]; match self { - Statement::Binding(loc, var, val) => { - // we're going to make the decision that a variable is not bound in the right - // hand side of its binding, which makes a lot of things easier. So we'll just - // immediately check the expression, and go from there. - let (mut exp_errors, mut exp_warnings) = val.validate(bound_variables); - - errors.append(&mut exp_errors); - warnings.append(&mut exp_warnings); - if let Some(original_binding_site) = bound_variables.get(&var.name) { - warnings.push(Warning::ShadowedVariable( - original_binding_site.clone(), - loc.clone(), - var.to_string(), - )); - } else { - bound_variables.insert(var.to_string(), loc.clone()); - } - } - Statement::Print(_, var) if bound_variables.contains_key(&var.name) => {} Statement::Print(loc, var) => { errors.push(Error::UnboundVariable(loc.clone(), var.to_string())) @@ -260,6 +241,24 @@ impl Expression { warnings.append(&mut local_warnings); } + (errors, warnings) + } + Expression::Binding(loc, var, val) => { + // we're going to make the decision that a variable is not bound in the right + // hand side of its binding, which makes a lot of things easier. So we'll just + // immediately check the expression, and go from there. + let (errors, mut warnings) = val.validate(variable_map); + + if let Some(original_binding_site) = variable_map.get(&var.name) { + warnings.push(Warning::ShadowedVariable( + original_binding_site.clone(), + loc.clone(), + var.to_string(), + )); + } else { + variable_map.insert(var.to_string(), loc.clone()); + } + (errors, warnings) } } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 5d263eb..1fdf95d 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -195,14 +195,6 @@ fn convert_statement( ir::Expression::Print(loc.clone(), ir::ValueOrRef::Ref(loc, varty, final_name)) } - syntax::Statement::Binding(loc, name, expr) => { - let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); - let final_name = finalize_name(bindings, renames, name); - bindings.insert(final_name.clone(), ty.clone()); - - ir::Expression::Bind(loc, final_name, ty, Box::new(expr)) - } - syntax::Statement::Expression(e) => { convert_expression(e, constraint_db, renames, bindings).0 } @@ -464,6 +456,17 @@ fn convert_expression( ret_type, ) } + + syntax::Expression::Binding(loc, name, expr) => { + let (expr, ty) = convert_expression(*expr, constraint_db, renames, bindings); + let final_name = finalize_name(bindings, renames, name); + bindings.insert(final_name.clone(), ty.clone()); + + ( + ir::Expression::Bind(loc, final_name, ty.clone(), Box::new(expr)), + ty, + ) + } } }