λ Support functions! #5

Open
acw wants to merge 59 commits from awick/functions into develop
4 changed files with 19 additions and 26 deletions
Showing only changes of commit 6c3fc2de01 - Show all commits

View File

@@ -1,7 +1,7 @@
x = 1; x = 1;
function add_x(y) x + y; function add_x(y) x + y;
a = 3; a = 3;
function add_x_twice(y) add_x(y) + x function add_x_twice(y) add_x(y) + x;
print x; print x;
result = add_x(a); result = add_x(a);
print x; print x;

View File

@@ -2,7 +2,7 @@ x = 1u64;
function mean_x(y) { function mean_x(y) {
base = x + y; base = x + y;
result = base / 2; result = base / 2;
result; result
}; };
a = 3; a = 3;
mean_x_and_a = mean_x(a); mean_x_and_a = mean_x(a);

View File

@@ -60,7 +60,8 @@ pub Program: Program = {
// a program is just a set of statements // a program is just a set of statements
<items:ProgramTopLevel> => Program { <items:ProgramTopLevel> => Program {
items items
} },
=> Program { items: vec![] },
} }
ProgramTopLevel: Vec<TopLevel> = { ProgramTopLevel: Vec<TopLevel> = {
@@ -68,17 +69,21 @@ ProgramTopLevel: Vec<TopLevel> = {
rest.push(t); rest.push(t);
rest rest
}, },
=> Vec::new(), <t:TopLevel> => vec![t],
} }
pub TopLevel: TopLevel = { pub TopLevel: TopLevel = {
<f:Function> => f, <f:Function> => f,
<s:Statement> => TopLevel::Statement(s), <s:Statement> ";" => TopLevel::Statement(s),
} }
Function: TopLevel = { Function: TopLevel = {
"function" <opt_name:OptionalName> "(" <args:Arguments> OptionalComma ")" <exp:Expression> ";" => "function" <opt_name:OptionalName> "(" <args:Arguments> OptionalComma ")" <exp:Expression> ";" =>
TopLevel::Function(opt_name, args, exp), TopLevel::Function(opt_name, args, exp),
// "function" <opt_name:OptionalName> "(" <args:Arguments> OptionalComma ")" <s:@L> "{" "}" <e:@L> =>
// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), vec![])),
// "function" <opt_name:OptionalName> "(" <args:Arguments> OptionalComma ")" <s:@L> "{" <stmts:Statements> "}" <e:@L> =>
// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), stmts)),
} }
OptionalName: Option<Name> = { OptionalName: Option<Name> = {
@@ -112,36 +117,22 @@ Statements: Vec<Statement> = {
// a statement is either a set of statements followed by another // a statement is either a set of statements followed by another
// statement (note, here, that you can name the result of a sub-parse // statement (note, here, that you can name the result of a sub-parse
// using <name: subrule>) ... // using <name: subrule>) ...
<mut stmts:Statements> <stmt:Statement> => { <mut stmts:Statements> ";" <stmt:Statement> => {
stmts.push(stmt); stmts.push(stmt);
stmts stmts
}, },
// ... or it's nothing. This may feel like an awkward way to define <stmt:Statement> => {
// lists of things -- and it is a bit awkward -- but there are actual vec![stmt]
// technical reasons that you want to (a) use recursivion to define
// these, and (b) use *left* recursion, specifically. That's why, in
// this file, all of the recursive cases are to the left, like they
// are above.
//
// the details of why left recursion is better is actually pretty
// fiddly and in the weeds, and if you're interested you should look
// up LALR parsers versus LL parsers; both their differences and how
// they're constructed, as they're kind of neat.
//
// but if you're just writing grammars with lalrpop, then you should
// just remember that you should always use left recursion, and be
// done with it.
=> {
Vec::new()
} }
} }
#[inline]
Statement: Statement = { Statement: Statement = {
// A statement can be a variable binding. Note, here, that we use this // 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 // funny @L thing to get the source location before the variable, so that
// we can say that this statement spans across everything. // we can say that this statement spans across everything.
<ls: @L> <v:"<var>"> <var_end: @L> "=" <e:Expression> ";" <le: @L> => <ls: @L> <v:"<var>"> <var_end: @L> "=" <e:Expression> <le: @L> =>
Statement::Binding( Statement::Binding(
Location::new(file_idx, ls..le), Location::new(file_idx, ls..le),
Name::new(v, Location::new(file_idx, ls..var_end)), Name::new(v, Location::new(file_idx, ls..var_end)),
@@ -149,14 +140,14 @@ Statement: Statement = {
), ),
// 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(
Location::new(file_idx, ls..le), Location::new(file_idx, ls..le),
Name::new(v, Location::new(file_idx, name_start..name_end)), Name::new(v, Location::new(file_idx, name_start..name_end)),
), ),
// A statement can just be an expression. // A statement can just be an expression.
<e: Expression> ";" => <e: Expression> =>
Statement::Expression(e), Statement::Expression(e),
} }
@@ -237,6 +228,7 @@ AtomicExpression: Expression = {
<l: @L> <n:"<num>"> <end: @L> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)), <l: @L> <n:"<num>"> <end: @L> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)),
// this expression could actually be a block! // this expression could actually be a block!
<s:@L> "{" <stmts:Statements> "}" <e:@L> => Expression::Block(Location::new(file_idx, s..e), stmts), <s:@L> "{" <stmts:Statements> "}" <e:@L> => Expression::Block(Location::new(file_idx, s..e), stmts),
<s:@L> "{" "}" <e:@L> => Expression::Block(Location::new(file_idx, s..e), vec![]),
// finally, let people parenthesize expressions and get back to a // finally, let people parenthesize expressions and get back to a
// lower precedence // lower precedence
"(" <e:Expression> ")" => e, "(" <e:Expression> ")" => e,

View File

@@ -68,6 +68,7 @@ impl Program {
/// actually a problem. /// actually a problem.
pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) { pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) {
let mut bound_variables = ScopedMap::new(); let mut bound_variables = ScopedMap::new();
println!("validate: {}", self);
self.validate_with_bindings(&mut bound_variables) self.validate_with_bindings(&mut bound_variables)
} }