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:
parent
2f335b02ae
commit
2e78b33524
4 changed files with 223 additions and 31 deletions
|
|
@ -12,7 +12,7 @@ edition = "2018"
|
||||||
# it myself.
|
# it myself.
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ahash = "0.7"
|
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" }
|
naive-wadler-prettier= { path = "../../../infra/redwood-lang/compiler/naive-wadler-prettier" }
|
||||||
os_pipe = "0.9"
|
os_pipe = "0.9"
|
||||||
serde = { version = "1.0", features = [ "derive" ] }
|
serde = { version = "1.0", features = [ "derive" ] }
|
||||||
|
|
@ -36,3 +36,4 @@ deno_web = "0.52"
|
||||||
rusty_v8 = "0.32"
|
rusty_v8 = "0.32"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
serde_v8 = "0.15"
|
serde_v8 = "0.15"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 Html exposing (Html, div, text)
|
||||||
import Svg exposing (Svg, svg)
|
import Svg exposing (Svg, svg)
|
||||||
|
|
@ -50,10 +50,27 @@ view5 { x } =
|
||||||
div []
|
div []
|
||||||
[ text x ]
|
[ 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 : String -> Int
|
||||||
badReturnType _ =
|
badReturnType _ =
|
||||||
42
|
42
|
||||||
|
|
||||||
|
privateFunction = div
|
||||||
|
|
||||||
true : Bool
|
true : Bool
|
||||||
true =
|
true =
|
||||||
True
|
True
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,12 @@ pub fn load_interfaces(
|
||||||
elmi::DependencyInterface::Public(interface) => {
|
elmi::DependencyInterface::Public(interface) => {
|
||||||
interfaces.insert(module_name, 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);
|
||||||
|
//}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
213
src/transpile.rs
213
src/transpile.rs
|
|
@ -75,6 +75,67 @@ pub fn transpile(
|
||||||
|
|
||||||
let objects = elm::load_objects(&elm_cache_dir)?;
|
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) {
|
if let Some(node) = objects.get(&entrypoint) {
|
||||||
match node {
|
match node {
|
||||||
elmi::Node::Define(elmi::Expr::Function(ref parameters, ref body), deps) => {
|
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;
|
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) =
|
let (parameter_types, return_type) =
|
||||||
extract_function_types(&tipe, parameters.len()).unwrap();
|
extract_function_types(&tipe, parameters.len()).unwrap();
|
||||||
|
|
||||||
|
|
@ -97,6 +164,7 @@ pub fn transpile(
|
||||||
let mut tokens = rust::Tokens::new();
|
let mut tokens = rust::Tokens::new();
|
||||||
codegen_function(
|
codegen_function(
|
||||||
&mut tokens,
|
&mut tokens,
|
||||||
|
&mut symbol_table,
|
||||||
&entrypoint.1,
|
&entrypoint.1,
|
||||||
&free_variables,
|
&free_variables,
|
||||||
&xs,
|
&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.
|
// 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.
|
// Accumulate the contents of each rust module in map.
|
||||||
//
|
//
|
||||||
|
|
@ -185,6 +251,21 @@ pub fn transpile(
|
||||||
Ok(())
|
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> {
|
fn validate_function_type(tipe: &elmi::Type) -> Result<(), TypeError> {
|
||||||
match tipe {
|
match tipe {
|
||||||
elmi::Type::TLambda(a, b) => {
|
elmi::Type::TLambda(a, b) => {
|
||||||
|
|
@ -283,6 +364,7 @@ fn validate_output_type(tipe: &elmi::Type) -> Result<(), TypeError> {
|
||||||
|
|
||||||
fn codegen_function(
|
fn codegen_function(
|
||||||
tokens: &mut rust::Tokens,
|
tokens: &mut rust::Tokens,
|
||||||
|
symbol_table: &mut SymbolTable,
|
||||||
name: &elmi::Name,
|
name: &elmi::Name,
|
||||||
type_variables: &HashSet<elmi::Name>,
|
type_variables: &HashSet<elmi::Name>,
|
||||||
parameters: &[(&elmi::Name, elmi::Type)],
|
parameters: &[(&elmi::Name, elmi::Type)],
|
||||||
|
|
@ -290,14 +372,14 @@ fn codegen_function(
|
||||||
body: &elmi::Expr,
|
body: &elmi::Expr,
|
||||||
) {
|
) {
|
||||||
quote_in! { *tokens =>
|
quote_in! { *tokens =>
|
||||||
fn #(&name.0)#(if !type_variables.is_empty() =>
|
fn #name#(if !type_variables.is_empty() =>
|
||||||
<#(for elmi::Name(ref tvar) in type_variables.iter() join (, ) =>
|
<#(for tvar in type_variables.iter() join (, ) =>
|
||||||
#tvar
|
#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) })
|
#parameter: #(ref out { codegen_type(out, tipe) })
|
||||||
)) -> #(ref out { codegen_type(out, &return_type) }) {
|
)) -> #(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,
|
name: &elmi::Name,
|
||||||
) {
|
) {
|
||||||
quote_in! { *tokens =>
|
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 {
|
match expr {
|
||||||
elmi::Expr::Bool(true) => quote_in! { *tokens => true },
|
elmi::Expr::Bool(true) => quote_in! { *tokens => true },
|
||||||
elmi::Expr::Bool(false) => quote_in! { *tokens => false },
|
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::Str(s) => quote_in! { *tokens => #(quoted(s)) },
|
||||||
elmi::Expr::Int(x) => quote_in! { *tokens => #(x.to_string()) },
|
elmi::Expr::Int(x) => quote_in! { *tokens => #(x.to_string()) },
|
||||||
elmi::Expr::Float(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 =>
|
quote_in! { *tokens =>
|
||||||
#name
|
#name
|
||||||
}
|
}
|
||||||
|
|
@ -411,7 +493,7 @@ fn codegen_expr(tokens: &mut rust::Tokens, expr: &elmi::Expr) {
|
||||||
} else {
|
} else {
|
||||||
quote_in! { *tokens =>
|
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) => {
|
elmi::Expr::Call(ref fexpr, args) => {
|
||||||
|
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 =>
|
quote_in! { *tokens =>
|
||||||
#(match &**fexpr {
|
Box::new(| #(for arg in closure_args.iter() join (, ) => #(ref out => codegen_expr(out, symbol_table, arg))) | {
|
||||||
elmi::Expr::VarGlobal(elmi::Global(home, name)) => {
|
#(ref out => {
|
||||||
#(ref out => codegen_name_from_global(out, home, name))
|
codegen_name_from_global(out, home, name)
|
||||||
}
|
})(
|
||||||
_ => {
|
#(for arg in args.iter().chain(closure_args.iter()) join (,#<push>) => #(ref out =>
|
||||||
"unknown"
|
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 =>
|
#(for arg in args join (,#<push>) => #(ref out =>
|
||||||
codegen_expr(out, arg) )
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elmi::Expr::VarLocal(name) => {
|
||||||
|
quote_in! { *tokens =>
|
||||||
|
#name(
|
||||||
|
#(for arg in args join (,#<push>) => #(ref out =>
|
||||||
|
codegen_expr(out, symbol_table, 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::TailCall(Name, Vec<(Name, Expr)>),
|
||||||
//elmi::Expr::If(Vec<(Expr, Expr)>, Box<Expr>),
|
elmi::Expr::If(branches, final_branch) => {
|
||||||
//elmi::Expr::Let(Def, Box<Expr>),
|
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::Destruct(Destructor, Box<Expr>),
|
||||||
//elmi::Expr::Case(Name, Name, Decider<Choice>, Vec<(i64, 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::Access(Box<Expr>, Name),
|
||||||
//elmi::Expr::Update(Box<Expr>, HashMap<Name, Expr>),
|
//elmi::Expr::Update(Box<Expr>, HashMap<Name, Expr>),
|
||||||
//elmi::Expr::Record(HashMap<Name, Expr>),
|
//elmi::Expr::Record(HashMap<Name, Expr>),
|
||||||
elmi::Expr::Unit => (),
|
elmi::Expr::Unit => (),
|
||||||
elmi::Expr::Tuple(a, b, None) => {
|
elmi::Expr::Tuple(a, b, None) => {
|
||||||
quote_in! { *tokens =>
|
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)) => {
|
elmi::Expr::Tuple(a, b, Some(c)) => {
|
||||||
quote_in! { *tokens =>
|
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>),
|
//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(
|
fn extract_function_types(
|
||||||
mut tipe: &elmi::Type,
|
mut tipe: &elmi::Type,
|
||||||
mut nargs: usize,
|
mut nargs: usize,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue