feat: transpile hello world view function

This commit is contained in:
YetAnotherMinion 2021-12-13 04:31:53 +00:00 committed by nobody
commit 2f335b02ae
Signed by: GrocerPublishAgent
GPG key ID: D460CD54A9E3AB86
6 changed files with 385 additions and 81 deletions

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# generate a 10MB test file # generate a 10MB test file
< /dev/urandom tr -dc "[:alnum:]" | head -c10000000 > file.txt < /dev/urandom tr -dc "[:alnum:]" | head -c10000000 > input.txt

View file

@ -1,4 +1,4 @@
module Main exposing (view, view2, view3, badReturnType) module Main exposing (view, view2, view3, view4, view5, badReturnType, true)
import Html exposing (Html, div, text) import Html exposing (Html, div, text)
import Svg exposing (Svg, svg) import Svg exposing (Svg, svg)
@ -36,6 +36,24 @@ view3 model =
Nothing -> Nothing ->
text "Failed to decode" text "Failed to decode"
view4 : String -> Html msg
view4 model =
if model == "bar" then
div []
[ text <| "Hello world" ++ model ]
else
div []
[ text "Hello world" ]
view5 : { t | x : String } -> Html msg
view5 { x } =
div []
[ text x ]
badReturnType : String -> Int badReturnType : String -> Int
badReturnType _ = badReturnType _ =
42 42
true : Bool
true =
True

View file

