feat: figured out starmelon needs expr types

To generate partial application boxed closures I need to know the arity
of the expression to tell if more args will remain after the given args.
Right now I think I can recalculate the types if I have a symbol table
where I can look up every variable. I think this will be a lot of work
because I have to reimplement most of unification to deal with
collections, patterns, and type variables. And I currently don't know
how unification works.
This commit is contained in:
YetAnotherMinion 2021-12-29 14:57:35 +00:00 committed by nobody
commit 2e78b33524
Signed by: GrocerPublishAgent
GPG key ID: D460CD54A9E3AB86
4 changed files with 223 additions and 31 deletions

View file

@ -12,7 +12,7 @@ edition = "2018"
# it myself.
[dependencies]
ahash = "0.7"
elmi = { path = "../../../infra/rust-elmi" }
elmi = { path = "../../../infra/rust-elmi", features = [ "genco" ] }
naive-wadler-prettier= { path = "../../../infra/redwood-lang/compiler/naive-wadler-prettier" }
os_pipe = "0.9"
serde = { version = "1.0", features = [ "derive" ] }
@ -36,3 +36,4 @@ deno_web = "0.52"
rusty_v8 = "0.32"
futures = "0.3.15"
serde_v8 = "0.15"

View file

@ -1,4 +1,4 @@
module Main exposing (view, view2, view3, view4, view5, badReturnType, true)
module Main exposing (view, view2, view3, view4, view5, view6, view7, view8, badReturnType, true)
import Html exposing (Html, div, text)
import Svg exposing (Svg, svg)
@ -50,10 +50,27 @@ view5 { x } =
div []
[ text x ]
view6 : String -> Html msg
view6 model =
let
f = div []
in
f [ text model ]
view7 : String -> Html msg
view7 model =
(if True then div [] else div []) [ text model ]
view8 : String -> Html msg
view8 model =
privateFunction [] []
badReturnType : String -> Int
badReturnType _ =
42
privateFunction = div
true : Bool
true =
True

View file

@ -68,7 +68,12 @@ pub fn load_interfaces(
elmi::DependencyInterface::Public(interface) => {
interfaces.insert(module_name, interface);
}
_ => {}
elmi::DependencyInterface::Private(package_name, unions, aliases) => {
println!("skipping private interface {}", package_name);
//for (k, v) in unions {
// println!(" {}", k);
//}
}
}
}
}

View file

