Allow chained equals.

This commit is contained in:
2024-04-15 16:31:44 -07:00
parent 3d8e0804bc
commit 763a895285
13 changed files with 114 additions and 99 deletions

View File

@@ -0,0 +1,2 @@
t = v = 5i64;
print t;

View File

@@ -129,14 +129,14 @@ proptest::proptest! {
let basic_result = basic_result.map(|x| x.replace('\n', "\r\n")); let basic_result = basic_result.map(|x| x.replace('\n', "\r\n"));
if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { 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("-------------")
// .append(allocator.line()) .append(allocator.line())
// .append(program.pretty(&allocator)) .append(program.pretty(&allocator))
// .append(allocator.line()); .append(allocator.line());
//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");
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);

View File

@@ -556,7 +556,6 @@ impl<M: Module> Backend<M> {
let pointer = self.module.target_config().pointer_type(); let pointer = self.module.target_config().pointer_type();
let pointer_to = builder.ins().symbol_value(pointer, *global_value); let pointer_to = builder.ins().symbol_value(pointer, *global_value);
builder.ins().store(MemFlags::new(), value, pointer_to, 0); builder.ins().store(MemFlags::new(), value, pointer_to, 0);
Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE))
} }
Some(ReferenceBuilder::Argument(_, _)) => { Some(ReferenceBuilder::Argument(_, _)) => {
@@ -571,9 +570,10 @@ impl<M: Module> Backend<M> {
builder.declare_var(variable, value_type); builder.declare_var(variable, value_type);
builder.def_var(variable, value); builder.def_var(variable, value);
variables.insert(name, ReferenceBuilder::Local(value_type, variable)); 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) => { Expression::Call(_, _, function, args) => {

View File

@@ -126,8 +126,8 @@ where
Expression::Bind(_, name, _, value) => { Expression::Bind(_, name, _, value) => {
let value = value.eval(env, stdout)?; let value = value.eval(env, stdout)?;
env.insert(name.clone(), value); env.insert(name.clone(), value.clone());
Ok(Value::Void) Ok(value)
} }
Expression::Call(loc, _, fun, args) => { Expression::Call(loc, _, fun, args) => {

View File

@@ -1,5 +1,5 @@
use crate::backend::{Backend, BackendError}; 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::type_infer::TypeInferenceResult;
use crate::util::scoped_map::ScopedMap; use crate::util::scoped_map::ScopedMap;
use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::diagnostic::Diagnostic;
@@ -130,7 +130,7 @@ impl REPL {
let syntax = TopLevel::parse(entry, source)?; let syntax = TopLevel::parse(entry, source)?;
let program = match syntax { 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, // 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, // 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. // then we won't use this definition until someone tries again.
@@ -142,7 +142,11 @@ impl REPL {
crate::syntax::Program { crate::syntax::Program {
items: vec![ 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)), TopLevel::Statement(Statement::Print(loc, name)),
], ],
} }

View File

@@ -285,10 +285,11 @@ fn order_of_operations() {
assert_eq!( assert_eq!(
Program::from_str(muladd1).unwrap(), Program::from_str(muladd1).unwrap(),
Program { Program {
items: vec![TopLevel::Statement(Statement::Binding( items: vec![TopLevel::Statement(Statement::Expression(
Expression::Binding(
Location::new(testfile, 0..1), Location::new(testfile, 0..1),
Name::manufactured("x"), Name::manufactured("x"),
Expression::Primitive( Box::new(Expression::Primitive(
Location::new(testfile, 6..7), Location::new(testfile, 6..7),
"+".to_string(), "+".to_string(),
vec![ vec![
@@ -311,6 +312,7 @@ fn order_of_operations() {
] ]
) )
] ]
))
) )
))], ))],
} }

View File

@@ -72,11 +72,11 @@ impl Arbitrary for Program {
genenv.bindings.insert(psi.name.clone(), psi.binding_type); genenv.bindings.insert(psi.name.clone(), psi.binding_type);
items.push( items.push(
expr.prop_map(move |expr| { expr.prop_map(move |expr| {
TopLevel::Statement(Statement::Binding( TopLevel::Statement(Statement::Expression(Expression::Binding(
Location::manufactured(), Location::manufactured(),
psi.name.clone(), psi.name.clone(),
expr, Box::new(expr),
)) )))
}) })
.boxed(), .boxed(),
); );

View File

@@ -99,7 +99,6 @@ impl fmt::Display for Name {
/// thing, not if they are the exact same statement. /// thing, not if they are the exact same statement.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Statement { pub enum Statement {
Binding(Location, Name, Expression),
Print(Location, Name), Print(Location, Name),
Expression(Expression), Expression(Expression),
} }
@@ -107,10 +106,6 @@ pub enum Statement {
impl PartialEq for Statement { impl PartialEq for Statement {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match self { match self {
Statement::Binding(_, name1, expr1) => match other {
Statement::Binding(_, name2, expr2) => name1 == name2 && expr1 == expr2,
_ => false,
},
Statement::Print(_, name1) => match other { Statement::Print(_, name1) => match other {
Statement::Print(_, name2) => name1 == name2, Statement::Print(_, name2) => name1 == name2,
_ => false, _ => false,
@@ -139,6 +134,7 @@ pub enum Expression {
Primitive(Location, String, Vec<Expression>), Primitive(Location, String, Vec<Expression>),
Call(Location, Box<Expression>, Vec<Expression>), Call(Location, Box<Expression>, Vec<Expression>),
Block(Location, Vec<Statement>), Block(Location, Vec<Statement>),
Binding(Location, Name, Box<Expression>),
} }
impl PartialEq for Expression { impl PartialEq for Expression {
@@ -176,6 +172,10 @@ impl PartialEq for Expression {
Expression::Block(_, stmts2) => stmts1 == stmts2, Expression::Block(_, stmts2) => stmts1 == stmts2,
_ => false, _ => 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::Primitive(loc, _, _) => loc,
Expression::Call(loc, _, _) => loc, Expression::Call(loc, _, _) => loc,
Expression::Block(loc, _) => loc, Expression::Block(loc, _) => loc,
Expression::Binding(loc, _, _) => loc,
} }
} }
} }

View File

@@ -59,12 +59,6 @@ impl Statement {
env: &mut ScopedMap<ArcIntern<String>, Value<Expression>>, env: &mut ScopedMap<ArcIntern<String>, Value<Expression>>,
) -> Result<Value<Expression>, EvalError<Expression>> { ) -> Result<Value<Expression>, EvalError<Expression>> {
match self { 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) => { Statement::Print(loc, name) => {
let value = env let value = env
.get(&name.clone().intern()) .get(&name.clone().intern())
@@ -197,6 +191,12 @@ impl Expression {
Ok(result) Ok(result)
} }
Expression::Binding(_, name, value) => {
let actual_value = value.eval(stdout, env)?;
env.insert(name.clone().intern(), actual_value.clone());
Ok(actual_value)
}
} }
} }
} }

View File

@@ -148,16 +148,6 @@ Statements: Vec<Statement> = {
#[inline] #[inline]
Statement: Statement = { 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.
<ls: @L> <v:"<var>"> <var_end: @L> "=" <e:Expression> <le: @L> =>
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. // A statement can just be a print statement.
<ls: @L> "print" <name_start: @L> <v:"<var>"> <name_end: @L> <le: @L> => <ls: @L> "print" <name_start: @L> <v:"<var>"> <name_end: @L> <le: @L> =>
Statement::Print( Statement::Print(
@@ -194,13 +184,27 @@ Statement: Statement = {
// parse something like "1 + 2 * 3", for example, versus "1 + 2 + 3" or // parse something like "1 + 2 * 3", for example, versus "1 + 2 + 3" or
// "1 * 2 + 3", and hopefully that'll help. // "1 * 2 + 3", and hopefully that'll help.
Expression: Expression = { 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.
<ls: @L> <v:"<var>"> <var_end: @L> "=" <e:BindingExpression> <le: @L> =>
Expression::Binding(
Location::new(file_idx, ls..le),
Name::new(v, Location::new(file_idx, ls..var_end)),
Box::new(e),
),
ConstructorExpression, ConstructorExpression,
AdditiveExpression,
} }
ConstructorExpression: Expression = { ConstructorExpression: Expression = {
<s:@L> <name:TypeName> "{" <fields:FieldSetter*> "}" <e:@L> => <s:@L> <name:TypeName> "{" <fields:FieldSetter*> "}" <e:@L> =>
Expression::Constructor(Location::new(file_idx, s..e), name, fields), Expression::Constructor(Location::new(file_idx, s..e), name, fields),
AdditiveExpression,
} }
FieldSetter: (Name, Expression) = { FieldSetter: (Name, Expression) = {

View File

@@ -90,12 +90,6 @@ impl TopLevel {
impl Statement { impl Statement {
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
match self { 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 Statement::Print(_, var) => allocator
.text("print") .text("print")
.append(allocator.space()) .append(allocator.space())
@@ -178,6 +172,12 @@ impl Expression {
.append(allocator.text("}")) .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)),
} }
} }
} }

View File

@@ -156,25 +156,6 @@ impl Statement {
let mut warnings = vec![]; let mut warnings = vec![];
match self { 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(_, var) if bound_variables.contains_key(&var.name) => {}
Statement::Print(loc, var) => { Statement::Print(loc, var) => {
errors.push(Error::UnboundVariable(loc.clone(), var.to_string())) errors.push(Error::UnboundVariable(loc.clone(), var.to_string()))
@@ -260,6 +241,24 @@ impl Expression {
warnings.append(&mut local_warnings); 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) (errors, warnings)
} }
} }

View File

@@ -195,14 +195,6 @@ fn convert_statement(
ir::Expression::Print(loc.clone(), ir::ValueOrRef::Ref(loc, varty, final_name)) 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) => { syntax::Statement::Expression(e) => {
convert_expression(e, constraint_db, renames, bindings).0 convert_expression(e, constraint_db, renames, bindings).0
} }
@@ -464,6 +456,17 @@ fn convert_expression(
ret_type, 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,
)
}
} }
} }