mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Code gen ints and floats
This commit is contained in:
parent
e949188319
commit
eeb2bb376c
13 changed files with 758 additions and 853 deletions
145
src/can/mod.rs
145
src/can/mod.rs
|
@ -55,11 +55,11 @@ pub fn canonicalize_declaration<'a>(
|
|||
// visited an Operator node we'd recursively try to apply this to each of its nested
|
||||
// operators, and thena again on *their* nested operators, ultimately applying the
|
||||
// rules multiple times unnecessarily.
|
||||
let loc_expr = operator::desugar(arena, loc_expr);
|
||||
let loc_expr = operator::desugar(arena, &loc_expr);
|
||||
|
||||
// If we're canonicalizing the declaration `foo = ...` inside the `Main` module,
|
||||
// scope_prefix will be "Main$foo$" and its first closure will be named "Main$foo$0"
|
||||
let scope_prefix = format!("{}${}$", home, name).into();
|
||||
// scope_prefix will be "Main.foo$" and its first closure will be named "Main.foo$0"
|
||||
let scope_prefix = format!("{}.{}$", home, name).into();
|
||||
let mut scope = Scope::new(scope_prefix, declared_idents.clone());
|
||||
let mut env = Env::new(home, declared_variants.clone());
|
||||
let (loc_expr, output) = canonicalize_expr(
|
||||
|
@ -103,19 +103,18 @@ fn canonicalize_expr(
|
|||
|
||||
let (expr, output) = match expr {
|
||||
ast::Expr::Int(string) => {
|
||||
let (constraint, answer) = int_from_parsed(subs, string, env, expected, region.clone());
|
||||
let (constraint, answer) = int_from_parsed(subs, string, env, expected, region);
|
||||
|
||||
(answer, Output::new(constraint))
|
||||
}
|
||||
ast::Expr::Float(string) => {
|
||||
let (constraint, answer) =
|
||||
float_from_parsed(subs, string, env, expected, region.clone());
|
||||
let (constraint, answer) = float_from_parsed(subs, string, env, expected, region);
|
||||
|
||||
(answer, Output::new(constraint))
|
||||
}
|
||||
ast::Expr::Record(fields) => {
|
||||
if fields.is_empty() {
|
||||
let constraint = Eq(EmptyRec, expected, region.clone());
|
||||
let constraint = Eq(EmptyRec, expected, region);
|
||||
|
||||
(EmptyRecord, Output::new(constraint))
|
||||
} else {
|
||||
|
@ -123,18 +122,14 @@ fn canonicalize_expr(
|
|||
}
|
||||
}
|
||||
ast::Expr::Str(string) => {
|
||||
let constraint = Eq(constrain::str_type(), expected, region.clone());
|
||||
let constraint = Eq(constrain::str_type(), expected, region);
|
||||
|
||||
(Str((*string).into()), Output::new(constraint))
|
||||
}
|
||||
ast::Expr::List(loc_elems) => {
|
||||
if loc_elems.is_empty() {
|
||||
let list_var = subs.mk_flex_var();
|
||||
let constraint = Eq(
|
||||
constrain::empty_list_type(list_var),
|
||||
expected,
|
||||
region.clone(),
|
||||
);
|
||||
let constraint = Eq(constrain::empty_list_type(list_var), expected, region);
|
||||
|
||||
(List(list_var, Vec::new()), Output::new(constraint))
|
||||
} else {
|
||||
|
@ -150,14 +145,14 @@ fn canonicalize_expr(
|
|||
let elem_expected = NoExpectation(elem_type.clone());
|
||||
let list_elem_constraint = Eq(
|
||||
list_type.clone(),
|
||||
ForReason(Reason::ElemInList, elem_type, region.clone()),
|
||||
region.clone(),
|
||||
ForReason(Reason::ElemInList, elem_type, region),
|
||||
region,
|
||||
);
|
||||
let (can_expr, elem_out) = canonicalize_expr(
|
||||
env,
|
||||
subs,
|
||||
scope,
|
||||
loc_elem.region.clone(),
|
||||
loc_elem.region,
|
||||
&loc_elem.value,
|
||||
elem_expected,
|
||||
);
|
||||
|
@ -170,11 +165,7 @@ fn canonicalize_expr(
|
|||
can_elems.push(can_expr);
|
||||
}
|
||||
|
||||
constraints.push(Eq(
|
||||
constrain::list_type(list_type),
|
||||
expected,
|
||||
region.clone(),
|
||||
));
|
||||
constraints.push(Eq(constrain::list_type(list_type), expected, region));
|
||||
|
||||
let mut output = Output::new(And(constraints));
|
||||
|
||||
|
@ -214,26 +205,19 @@ fn canonicalize_expr(
|
|||
|
||||
// (expr, output)
|
||||
//}
|
||||
ast::Expr::Apply((loc_fn, loc_args, application_style)) => {
|
||||
ast::Expr::Apply(loc_fn, loc_args, application_style) => {
|
||||
// The expression that evaluates to the function being called, e.g. `foo` in
|
||||
// (foo) bar baz
|
||||
let fn_var = subs.mk_flex_var();
|
||||
let fn_type = Variable(fn_var);
|
||||
let fn_region = loc_fn.region.clone();
|
||||
let fn_region = loc_fn.region;
|
||||
let fn_expected = NoExpectation(fn_type.clone());
|
||||
// TODO look up the name and use NamedFnArg if possible.
|
||||
let fn_reason = Reason::AnonymousFnCall(loc_args.len() as u8);
|
||||
|
||||
// Canonicalize the function expression and its arguments
|
||||
let (fn_expr, mut output) = canonicalize_expr(
|
||||
env,
|
||||
subs,
|
||||
scope,
|
||||
loc_fn.region.clone(),
|
||||
&loc_fn.value,
|
||||
fn_expected,
|
||||
);
|
||||
let fn_con = output.constraint;
|
||||
let (fn_expr, mut output) =
|
||||
canonicalize_expr(env, subs, scope, loc_fn.region, &loc_fn.value, fn_expected);
|
||||
|
||||
// The function's return type
|
||||
let ret_var = subs.mk_flex_var();
|
||||
|
@ -252,17 +236,17 @@ fn canonicalize_expr(
|
|||
let mut outputs = Vec::new();
|
||||
|
||||
for (index, loc_arg) in loc_args.iter().enumerate() {
|
||||
let region = loc_arg.region.clone();
|
||||
let region = loc_arg.region;
|
||||
let arg_var = subs.mk_flex_var();
|
||||
let arg_type = Variable(arg_var);
|
||||
// TODO look up the name and use NamedFnArg if possible.
|
||||
let reason = Reason::AnonymousFnArg(index as u8);
|
||||
let expected_arg = ForReason(reason, arg_type.clone(), region.clone());
|
||||
let expected_arg = ForReason(reason, arg_type.clone(), region);
|
||||
let (arg_expr, arg_out) = canonicalize_expr(
|
||||
env,
|
||||
subs,
|
||||
scope,
|
||||
loc_arg.region.clone(),
|
||||
loc_arg.region,
|
||||
&loc_arg.value,
|
||||
expected_arg,
|
||||
);
|
||||
|
@ -277,6 +261,9 @@ fn canonicalize_expr(
|
|||
outputs.push(arg_out);
|
||||
}
|
||||
|
||||
// We're not tail-calling a symbol (by name), we're tail-calling a function value.
|
||||
output.tail_call = None;
|
||||
|
||||
let expr = match &fn_expr.value {
|
||||
&Var(_, ref sym) | &FunctionPointer(_, ref sym) => {
|
||||
// In the FunctionPointer case, we're calling an inline closure;
|
||||
|
@ -285,10 +272,14 @@ fn canonicalize_expr(
|
|||
|
||||
CallByName(sym.clone(), args, *application_style)
|
||||
}
|
||||
_ => {
|
||||
&RuntimeError(_) => {
|
||||
// We can't call a runtime error; bail out by propagating it!
|
||||
return (fn_expr, output);
|
||||
}
|
||||
not_var => {
|
||||
// This could be something like ((if True then fn1 else fn2) arg1 arg2).
|
||||
// Use CallPointer here.
|
||||
panic!("TODO support function calls that aren't by name, via CallPointer");
|
||||
panic!("TODO support function calls that aren't by name, via CallPointer, in this case: {:?}", not_var);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -296,8 +287,7 @@ fn canonicalize_expr(
|
|||
output.references = output.references.union(arg_out.references);
|
||||
}
|
||||
|
||||
// We're not tail-calling a symbol (by name), we're tail-calling a function value.
|
||||
output.tail_call = None;
|
||||
let fn_con = output.constraint;
|
||||
|
||||
// TODO occurs check!
|
||||
// return $ exists vars $ CAnd ...
|
||||
|
@ -305,14 +295,14 @@ fn canonicalize_expr(
|
|||
let expected_fn_type = ForReason(
|
||||
fn_reason,
|
||||
Function(arg_types, Box::new(ret_type.clone())),
|
||||
region.clone(),
|
||||
region,
|
||||
);
|
||||
|
||||
output.constraint = And(vec![
|
||||
fn_con,
|
||||
Eq(fn_type, expected_fn_type, fn_region),
|
||||
And(arg_cons),
|
||||
Eq(ret_type, expected, region.clone()),
|
||||
Eq(ret_type, expected, region),
|
||||
]);
|
||||
|
||||
(expr, output)
|
||||
|
@ -323,13 +313,14 @@ fn canonicalize_expr(
|
|||
} else {
|
||||
Symbol::from_parts(module_parts, name)
|
||||
};
|
||||
let mut output = Output::new(Lookup(symbol, expected, region.clone()));
|
||||
|
||||
let mut output = Output::new(Lookup(symbol, expected, region));
|
||||
let ident = Ident::new(module_parts, name);
|
||||
let can_expr = match resolve_ident(&env, &scope, ident, &mut output.references) {
|
||||
Ok(symbol) => Var(subs.mk_flex_var(), symbol),
|
||||
Err(ident) => {
|
||||
let loc_ident = Located {
|
||||
region: region.clone(),
|
||||
region: region,
|
||||
value: ident,
|
||||
};
|
||||
|
||||
|
@ -360,7 +351,7 @@ fn canonicalize_expr(
|
|||
// Ok(symbol) => Var(symbol),
|
||||
// Err(ident) => {
|
||||
// let loc_ident = Located {
|
||||
// region: loc_ident.region.clone(),
|
||||
// region: loc_ident.region,
|
||||
// value: ident,
|
||||
// };
|
||||
|
||||
|
@ -411,7 +402,7 @@ fn canonicalize_expr(
|
|||
// Ok(symbol) => ApplyVariant(symbol, opt_can_args),
|
||||
// Err(variant_name) => {
|
||||
// let loc_variant = Located {
|
||||
// region: loc_expr.region.clone(),
|
||||
// region: loc_expr.region,
|
||||
// value: variant_name,
|
||||
// };
|
||||
|
||||
|
@ -423,12 +414,12 @@ fn canonicalize_expr(
|
|||
|
||||
// (can_expr, output)
|
||||
//}
|
||||
ast::Expr::Defs((defs, loc_ret)) => {
|
||||
ast::Expr::Defs(defs, loc_ret) => {
|
||||
// The body expression gets a new scope for canonicalization,
|
||||
// so clone it.
|
||||
can_defs(env, subs, scope.clone(), defs, expected, loc_ret)
|
||||
}
|
||||
ast::Expr::Closure((loc_arg_patterns, loc_body_expr)) => {
|
||||
ast::Expr::Closure(loc_arg_patterns, loc_body_expr) => {
|
||||
// The globally unique symbol that will refer to this closure once it gets converted
|
||||
// into a top-level procedure for code gen.
|
||||
//
|
||||
|
@ -479,7 +470,7 @@ fn canonicalize_expr(
|
|||
env,
|
||||
subs,
|
||||
&mut scope,
|
||||
loc_body_expr.region.clone(),
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
);
|
||||
|
@ -505,9 +496,9 @@ fn canonicalize_expr(
|
|||
ret_constraint,
|
||||
})),
|
||||
// "the closure's type is equal to the var we've stored for later use in the proc"
|
||||
Eq(args.typ, NoExpectation(typ.clone()), region.clone()),
|
||||
Eq(args.typ, NoExpectation(typ.clone()), region),
|
||||
// "the var we've stored for later is equal to the overall expected type"
|
||||
Eq(typ, expected, region.clone()),
|
||||
Eq(typ, expected, region),
|
||||
]);
|
||||
|
||||
// Now that we've collected all the references, check to see if any of the args we defined
|
||||
|
@ -534,7 +525,7 @@ fn canonicalize_expr(
|
|||
symbol.clone(),
|
||||
can_args,
|
||||
loc_body_expr,
|
||||
region.clone(),
|
||||
region,
|
||||
output.references.clone(),
|
||||
var,
|
||||
args.ret_var,
|
||||
|
@ -597,7 +588,7 @@ fn canonicalize_expr(
|
|||
// for (ident, (symbol, region)) in assigned_idents {
|
||||
// if !output.references.has_local(&symbol) {
|
||||
// let loc_ident = Located {
|
||||
// region: region.clone(),
|
||||
// region: region,
|
||||
// value: ident.clone(),
|
||||
// };
|
||||
|
||||
|
@ -622,14 +613,6 @@ fn canonicalize_expr(
|
|||
|
||||
// (expr, output)
|
||||
// }
|
||||
ast::Expr::SpaceBefore(sub_expr, _spaces) => {
|
||||
// Continue on; spaces don't do anything.
|
||||
return canonicalize_expr(env, subs, scope, region, sub_expr, expected);
|
||||
}
|
||||
ast::Expr::SpaceAfter(sub_expr, _spaces) => {
|
||||
// Continue on; spaces don't do anything.
|
||||
return canonicalize_expr(env, subs, scope, region, sub_expr, expected);
|
||||
}
|
||||
ast::Expr::BlockStr(_)
|
||||
| ast::Expr::Field(_, _)
|
||||
| ast::Expr::QualifiedField(_, _)
|
||||
|
@ -647,21 +630,35 @@ fn canonicalize_expr(
|
|||
);
|
||||
}
|
||||
ast::Expr::BinaryInt(string) => {
|
||||
let (constraint, answer) = bin_from_parsed(subs, string, env, expected, region.clone());
|
||||
let (constraint, answer) = bin_from_parsed(subs, string, env, expected, region);
|
||||
|
||||
(answer, Output::new(constraint))
|
||||
}
|
||||
ast::Expr::HexInt(string) => {
|
||||
let (constraint, answer) = hex_from_parsed(subs, string, env, expected, region.clone());
|
||||
let (constraint, answer) = hex_from_parsed(subs, string, env, expected, region);
|
||||
|
||||
(answer, Output::new(constraint))
|
||||
}
|
||||
ast::Expr::OctalInt(string) => {
|
||||
let (constraint, answer) = oct_from_parsed(subs, string, env, expected, region.clone());
|
||||
let (constraint, answer) = oct_from_parsed(subs, string, env, expected, region);
|
||||
|
||||
(answer, Output::new(constraint))
|
||||
}
|
||||
ast::Expr::Operator((loc_left, loc_op, loc_right)) => {
|
||||
// Below this point, we shouln't see any of these nodes anymore because
|
||||
// operator desugaring should have removed them!
|
||||
ast::Expr::SpaceBefore(sub_expr, _spaces) => {
|
||||
panic!(
|
||||
"A SpaceBefore did not get removed during operator desugaring somehow: {:?}",
|
||||
sub_expr
|
||||
);
|
||||
}
|
||||
ast::Expr::SpaceAfter(sub_expr, _spaces) => {
|
||||
panic!(
|
||||
"A SpaceAfter did not get removed during operator desugaring somehow: {:?}",
|
||||
sub_expr
|
||||
);
|
||||
}
|
||||
ast::Expr::Operator((_, loc_op, _)) => {
|
||||
panic!("An operator did not get desugared somehow: {:?}", loc_op);
|
||||
}
|
||||
};
|
||||
|
@ -892,10 +889,7 @@ fn add_idents_from_pattern<'a>(
|
|||
&Identifier(name) => {
|
||||
let symbol = scope.symbol(&name);
|
||||
|
||||
answer.push((
|
||||
Ident::Unqualified(name.to_string()),
|
||||
(symbol, region.clone()),
|
||||
));
|
||||
answer.push((Ident::Unqualified(name.to_string()), (symbol, *region)));
|
||||
}
|
||||
&QualifiedIdentifier(_name) => {
|
||||
panic!("TODO implement QualifiedIdentifier pattern.");
|
||||
|
@ -1294,7 +1288,7 @@ fn can_defs<'a>(
|
|||
let value = Expr::RuntimeError(NoImplementation);
|
||||
let loc_expr = Located {
|
||||
value,
|
||||
region: loc_annotation.region.clone(),
|
||||
region: loc_annotation.region,
|
||||
};
|
||||
|
||||
(None, (loc_expr, Output::new(True)))
|
||||
|
@ -1336,7 +1330,7 @@ fn can_defs<'a>(
|
|||
env,
|
||||
subs,
|
||||
&mut scope,
|
||||
loc_expr.region.clone(),
|
||||
loc_expr.region,
|
||||
&loc_expr.value,
|
||||
NoExpectation(expr_type),
|
||||
);
|
||||
|
@ -1394,6 +1388,7 @@ fn can_defs<'a>(
|
|||
// Since everywhere in the code it'll be referred to by its assigned name,
|
||||
// remove its generated name from the procedure map. (We'll re-insert it later.)
|
||||
let mut procedure = env.procedures.remove(&symbol).unwrap();
|
||||
let proc_var = procedure.var;
|
||||
|
||||
// The original ident name will be used for debugging and stack traces.
|
||||
procedure.name = Some((*name).into());
|
||||
|
@ -1420,7 +1415,7 @@ fn can_defs<'a>(
|
|||
|
||||
// Return a reference to the assigned symbol, since the auto-generated one no
|
||||
// longer references any entry in the procedure map!
|
||||
Var(subs.mk_flex_var(), assigned_symbol.clone())
|
||||
Var(proc_var, assigned_symbol.clone())
|
||||
}
|
||||
_ => loc_can_expr.value,
|
||||
};
|
||||
|
@ -1462,7 +1457,7 @@ fn can_defs<'a>(
|
|||
(
|
||||
loc_can_pattern.clone(),
|
||||
Located {
|
||||
region: loc_can_expr.region.clone(),
|
||||
region: loc_can_expr.region,
|
||||
value: can_expr.clone(),
|
||||
},
|
||||
),
|
||||
|
@ -1477,7 +1472,7 @@ fn can_defs<'a>(
|
|||
env,
|
||||
subs,
|
||||
&mut scope,
|
||||
loc_ret.region.clone(),
|
||||
loc_ret.region,
|
||||
&loc_ret.value,
|
||||
expected,
|
||||
);
|
||||
|
@ -1554,7 +1549,7 @@ fn can_defs<'a>(
|
|||
for (ident, (symbol, region)) in assigned_idents.clone() {
|
||||
if !output.references.has_local(&symbol) {
|
||||
let loc_ident = Located {
|
||||
region: region.clone(),
|
||||
region: region,
|
||||
value: ident.clone(),
|
||||
};
|
||||
|
||||
|
@ -1619,7 +1614,7 @@ fn can_defs<'a>(
|
|||
let mut regions = Vec::with_capacity(can_assignments_by_symbol.len());
|
||||
|
||||
for (loc_pattern, loc_expr) in can_assignments_by_symbol.values() {
|
||||
regions.push((loc_pattern.region.clone(), loc_expr.region.clone()));
|
||||
regions.push((loc_pattern.region, loc_expr.region));
|
||||
}
|
||||
|
||||
(
|
||||
|
|
|
@ -2,6 +2,7 @@ use bumpalo::collections::Vec;
|
|||
use bumpalo::Bump;
|
||||
use operator::Operator::Pizza;
|
||||
use operator::{CalledVia, Operator};
|
||||
use parse::ast::Def;
|
||||
use parse::ast::Expr::{self, *};
|
||||
use region::{Located, Region};
|
||||
use types;
|
||||
|
@ -31,16 +32,66 @@ fn new_op_expr<'a>(
|
|||
}
|
||||
|
||||
/// Reorder the expression tree based on operator precedence and associativity rules,
|
||||
/// then replace the Operator nodes with Apply nodes.
|
||||
pub fn desugar<'a>(arena: &'a Bump, expr: Located<Expr<'a>>) -> Located<Expr<'a>> {
|
||||
/// then replace the Operator nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
|
||||
pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> {
|
||||
use operator::Associativity::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// NOTE: A potentially nice performance optimization here would be to use
|
||||
// arena bump allocation for Infixes, arg_stack, and op_stack. As long as we
|
||||
// allocate each element inside arg_stack outside the arena, this should end
|
||||
// up being a decent bit more efficient.
|
||||
let mut infixes = Infixes::new(arena.alloc(expr));
|
||||
match &loc_expr.value {
|
||||
Float(_)
|
||||
| Int(_)
|
||||
| HexInt(_)
|
||||
| OctalInt(_)
|
||||
| BinaryInt(_)
|
||||
| Str(_)
|
||||
| BlockStr(_)
|
||||
| QualifiedField(_, _)
|
||||
| AccessorFunction(_)
|
||||
| Var(_, _)
|
||||
| MalformedIdent(_)
|
||||
| MalformedClosure
|
||||
| PrecedenceConflict(_, _, _)
|
||||
| Variant(_, _) => loc_expr,
|
||||
|
||||
Field(sub_expr, paths) => arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value: Field(desugar(arena, sub_expr), paths.clone()),
|
||||
}),
|
||||
List(elems) => {
|
||||
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
|
||||
|
||||
for elem in elems {
|
||||
new_elems.push(desugar(arena, elem));
|
||||
}
|
||||
let value: Expr<'a> = List(new_elems);
|
||||
|
||||
arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value,
|
||||
})
|
||||
}
|
||||
Record(elems) => {
|
||||
let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
|
||||
|
||||
for elem in elems {
|
||||
new_elems.push(desugar(arena, elem));
|
||||
}
|
||||
|
||||
arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value: Record(new_elems),
|
||||
})
|
||||
}
|
||||
AssignField(string, sub_expr) => arena.alloc(Located {
|
||||
value: AssignField(string.clone(), desugar(arena, sub_expr)),
|
||||
region: loc_expr.region,
|
||||
}),
|
||||
Closure(loc_patterns, loc_ret) => arena.alloc(Located {
|
||||
region: loc_expr.region,
|
||||
value: Closure(loc_patterns, desugar(arena, loc_ret)),
|
||||
}),
|
||||
Operator(_) => {
|
||||
let mut infixes = Infixes::new(arena.alloc(loc_expr));
|
||||
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
|
||||
let mut op_stack: Vec<Located<Operator>> = Vec::new_in(arena);
|
||||
|
||||
|
@ -108,14 +159,14 @@ pub fn desugar<'a>(arena: &'a Bump, expr: Located<Expr<'a>>) -> Located<Expr<'a>
|
|||
next_op,
|
||||
right.clone(),
|
||||
);
|
||||
let region = broken_expr.region.clone();
|
||||
let region = broken_expr.region;
|
||||
let value = Expr::PrecedenceConflict(
|
||||
bad_op,
|
||||
stack_op,
|
||||
arena.alloc(broken_expr),
|
||||
);
|
||||
|
||||
return Located { region, value };
|
||||
return arena.alloc(Located { region, value });
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
@ -144,7 +195,7 @@ pub fn desugar<'a>(arena: &'a Bump, expr: Located<Expr<'a>>) -> Located<Expr<'a>
|
|||
let left = arg_stack.pop().unwrap();
|
||||
|
||||
let region = Region::span_across(&left.region, &right.region);
|
||||
let expr = match loc_op.value {
|
||||
let value = match loc_op.value {
|
||||
Pizza => {
|
||||
// Rewrite the Pizza operator into an Apply
|
||||
panic!("TODO desugar |> operator into an Apply");
|
||||
|
@ -155,27 +206,99 @@ pub fn desugar<'a>(arena: &'a Bump, expr: Located<Expr<'a>>) -> Located<Expr<'a>
|
|||
let (module_parts, name) = desugar_binop(&binop, arena);
|
||||
let mut args = Vec::with_capacity_in(2, arena);
|
||||
|
||||
args.push(left.clone());
|
||||
args.push(right.clone());
|
||||
args.push(*arena.alloc(left));
|
||||
args.push(*arena.alloc(right));
|
||||
|
||||
let loc_expr = Located {
|
||||
let loc_expr = arena.alloc(Located {
|
||||
value: Expr::Var(module_parts, name),
|
||||
region: loc_op.region,
|
||||
};
|
||||
});
|
||||
|
||||
Apply(arena.alloc((loc_expr, args, CalledVia::Operator(binop))))
|
||||
Apply(loc_expr, args, CalledVia::Operator(binop))
|
||||
}
|
||||
};
|
||||
|
||||
arg_stack.push(arena.alloc(Located {
|
||||
region,
|
||||
value: expr,
|
||||
}));
|
||||
arg_stack.push(arena.alloc(Located { region, value }));
|
||||
}
|
||||
|
||||
assert_eq!(arg_stack.len(), 1);
|
||||
|
||||
arg_stack.pop().unwrap().clone()
|
||||
arg_stack.pop().unwrap()
|
||||
}
|
||||
|
||||
Defs(pairs, loc_ret) => {
|
||||
let mut desugared_defs = Vec::with_capacity_in(pairs.len(), arena);
|
||||
|
||||
for (_, def) in pairs {
|
||||
let def = match def {
|
||||
Def::AnnotationOnly(ann) => Def::AnnotationOnly(ann.clone()),
|
||||
Def::BodyOnly(pattern, loc_expr) => {
|
||||
Def::BodyOnly(pattern.clone(), desugar(arena, loc_expr))
|
||||
}
|
||||
Def::AnnotatedBody(annotation, pattern, loc_body) => Def::AnnotatedBody(
|
||||
annotation.clone(),
|
||||
pattern.clone(),
|
||||
desugar(arena, loc_body),
|
||||
),
|
||||
};
|
||||
|
||||
desugared_defs.push((Vec::new_in(arena).into_bump_slice(), def));
|
||||
}
|
||||
|
||||
arena.alloc(Located {
|
||||
value: Defs(desugared_defs, desugar(arena, loc_ret)),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
Apply(loc_fn, loc_args, called_via) => {
|
||||
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
|
||||
for loc_arg in loc_args {
|
||||
desugared_args.push(desugar(arena, loc_arg));
|
||||
}
|
||||
|
||||
arena.alloc(Located {
|
||||
value: Apply(desugar(arena, loc_fn), desugared_args, called_via.clone()),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
// If(&'a (Loc<Expr<'a>>, Loc<Expr<'a>>, Loc<Expr<'a>>)),
|
||||
// Case(
|
||||
// &'a Loc<Expr<'a>>,
|
||||
// Vec<'a, &'a (Loc<Pattern<'a>>, Loc<Expr<'a>>)>,
|
||||
// ),
|
||||
SpaceBefore(expr, _) => {
|
||||
// Since we've already begun canonicalization, these are no longer needed
|
||||
// and should be dropped.
|
||||
desugar(
|
||||
arena,
|
||||
arena.alloc(Located {
|
||||
// TODO FIXME performance disaster!!! Must remove this clone!
|
||||
//
|
||||
// This won't be easy because:
|
||||
//
|
||||
// * If this function takes an &'a Expr, then Infixes hits a problem.
|
||||
// * If SpaceBefore holds a Loc<&'a Expr>, then Spaceable hits a problem.
|
||||
// * If all the existing &'a Loc<Expr> values become Loc<&'a Expr>...who knows?
|
||||
value: (*expr).clone(),
|
||||
region: loc_expr.region,
|
||||
}),
|
||||
)
|
||||
}
|
||||
SpaceAfter(expr, _) => {
|
||||
// Since we've already begun canonicalization, these are no longer needed
|
||||
// and should be dropped.
|
||||
desugar(
|
||||
arena,
|
||||
arena.alloc(Located {
|
||||
// TODO FIXME performance disaster!!! Must remove this clone! (Not easy.)
|
||||
value: (*expr).clone(),
|
||||
region: loc_expr.region,
|
||||
}),
|
||||
)
|
||||
}
|
||||
other => panic!("TODO desugar {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -197,7 +320,7 @@ fn desugar_binop<'a>(binop: &Operator, arena: &'a Bump) -> (&'a [&'a str], &'a s
|
|||
),
|
||||
DoubleSlash => (
|
||||
bumpalo::vec![ in arena; types::MOD_INT ].into_bump_slice(),
|
||||
"div",
|
||||
"divFloor",
|
||||
),
|
||||
Percent => (
|
||||
bumpalo::vec![ in arena; types::MOD_NUM ].into_bump_slice(),
|
||||
|
@ -303,14 +426,17 @@ impl<'a> Iterator for Infixes<'a> {
|
|||
fn next(&mut self) -> Option<InfixToken<'a>> {
|
||||
match self.next_op.take() {
|
||||
Some(op) => Some(InfixToken::Op(op)),
|
||||
None => self.remaining_expr.take().map(|expr| match expr.value {
|
||||
None => self
|
||||
.remaining_expr
|
||||
.take()
|
||||
.map(|loc_expr| match loc_expr.value {
|
||||
Expr::Operator((left, op, right)) => {
|
||||
self.remaining_expr = Some(right);
|
||||
self.next_op = Some(op.clone());
|
||||
|
||||
InfixToken::Arg(left)
|
||||
}
|
||||
_ => InfixToken::Arg(expr),
|
||||
_ => InfixToken::Arg(loc_expr),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
use self::PatternType::*;
|
||||
use can::ast::Pattern::*;
|
||||
|
||||
let region = loc_pattern.region.clone();
|
||||
let region = loc_pattern.region;
|
||||
let pattern = match &loc_pattern.value {
|
||||
&Identifier(ref name) => {
|
||||
let unqualified_ident = Ident::Unqualified(name.to_string());
|
||||
|
@ -66,7 +66,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
match shadowable_idents.get(&unqualified_ident) {
|
||||
Some((_, region)) => {
|
||||
let loc_shadowed_ident = Located {
|
||||
region: region.clone(),
|
||||
region: *region,
|
||||
value: unqualified_ident,
|
||||
};
|
||||
|
||||
|
@ -86,7 +86,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
match scope.idents.get(&qualified_ident) {
|
||||
Some((_, region)) => {
|
||||
let loc_shadowed_ident = Located {
|
||||
region: region.clone(),
|
||||
region: *region,
|
||||
value: qualified_ident,
|
||||
};
|
||||
|
||||
|
@ -105,7 +105,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
|
||||
// This is a fresh identifier that wasn't already in scope.
|
||||
// Add it to scope!
|
||||
let symbol_and_region = (symbol.clone(), region.clone());
|
||||
let symbol_and_region = (symbol.clone(), region);
|
||||
|
||||
// Add this to both scope.idents *and* shadowable_idents.
|
||||
// The latter is relevant when recursively canonicalizing Variant patterns,
|
||||
|
@ -114,7 +114,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
scope
|
||||
.idents
|
||||
.insert(new_ident.clone(), symbol_and_region.clone());
|
||||
shadowable_idents.insert(new_ident, symbol_and_region);
|
||||
shadowable_idents.insert(new_ident, symbol_and_region.clone());
|
||||
|
||||
Pattern::Identifier(subs.mk_flex_var(), symbol)
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
Pattern::Variant(subs.mk_flex_var(), symbol)
|
||||
} else {
|
||||
let loc_name = Located {
|
||||
region: region.clone(),
|
||||
region: region,
|
||||
value: variant,
|
||||
};
|
||||
// We couldn't find the variant name in scope. NAMING PROBLEM!
|
||||
|
@ -188,7 +188,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
// },
|
||||
&Underscore => match pattern_type {
|
||||
CaseBranch | FunctionArg => Pattern::Underscore(subs.mk_flex_var()),
|
||||
Assignment => unsupported_pattern(env, Assignment, region.clone()),
|
||||
Assignment => unsupported_pattern(env, Assignment, region),
|
||||
},
|
||||
|
||||
// &EmptyRecordLiteral => Pattern::EmptyRecordLiteral,
|
||||
|
@ -204,7 +204,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
|
||||
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
|
||||
fn unsupported_pattern<'a>(env: &'a mut Env, pattern_type: PatternType, region: Region) -> Pattern {
|
||||
env.problem(Problem::UnsupportedPattern(pattern_type, region.clone()));
|
||||
env.problem(Problem::UnsupportedPattern(pattern_type, region));
|
||||
|
||||
Pattern::UnsupportedPattern(region)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,11 @@ impl Symbol {
|
|||
}
|
||||
|
||||
pub fn from_parts(module_parts: &[&str], name: &str) -> Symbol {
|
||||
Symbol(format!("{}{}", module_parts.join("."), name).into())
|
||||
Symbol(if module_parts.is_empty() {
|
||||
name.into()
|
||||
} else {
|
||||
format!("{}.{}", module_parts.join("."), name).into()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_variant(variant_name: &VariantName, home: &str) -> Symbol {
|
||||
|
|
|
@ -30,11 +30,11 @@ fn num_literal(
|
|||
) -> Constraint {
|
||||
let num_var = subs.mk_flex_var();
|
||||
let num_type = Variable(num_var);
|
||||
let expected_literal = ForReason(reason, literal_type, region.clone());
|
||||
let expected_literal = ForReason(reason, literal_type, region);
|
||||
|
||||
And(vec![
|
||||
Eq(num_type.clone(), expected_literal, region.clone()),
|
||||
Eq(num_type, expected, region.clone()),
|
||||
Eq(num_type.clone(), expected_literal, region),
|
||||
Eq(num_type, expected, region),
|
||||
])
|
||||
}
|
||||
|
||||
|
|
775
src/gen/mod.rs
775
src/gen/mod.rs
|
@ -1,73 +1,27 @@
|
|||
//! This is an example of the [Kaleidoscope tutorial](https://llvm.org/docs/tutorial/)
|
||||
//! made in Rust, using Inkwell.
|
||||
//! Currently, all features up to the [7th chapter](https://llvm.org/docs/tutorial/LangImpl07.html)
|
||||
//! are available.
|
||||
//! This example is supposed to be ran as a executable, which launches a REPL.
|
||||
//! The source code is in the following order:
|
||||
//! - Lexer,
|
||||
//! - Parser,
|
||||
//! - Emitter,
|
||||
//! - Program.
|
||||
//!
|
||||
//! Both the `Parser` and the `Emitter` may fail, in which case they would return
|
||||
//! an error represented by `Result<T, &'static str>`, for easier error reporting.
|
||||
|
||||
extern crate inkwell;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
// use std::iter::Peekable;
|
||||
// use std::ops::DerefMut;
|
||||
// use std::str::Chars;
|
||||
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::module::Module;
|
||||
use inkwell::passes::PassManager;
|
||||
use inkwell::types::BasicTypeEnum;
|
||||
use inkwell::types::{BasicType, BasicTypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue};
|
||||
use inkwell::FloatPredicate;
|
||||
|
||||
use can::expr;
|
||||
use can::expr::Expr;
|
||||
use can::procedure::Procedure;
|
||||
use can::symbol::Symbol;
|
||||
use collections::ImMap;
|
||||
use collections::MutMap;
|
||||
use subs::Variable;
|
||||
|
||||
// ======================================================================================
|
||||
// LEXER ================================================================================
|
||||
// ======================================================================================
|
||||
|
||||
/// Represents a primitive syntax token.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token {
|
||||
Binary,
|
||||
Comma,
|
||||
Comment,
|
||||
Def,
|
||||
Else,
|
||||
EOF,
|
||||
Extern,
|
||||
For,
|
||||
Ident(String),
|
||||
If,
|
||||
In,
|
||||
LParen,
|
||||
Number(f64),
|
||||
Op(char),
|
||||
RParen,
|
||||
Then,
|
||||
Unary,
|
||||
Var,
|
||||
}
|
||||
use subs::FlatType::*;
|
||||
use subs::{Content, Subs, Variable};
|
||||
use types;
|
||||
|
||||
/// Defines the prototype (name and parameters) of a function.
|
||||
#[derive(Debug)]
|
||||
pub struct Prototype {
|
||||
pub name: String,
|
||||
pub args: Vec<String>,
|
||||
pub args: Vec<(String, BasicTypeEnum)>,
|
||||
pub ret: BasicTypeEnum,
|
||||
}
|
||||
|
||||
/// Defines a user-defined or external function.
|
||||
|
@ -84,46 +38,8 @@ pub struct Emitter<'a> {
|
|||
pub fpm: &'a PassManager<FunctionValue>,
|
||||
pub module: &'a Module,
|
||||
pub function: &'a Function,
|
||||
|
||||
variables: HashMap<String, PointerValue>,
|
||||
fn_value_opt: Option<FunctionValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Expr {
|
||||
Binary {
|
||||
op: char,
|
||||
left: Box<Expr>,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
|
||||
Call {
|
||||
fn_name: String,
|
||||
args: Vec<Expr>,
|
||||
},
|
||||
|
||||
Conditional {
|
||||
cond: Box<Expr>,
|
||||
consequence: Box<Expr>,
|
||||
alternative: Box<Expr>,
|
||||
},
|
||||
|
||||
For {
|
||||
var_name: String,
|
||||
start: Box<Expr>,
|
||||
end: Box<Expr>,
|
||||
step: Option<Box<Expr>>,
|
||||
body: Box<Expr>,
|
||||
},
|
||||
|
||||
Number(f64),
|
||||
|
||||
Variable(String),
|
||||
|
||||
VarIn {
|
||||
variables: Vec<(String, Option<Expr>)>,
|
||||
body: Box<Expr>,
|
||||
},
|
||||
// variables: HashMap<String, PointerValue>,
|
||||
// fn_value_opt: Option<FunctionValue>,
|
||||
}
|
||||
|
||||
pub struct ModuleBuilder<'a> {
|
||||
|
@ -132,8 +48,8 @@ pub struct ModuleBuilder<'a> {
|
|||
pub fpm: &'a PassManager<FunctionValue>,
|
||||
pub module: &'a Module,
|
||||
pub function: &'a Function,
|
||||
pub procedures: &'a MutMap<Symbol, Procedure>,
|
||||
|
||||
procedures: &'a MutMap<Symbol, Procedure>,
|
||||
fn_value_opt: Option<FunctionValue>,
|
||||
}
|
||||
|
||||
|
@ -161,11 +77,123 @@ impl<'a> ModuleBuilder<'a> {
|
|||
self.module.get_function(name)
|
||||
}
|
||||
|
||||
fn compile_expr(
|
||||
&mut self,
|
||||
expr: &expr::Expr,
|
||||
vars: &mut ImMap<Symbol, PointerValue>,
|
||||
) -> TypedVal {
|
||||
/// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`.
|
||||
pub fn build(
|
||||
context: &'a Context,
|
||||
builder: &'a Builder,
|
||||
pass_manager: &'a PassManager<FunctionValue>,
|
||||
procedures: &'a MutMap<Symbol, Procedure>,
|
||||
module: &'a Module,
|
||||
function: &Function,
|
||||
) -> Result<FunctionValue, &'static str> {
|
||||
let mut compiler = ModuleBuilder {
|
||||
context,
|
||||
builder,
|
||||
fpm: pass_manager,
|
||||
module,
|
||||
function,
|
||||
procedures,
|
||||
fn_value_opt: None,
|
||||
};
|
||||
|
||||
compiler.compile_fn()
|
||||
}
|
||||
|
||||
/// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`.
|
||||
fn compile_prototype(&self, proto: &Prototype) -> Result<FunctionValue, &'static str> {
|
||||
let arg_types = proto
|
||||
.args
|
||||
.iter()
|
||||
.map(|(_, typ)| typ.clone())
|
||||
.collect::<Vec<BasicTypeEnum>>();
|
||||
|
||||
let fn_type = proto.ret.fn_type(&arg_types, false);
|
||||
let fn_val = self.module.add_function(proto.name.as_str(), fn_type, None);
|
||||
|
||||
// set arguments names
|
||||
// for (i, arg) in fn_val.get_param_iter().enumerate() {
|
||||
// arg.set_name(proto.args[i].0.as_str());
|
||||
// }
|
||||
|
||||
// finally return built prototype
|
||||
Ok(fn_val)
|
||||
}
|
||||
|
||||
/// Returns the `FunctionValue` representing the function being compiled.
|
||||
#[inline]
|
||||
fn fn_value(&self) -> FunctionValue {
|
||||
// TODO shouldn't there be like a stack of these? What about functions inside functions?
|
||||
// Also, do we even need this?
|
||||
self.fn_value_opt.unwrap()
|
||||
}
|
||||
|
||||
/// Creates a new stack allocation instruction in the entry block of the function.
|
||||
fn create_entry_block_alloca(&self, name: &str) -> PointerValue {
|
||||
let builder = self.context.create_builder();
|
||||
|
||||
let entry = self.fn_value().get_first_basic_block().unwrap();
|
||||
|
||||
match entry.get_first_instruction() {
|
||||
Some(first_instr) => builder.position_before(&first_instr),
|
||||
None => builder.position_at_end(&entry),
|
||||
}
|
||||
|
||||
builder.build_alloca(self.context.f64_type(), name)
|
||||
}
|
||||
|
||||
/// Compiles the specified `Function` into an LLVM `FunctionValue`.
|
||||
fn compile_fn(&mut self) -> Result<FunctionValue, &'static str> {
|
||||
let proto = &self.function.prototype;
|
||||
let function = self.compile_prototype(proto)?;
|
||||
|
||||
// got external function, returning only compiled prototype
|
||||
if self.function.body.is_none() {
|
||||
return Ok(function);
|
||||
}
|
||||
|
||||
let entry = self.context.append_basic_block(&function, "entry");
|
||||
|
||||
self.builder.position_at_end(&entry);
|
||||
|
||||
// update fn field
|
||||
self.fn_value_opt = Some(function);
|
||||
|
||||
// build variables map
|
||||
let mut vars = ImMap::default(); // TODO with_capacity(proto.args.len())
|
||||
|
||||
for (i, arg) in function.get_param_iter().enumerate() {
|
||||
let arg_name = proto.args[i].0.as_str();
|
||||
let alloca = self.create_entry_block_alloca(arg_name);
|
||||
|
||||
self.builder.build_store(alloca, arg);
|
||||
|
||||
vars.insert(
|
||||
Symbol::from_parts(&[], proto.args[i].0.clone().as_str()),
|
||||
alloca,
|
||||
);
|
||||
}
|
||||
|
||||
// compile body
|
||||
let body = self.compile_expr(self.function.body.as_ref().unwrap(), &mut vars);
|
||||
let basic_val: BasicValueEnum = body.into();
|
||||
|
||||
self.builder.build_return(Some(&basic_val));
|
||||
|
||||
// return the whole thing after verification and optimization
|
||||
if function.verify(true) {
|
||||
self.fpm.run_on(&function);
|
||||
|
||||
Ok(function)
|
||||
} else {
|
||||
unsafe {
|
||||
function.delete();
|
||||
}
|
||||
|
||||
Err("Invalid generated function.")
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_expr(&mut self, expr: &Expr, vars: &mut ImMap<Symbol, PointerValue>) -> TypedVal {
|
||||
use self::TypedVal::*;
|
||||
use can::expr::Expr::*;
|
||||
|
||||
|
@ -191,7 +219,7 @@ impl<'a> ModuleBuilder<'a> {
|
|||
}
|
||||
|
||||
Defs(_, _, _) => panic!("TODO gen defs"),
|
||||
CallByName(symbol, loc_args, _) => {
|
||||
CallByName(ref symbol, ref loc_args, _) => {
|
||||
let func = self
|
||||
.get_function(&*(symbol.clone()).into_boxed_str())
|
||||
.unwrap_or_else(|| {
|
||||
|
@ -214,500 +242,73 @@ impl<'a> ModuleBuilder<'a> {
|
|||
.unwrap_or_else(|| panic!("Roc compiler error: Invalid call.")),
|
||||
)
|
||||
}
|
||||
Str(_) | List(_, _) | Case(_, _, _) | Record(_, _) | EmptyRecord | RuntimeError(_) => {
|
||||
Str(_)
|
||||
| CallPointer(_, _, _)
|
||||
| List(_, _)
|
||||
| Case(_, _, _)
|
||||
| Record(_, _)
|
||||
| EmptyRecord
|
||||
| RuntimeError(_) => {
|
||||
panic!("TODO compile_expr for {:?}", expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Emitter<'a> {
|
||||
/// Gets a defined function given its name.
|
||||
#[inline]
|
||||
fn get_function(&self, name: &str) -> Option<FunctionValue> {
|
||||
self.module.get_function(name)
|
||||
}
|
||||
|
||||
/// Returns the `FunctionValue` representing the function being compiled.
|
||||
#[inline]
|
||||
fn fn_value(&self) -> FunctionValue {
|
||||
// TODO shouldn't there be like a stack of these? What about functions inside functions?
|
||||
// Also, do we even need this?
|
||||
self.fn_value_opt.unwrap()
|
||||
}
|
||||
|
||||
/// Creates a new stack allocation instruction in the entry block of the function.
|
||||
fn create_entry_block_alloca(&self, name: &str) -> PointerValue {
|
||||
let builder = self.context.create_builder();
|
||||
|
||||
let entry = self.fn_value().get_first_basic_block().unwrap();
|
||||
|
||||
match entry.get_first_instruction() {
|
||||
Some(first_instr) => builder.position_before(&first_instr),
|
||||
None => builder.position_at_end(&entry),
|
||||
}
|
||||
|
||||
builder.build_alloca(self.context.f64_type(), name)
|
||||
}
|
||||
|
||||
/// Compiles the specified `Expr` into an LLVM `FloatValue`.
|
||||
fn compile_expr(&mut self, expr: &Expr) -> Result<FloatValue, &'static str> {
|
||||
match *expr {
|
||||
// Expr::IntLiteral(num) => Ok(self.context.i64_type().const_int(num as u64, false)),
|
||||
Expr::Number(num) => Ok(self.context.f64_type().const_float(num)),
|
||||
|
||||
Expr::Variable(ref name) => match self.variables.get(name.as_str()) {
|
||||
Some(var) => Ok(self
|
||||
.builder
|
||||
.build_load(*var, name.as_str())
|
||||
.into_float_value()),
|
||||
None => Err("Could not find a matching variable."),
|
||||
},
|
||||
|
||||
Expr::VarIn {
|
||||
ref variables,
|
||||
ref body,
|
||||
} => {
|
||||
let mut old_bindings = Vec::new();
|
||||
|
||||
for &(ref var_name, ref initializer) in variables {
|
||||
let var_name = var_name.as_str();
|
||||
|
||||
let initial_val = match *initializer {
|
||||
Some(ref init) => self.compile_expr(init)?,
|
||||
None => self.context.f64_type().const_float(0.),
|
||||
};
|
||||
|
||||
let alloca = self.create_entry_block_alloca(var_name);
|
||||
|
||||
self.builder.build_store(alloca, initial_val);
|
||||
|
||||
if let Some(old_binding) = self.variables.remove(var_name) {
|
||||
old_bindings.push(old_binding);
|
||||
}
|
||||
|
||||
self.variables.insert(var_name.to_string(), alloca);
|
||||
}
|
||||
|
||||
let body = self.compile_expr(body)?;
|
||||
|
||||
for binding in old_bindings {
|
||||
self.variables
|
||||
.insert(binding.get_name().to_str().unwrap().to_string(), binding);
|
||||
}
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
Expr::Binary {
|
||||
op,
|
||||
ref left,
|
||||
ref right,
|
||||
} => {
|
||||
if op == '=' {
|
||||
// handle assignement
|
||||
let var_name = match *left.borrow() {
|
||||
Expr::Variable(ref var_name) => var_name,
|
||||
_ => {
|
||||
return Err("Expected variable as left-hand operator of assignement.");
|
||||
}
|
||||
};
|
||||
|
||||
let var_val = self.compile_expr(right)?;
|
||||
let var = self
|
||||
.variables
|
||||
.get(var_name.as_str())
|
||||
.ok_or("Undefined variable.")?;
|
||||
|
||||
self.builder.build_store(*var, var_val);
|
||||
|
||||
Ok(var_val)
|
||||
} else {
|
||||
let lhs = self.compile_expr(left)?;
|
||||
let rhs = self.compile_expr(right)?;
|
||||
|
||||
match op {
|
||||
'+' => Ok(self.builder.build_float_add(lhs, rhs, "tmpadd")),
|
||||
'-' => Ok(self.builder.build_float_sub(lhs, rhs, "tmpsub")),
|
||||
'*' => Ok(self.builder.build_float_mul(lhs, rhs, "tmpmul")),
|
||||
'/' => Ok(self.builder.build_float_div(lhs, rhs, "tmpdiv")),
|
||||
'<' => Ok({
|
||||
let cmp = self.builder.build_float_compare(
|
||||
FloatPredicate::ULT,
|
||||
lhs,
|
||||
rhs,
|
||||
"tmpcmp",
|
||||
);
|
||||
|
||||
self.builder.build_unsigned_int_to_float(
|
||||
cmp,
|
||||
self.context.f64_type(),
|
||||
"tmpbool",
|
||||
)
|
||||
}),
|
||||
'>' => Ok({
|
||||
let cmp = self.builder.build_float_compare(
|
||||
FloatPredicate::ULT,
|
||||
rhs,
|
||||
lhs,
|
||||
"tmpcmp",
|
||||
);
|
||||
|
||||
self.builder.build_unsigned_int_to_float(
|
||||
cmp,
|
||||
self.context.f64_type(),
|
||||
"tmpbool",
|
||||
)
|
||||
}),
|
||||
|
||||
custom => {
|
||||
let mut name = String::from("binary");
|
||||
|
||||
name.push(custom);
|
||||
|
||||
match self.get_function(name.as_str()) {
|
||||
Some(fun) => {
|
||||
match self
|
||||
.builder
|
||||
.build_call(fun, &[lhs.into(), rhs.into()], "tmpbin")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
{
|
||||
Some(value) => Ok(value.into_float_value()),
|
||||
None => Err("Invalid call produced."),
|
||||
}
|
||||
}
|
||||
|
||||
None => Err("Undefined binary operator."),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Call {
|
||||
ref fn_name,
|
||||
ref args,
|
||||
} => match self.get_function(fn_name.as_str()) {
|
||||
Some(fun) => {
|
||||
let mut compiled_args = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args {
|
||||
compiled_args.push(self.compile_expr(arg)?);
|
||||
}
|
||||
|
||||
let argsv: Vec<BasicValueEnum> = compiled_args
|
||||
.iter()
|
||||
.by_ref()
|
||||
.map(|&val| val.into())
|
||||
.collect();
|
||||
|
||||
match self
|
||||
.builder
|
||||
.build_call(fun, argsv.as_slice(), "tmp")
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
{
|
||||
Some(value) => Ok(value.into_float_value()),
|
||||
None => Err("Invalid call produced."),
|
||||
}
|
||||
}
|
||||
None => Err("Unknown function."),
|
||||
},
|
||||
|
||||
Expr::Conditional {
|
||||
ref cond,
|
||||
ref consequence,
|
||||
ref alternative,
|
||||
} => {
|
||||
let parent = self.fn_value();
|
||||
let zero_const = self.context.f64_type().const_float(0.0);
|
||||
|
||||
// create condition by comparing without 0.0 and returning an int
|
||||
let cond = self.compile_expr(cond)?;
|
||||
let cond = self.builder.build_float_compare(
|
||||
FloatPredicate::ONE,
|
||||
cond,
|
||||
zero_const,
|
||||
"ifcond",
|
||||
);
|
||||
|
||||
// build branch
|
||||
let then_bb = self.context.append_basic_block(&parent, "then");
|
||||
let else_bb = self.context.append_basic_block(&parent, "else");
|
||||
let cont_bb = self.context.append_basic_block(&parent, "ifcont");
|
||||
|
||||
self.builder
|
||||
.build_conditional_branch(cond, &then_bb, &else_bb);
|
||||
|
||||
// build then block
|
||||
self.builder.position_at_end(&then_bb);
|
||||
let then_val = self.compile_expr(consequence)?;
|
||||
self.builder.build_unconditional_branch(&cont_bb);
|
||||
|
||||
let then_bb = self.builder.get_insert_block().unwrap();
|
||||
|
||||
// build else block
|
||||
self.builder.position_at_end(&else_bb);
|
||||
let else_val = self.compile_expr(alternative)?;
|
||||
self.builder.build_unconditional_branch(&cont_bb);
|
||||
|
||||
let else_bb = self.builder.get_insert_block().unwrap();
|
||||
|
||||
// emit merge block
|
||||
self.builder.position_at_end(&cont_bb);
|
||||
|
||||
let phi = self.builder.build_phi(self.context.f64_type(), "iftmp");
|
||||
|
||||
phi.add_incoming(&[(&then_val, &then_bb), (&else_val, &else_bb)]);
|
||||
|
||||
Ok(phi.as_basic_value().into_float_value())
|
||||
}
|
||||
|
||||
Expr::For {
|
||||
ref var_name,
|
||||
ref start,
|
||||
ref end,
|
||||
ref step,
|
||||
ref body,
|
||||
} => {
|
||||
let parent = self.fn_value();
|
||||
|
||||
let start_alloca = self.create_entry_block_alloca(var_name);
|
||||
let start = self.compile_expr(start)?;
|
||||
|
||||
self.builder.build_store(start_alloca, start);
|
||||
|
||||
// go from current block to loop block
|
||||
let loop_bb = self.context.append_basic_block(&parent, "loop");
|
||||
|
||||
self.builder.build_unconditional_branch(&loop_bb);
|
||||
self.builder.position_at_end(&loop_bb);
|
||||
|
||||
let old_val = self.variables.remove(var_name.as_str());
|
||||
|
||||
self.variables.insert(var_name.to_owned(), start_alloca);
|
||||
|
||||
// emit body
|
||||
self.compile_expr(body)?;
|
||||
|
||||
// emit step
|
||||
let step = match *step {
|
||||
Some(ref step) => self.compile_expr(step)?,
|
||||
None => self.context.f64_type().const_float(1.0),
|
||||
};
|
||||
|
||||
// compile end condition
|
||||
let end_cond = self.compile_expr(end)?;
|
||||
|
||||
let curr_var = self.builder.build_load(start_alloca, var_name);
|
||||
let next_var =
|
||||
self.builder
|
||||
.build_float_add(curr_var.into_float_value(), step, "nextvar");
|
||||
|
||||
self.builder.build_store(start_alloca, next_var);
|
||||
|
||||
let end_cond = self.builder.build_float_compare(
|
||||
FloatPredicate::ONE,
|
||||
end_cond,
|
||||
self.context.f64_type().const_float(0.0),
|
||||
"loopcond",
|
||||
);
|
||||
let after_bb = self.context.append_basic_block(&parent, "afterloop");
|
||||
|
||||
self.builder
|
||||
.build_conditional_branch(end_cond, &loop_bb, &after_bb);
|
||||
self.builder.position_at_end(&after_bb);
|
||||
|
||||
self.variables.remove(var_name);
|
||||
|
||||
if let Some(val) = old_val {
|
||||
self.variables.insert(var_name.to_owned(), val);
|
||||
}
|
||||
|
||||
Ok(self.context.f64_type().const_float(0.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`.
|
||||
fn compile_prototype(&self, proto: &Prototype) -> Result<FunctionValue, &'static str> {
|
||||
let ret_type = self.context.f64_type();
|
||||
let args_types = std::iter::repeat(ret_type)
|
||||
.take(proto.args.len())
|
||||
.map(|f| f.into())
|
||||
.collect::<Vec<BasicTypeEnum>>();
|
||||
let args_types = args_types.as_slice();
|
||||
|
||||
let fn_type = self.context.f64_type().fn_type(args_types, false);
|
||||
let fn_val = self.module.add_function(proto.name.as_str(), fn_type, None);
|
||||
|
||||
// set arguments names
|
||||
for (i, arg) in fn_val.get_param_iter().enumerate() {
|
||||
arg.into_float_value().set_name(proto.args[i].as_str());
|
||||
}
|
||||
|
||||
// finally return built prototype
|
||||
Ok(fn_val)
|
||||
}
|
||||
|
||||
/// Compiles the specified `Function` into an LLVM `FunctionValue`.
|
||||
fn compile_fn(&mut self) -> Result<FunctionValue, &'static str> {
|
||||
let proto = &self.function.prototype;
|
||||
let function = self.compile_prototype(proto)?;
|
||||
|
||||
// got external function, returning only compiled prototype
|
||||
if self.function.body.is_none() {
|
||||
return Ok(function);
|
||||
}
|
||||
|
||||
let entry = self.context.append_basic_block(&function, "entry");
|
||||
|
||||
self.builder.position_at_end(&entry);
|
||||
|
||||
// update fn field
|
||||
self.fn_value_opt = Some(function);
|
||||
|
||||
// build variables map
|
||||
self.variables.reserve(proto.args.len());
|
||||
|
||||
for (i, arg) in function.get_param_iter().enumerate() {
|
||||
let arg_name = proto.args[i].as_str();
|
||||
let alloca = self.create_entry_block_alloca(arg_name);
|
||||
|
||||
self.builder.build_store(alloca, arg);
|
||||
|
||||
self.variables.insert(proto.args[i].clone(), alloca);
|
||||
}
|
||||
|
||||
// compile body
|
||||
let body = self.compile_expr(self.function.body.as_ref().unwrap())?;
|
||||
|
||||
self.builder.build_return(Some(&body));
|
||||
|
||||
// return the whole thing after verification and optimization
|
||||
if function.verify(true) {
|
||||
self.fpm.run_on(&function);
|
||||
|
||||
Ok(function)
|
||||
} else {
|
||||
unsafe {
|
||||
function.delete();
|
||||
}
|
||||
|
||||
Err("Invalid generated function.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`.
|
||||
pub fn compile(
|
||||
context: &'a Context,
|
||||
builder: &'a Builder,
|
||||
pass_manager: &'a PassManager<FunctionValue>,
|
||||
module: &'a Module,
|
||||
function: &Function,
|
||||
) -> Result<FunctionValue, &'static str> {
|
||||
let mut compiler = Emitter {
|
||||
context: context,
|
||||
builder: builder,
|
||||
fpm: pass_manager,
|
||||
module: module,
|
||||
function: function,
|
||||
fn_value_opt: None,
|
||||
variables: HashMap::new(),
|
||||
};
|
||||
|
||||
compiler.compile_fn()
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================================
|
||||
// PROGRAM ==============================================================================
|
||||
// ======================================================================================
|
||||
|
||||
// macro used to print & flush without printing a new line
|
||||
macro_rules! print_flush {
|
||||
( $( $x:expr ),* ) => {
|
||||
print!( $($x, )* );
|
||||
|
||||
std::io::stdout().flush().expect("Could not flush to standard output.");
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn putchard(x: f64) -> f64 {
|
||||
print_flush!("{}", x as u8 as char);
|
||||
x
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn printd(x: f64) -> f64 {
|
||||
println!("{}", x);
|
||||
x
|
||||
}
|
||||
|
||||
// Adding the functions above to a global array,
|
||||
// so Rust compiler won't remove them.
|
||||
#[used]
|
||||
static EXTERNAL_FNS: [extern "C" fn(f64) -> f64; 2] = [putchard, printd];
|
||||
|
||||
#[test]
|
||||
fn gen() {
|
||||
use inkwell::OptimizationLevel;
|
||||
// use self::inkwell::support::add_symbol;
|
||||
|
||||
let context = Context::create();
|
||||
let module = context.create_module("repl");
|
||||
let builder = context.create_builder();
|
||||
|
||||
// Create FPM
|
||||
let fpm = PassManager::create(&module);
|
||||
|
||||
fpm.add_instruction_combining_pass();
|
||||
fpm.add_reassociate_pass();
|
||||
fpm.add_gvn_pass();
|
||||
fpm.add_cfg_simplification_pass();
|
||||
fpm.add_basic_alias_analysis_pass();
|
||||
fpm.add_promote_memory_to_register_pass();
|
||||
fpm.add_instruction_combining_pass();
|
||||
fpm.add_reassociate_pass();
|
||||
|
||||
fpm.initialize();
|
||||
|
||||
// make module
|
||||
let module = context.create_module("tmp");
|
||||
|
||||
let name = "main".to_string();
|
||||
let prototype = Prototype {
|
||||
pub fn content_to_basic_type(
|
||||
content: Content,
|
||||
subs: &mut Subs,
|
||||
context: &Context,
|
||||
) -> Result<BasicTypeEnum, String> {
|
||||
match content {
|
||||
Content::Structure(flat_type) => match flat_type {
|
||||
Apply {
|
||||
module_name,
|
||||
name,
|
||||
args: Vec::new(),
|
||||
};
|
||||
let function = Function {
|
||||
prototype,
|
||||
body: Some(Expr::Number(12345.0)),
|
||||
is_anon: false,
|
||||
};
|
||||
|
||||
// make main(), a function which returns an f64
|
||||
Emitter::compile(&context, &builder, &fpm, &module, &function).expect("Error compiling main");
|
||||
|
||||
// let fn_type = context.f64_type().fn_type(&Vec::new(), false);
|
||||
// let function = module.add_function(proto.name.as_str(), fn_type, None);
|
||||
|
||||
// make execution engine
|
||||
let ee = module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.unwrap();
|
||||
|
||||
let maybe_fn = unsafe { ee.get_function::<unsafe extern "C" fn() -> f64>("main") };
|
||||
let compiled_fn = match maybe_fn {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
panic!("!> Error during execution: {:?}", err);
|
||||
args,
|
||||
} => {
|
||||
if &*module_name == types::MOD_NUM && &*name == types::TYPE_NUM {
|
||||
num_to_basic_type(subs.get(*args.iter().next().unwrap()).content, context)
|
||||
} else {
|
||||
panic!(
|
||||
"TODO handle content_to_basic_type for flat_type {}.{} with args {:?}",
|
||||
module_name, name, args
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
assert_eq!(12345.0, compiled_fn.call());
|
||||
}
|
||||
other => panic!("TODO handle content_to_basic_type for {:?}", other),
|
||||
},
|
||||
other => Err(format!("Cannot convert {:?} to BasicTypeEnum", other)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_to_basic_type(content: Content, context: &Context) -> Result<BasicTypeEnum, String> {
|
||||
match content {
|
||||
Content::Structure(flat_type) => match flat_type {
|
||||
Apply {
|
||||
module_name,
|
||||
name,
|
||||
args,
|
||||
} => {
|
||||
if &*module_name == types::MOD_FLOAT && &*name == types::TYPE_FLOATINGPOINT && args.is_empty() {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(BasicTypeEnum::FloatType(context.f64_type()))
|
||||
} else if &*module_name == types::MOD_INT && &*name == types::TYPE_INTEGER && args.is_empty() {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(BasicTypeEnum::IntType(context.i64_type()))
|
||||
} else {
|
||||
Err(format!("Unrecognized numeric type: Num {}.{} with args {:?}", module_name, name, args))
|
||||
}
|
||||
}
|
||||
other => panic!(
|
||||
"TODO handle content_to_basic_type (branch 0) for {:?} which is NESTED inside Num.Num",
|
||||
other
|
||||
),
|
||||
},
|
||||
|
||||
other => panic!(
|
||||
"TODO handle content_to_basic_type (branch 1) for {:?} which is NESTED inside Num.Num",
|
||||
other
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,9 @@ impl Operator {
|
|||
LeftAssociative
|
||||
}
|
||||
And | Or | Caret => RightAssociative,
|
||||
Equals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => NonAssociative,
|
||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
|
||||
NonAssociative
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +82,7 @@ impl Operator {
|
|||
Caret => 7,
|
||||
Star | Slash | DoubleSlash | DoublePercent | Percent => 6,
|
||||
Plus | Minus => 5,
|
||||
Equals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
|
||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
|
||||
And => 3,
|
||||
Or => 2,
|
||||
Pizza => 1,
|
||||
|
|
|
@ -45,8 +45,8 @@ pub enum Expr<'a> {
|
|||
AccessorFunction(&'a str),
|
||||
|
||||
// Collection Literals
|
||||
List(Vec<'a, Loc<Expr<'a>>>),
|
||||
Record(Vec<'a, Loc<Expr<'a>>>),
|
||||
List(Vec<'a, &'a Loc<Expr<'a>>>),
|
||||
Record(Vec<'a, &'a Loc<Expr<'a>>>),
|
||||
AssignField(Loc<&'a str>, &'a Loc<Expr<'a>>),
|
||||
|
||||
// Lookups
|
||||
|
@ -54,19 +54,17 @@ pub enum Expr<'a> {
|
|||
Variant(&'a [&'a str], &'a str),
|
||||
|
||||
// Pattern Matching
|
||||
Closure(&'a (Vec<'a, Loc<Pattern<'a>>>, Loc<Expr<'a>>)),
|
||||
Closure(&'a Vec<'a, Loc<Pattern<'a>>>, &'a Loc<Expr<'a>>),
|
||||
/// Multiple defs in a row
|
||||
Defs(
|
||||
&'a (
|
||||
Vec<'a, (&'a [CommentOrNewline<'a>], Def<'a>)>,
|
||||
Loc<Expr<'a>>,
|
||||
),
|
||||
&'a Loc<Expr<'a>>,
|
||||
),
|
||||
|
||||
// Application
|
||||
/// To apply by name, do Apply(Var(...), ...)
|
||||
/// To apply a variant by name, do Apply(Variant(...), ...)
|
||||
Apply(&'a (Loc<Expr<'a>>, Vec<'a, Loc<Expr<'a>>>, CalledVia)),
|
||||
Apply(&'a Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>, CalledVia),
|
||||
Operator(&'a (Loc<Expr<'a>>, Loc<Operator>, Loc<Expr<'a>>)),
|
||||
|
||||
// Conditionals
|
||||
|
@ -384,7 +382,7 @@ pub fn format<'a>(
|
|||
|
||||
buf.push_str(name);
|
||||
}
|
||||
Apply((loc_expr, loc_args, _)) => {
|
||||
Apply(loc_expr, loc_args, _) => {
|
||||
if apply_needs_parens {
|
||||
buf.push('(');
|
||||
}
|
||||
|
@ -434,10 +432,10 @@ pub fn format<'a>(
|
|||
|
||||
buf.push('}');
|
||||
}
|
||||
Closure((loc_patterns, loc_ret)) => {
|
||||
Closure(loc_patterns, loc_ret) => {
|
||||
buf.push('\\');
|
||||
|
||||
for loc_pattern in loc_patterns {
|
||||
for loc_pattern in loc_patterns.iter() {
|
||||
buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
|
||||
|
||||
buf.push(' ');
|
||||
|
@ -447,7 +445,7 @@ pub fn format<'a>(
|
|||
|
||||
buf.push_str(&format(arena, &loc_ret.value, indent, false));
|
||||
}
|
||||
Defs((defs, ret)) => {
|
||||
Defs(defs, ret) => {
|
||||
// The first def is actually at the end of the list, because
|
||||
// it gets added there with .push() for efficiency. (The order of parsed defs doesn't
|
||||
// matter because canonicalization sorts them anyway.) The other
|
||||
|
|
|
@ -160,13 +160,25 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
|
|||
let (loc_expr, opt_extras) = loc_expr_with_extras.value;
|
||||
|
||||
match opt_extras {
|
||||
Some(Either::First(loc_args)) => Ok((
|
||||
Some(Either::First(loc_args)) => {
|
||||
let mut allocated_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
|
||||
for loc_arg in loc_args {
|
||||
allocated_args.push(&*arena.alloc(loc_arg));
|
||||
}
|
||||
|
||||
Ok((
|
||||
Located {
|
||||
region: loc_expr_with_extras.region,
|
||||
value: Expr::Apply(arena.alloc((loc_expr, loc_args, CalledVia::Space))),
|
||||
value: Expr::Apply(
|
||||
arena.alloc(loc_expr),
|
||||
allocated_args,
|
||||
CalledVia::Space,
|
||||
),
|
||||
},
|
||||
state,
|
||||
)),
|
||||
))
|
||||
}
|
||||
// '=' after optional spaces
|
||||
Some(Either::Second(Either::Second((spaces_before_equals, equals_indent)))) => {
|
||||
let region = loc_expr.region;
|
||||
|
@ -185,7 +197,7 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
|
|||
};
|
||||
|
||||
let loc_first_pattern = Located {
|
||||
region: region.clone(),
|
||||
region: region,
|
||||
value,
|
||||
};
|
||||
|
||||
|
@ -231,15 +243,15 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
|||
}
|
||||
}
|
||||
Expr::Variant(module_parts, value) => Ok(Pattern::Variant(module_parts, value)),
|
||||
Expr::Apply((loc_val, loc_args, _)) => {
|
||||
let region = loc_val.region.clone();
|
||||
Expr::Apply(loc_val, loc_args, _) => {
|
||||
let region = loc_val.region;
|
||||
let value = expr_to_pattern(arena, &loc_val.value)?;
|
||||
let val_pattern = arena.alloc(Located { region, value });
|
||||
|
||||
let mut arg_patterns = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
|
||||
for loc_arg in loc_args {
|
||||
let region = loc_arg.region.clone();
|
||||
let region = loc_arg.region;
|
||||
let value = expr_to_pattern(arena, &loc_arg.value)?;
|
||||
|
||||
arg_patterns.push(Located { region, value });
|
||||
|
@ -263,7 +275,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
|||
let mut loc_patterns = Vec::with_capacity_in(loc_exprs.len(), arena);
|
||||
|
||||
for loc_expr in loc_exprs {
|
||||
let region = loc_expr.region.clone();
|
||||
let region = loc_expr.region;
|
||||
let value = expr_to_pattern(arena, &loc_expr.value)?;
|
||||
|
||||
loc_patterns.push(Located { region, value });
|
||||
|
@ -285,10 +297,10 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
|||
| Expr::AccessorFunction(_)
|
||||
| Expr::Field(_, _)
|
||||
| Expr::List(_)
|
||||
| Expr::Closure(_)
|
||||
| Expr::Closure(_, _)
|
||||
| Expr::Operator(_)
|
||||
| Expr::AssignField(_, _)
|
||||
| Expr::Defs(_)
|
||||
| Expr::Defs(_, _)
|
||||
| Expr::If(_)
|
||||
| Expr::Case(_, _)
|
||||
| Expr::MalformedClosure
|
||||
|
@ -430,7 +442,7 @@ fn parse_def_expr<'a>(
|
|||
// their regions will ever be visible to the user.)
|
||||
defs.push((&[], first_def));
|
||||
|
||||
Ok((Expr::Defs(arena.alloc((defs, loc_ret))), state))
|
||||
Ok((Expr::Defs(defs, arena.alloc(loc_ret)), state))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -507,7 +519,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
),
|
||||
|arena, opt_contents| match opt_contents {
|
||||
None => Expr::MalformedClosure,
|
||||
Some((params, loc_body)) => Expr::Closure(arena.alloc((params, loc_body))),
|
||||
Some((params, loc_body)) => Expr::Closure(arena.alloc(params), arena.alloc(loc_body)),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -759,8 +771,14 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
value: ident_to_expr(loc_ident.value),
|
||||
};
|
||||
|
||||
let mut allocated_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
|
||||
for loc_arg in loc_args {
|
||||
allocated_args.push(&*arena.alloc(loc_arg));
|
||||
}
|
||||
|
||||
Ok((
|
||||
Expr::Apply(arena.alloc((loc_expr, loc_args, CalledVia::Space))),
|
||||
Expr::Apply(arena.alloc(loc_expr), allocated_args, CalledVia::Space),
|
||||
state,
|
||||
))
|
||||
}
|
||||
|
@ -880,7 +898,18 @@ pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
min_indent,
|
||||
);
|
||||
|
||||
attempt(Attempting::List, map(elems, Expr::List))
|
||||
attempt(
|
||||
Attempting::List,
|
||||
map_with_arena(elems, |arena, parsed_elems| {
|
||||
let mut allocated = Vec::with_capacity_in(parsed_elems.len(), arena);
|
||||
|
||||
for parsed_elem in parsed_elems {
|
||||
allocated.push(&*arena.alloc(parsed_elem));
|
||||
}
|
||||
|
||||
Expr::List(allocated)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
|
@ -912,7 +941,15 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
optional(and(space0(min_indent), equals_with_indent())),
|
||||
),
|
||||
move |arena, state, (loc_field_exprs, opt_def)| match opt_def {
|
||||
None => Ok((Expr::Record(loc_field_exprs.value), state)),
|
||||
None => {
|
||||
let mut allocated = Vec::with_capacity_in(loc_field_exprs.value.len(), arena);
|
||||
|
||||
for loc_field_expr in loc_field_exprs.value {
|
||||
allocated.push(&*arena.alloc(loc_field_expr));
|
||||
}
|
||||
|
||||
Ok((Expr::Record(allocated), state))
|
||||
}
|
||||
Some((spaces_before_equals, equals_indent)) => {
|
||||
let region = loc_field_exprs.region;
|
||||
let field_exprs = loc_field_exprs.value;
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
|||
/// TODO replace Located with this
|
||||
pub type Loc<T> = Located<T>;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Region {
|
||||
pub start_line: u32,
|
||||
pub end_line: u32,
|
||||
|
@ -81,7 +81,7 @@ impl<T> Located<T> {
|
|||
impl<T> Located<T> {
|
||||
pub fn with_value<U>(&self, value: U) -> Located<U> {
|
||||
Located {
|
||||
region: self.region.clone(),
|
||||
region: self.region,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ impl<T> Located<T> {
|
|||
F: (FnOnce(&T) -> U),
|
||||
{
|
||||
Located {
|
||||
region: self.region.clone(),
|
||||
region: self.region,
|
||||
value: transform(&self.value),
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ where
|
|||
T: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let region = self.region.clone();
|
||||
let region = self.region;
|
||||
|
||||
if region.start_line == 0
|
||||
&& region.start_col == 0
|
||||
|
|
|
@ -17,6 +17,8 @@ pub const MOD_NUM: &'static str = "Num";
|
|||
pub const MOD_DEFAULT: &'static str = "Default";
|
||||
|
||||
pub const TYPE_NUM: &'static str = "Num";
|
||||
pub const TYPE_INTEGER: &'static str = "Integer";
|
||||
pub const TYPE_FLOATINGPOINT: &'static str = "FloatingPoint";
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum Type {
|
||||
|
|
117
tests/test_gen.rs
Normal file
117
tests/test_gen.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
extern crate bumpalo;
|
||||
extern crate inkwell;
|
||||
extern crate roc;
|
||||
|
||||
mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_gen {
|
||||
use helpers::can_expr;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::ExecutionEngine;
|
||||
use inkwell::passes::PassManager;
|
||||
use roc::gen::{content_to_basic_type, Function, ModuleBuilder, Prototype};
|
||||
use roc::infer::infer_expr;
|
||||
|
||||
// HELPERS
|
||||
|
||||
fn gen_engine(src: &str) -> ExecutionEngine {
|
||||
use inkwell::OptimizationLevel;
|
||||
// use self::inkwell::support::add_symbol;
|
||||
|
||||
let (expr, output, _problems, procedures, mut subs, variable) = can_expr(src);
|
||||
|
||||
let content = infer_expr(
|
||||
&mut subs,
|
||||
procedures.clone(), /* TODO shouldn't have to clone this... */
|
||||
&output.constraint,
|
||||
variable,
|
||||
);
|
||||
|
||||
let context = Context::create();
|
||||
let module = context.create_module("repl");
|
||||
let builder = context.create_builder();
|
||||
|
||||
// Create FPM
|
||||
let fpm = PassManager::create(&module);
|
||||
|
||||
fpm.add_instruction_combining_pass();
|
||||
fpm.add_reassociate_pass();
|
||||
fpm.add_gvn_pass();
|
||||
fpm.add_cfg_simplification_pass();
|
||||
fpm.add_basic_alias_analysis_pass();
|
||||
fpm.add_promote_memory_to_register_pass();
|
||||
fpm.add_instruction_combining_pass();
|
||||
fpm.add_reassociate_pass();
|
||||
|
||||
fpm.initialize();
|
||||
|
||||
// make module
|
||||
let module = context.create_module("tmp");
|
||||
|
||||
let name = "main".to_string();
|
||||
let prototype = Prototype {
|
||||
name,
|
||||
args: Vec::new(),
|
||||
ret: content_to_basic_type(content, &mut subs, &context).unwrap_or_else(|reason| {
|
||||
panic!(
|
||||
"content_to_basic_type failed during test because: {}",
|
||||
reason
|
||||
)
|
||||
}),
|
||||
};
|
||||
let function = Function {
|
||||
prototype,
|
||||
body: Some(expr),
|
||||
is_anon: false,
|
||||
};
|
||||
|
||||
// make main(), a function which returns an f64
|
||||
ModuleBuilder::build(&context, &builder, &fpm, &procedures, &module, &function)
|
||||
.expect("Error compiling main");
|
||||
|
||||
// make execution engine
|
||||
module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gen_float() {
|
||||
let ee = gen_engine("12345.0");
|
||||
|
||||
let maybe_fn = unsafe { ee.get_function::<unsafe extern "C" fn() -> f64>("main") };
|
||||
let compiled_fn = match maybe_fn {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
panic!("!> Error during execution: {:?}", err);
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
assert_eq!(12345.0, compiled_fn.call());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gen_int() {
|
||||
let ee = gen_engine(indoc!(r#"321"#));
|
||||
|
||||
let maybe_fn = unsafe { ee.get_function::<unsafe extern "C" fn() -> i64>("main") };
|
||||
let compiled_fn = match maybe_fn {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
panic!("!> Error during execution: {:?}", err);
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
assert_eq!(321, compiled_fn.call());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ mod test_parse {
|
|||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::{self, Bump};
|
||||
use helpers::parse_with;
|
||||
use roc::operator::CalledVia;
|
||||
use roc::operator::Operator::*;
|
||||
use roc::parse::ast::CommentOrNewline::*;
|
||||
use roc::parse::ast::Expr::{self, *};
|
||||
|
@ -480,7 +481,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn packed_singleton_list() {
|
||||
let arena = Bump::new();
|
||||
let elems = bumpalo::vec![in &arena; Located::new(0, 0, 1, 2, Int("1"))];
|
||||
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 1, 2, Int("1")))];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[1]");
|
||||
|
||||
|
@ -490,7 +491,7 @@ mod test_parse {
|
|||
#[test]
|
||||
fn spaced_singleton_list() {
|
||||
let arena = Bump::new();
|
||||
let elems = bumpalo::vec![in &arena; Located::new(0, 0, 2, 3, Int("1"))];
|
||||
let elems = bumpalo::vec![in &arena; &*arena.alloc(Located::new(0, 0, 2, 3, Int("1")))];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[ 1 ]");
|
||||
|
||||
|
@ -555,10 +556,13 @@ mod test_parse {
|
|||
fn basic_apply() {
|
||||
let arena = Bump::new();
|
||||
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
||||
let arg = Located::new(0, 0, 5, 6, Int("1"));
|
||||
let args = bumpalo::vec![in &arena; arg];
|
||||
let tuple = arena.alloc((Located::new(0, 0, 0, 4, Var(module_parts, "whee")), args));
|
||||
let expected = Expr::Apply(tuple);
|
||||
let arg = arena.alloc(Located::new(0, 0, 5, 6, Int("1")));
|
||||
let args = bumpalo::vec![in &arena; &*arg];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 0, 4, Var(module_parts, "whee"))),
|
||||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "whee 1");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -568,11 +572,14 @@ mod test_parse {
|
|||
fn apply_two_args() {
|
||||
let arena = Bump::new();
|
||||
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
||||
let arg1 = Located::new(0, 0, 6, 8, Int("12"));
|
||||
let arg2 = Located::new(0, 0, 10, 12, Int("34"));
|
||||
let args = bumpalo::vec![in &arena; arg1, arg2];
|
||||
let tuple = arena.alloc((Located::new(0, 0, 0, 4, Var(module_parts, "whee")), args));
|
||||
let expected = Expr::Apply(tuple);
|
||||
let arg1 = arena.alloc(Located::new(0, 0, 6, 8, Int("12")));
|
||||
let arg2 = arena.alloc(Located::new(0, 0, 10, 12, Int("34")));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 0, 4, Var(module_parts, "whee"))),
|
||||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "whee 12 34");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -582,12 +589,15 @@ mod test_parse {
|
|||
fn apply_three_args() {
|
||||
let arena = Bump::new();
|
||||
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
||||
let arg1 = Located::new(0, 0, 2, 3, Var(module_parts, "b"));
|
||||
let arg2 = Located::new(0, 0, 4, 5, Var(module_parts, "c"));
|
||||
let arg3 = Located::new(0, 0, 6, 7, Var(module_parts, "d"));
|
||||
let args = bumpalo::vec![in &arena; arg1, arg2, arg3];
|
||||
let tuple = arena.alloc((Located::new(0, 0, 0, 1, Var(module_parts, "a")), args));
|
||||
let expected = Expr::Apply(tuple);
|
||||
let arg1 = arena.alloc(Located::new(0, 0, 2, 3, Var(module_parts, "b")));
|
||||
let arg2 = arena.alloc(Located::new(0, 0, 4, 5, Var(module_parts, "c")));
|
||||
let arg3 = arena.alloc(Located::new(0, 0, 6, 7, Var(module_parts, "d")));
|
||||
let args = bumpalo::vec![in &arena; &*arg1, &*arg2, &*arg3];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 0, 1, Var(module_parts, "a"))),
|
||||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "a b c d");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -597,10 +607,13 @@ mod test_parse {
|
|||
fn parenthetical_apply() {
|
||||
let arena = Bump::new();
|
||||
let module_parts = Vec::new_in(&arena).into_bump_slice();
|
||||
let arg = Located::new(0, 0, 7, 8, Int("1"));
|
||||
let args = bumpalo::vec![in &arena; arg];
|
||||
let tuple = arena.alloc((Located::new(0, 0, 1, 5, Var(module_parts, "whee")), args));
|
||||
let expected = Expr::Apply(tuple);
|
||||
let arg = arena.alloc(Located::new(0, 0, 7, 8, Int("1")));
|
||||
let args = bumpalo::vec![in &arena; &*arg];
|
||||
let expected = Expr::Apply(
|
||||
arena.alloc(Located::new(0, 0, 1, 5, Var(module_parts, "whee"))),
|
||||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "(whee) 1");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -613,8 +626,10 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let pattern = Located::new(0, 0, 1, 2, Identifier("a"));
|
||||
let patterns = bumpalo::vec![in &arena; pattern];
|
||||
let tuple = arena.alloc((patterns, Located::new(0, 0, 6, 8, Int("42"))));
|
||||
let expected = Closure(tuple);
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 6, 8, Int("42"))),
|
||||
);
|
||||
let actual = parse_with(&arena, "\\a -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -625,8 +640,10 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let pattern = Located::new(0, 0, 1, 2, Underscore);
|
||||
let patterns = bumpalo::vec![in &arena; pattern];
|
||||
let tuple = arena.alloc((patterns, Located::new(0, 0, 6, 8, Int("42"))));
|
||||
let expected = Closure(tuple);
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 6, 8, Int("42"))),
|
||||
);
|
||||
let actual = parse_with(&arena, "\\_ -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -638,8 +655,10 @@ mod test_parse {
|
|||
let arg1 = Located::new(0, 0, 1, 2, Identifier("a"));
|
||||
let arg2 = Located::new(0, 0, 3, 4, Identifier("b"));
|
||||
let patterns = bumpalo::vec![in &arena; arg1, arg2];
|
||||
let tuple = arena.alloc((patterns, Located::new(0, 0, 8, 10, Int("42"))));
|
||||
let expected = Closure(tuple);
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 8, 10, Int("42"))),
|
||||
);
|
||||
let actual = parse_with(&arena, "\\a b -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -652,8 +671,10 @@ mod test_parse {
|
|||
let arg2 = Located::new(0, 0, 3, 4, Identifier("b"));
|
||||
let arg3 = Located::new(0, 0, 5, 6, Identifier("c"));
|
||||
let patterns = bumpalo::vec![in &arena; arg1, arg2, arg3];
|
||||
let tuple = arena.alloc((patterns, Located::new(0, 0, 10, 12, Int("42"))));
|
||||
let expected = Closure(tuple);
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 10, 12, Int("42"))),
|
||||
);
|
||||
let actual = parse_with(&arena, "\\a b c -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -665,8 +686,10 @@ mod test_parse {
|
|||
let underscore1 = Located::new(0, 0, 1, 2, Underscore);
|
||||
let underscore2 = Located::new(0, 0, 3, 4, Underscore);
|
||||
let patterns = bumpalo::vec![in &arena; underscore1, underscore2];
|
||||
let tuple = arena.alloc((patterns, Located::new(0, 0, 8, 10, Int("42"))));
|
||||
let expected = Closure(tuple);
|
||||
let expected = Closure(
|
||||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 8, 10, Int("42"))),
|
||||
);
|
||||
let actual = parse_with(&arena, "\\_ _ -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
|
@ -687,7 +710,7 @@ mod test_parse {
|
|||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(arena.alloc((defs, loc_ret)))),
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
);
|
||||
|
||||
|
@ -716,7 +739,7 @@ mod test_parse {
|
|||
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(arena.alloc((defs, loc_ret)))),
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
);
|
||||
|
||||
|
@ -756,7 +779,7 @@ mod test_parse {
|
|||
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(arena.alloc((defs, loc_ret)))),
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
);
|
||||
|
||||
|
@ -801,7 +824,7 @@ mod test_parse {
|
|||
let loc_ret = Located::new(4, 4, 0, 2, ret);
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(arena.alloc((defs, loc_ret)))),
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
reset_indentation.into_bump_slice(),
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue