λ Support functions! #5
2
examples/basic/test0005.ngr
Normal file
2
examples/basic/test0005.ngr
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
t = v = 5i64;
|
||||||
|
print t;
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
10
src/repl.rs
10
src/repl.rs
@@ -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)),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,32 +285,34 @@ 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(
|
||||||
Location::new(testfile, 0..1),
|
Expression::Binding(
|
||||||
Name::manufactured("x"),
|
Location::new(testfile, 0..1),
|
||||||
Expression::Primitive(
|
Name::manufactured("x"),
|
||||||
Location::new(testfile, 6..7),
|
Box::new(Expression::Primitive(
|
||||||
"+".to_string(),
|
Location::new(testfile, 6..7),
|
||||||
vec![
|
"+".to_string(),
|
||||||
Expression::Value(
|
vec![
|
||||||
Location::new(testfile, 4..5),
|
Expression::Value(
|
||||||
Value::Number(None, None, 1),
|
Location::new(testfile, 4..5),
|
||||||
),
|
Value::Number(None, None, 1),
|
||||||
Expression::Primitive(
|
),
|
||||||
Location::new(testfile, 10..11),
|
Expression::Primitive(
|
||||||
"*".to_string(),
|
Location::new(testfile, 10..11),
|
||||||
vec![
|
"*".to_string(),
|
||||||
Expression::Value(
|
vec![
|
||||||
Location::new(testfile, 8..9),
|
Expression::Value(
|
||||||
Value::Number(None, None, 2),
|
Location::new(testfile, 8..9),
|
||||||
),
|
Value::Number(None, None, 2),
|
||||||
Expression::Value(
|
),
|
||||||
Location::new(testfile, 12..13),
|
Expression::Value(
|
||||||
Value::Number(None, None, 3),
|
Location::new(testfile, 12..13),
|
||||||
),
|
Value::Number(None, None, 3),
|
||||||
]
|
),
|
||||||
)
|
]
|
||||||
]
|
)
|
||||||
|
]
|
||||||
|
))
|
||||||
)
|
)
|
||||||
))],
|
))],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) = {
|
||||||
|
|||||||
@@ -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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user