feat: transpile hello world view function
This commit is contained in:
parent
8354d51ecb
commit
2f335b02ae
6 changed files with 385 additions and 81 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
43
src/elm.rs
43
src/elm.rs
|
|
@ -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) =
|
||||||
|
elmi::OptGlobalGraph::get(&data).map_err(|_err| {
|
||||||
CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
|
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,7 +151,8 @@ 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) =
|
||||||
|
elmi::OptLocalGraph::get(&data).map_err(|_err| {
|
||||||
CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
|
CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
|
||||||
22
src/main.rs
22
src/main.rs
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
))
|
))
|
||||||
|
|
|
||||||
302
src/transpile.rs
302
src/transpile.rs
|
|
@ -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,25 +75,52 @@ 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) => {
|
||||||
|
//println!("key => {:?}", expr);
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
elmi::Node::DefineTailFunc(_, _, deps) => {
|
elmi::Node::DefineTailFunc(_, _, deps) => {
|
||||||
for dep in deps.iter() {
|
for dep in deps.iter() {
|
||||||
|
|
@ -90,12 +129,9 @@ pub fn transpile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elmi::Node::Ctor(_, _) => {
|
elmi::Node::Ctor(_, _) => {}
|
||||||
}
|
elmi::Node::Enum(_) => {}
|
||||||
elmi::Node::Enum(_) => {
|
elmi::Node::Box => {}
|
||||||
}
|
|
||||||
elmi::Node::Box => {
|
|
||||||
}
|
|
||||||
elmi::Node::Link(dep) => {
|
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);
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue