From fcac9c90beb389b7e56331b2283f70ed6c3584d2 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Tue, 27 Aug 2024 17:27:55 -0500 Subject: [PATCH] Consolidate the Binds{T} and BindsGeneric{T, ...} into one ctype (#867) --- src/lntors/function.rs | 82 ++----------------------------- src/lntors/typen.rs | 37 +++++++++++--- src/parse.rs | 2 +- src/program/ctype.rs | 91 +++++++++++++---------------------- src/program/microstatement.rs | 8 +-- src/program/scope.rs | 4 +- src/std/root.ln | 7 ++- 7 files changed, 78 insertions(+), 153 deletions(-) diff --git a/src/lntors/function.rs b/src/lntors/function.rs index ffa85f19..9b6406a1 100644 --- a/src/lntors/function.rs +++ b/src/lntors/function.rs @@ -75,7 +75,7 @@ pub fn from_microstatement( CType::Type(n, _) if n == "string" => { Ok((format!("{}.to_string()", representation).to_string(), out)) } - CType::Binds(a) if a == "String" => { + CType::Binds(a, _) if a == "String" => { Ok((format!("{}.to_string()", representation).to_string(), out)) } CType::Function(..) => { @@ -216,7 +216,7 @@ pub fn from_microstatement( CType::Type(n, _) if n == "string" => { Ok((format!("{}.to_string()", representation).to_string(), out)) } - CType::Binds(a) if a == "String" => { + CType::Binds(a, _) if a == "String" => { Ok((format!("{}.to_string()", representation).to_string(), out)) } _ => Ok((representation.clone(), out)), @@ -850,81 +850,7 @@ pub fn from_microstatement( out, )); } - CType::Binds(n) if *n == enum_name => { - // Special-casing for Option and Result mapping. TODO: - // Make this more centralized - if ts.len() == 2 { - if let CType::Void = &ts[1] { - if let CType::Void = t { - return Ok(("None".to_string(), out)); - } else { - return Ok(( - format!( - "Some({})", - match argstrs[0] - .strip_prefix("&mut ") - { - Some(s) => s, - None => &argstrs[0], - } - ), - out, - )); - } - } else if let CType::Type(name, _) = &ts[1] { - if name == "Error" { - let okrustname = - typen::ctype_to_rtype(&ts[0], true)?; - let errrustname = - typen::ctype_to_rtype(&ts[1], true)?; - if let CType::Binds(..) = t { - return Ok(( - format!( - "Err::<{}, {}>({})", - okrustname, - errrustname, - match argstrs[0] - .strip_prefix("&mut ") - { - Some(s) => s, - None => &argstrs[0], - } - ), - out, - )); - } else { - return Ok(( - format!( - "Ok::<{}, {}>({})", - okrustname, - errrustname, - match argstrs[0] - .strip_prefix("&mut ") - { - Some(s) => s, - None => &argstrs[0], - } - ), - out, - )); - } - } - } - } - return Ok(( - format!( - "{}::{}({})", - function.name, - enum_name, - match argstrs[0].strip_prefix("&mut ") { - Some(s) => s, - None => &argstrs[0], - }, - ), - out, - )); - } - CType::BindsGeneric(n, ..) if *n == enum_name => { + CType::Binds(n, ..) if *n == enum_name => { // Special-casing for Option and Result mapping. TODO: // Make this more centralized if ts.len() == 2 { @@ -1075,7 +1001,7 @@ pub fn from_microstatement( out, )); } - CType::Binds(_) => { + CType::Binds(..) => { return Ok((argstrs.join(", "), out)); } otherwise => { diff --git a/src/lntors/typen.rs b/src/lntors/typen.rs index efbfbff3..3c7a9d1e 100644 --- a/src/lntors/typen.rs +++ b/src/lntors/typen.rs @@ -67,18 +67,31 @@ pub fn ctype_to_rtype( } Ok(format!("({})", out.join(", "))) } - CType::Binds(n) => Ok(n.clone()), + CType::Binds(name, args) => { + let mut out_args = Vec::new(); + for arg in args { + out_args.push(ctype_to_rtype(arg, in_function_type)?); + } + if out_args.is_empty() { + Ok(name.clone()) + } else { + Ok(format!("{}<{}>", name, out_args.join(", "))) + } + } _ => Ok("".to_string()), // TODO: Is this correct? } } CType::Generic(name, args, _) => Ok(format!("{}<{}>", name, args.join(", "))), - CType::Binds(name) => Ok(name.clone()), - CType::BindsGeneric(name, args) => { + CType::Binds(name, args) => { let mut out_args = Vec::new(); for arg in args { out_args.push(ctype_to_rtype(arg, in_function_type)?); } - Ok(format!("{}<{}>", name, out_args.join(", "))) + if out_args.is_empty() { + Ok(name.clone()) + } else { + Ok(format!("{}<{}>", name, out_args.join(", "))) + } } CType::IntrinsicGeneric(name, _) => Ok(name.clone()), // How would this even be reached? CType::Int(i) => Ok(i.to_string()), @@ -138,8 +151,8 @@ pub fn ctype_to_rtype( if ts.len() == 2 { match &ts[1] { CType::Void => Ok(format!("Option<{}>", ctype_to_rtype(&ts[0], in_function_type)?)), - CType::Binds(rustname) if rustname == "AlanError" => Ok(format!("Result<{}, {}>", ctype_to_rtype(&ts[0], in_function_type)?, rustname)), - CType::Type(_, t) if matches!(&**t, CType::Binds(rustname) if rustname == "AlanError") => Ok(format!("Result<{}, {}>", ctype_to_rtype(&ts[0], in_function_type)?, "AlanError")), + CType::Binds(rustname, _) if rustname == "AlanError" => Ok(format!("Result<{}, {}>", ctype_to_rtype(&ts[0], in_function_type)?, rustname)), + CType::Type(_, t) if matches!(&**t, CType::Binds(rustname, _) if rustname == "AlanError") => Ok(format!("Result<{}, {}>", ctype_to_rtype(&ts[0], in_function_type)?, "AlanError")), _ => Ok(CType::Either(ts.clone()).to_callable_string()), } } else { @@ -172,7 +185,17 @@ pub fn generate( // assuming no bugs in the standard library) so they do not alter the generated source // output, while the `Structlike` type requires a new struct to be created and inserted // into the source definition, potentially inserting inner types as needed - CType::Binds(rtype) => Ok((rtype.clone(), out)), + CType::Binds(n, ts) => { + let mut genargs = Vec::new(); + for t in ts { + genargs.push(ctype_to_rtype(t, false)?); + } + if genargs.is_empty() { + Ok((n.clone(), out)) + } else { + Ok((format!("{}<{}>", n, genargs.join(", ")), out)) + } + } // TODO: The complexity of this function indicates more fundamental issues in the type // generation. This needs a rethink and rewrite. CType::Type(name, t) => match &**t { diff --git a/src/parse.rs b/src/parse.rs index 647cc5a0..ad761468 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -965,7 +965,7 @@ named_and!(types: Types => test!(types => pass "type Foo = bar: string;"; pass "type Foo = Bar"; - pass "type Result{T, Error} = BindsGeneric{'Result', T, Error};"; + pass "type Result{T, Error} = Binds{'Result', T, Error};"; pass "type ExitCode = Binds{'std::process::ExitCode'};"; pass "type{Windows} Path = driveLetter: string, pathsegments: Array{string};"; ); diff --git a/src/program/ctype.rs b/src/program/ctype.rs index 073cf6a1..6a12deb5 100644 --- a/src/program/ctype.rs +++ b/src/program/ctype.rs @@ -14,8 +14,7 @@ pub enum CType { Infer(String, String), // TODO: Switch to an Interface here once they exist Type(String, Box), Generic(String, Vec, Box), - Binds(String), - BindsGeneric(String, Vec), + Binds(String, Vec), IntrinsicGeneric(String, usize), Int(i128), Float(f64), @@ -76,12 +75,12 @@ impl CType { false => t.to_strict_string(strict), }, CType::Generic(n, a, _) => format!("{}{{{}}}", n, a.join(", ")), - CType::Binds(s) => s.to_string(), - CType::BindsGeneric(s, a) => format!( - "{}{{{}}}", - s, - a.iter() - .map(|b| b.to_strict_string(strict)) + CType::Binds(n, ts) => format!( + "Binds{{{}{}{}}}", + n, + if ts.is_empty() { "" } else { ", " }, + ts.iter() + .map(|t| t.to_strict_string(strict)) .collect::>() .join(", ") ), @@ -278,10 +277,10 @@ impl CType { CType::Infer(s, _) => s.clone(), // TODO: What to do here? CType::Type(_, t) => t.to_functional_string(), CType::Generic(n, gs, _) => format!("{}{{{}}}", n, gs.join(", ")), - CType::Binds(n) => n.clone(), - CType::BindsGeneric(n, ts) => format!( - "{}{{{}}}", + CType::Binds(n, ts) => format!( + "Binds{{{}{}{}}}", n, + if ts.is_empty() { "" } else { ", " }, ts.iter() .map(|t| t.to_functional_string()) .collect::>() @@ -517,7 +516,7 @@ impl CType { CType::Int(_) | CType::Float(_) => format!("_{}", self.to_functional_string()), CType::Type(n, t) => match **t { CType::Int(_) | CType::Float(_) => format!("_{}", self.to_functional_string()), - CType::Binds(_) => n.clone(), + CType::Binds(..) => n.clone(), _ => self.to_functional_string(), }, _ => self.to_functional_string(), @@ -537,8 +536,7 @@ impl CType { CType::Infer(s, i) => CType::Infer(s.clone(), i.clone()), CType::Type(n, t) => CType::Type(n.clone(), Box::new((*t).degroup())), CType::Generic(n, gs, wtos) => CType::Generic(n.clone(), gs.clone(), wtos.clone()), - CType::Binds(n) => CType::Binds(n.clone()), - CType::BindsGeneric(n, ts) => CType::BindsGeneric( + CType::Binds(n, ts) => CType::Binds( n.clone(), ts.iter().map(|t| t.degroup()).collect::>(), ), @@ -633,11 +631,11 @@ impl CType { arg.push(t1); input.push(t2); } - (Some(CType::Type(_, t1)), Some(b)) if !matches!(&**t1, CType::Binds(_)) => { + (Some(CType::Type(_, t1)), Some(b)) if !matches!(&**t1, CType::Binds(..)) => { arg.push(t1); input.push(b); } - (Some(a), Some(CType::Type(_, t2))) if !matches!(&**t2, CType::Binds(_)) => { + (Some(a), Some(CType::Type(_, t2))) if !matches!(&**t2, CType::Binds(..)) => { arg.push(a); input.push(t2); } @@ -648,16 +646,7 @@ impl CType { ) .into()); } - (Some(CType::Binds(n1)), Some(CType::Binds(n2))) => { - if n1 != n2 { - return Err(format!( - "Mismatched bound types {} and {} during inference", - n1, n2 - ) - .into()); - } - } - (Some(CType::BindsGeneric(n1, ts1)), Some(CType::BindsGeneric(n2, ts2))) => { + (Some(CType::Binds(n1, ts1)), Some(CType::Binds(n2, ts2))) => { if !(n1 == n2 && ts1.len() == ts2.len()) { // TODO: Better generic arg matching return Err(format!("Mismatched resolved bound generic types {}{{{}}} and {}{{{}}} during inference", n1, ts1.iter().map(|t| t.to_strict_string(false)).collect::>().join(", "), n2, ts2.iter().map(|t| t.to_strict_string(false)).collect::>().join(", ")).into()); @@ -1522,7 +1511,7 @@ impl CType { // will return this value as a string. let string = CType::Type( "string".to_string(), - Box::new(CType::Binds("String".to_string())), + Box::new(CType::Binds("String".to_string(), Vec::new())), ); fs.push(Function { name: n.clone(), @@ -1542,7 +1531,7 @@ impl CType { // Create an accessor function for this value, but do not add // it to the args array to construct it. The accessor function // will return this value as an i64. - let int64 = CType::Binds("i64".to_string()); + let int64 = CType::Binds("i64".to_string(), Vec::new()); fs.push(Function { name: n.clone(), args: vec![("arg0".to_string(), t.clone())], @@ -1558,7 +1547,7 @@ impl CType { // Create an accessor function for this value, but do not add // it to the args array to construct it. The accessor function // will return this value as an f64. - let float64 = CType::Binds("f64".to_string()); + let float64 = CType::Binds("f64".to_string(), Vec::new()); fs.push(Function { name: n.clone(), args: vec![("arg0".to_string(), t.clone())], @@ -1574,7 +1563,7 @@ impl CType { // Create an accessor function for this value, but do not add // it to the args array to construct it. The accessor function // will return this value as a bool. - let booln = CType::Binds("bool".to_string()); + let booln = CType::Binds("bool".to_string(), Vec::new()); fs.push(Function { name: n.clone(), args: vec![("arg0".to_string(), t.clone())], @@ -1656,7 +1645,7 @@ impl CType { // will return this value as a string. let string = CType::Type( "string".to_string(), - Box::new(CType::Binds("String".to_string())), + Box::new(CType::Binds("String".to_string(), Vec::new())), ); fs.push(Function { name: n.clone(), @@ -1673,7 +1662,7 @@ impl CType { // Create an accessor function for this value, but do not add // it to the args array to construct it. The accessor function // will return this value as an i64. - let int64 = CType::Binds("i64".to_string()); + let int64 = CType::Binds("i64".to_string(), Vec::new()); fs.push(Function { name: n.clone(), args: vec![("arg0".to_string(), t.clone())], @@ -1689,7 +1678,7 @@ impl CType { // Create an accessor function for this value, but do not add // it to the args array to construct it. The accessor function // will return this value as an f64. - let float64 = CType::Binds("f64".to_string()); + let float64 = CType::Binds("f64".to_string(), Vec::new()); fs.push(Function { name: n.clone(), args: vec![("arg0".to_string(), t.clone())], @@ -1705,7 +1694,7 @@ impl CType { // Create an accessor function for this value, but do not add // it to the args array to construct it. The accessor function // will return this value as a bool. - let booln = CType::Binds("bool".to_string()); + let booln = CType::Binds("bool".to_string(), Vec::new()); fs.push(Function { name: n.clone(), args: vec![("arg0".to_string(), t.clone())], @@ -1848,10 +1837,10 @@ impl CType { fs.push(Function { name: constructor_fn_name.clone(), args: Vec::new(), - rettype: CType::Binds("i64".to_string()), + rettype: CType::Binds("i64".to_string(), Vec::new()), microstatements: vec![Microstatement::Return { value: Some(Box::new(Microstatement::Value { - typen: CType::Binds("i64".to_string()), + typen: CType::Binds("i64".to_string(), Vec::new()), representation: format!("{}", i), })), }], @@ -1863,10 +1852,10 @@ impl CType { fs.push(Function { name: constructor_fn_name.clone(), args: Vec::new(), - rettype: CType::Binds("f64".to_string()), + rettype: CType::Binds("f64".to_string(), Vec::new()), microstatements: vec![Microstatement::Return { value: Some(Box::new(Microstatement::Value { - typen: CType::Binds("f64".to_string()), + typen: CType::Binds("f64".to_string(), Vec::new()), representation: format!("{}", f), })), }], @@ -1877,10 +1866,10 @@ impl CType { fs.push(Function { name: constructor_fn_name.clone(), args: Vec::new(), - rettype: CType::Binds("bool".to_string()), + rettype: CType::Binds("bool".to_string(), Vec::new()), microstatements: vec![Microstatement::Return { value: Some(Box::new(Microstatement::Value { - typen: CType::Binds("bool".to_string()), + typen: CType::Binds("bool".to_string(), Vec::new()), representation: match b { true => "true".to_string(), false => "false".to_string(), @@ -1894,10 +1883,10 @@ impl CType { fs.push(Function { name: constructor_fn_name.clone(), args: Vec::new(), - rettype: CType::Binds("String".to_string()), + rettype: CType::Binds("String".to_string(), Vec::new()), microstatements: vec![Microstatement::Return { value: Some(Box::new(Microstatement::Value { - typen: CType::Binds("String".to_string()), + typen: CType::Binds("String".to_string(), Vec::new()), representation: format!("\"{}\"", s.replace("\"", "\\\"")), })), }], @@ -2095,7 +2084,6 @@ impl CType { CType::Void | CType::Infer(..) | CType::Generic(..) - | CType::Binds(..) | CType::IntrinsicGeneric(..) | CType::Int(_) | CType::Float(_) @@ -2105,7 +2093,7 @@ impl CType { CType::Type(name, ct) => { CType::Type(name.clone(), Box::new(ct.swap_subtype(old_type, new_type))) } - CType::BindsGeneric(name, gen_type_resolved) => CType::BindsGeneric( + CType::Binds(name, gen_type_resolved) => CType::Binds( name.clone(), gen_type_resolved .iter() @@ -2287,16 +2275,6 @@ impl CType { } } pub fn binds(args: Vec) -> CType { - if args.len() != 1 { - CType::fail("Binds{T} takes only one argument, the string representation of the target-language type being bound") - } else { - match &args[0] { - CType::TString(s) => CType::Binds(s.clone()), - _ => CType::fail("Binds{T} accepts only strings"), - } - } - } - pub fn binds_generic(args: Vec) -> CType { let base_type = args[0].clone(); if let CType::TString(base) = base_type { let mut out_vec = Vec::new(); @@ -2304,9 +2282,9 @@ impl CType { for i in 1..args.len() { out_vec.push(args[i].clone()); } - CType::BindsGeneric(base.clone(), out_vec) + CType::Binds(base.clone(), out_vec) } else { - CType::fail("BindsGeneric{T, ...} must be given a string for the base type to bind"); + CType::fail("Binds{T, ...} must be given a string for the base type to bind"); } } // Special implementation for the tuple and either types since they *are* CTypes, but if one of @@ -3404,7 +3382,6 @@ pub fn typebaselist_to_ctype( // TODO: Is there a better way to do this? match name.as_str() { "Binds" => CType::binds(args.clone()), - "BindsGeneric" => CType::binds_generic(args.clone()), "Group" => CType::Group(Box::new(args[0].clone())), "Function" => CType::Function( Box::new(args[0].clone()), diff --git a/src/program/microstatement.rs b/src/program/microstatement.rs index 531e343d..73ab5ec5 100644 --- a/src/program/microstatement.rs +++ b/src/program/microstatement.rs @@ -291,7 +291,7 @@ pub fn baseassignablelist_to_microstatements<'a>( prior_value = Some(Microstatement::Value { typen: CType::Type( "bool".to_string(), - Box::new(CType::Binds("bool".to_string())), + Box::new(CType::Binds("bool".to_string(), Vec::new())), ), representation: b.clone(), }); @@ -300,7 +300,7 @@ pub fn baseassignablelist_to_microstatements<'a>( prior_value = Some(Microstatement::Value { typen: CType::Type( "string".to_string(), - Box::new(CType::Binds("String".to_string())), + Box::new(CType::Binds("String".to_string(), Vec::new())), ), representation: if s.starts_with('"') { s.clone() @@ -320,7 +320,7 @@ pub fn baseassignablelist_to_microstatements<'a>( // that accept them typen: CType::Type( "f64".to_string(), - Box::new(CType::Binds("f64".to_string())), + Box::new(CType::Binds("f64".to_string(), Vec::new())), ), representation: r.clone(), }); @@ -331,7 +331,7 @@ pub fn baseassignablelist_to_microstatements<'a>( // accept them typen: CType::Type( "i64".to_string(), - Box::new(CType::Binds("i64".to_string())), + Box::new(CType::Binds("i64".to_string(), Vec::new())), ), representation: i.clone(), }); diff --git a/src/program/scope.rs b/src/program/scope.rs index 66075019..f94a401e 100644 --- a/src/program/scope.rs +++ b/src/program/scope.rs @@ -93,13 +93,13 @@ impl<'a> Scope<'a> { // to construct their own types. match c.name.as_str() { "Type" | "Generic" | "Int" | "Float" | "Bool" | "String" => { /* Do nothing for the 'structural' types */ } - g @ ("Binds" | "Group" | "Array" | "Fail" | "Neg" | "Len" | "Size" | "FileStr" + g @ ("Group" | "Array" | "Fail" | "Neg" | "Len" | "Size" | "FileStr" | "Env" | "EnvExists" | "Not") => s = CType::from_generic(s, g, 1), g @ ("Function" | "Call" | "Field" | "Prop" | "Buffer" | "Add" | "Sub" | "Mul" | "Div" | "Mod" | "Pow" | "Min" | "Max" | "If" | "And" | "Or" | "Xor" | "Nand" | "Nor" | "Xnor" | "Eq" | "Neq" | "Lt" | "Lte" | "Gt" | "Gte") => s = CType::from_generic(s, g, 2), - g @ ("BindsGeneric" | "Tuple" | "Either" | "AnyOf") => s = CType::from_generic(s, g, 0), // Not kosher in Rust land, but 0 means "as many as we want" + g @ ("Binds" | "Tuple" | "Either" | "AnyOf") => s = CType::from_generic(s, g, 0), // Not kosher in Rust land, but 0 means "as many as we want" // TODO: Also add support for three arg `If` and `Env` with a // default property via overloading types unknown => { diff --git a/src/std/root.ln b/src/std/root.ln index 4e5cc667..ec4d75c7 100644 --- a/src/std/root.ln +++ b/src/std/root.ln @@ -8,8 +8,7 @@ // Declaration of the types the compiler-time type system is built on export ctype Type; // Any kind of concrete type export ctype Generic; // Any type that is a generic type (not yet realized into a concrete type) -export ctype Binds{T}; // A direct reference into the platform language's type system equivalen to a concrete type -export ctype BindsGeneric{T, N}; // A direct reference into the platform language's generic type system +export ctype Binds{T}; // A direct reference into the platform language's type system export ctype Int; // An integer used *to define a type*, like the length of a fixed array export ctype Float; // A float used to define a type. I have no idea why you'd want this, yet export ctype Bool; // A bool used to define a type. Heavily used for conditional compilation @@ -132,8 +131,8 @@ export type Duration = Binds{"std::time::Duration"}; export type uuid = Binds{"uuid::Uuid"}; // Binding the Dict and Set types -export type Dict{K, V} = BindsGeneric{"OrderedHashMap", K, V}; -export type Set{V} = BindsGeneric{"std::collections::HashSet", V}; +export type Dict{K, V} = Binds{"OrderedHashMap", K, V}; +export type Set{V} = Binds{"std::collections::HashSet", V}; /// Functions for (potentially) every type export fn clone{T} "clone" :: T -> T;