@ -4,7 +4,7 @@ use std::collections::HashMap;
use std::fs::{self, canonicalize, metadata}; use std::fs::{self, canonicalize, metadata};
use std::io::{self, BufRead, Error, ErrorKind, Write}; use std::io::{self, BufRead, Error, ErrorKind, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Command}; use std::process::Command;
use tracing::info_span; use tracing::info_span;
pub fn make(file: &Path, debug: bool, verbosity: u64) -> Result<(), Problem> { pub fn make(file: &Path, debug: bool, verbosity: u64) -> Result<(), Problem> {
@ -39,7 +39,9 @@ pub fn make(file: &Path, debug: bool, verbosity: u64) -> Result<(), Problem> {
Ok(()) Ok(())
} }
pub fn load_interfaces(elm_cache_dir: &Path) -> Result<HashMap<String, elmi::Interface>, Problem> { pub fn load_interfaces(
elm_cache_dir: &Path,
) -> Result<HashMap<elmi::ModuleNameCanonical, elmi::Interface>, Problem> {
let mut interfaces = HashMap::new(); let mut interfaces = HashMap::new();
let entries = fs::read_dir(&elm_cache_dir) let entries = fs::read_dir(&elm_cache_dir)
@ -51,6 +53,27 @@ pub fn load_interfaces(elm_cache_dir: &Path) -> Result<HashMap<String, elmi::Int
})?; })?;
let path = entry.path(); let path = entry.path();
if path.is_file() { if path.is_file() {
match path.file_name() {
Some(file_name) if file_name == "i.dat" => {
let data = std::fs::read(&path)
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?;
let (_remaining, i) = <elmi::Interfaces as elmi::DataBinary>::get(&data)
.map_err(|_err| {
CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
})?;
for (module_name, dep_interface) in i.into_iter() {
match dep_interface {
elmi::DependencyInterface::Public(interface) => {
interfaces.insert(module_name, interface);
}
_ => {}
}
}
}
_ => {}
}
match path.extension() { match path.extension() {
Some(ext) if ext == "elmi" => { Some(ext) if ext == "elmi" => {
// step 4 load all the modules // step 4 load all the modules
@ -70,12 +93,16 @@ pub fn load_interfaces(elm_cache_dir: &Path) -> Result<HashMap<String, elmi::Int
if let Some(stem) = path.file_stem() { if let Some(stem) = path.file_stem() {
// in theory the module name of the interface can be determined by // in theory the module name of the interface can be determined by
// the filename in elm 0.19.0 and 0.19.1 // the filename in elm 0.19.0 and 0.19.1
let module_name: String = stem let module: String = stem
.to_string_lossy() .to_string_lossy()
.split('-') .split('-')
.collect::<Vec<&str>>() .collect::<Vec<&str>>()
.join("."); .join(".");
interfaces.insert(module_name, i); let cannonical_module = elmi::ModuleNameCanonical {
package: elmi::PackageName::new("author", "project"),
module: elmi::Name(module),
};
interfaces.insert(cannonical_module, i);
} }
} }
_ => (), _ => (),
@ -103,13 +130,14 @@ pub fn load_objects(elm_cache_dir: &Path) -> Result<HashMap<elmi::Global, elmi::
let data = std::fs::read(&path) let data = std::fs::read(&path)
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?; .map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?;
let (_remaining, opt_global_graph) = elmi::OptGlobalGraph::get(&data).map_err(|_err| { let (_remaining, opt_global_graph) =
CompilerError::FailedElmiParse("todo elmi parsing".to_owned()) elmi::OptGlobalGraph::get(&data).map_err(|_err| {
})?; CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
})?;
objects.extend(opt_global_graph.nodes.into_iter()); objects.extend(opt_global_graph.nodes.into_iter());
} }
_ => {}, _ => {}
} }
match path.extension() { match path.extension() {
Some(ext) if ext == "elmo" => { Some(ext) if ext == "elmo" => {
@ -123,9 +151,10 @@ pub fn load_objects(elm_cache_dir: &Path) -> Result<HashMap<elmi::Global, elmi::
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?; .map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?;
drop(load_guard); drop(load_guard);
let (_remaining, opt_local_graph) = elmi::OptLocalGraph::get(&data).map_err(|_err| { let (_remaining, opt_local_graph) =
CompilerError::FailedElmiParse("todo elmi parsing".to_owned()) elmi::OptLocalGraph::get(&data).map_err(|_err| {
})?; CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
})?;
objects.extend(opt_local_graph.nodes.into_iter()); objects.extend(opt_local_graph.nodes.into_iter());
} }

View file

@ -2,7 +2,6 @@ extern crate naive_wadler_prettier as pretty;
use crate::reporting::{CompilerError, InterpreterError, Problem, SetupError, TypeError}; use crate::reporting::{CompilerError, InterpreterError, Problem, SetupError, TypeError};
use crate::timings::Timings; use crate::timings::Timings;
use elm_project_utils::ChecksumConstraint; use elm_project_utils::ChecksumConstraint;
use elmi::DataBinary;
use os_pipe::dup_stderr; use os_pipe::dup_stderr;
use pretty::pretty; use pretty::pretty;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -115,7 +114,14 @@ fn exec(
let source_checksum = hasher.finish(); let source_checksum = hasher.finish();
// step 2.5 get the module name out of the file. // step 2.5 get the module name out of the file.
let elmi::Name(target_module) = elm::parse_module_name(&data)?;
let entrypoint = elmi::Global(
elmi::ModuleNameCanonical {
package: elmi::PackageName::new("author", "project"),
module: elm::parse_module_name(&data)?,
},
elmi::Name(function.clone()),
);
// step 3 find all the filepaths in the elm-stuff/0.19.1/* folder // step 3 find all the filepaths in the elm-stuff/0.19.1/* folder
let interfaces = elm::load_interfaces(&elm_cache_dir)?; let interfaces = elm::load_interfaces(&elm_cache_dir)?;
@ -123,16 +129,16 @@ fn exec(
// Step 5, check for the desired function // Step 5, check for the desired function
let span = info_span!("resolved target function"); let span = info_span!("resolved target function");
let timing_guard = span.enter(); let timing_guard = span.enter();
let (input_type, output_type) = match interfaces.get(&target_module) { let (input_type, output_type) = match interfaces.get(&entrypoint.0) {
Some(interface) => match interface.values.get(&elmi::Name::from(&function)) { Some(interface) => match interface.values.get(&entrypoint.1) {
Some(annotation) => { Some(annotation) => {
let elmi::CannonicalAnnotation(_free_vars, tipe) = annotation; let elmi::CannonicalAnnotation(_free_vars, tipe) = annotation;
resolve_function_type(tipe)? resolve_function_type(tipe)?
} }
None => return Err(CompilerError::BadImport(target_module, function).into()), None => return Err(CompilerError::BadImport(entrypoint).into()),
}, },
None => return Err(CompilerError::MissingModuleTypeInformation(target_module).into()), None => return Err(CompilerError::MissingModuleTypeInformation(entrypoint.0).into()),
}; };
drop(timing_guard); drop(timing_guard);
@ -144,8 +150,8 @@ fn exec(
let timing_guard = span.enter(); let timing_guard = span.enter();
let (gen_module_name, source) = fixture::generate( let (gen_module_name, source) = fixture::generate(
source_checksum, source_checksum,
target_module, entrypoint.0.module.clone(),
function, entrypoint.1.clone(),
input_type, input_type,
output_type, output_type,
); );

View file

@ -1,3 +1,4 @@
use crate::reporting::doc::reflow;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use elm_project_utils::RedoScriptError; use elm_project_utils::RedoScriptError;
use elmi; use elmi;
@ -89,8 +90,8 @@ pub enum CompilerError {
MissingElmStuff(PathBuf), MissingElmStuff(PathBuf),
CantParseModule(String), // first line CantParseModule(String), // first line
EmptyModule, EmptyModule,
MissingModuleTypeInformation(String), MissingModuleTypeInformation(elmi::ModuleNameCanonical),
BadImport(String, String), BadImport(elmi::Global),
FailedBuildingFixture, FailedBuildingFixture,
ReadInputFailed(io::Error, PathBuf), ReadInputFailed(io::Error, PathBuf),
WriteOutputFailed(io::Error, PathBuf), WriteOutputFailed(io::Error, PathBuf),
@ -147,9 +148,9 @@ impl CompilerError {
CantParseModule(_first_line_prefix) => Doc::text("todo could not parse module"), CantParseModule(_first_line_prefix) => Doc::text("todo could not parse module"),
EmptyModule => Doc::text("I did not expect the module file to be empty"), EmptyModule => Doc::text("I did not expect the module file to be empty"),
MissingModuleTypeInformation(_) => Doc::text("todo missing module type information"), MissingModuleTypeInformation(_) => Doc::text("todo missing module type information"),
BadImport(module, symbol) => { BadImport(elmi::Global(module, symbol)) => {
// TODO suggest alternatives using edit distance // TODO suggest alternatives using edit distance
Doc::text(format!( reflow(format!(
"The `{}` module does not expose `{}`", "The `{}` module does not expose `{}`",
module, symbol module, symbol
)) ))

View file

@ -1,9 +1,11 @@
use crate::reporting::{CompilerError, Problem, TypeError};
use elmi::DataBinary;
use std::collections::HashMap;
use std::path::{PathBuf};
use tracing::info_span;
use crate::elm; use crate::elm;
use crate::reporting::{CompilerError, Problem, TypeError};
use genco::lang::rust;
use genco::tokens::quoted;
use genco::{quote, quote_in};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use tracing::info_span;
pub fn transpile( pub fn transpile(
file: PathBuf, file: PathBuf,
@ -36,22 +38,32 @@ pub fn transpile(
// used in the project. So I would want to reduce the tuples of file function into a set of // used in the project. So I would want to reduce the tuples of file function into a set of
// files to load. Then recursively load all dependencies. // files to load. Then recursively load all dependencies.
let entrypoint = elmi::Global(
elmi::ModuleNameCanonical {
package: elmi::PackageName::new("author", "project"),
module: elmi::Name(target_module.clone()),
},
elmi::Name(function.clone()),
);
// step 3 find all the filepaths in the elm-stuff/0.19.1/* folder // step 3 find all the filepaths in the elm-stuff/0.19.1/* folder
let interfaces = elm::load_interfaces(&elm_cache_dir)?; let interfaces = elm::load_interfaces(&elm_cache_dir)?;
// Step 4, check for the desired functions have types that we can compile. // Step 4, check for the desired functions have types that we can compile.
let span = info_span!("resolved target function"); let span = info_span!("resolved target function");
let timing_guard = span.enter(); let timing_guard = span.enter();
match interfaces.get(&target_module) { let signature = match interfaces.get(&entrypoint.0) {
Some(interface) => match interface.values.get(&elmi::Name::from(&function)) { Some(interface) => match interface.values.get(&elmi::Name::from(&function)) {
Some(annotation) => { Some(annotation) => {
let elmi::CannonicalAnnotation(_free_vars, tipe) = annotation; let elmi::CannonicalAnnotation(_free_vars, ref tipe) = annotation;
validate_function_type(tipe)? validate_function_type(tipe)?;
annotation
} }
None => return Err(CompilerError::BadImport(target_module, function).into()), None => return Err(CompilerError::BadImport(entrypoint).into()),
}, },
None => return Err(CompilerError::MissingModuleTypeInformation(target_module).into()), None => return Err(CompilerError::MissingModuleTypeInformation(entrypoint.0).into()),
}; };
drop(timing_guard); drop(timing_guard);
@ -63,74 +75,98 @@ pub fn transpile(
let objects = elm::load_objects(&elm_cache_dir)?; let objects = elm::load_objects(&elm_cache_dir)?;
let entrypoint = elmi::Global( if let Some(node) = objects.get(&entrypoint) {
elmi::ModuleNameCanonical { match node {
package: elmi::PackageName::new("author", "project"), elmi::Node::Define(elmi::Expr::Function(ref parameters, ref body), deps) => {
module: elmi::Name(target_module.clone()), for dep in deps {
}, println!("I depend on {}", dep);
elmi::Name(function.clone()), }
); let elmi::CannonicalAnnotation(elmi::FreeVars(free_variables), tipe) = signature;
println!("the artifacts has the symbol {}", objects.contains_key(&entrypoint)); let (parameter_types, return_type) =
extract_function_types(&tipe, parameters.len()).unwrap();
let xs = parameters
.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,
&entrypoint.1,
&free_variables,
&xs,
return_type,
body,
);
println!("{}", tokens.to_file_string().unwrap());
}
_ => println!("I don't know how to transpile that node"),
}
}
println!("total symbols {}", objects.len());
//let visited: HashSet<Global> = HashSet::new(); //let visited: HashSet<Global> = HashSet::new();
for (_key, node) in objects.iter() { for (key, node) in objects.iter() {
//println!("key {}", key); //println!("key {}", key);
match node { match node {
elmi::Node::Define(_expr, deps) => { elmi::Node::Define(expr, deps) => {
for dep in deps.iter() { //println!("key => {:?}", expr);
if !objects.contains_key(&dep) { for dep in deps.iter() {
if !objects.contains_key(&dep) {
println!("could not find dep {}", dep); println!("could not find dep {}", dep);
} }
} }
} break;
elmi::Node::DefineTailFunc(_, _, deps) => { }
for dep in deps.iter() { elmi::Node::DefineTailFunc(_, _, deps) => {
if !objects.contains_key(&dep) { for dep in deps.iter() {
if !objects.contains_key(&dep) {
println!("could not find dep {}", dep); println!("could not find dep {}", dep);
} }
} }
} }
elmi::Node::Ctor(_, _) => { elmi::Node::Ctor(_, _) => {}
} elmi::Node::Enum(_) => {}
elmi::Node::Enum(_) => { elmi::Node::Box => {}
} elmi::Node::Link(dep) => {
elmi::Node::Box => {
}
elmi::Node::Link(dep) => {
if !objects.contains_key(&dep) { if !objects.contains_key(&dep) {
println!("could not find dep {}", dep); println!("could not find dep {}", dep);
} }
} }
elmi::Node::Cycle(_, _, _, deps) => { elmi::Node::Cycle(_, _, _, deps) => {
for dep in deps.iter() { for dep in deps.iter() {
if !objects.contains_key(&dep) { if !objects.contains_key(&dep) {
println!("could not find dep {}", dep); println!("could not find dep {}", dep);
} }
} }
} }
elmi::Node::Manager(_) => (), elmi::Node::Manager(_) => (),
elmi::Node::Kernel(_, deps) => { elmi::Node::Kernel(_, deps) => {
for dep in deps.iter() { for dep in deps.iter() {
if !objects.contains_key(&dep) { if !objects.contains_key(&dep) {
println!("could not find dep {}", dep); println!("could not find dep {}", dep);
} }
} }
} }
elmi::Node::PortIncoming(_, deps) => { elmi::Node::PortIncoming(_, deps) => {
for dep in deps.iter() { for dep in deps.iter() {
if !objects.contains_key(&dep) { if !objects.contains_key(&dep) {
println!("could not find dep {}", dep); println!("could not find dep {}", dep);
} }
} }
} }
elmi::Node::PortOutgoing(_, deps) => { elmi::Node::PortOutgoing(_, deps) => {
for dep in deps.iter() { for dep in deps.iter() {
if !objects.contains_key(&dep) { if !objects.contains_key(&dep) {
println!("could not find dep {}", dep); println!("could not find dep {}", dep);
} }
} }
} }
} }
} }
let _symbol_table: HashMap<elmi::Global, elmi::Node> = HashMap::new(); let _symbol_table: HashMap<elmi::Global, elmi::Node> = HashMap::new();
@ -217,6 +253,7 @@ fn validate_input_type(tipe: &elmi::Type) -> Result<(), TypeError> {
elmi::AliasType::Filled(tipe) => validate_input_type(tipe), elmi::AliasType::Filled(tipe) => validate_input_type(tipe),
elmi::AliasType::Holey(_) => Err(TypeError::CantEvalHoleyAlias), elmi::AliasType::Holey(_) => Err(TypeError::CantEvalHoleyAlias),
}, },
elmi::Type::TRecord(_, _) => Ok(()),
_ => Err(TypeError::OutputTypeNotSupported(tipe.clone())), _ => Err(TypeError::OutputTypeNotSupported(tipe.clone())),
} }
} }
@ -244,6 +281,219 @@ fn validate_output_type(tipe: &elmi::Type) -> Result<(), TypeError> {
} }
} }
fn codegen_function(
tokens: &mut rust::Tokens,
name: &elmi::Name,
type_variables: &HashSet<elmi::Name>,
parameters: &[(&elmi::Name, elmi::Type)],
return_type: elmi::Type,
body: &elmi::Expr,
) {
quote_in! { *tokens =>
fn #(&name.0)#(if !type_variables.is_empty() =>
<#(for elmi::Name(ref tvar) in type_variables.iter() join (, ) =>
#tvar
)>
)(#(for (elmi::Name(ref 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) })
}
}
}
fn codegen_type(tokens: &mut rust::Tokens, tipe: &elmi::Type) {
quote_in! { *tokens =>
#(match tipe {
elmi::Type::TLambda(a, b) => {
( #(ref out => codegen_type(out, a) ) -> #(ref out => codegen_type(out, b) ) )
}
elmi::Type::TVar(elmi::Name(variable)) => {
#variable
},
elmi::Type::TType(module_name, name, args) if module_name == "elm/core/String" && name == "String" && args.is_empty() => {
String
}
elmi::Type::TType(home, name, args) if args.is_empty() => {
#(ref out => codegen_name_from_global(out, home, name))
}
elmi::Type::TType(home, name, args) => {
#(ref out => codegen_name_from_global(out, home, name))<#(for arg in args join(, ) =>
#(ref out => codegen_type(out, arg))
)>
}
// // Might be a primitive type
// #(if module_name == "elm/core/String" && name == "String" => String)
// #(if module_name == "elm/core/Basics" && name == "Int" => i64)
// #(if module_name == "elm/core/Basics" && name == "Float" => f64)
// #(if module_name == "elm/core/Basics" && name == "Bool" => bool)
// #(if module_name == "elm/core/Maybe" && name == "Maybe" => Option<i32>)
// #(if module_name == "elm/bytes/Bytes" && name == "Bytes" => Vec<u8>)
//}
//elmi::Type::TType(_, _, _) => Err(TypeError::CantEvalCustomType),
//elmi::Type::TRecord(_, _) => Err(TypeError::CantEvalRecord),
elmi::Type::TUnit => (),
_ => {
println!("failed to solve code {:?}", tipe);
todo_tipe
},
//elmi::Type::TTuple(_, _, _) => Err(TypeError::CantEvalTuple),
//elmi::Type::TAlias(_, _, _, ref alias) => {
// match &**alias {
// elmi::AliasType::Filled(tipe) => {
// // I think the recursion is limited to a single step. I have not tested what
// // the CannonicalAnnotation would look like for a doubly indirect alias, for
// // example for `view` below
// // ```elm
// // type alias Foo = Int
// // type alias Bar = String
// //
// // type alias Zap = Foo -> Bar
// //
// // view : Zap
// // ```
// validate_function_type(tipe)
// }
// elmi::AliasType::Holey(_) => return Err(TypeError::CantEvalHoleyAlias),
// }
//}
})
};
}
fn codegen_name_from_global(
tokens: &mut rust::Tokens,
home: &elmi::ModuleNameCanonical,
name: &elmi::Name,
) {
quote_in! { *tokens =>
#(ref out => codegen_home_to_builder(out, home) )__#(&name.0)
}
}
fn codegen_home_to_builder(tokens: &mut rust::Tokens, global: &elmi::ModuleNameCanonical) {
let elmi::ModuleNameCanonical {
package: elmi::PackageName { author, project },
module: home,
} = global;
quote_in! { *tokens =>
_#(author.replace("-", "_"))_#(project.replace("-", "_"))__#(home.0.replace(".", "_"))
}
}
fn codegen_expr(tokens: &mut rust::Tokens, expr: &elmi::Expr) {
match expr {
elmi::Expr::Bool(true) => quote_in! { *tokens => true },
elmi::Expr::Bool(false) => quote_in! { *tokens => false },
elmi::Expr::Chr(c) => quote_in! { *tokens => #("'")#c#("'") },
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)) => {
quote_in! { *tokens =>
#name
}
}
elmi::Expr::VarGlobal(elmi::Global(home, name)) => {
quote_in! { *tokens =>
#(ref out => codegen_name_from_global(out, home, name))
}
}
//elmi::Expr::VarEnum(Global, IndexZeroBased),
//elmi::Expr::VarBox(Global),
//elmi::Expr::VarCycle(ModuleNameCanonical, Name),
//elmi::Expr::VarDebug(Name, ModuleNameCanonical, AnnotationRegion, Option<Name>),
//elmi::Expr::VarKernel(Name, Name),
elmi::Expr::List(xs) => {
if xs.is_empty() {
quote_in! { *tokens => &[] }
} else {
quote_in! { *tokens =>
&[
#(for x in xs join (,#<push>) => #(ref out => codegen_expr(out, x) ) )
]
}
}
}
elmi::Expr::Function(parameters, body) => {
quote_in! { *tokens =>
"i don't know how to code gen a function expression"
//#(for elmi::Name(ref parameter) in parameters.iter() join (, ) =>
//)
}
}
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))
}
_ => {
"unknown"
}
})(
#(for arg in args join (,#<push>) => #(ref out =>
codegen_expr(out, arg) )
)
)
}
}
//elmi::Expr::TailCall(Name, Vec<(Name, Expr)>),
//elmi::Expr::If(Vec<(Expr, Expr)>, Box<Expr>),
//elmi::Expr::Let(Def, Box<Expr>),
//elmi::Expr::Destruct(Destructor, Box<Expr>),
//elmi::Expr::Case(Name, Name, Decider<Choice>, Vec<(i64, Expr)>),
//elmi::Expr::Accessor(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) ) )
}
}
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) ) )
}
}
//elmi::Expr::Shader(ShaderSource, HashSet<Name>, HashSet<Name>),
_ => quote_in! { *tokens => #(format!("{:?}", expr)) },
}
}
fn extract_function_types(
mut tipe: &elmi::Type,
mut nargs: usize,
) -> Option<(Vec<elmi::Type>, elmi::Type)> {
let mut parameters = Vec::with_capacity(nargs);
loop {
if nargs == 0 {
return Some((parameters, tipe.clone()));
}
match tipe {
elmi::Type::TLambda(a, b) => {
parameters.push(reduce_alias_types(&*a).clone());
tipe = reduce_alias_types(&*b);
nargs -= 1;
}
_ => return None,
}
}
}
fn reduce_alias_types(a: &elmi::Type) -> &elmi::Type {
match a {
elmi::Type::TAlias(_, _, _, ref alias) => match &**alias {
elmi::AliasType::Filled(b) => &b,
elmi::AliasType::Holey(_) => a,
},
_ => a,
}
}
// Figure out how to do structural types. If I could name mangle all the functions I could write // Figure out how to do structural types. If I could name mangle all the functions I could write
// them out in the same namespace as lambdas which would avoid the structural typing problem if the // them out in the same namespace as lambdas which would avoid the structural typing problem if the
// lambda was used by one type. Monomorphism. But if the lambda is used by multiple types then I // lambda was used by one type. Monomorphism. But if the lambda is used by multiple types then I