@ -75,6 +75,67 @@ pub fn transpile(
let objects = elm::load_objects(&elm_cache_dir)?;
let mut symbol_table: HashMap<Symbol, SymbolKind> = HashMap::new();
for (key, node) in objects.iter() {
match interfaces.get(&key.0) {
None => {
//eprintln!("failed to find interface for object {}", key);
println!("skipping symbol because we don't have an interface {}", key);
//println!("{:?}", node);
}
Some(module_interface) => match module_interface.values.get(&key.1) {
None => {
println!("skipping symbol {}", key);
}
Some(annotation) => {
let elmi::CannonicalAnnotation(_free_vars, ref tipe) = annotation;
match node {
elmi::Node::Define(elmi::Expr::Function(ref parameters, _body), _deps) => {
//println!("found a definition {} arity {}", key, parameters.len() );
symbol_table.insert(
Symbol::Global(key),
SymbolKind::Function {
arity: parameters.len(),
tipe,
},
);
}
elmi::Node::Define(_, _deps) => {
let mut arity = 0;
let mut xs = tipe;
loop {
match xs {
elmi::Type::TLambda(_head, tail) => {
xs = tail;
arity += 1;
}
_ => break,
}
}
if arity == 0 {
symbol_table
.insert(Symbol::Global(key), SymbolKind::Value { tipe });
} else {
symbol_table.insert(
Symbol::Global(key),
SymbolKind::Function { arity, tipe },
);
}
}
elmi::Node::DefineTailFunc(arg_names, expr, _deps) => {
println!("found tail func {}", key);
}
_ => {
symbol_table.insert(Symbol::Global(key), SymbolKind::Value { tipe });
}
}
}
},
}
}
if let Some(node) = objects.get(&entrypoint) {
match node {
elmi::Node::Define(elmi::Expr::Function(ref parameters, ref body), deps) => {
@ -83,6 +144,12 @@ pub fn transpile(
}
let elmi::CannonicalAnnotation(elmi::FreeVars(free_variables), tipe) = signature;
// So I want a map of symbol to is function with arity or is value. I don't need a
// scope mechansim because elm prevents variable shadowing. So the map can
// overwrite values for sibling scopes. So everytime I visit an Expr I want to
// return the arity of that expression. One problem is I need to generate the
// arguments before serializing the body of the closure
let (parameter_types, return_type) =
extract_function_types(&tipe, parameters.len()).unwrap();
@ -90,13 +157,14 @@ pub fn transpile(
.into_iter()
.zip(parameter_types.into_iter())
.collect::<Vec<_>>();
// TODO add any TLambdas in the signature to the type parameters of the functions
// as where bounds
let mut tokens = rust::Tokens::new();
codegen_function(
&mut tokens,
&mut symbol_table,
&entrypoint.1,
&free_variables,
&xs,
@ -169,8 +237,6 @@ pub fn transpile(
}
}
let _symbol_table: HashMap<elmi::Global, elmi::Node> = HashMap::new();
// step 6, start generating rust code using a tree visitor on each of the entry points.
// Accumulate the contents of each rust module in map.
//
@ -185,6 +251,21 @@ pub fn transpile(
Ok(())
}
// So when I have a destructuring, I will want to take a look at the type of that symbol and
// derive the tipe of the child so I can insert it into my table
type SymbolTable<'a> = HashMap<Symbol<'a>, SymbolKind<'a>>;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
enum Symbol<'a> {
Global(&'a elmi::Global),
Local(&'a elmi::Name),
}
enum SymbolKind<'a> {
Function { arity: usize, tipe: &'a elmi::Type },
Value { tipe: &'a elmi::Type },
}
fn validate_function_type(tipe: &elmi::Type) -> Result<(), TypeError> {
match tipe {
elmi::Type::TLambda(a, b) => {
@ -283,6 +364,7 @@ fn validate_output_type(tipe: &elmi::Type) -> Result<(), TypeError> {
fn codegen_function(
tokens: &mut rust::Tokens,
symbol_table: &mut SymbolTable,
name: &elmi::Name,
type_variables: &HashSet<elmi::Name>,
parameters: &[(&elmi::Name, elmi::Type)],
@ -290,14 +372,14 @@ fn codegen_function(
body: &elmi::Expr,
) {
quote_in! { *tokens =>
fn #(&name.0)#(if !type_variables.is_empty() =>
<#(for elmi::Name(ref tvar) in type_variables.iter() join (, ) =>
fn #name#(if !type_variables.is_empty() =>
<#(for tvar in type_variables.iter() join (, ) =>
#tvar
)>
)(#(for (elmi::Name(ref parameter), tipe) in parameters.iter() join (, ) =>
)(#(for (parameter, tipe) in parameters.iter() join (, ) =>
#parameter: #(ref out { codegen_type(out, tipe) })
)) -> #(ref out { codegen_type(out, &return_type) }) {
#(ref out { codegen_expr(out, body) })
#(ref out { codegen_expr(out, symbol_table, body) })
}
}
}
@ -367,7 +449,7 @@ fn codegen_name_from_global(
name: &elmi::Name,
) {
quote_in! { *tokens =>
#(ref out => codegen_home_to_builder(out, home) )__#(&name.0)
#(ref out => codegen_home_to_builder(out, home) )__#name
}
}
@ -382,7 +464,7 @@ fn codegen_home_to_builder(tokens: &mut rust::Tokens, global: &elmi::ModuleNameC
}
}
fn codegen_expr(tokens: &mut rust::Tokens, expr: &elmi::Expr) {
fn codegen_expr(tokens: &mut rust::Tokens, symbol_table: &mut SymbolTable, expr: &elmi::Expr) {
match expr {
elmi::Expr::Bool(true) => quote_in! { *tokens => true },
elmi::Expr::Bool(false) => quote_in! { *tokens => false },
@ -390,7 +472,7 @@ fn codegen_expr(tokens: &mut rust::Tokens, expr: &elmi::Expr) {
elmi::Expr::Str(s) => quote_in! { *tokens => #(quoted(s)) },
elmi::Expr::Int(x) => quote_in! { *tokens => #(x.to_string()) },
elmi::Expr::Float(x) => quote_in! { *tokens => #(x.to_string()) },
elmi::Expr::VarLocal(elmi::Name(ref name)) => {
elmi::Expr::VarLocal(name) => {
quote_in! { *tokens =>
#name
}
@ -411,7 +493,7 @@ fn codegen_expr(tokens: &mut rust::Tokens, expr: &elmi::Expr) {
} else {
quote_in! { *tokens =>
&[
#(for x in xs join (,#<push>) => #(ref out => codegen_expr(out, x) ) )
#(for x in xs join (,#<push>) => #(ref out => codegen_expr(out, symbol_table, x) ) )
]
}
}
@ -424,39 +506,107 @@ fn codegen_expr(tokens: &mut rust::Tokens, expr: &elmi::Expr) {
}
}
elmi::Expr::Call(ref fexpr, args) => {
quote_in! { *tokens =>
#(match &**fexpr {
elmi::Expr::VarGlobal(elmi::Global(home, name)) => {
#(ref out => codegen_name_from_global(out, home, name))
match &**fexpr {
elmi::Expr::VarGlobal(global @ elmi::Global(home, name)) => {
match symbol_table.get(&Symbol::Global(global)) {
Some(SymbolKind::Function { arity, tipe }) => {
if args.len() < *arity {
let mut closure_args = Vec::new();
for i in 0..(*arity - args.len()) {
closure_args.push(elmi::Expr::VarLocal(elmi::Name(format!(
"_partial{}",
i
))));
}
quote_in! { *tokens =>
Box::new(| #(for arg in closure_args.iter() join (, ) => #(ref out => codegen_expr(out, symbol_table, arg))) | {
#(ref out => {
codegen_name_from_global(out, home, name)
})(
#(for arg in args.iter().chain(closure_args.iter()) join (,#<push>) => #(ref out =>
codegen_expr(out, symbol_table, arg) )
)
)
})
}
} else {
quote_in! { *tokens =>
#(ref out => {
codegen_name_from_global(out, home, name)
})(
#(for arg in args join (,#<push>) => #(ref out =>
codegen_expr(out, symbol_table, arg) )
)
)
}
}
//println!("found the function symbol {}, arity {}", global, arity);
}
Some(SymbolKind::Value { tipe }) => {
panic!("tried to call a symbol we thought was a value: {}", global);
}
None => {
panic!("tried to call a symbol we don't know about: {}", global);
}
}
_ => {
"unknown"
}
elmi::Expr::VarLocal(name) => {
quote_in! { *tokens =>
#name(
#(for arg in args join (,#<push>) => #(ref out =>
codegen_expr(out, symbol_table, arg) )
)
)
}
})(
#(for arg in args join (,#<push>) => #(ref out =>
codegen_expr(out, arg) )
)
)
}
}
_ => {
println!("I was unable to call an expression");
// TODO write a function that can take an expression and return the arity using
// the symbol table from the bottom up.
quote_in! { *tokens =>
#(format!("{:?}", fexpr))
}
//panic!("calling an expression not yet supported");
}
};
}
//elmi::Expr::TailCall(Name, Vec<(Name, Expr)>),
//elmi::Expr::If(Vec<(Expr, Expr)>, Box<Expr>),
//elmi::Expr::Let(Def, Box<Expr>),
elmi::Expr::If(branches, final_branch) => {
quote_in! { *tokens =>
#(for (condition, expr) in branches join (#<push>#("} else")) =>
if #(ref out => codegen_expr(out, symbol_table, condition)) #("{")
#(ref out => codegen_expr(out, symbol_table, expr))
) #("} else {")
#(ref out => codegen_expr(out, symbol_table, expr))
#("}")
}
}
elmi::Expr::Let(def, expr) => {
quote_in! { *tokens =>
#(ref out => codegen_def(out, symbol_table, def))
#<push>
#(ref out => codegen_expr(out, symbol_table, expr))
}
}
//elmi::Expr::Destruct(Destructor, Box<Expr>),
//elmi::Expr::Case(Name, Name, Decider<Choice>, Vec<(i64, Expr)>),
//elmi::Expr::Accessor(Name),
elmi::Expr::Accessor(name) => {
quote_in! { *tokens =>
Box::new(|_v| { _v.#name })
}
}
//elmi::Expr::Access(Box<Expr>, Name),
//elmi::Expr::Update(Box<Expr>, HashMap<Name, Expr>),
//elmi::Expr::Record(HashMap<Name, Expr>),
elmi::Expr::Unit => (),
elmi::Expr::Tuple(a, b, None) => {
quote_in! { *tokens =>
( #(ref out => codegen_expr(out, a) ), #(ref out => codegen_expr(out, b) ) )
( #(ref out => codegen_expr(out, symbol_table, a) ), #(ref out => codegen_expr(out, symbol_table, b) ) )
}
}
elmi::Expr::Tuple(a, b, Some(c)) => {
quote_in! { *tokens =>
( #(ref out => codegen_expr(out, a) ), #(ref out => codegen_expr(out, b) ), #(ref out => codegen_expr(out, c) ) )
( #(ref out => codegen_expr(out, symbol_table, a) ), #(ref out => codegen_expr(out, symbol_table, b) ), #(ref out => codegen_expr(out, symbol_table, c) ) )
}
}
//elmi::Expr::Shader(ShaderSource, HashSet<Name>, HashSet<Name>),
@ -464,6 +614,25 @@ fn codegen_expr(tokens: &mut rust::Tokens, expr: &elmi::Expr) {
}
}
fn codegen_def(tokens: &mut rust::Tokens, symbol_table: &mut SymbolTable, def: &elmi::Def) {
match def {
elmi::Def::Def(name, expr) => {
quote_in! { *tokens =>
let #name = #(ref out => codegen_expr(out, symbol_table, expr) );
}
}
elmi::Def::TailDef(name, arg_names, expr) => {
quote_in! { *tokens =>
|#(for arg in arg_names join (, ) => mut #arg) | {
#("'")#name : loop {
#(ref out => codegen_expr(out, symbol_table, expr))
}
}
}
}
}
}
fn extract_function_types(
mut tipe: &elmi::Type,
mut nargs: usize,