From 1d1ab1900300314c87f70e0adf7f9489673a35c6 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 15 Sep 2019 20:53:06 +0200 Subject: [PATCH 01/83] Added simple refinement types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For design, see https://github.com/PistonDevelopers/dyon/issues/636 - Added “typechk/refinement.dyon” - Added “typechk/refinement_2.dyon” --- assets/syntax.txt | 3 +- source/test.dyon | 12 +++-- source/typechk/refinement.dyon | 12 +++++ source/typechk/refinement_2.dyon | 12 +++++ src/ast/mod.rs | 5 ++ src/lifetime/kind.rs | 6 +++ src/lifetime/node.rs | 9 ++++ src/lifetime/typecheck.rs | 82 ++++++++++++++++++++++++++++++-- src/ty.rs | 27 +++++++++++ tests/lib.rs | 2 + 10 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 source/typechk/refinement.dyon create mode 100644 source/typechk/refinement_2.dyon diff --git a/assets/syntax.txt b/assets/syntax.txt index 1729d269..cea37431 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -17,9 +17,10 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" ["fn" .w! .."("!:"name" ?w "(" ?w args ?w ")" ?w ?currents ?w { ["->":"returns" ?w ?type:"ret_type"] !"->":!"returns" - } ?w block:"block"] + } ?w block:"block" ?.l([?w ty:"ty" ?w])] [.."("!:"name" ?w "(" ?w args ?w ")" ?w ?currents ?w "=" ?w expr:"expr"] } +4 ty = ["(" ?w .s?.(, type:"ty_arg") ?w ")" .w? "->" ?w type:"ty_ret"] 4 args = .s?.(, arg:"arg") 5 arg = [?"mut":"mut" ?w .._seps!:"name" ?[?w ":" ?w ?["'" ?w .._seps!:"lifetime"] ?w ?type:"type"]] diff --git a/source/test.dyon b/source/test.dyon index f6fbd621..ccad53ce 100644 --- a/source/test.dyon +++ b/source/test.dyon @@ -1,6 +1,12 @@ -norm(x: f64) = x+1 +// Use `any` implicitly. +fn foo(x) -> {return x + 1} +// Add simple refinement types to catch more errors. +(f64) -> f64 +(any) -> vec4 + +bar(x: f64) = x + 1 fn main() { - a := \(x: f64) = |x| - println(\a(2)) + x := bar(foo((1, 2))) + println(x) } diff --git a/source/typechk/refinement.dyon b/source/typechk/refinement.dyon new file mode 100644 index 00000000..5c47655a --- /dev/null +++ b/source/typechk/refinement.dyon @@ -0,0 +1,12 @@ +// Use `any` implicitly. +fn foo(x) -> {return x + 1} +// Add simple refinement types to catch more errors. +(f64) -> f64 +(vec4) -> vec4 + +bar(x: f64) = x + 1 + +fn main() { + x := bar(foo((1, 2))) + println(x) +} diff --git a/source/typechk/refinement_2.dyon b/source/typechk/refinement_2.dyon new file mode 100644 index 00000000..ccad53ce --- /dev/null +++ b/source/typechk/refinement_2.dyon @@ -0,0 +1,12 @@ +// Use `any` implicitly. +fn foo(x) -> {return x + 1} +// Add simple refinement types to catch more errors. +(f64) -> f64 +(any) -> vec4 + +bar(x: f64) = x + 1 + +fn main() { + x := bar(foo((1, 2))) + println(x) +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index cdb0e035..ecc60c73 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -416,6 +416,11 @@ impl Function { convert.update(range); expr = Some(val); ret = Some(Type::Any); + } else if let Ok(_) = convert.start_node("ty") { + // Ignore extra type information, + // since this is only used by type checker. + let range = convert.ignore(); + convert.update(range); } else { let range = convert.ignore(); convert.update(range); diff --git a/src/lifetime/kind.rs b/src/lifetime/kind.rs index 60af10ed..ffa3a81d 100644 --- a/src/lifetime/kind.rs +++ b/src/lifetime/kind.rs @@ -102,6 +102,9 @@ pub enum Kind { Grab, TryExpr, In, + Ty, + TyArg, + TyRet, } impl Kind { @@ -210,6 +213,9 @@ impl Kind { "grab" => Kind::Grab, "try_expr" => Kind::TryExpr, "in" => Kind::In, + "ty" => Kind::Ty, + "ty_arg" => Kind::TyArg, + "ty_ret" => Kind::TyRet, _ => return None }) } diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 36e634e5..62440f23 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -330,6 +330,15 @@ pub fn convert_meta_data( Kind::Min | Kind::MinIn | Kind::Max | Kind::MaxIn => Some(Type::Secret(Box::new(Type::F64))), Kind::For | Kind::ForN => Some(Type::Void), + Kind::TyArg | Kind::TyRet => { + // Parse extra type information. + let convert = Convert::new(&data[i..]); + if let Ok((_, val)) = Type::from_meta_data(kind_name, convert, ignored) { + Some(val) + } else { + None + } + } _ => None }; diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 99b8c441..1f12a43d 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -46,12 +46,23 @@ use ast::UseLookup; /// After type propagation, all blocks in the `if` expression should have some type information, /// but no further propagation is necessary, so it only need to check for consistency. pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> Result<(), Range> { + // Keep an extra todo-list for nodes that are affected by type refinement. + let mut todo: Vec = vec![]; // Type propagation. let mut changed; loop { changed = false; + // Prepare todo-list for binary search. + todo.sort(); + todo.dedup(); + // Keep track of the old length of the todo-list, + // in order to separate tasks already done from new tasks. + let todo_len = todo.len(); 'node: for i in 0..nodes.len() { - if nodes[i].ty.is_some() { continue; } + if nodes[i].ty.is_some() { + // Check if this node has been affected by type refinement. + if todo[..todo_len].binary_search(&i).is_err() {continue} + } let kind = nodes[i].kind; let mut this_ty = None; match kind { @@ -179,8 +190,51 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::Call => { if let Some(decl) = nodes[i].declaration { - if let Some(ref ty) = nodes[decl].ty { - this_ty = Some(ty.clone()); + if nodes[i].ty.is_none() || + !nodes[i].ty.as_ref().unwrap().is_concrete() || + todo.binary_search(&i).is_ok() { + // Refine types using extra type information. + let mut found = false; + for &ty in nodes[decl].children.iter() + .filter(|&&ty| nodes[ty].kind == Kind::Ty) + { + let mut all = true; + for (arg_expr, &ty_arg) in nodes[i].children.iter() + .filter(|&&arg| nodes[arg].kind == Kind::CallArg && + !nodes[arg].children.is_empty()) + .map(|&arg| nodes[arg].children[0]) + .zip(nodes[ty].children.iter() + .filter(|&&ty_arg| nodes[ty_arg].kind == Kind::TyArg)) + { + let found_arg = if let (&Some(ref a), &Some(ref b)) = + (&nodes[arg_expr].ty, &nodes[ty_arg].ty) {a.goes_with(b)} + else {false}; + if !found_arg { + all = false; + break; + } + } + if all { + if let Some(&ind) = nodes[ty].children.iter() + .filter(|&&ty| nodes[ty].kind == Kind::TyRet) + .next() { + this_ty = nodes[ind].ty.clone(); + found = true; + break; + } + } + } + if !found { + // Delay completion of this call until extra type information matches. + todo.push(i) + } + } + + // If the type has not been refined, fall back to default type signature. + if this_ty.is_none() && nodes[i].ty.is_none() { + if let Some(ref ty) = nodes[decl].ty { + this_ty = Some(ty.clone()); + } } } else if let Some(ref alias) = nodes[i].alias { use ast::FnAlias; @@ -519,10 +573,32 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> _ => {} } if this_ty.is_some() { + if let (&Some(ref old_ty), &Some(ref new_ty)) = + (&nodes[i].ty, &this_ty) { + if old_ty != new_ty { + // If type was refined, propagate changes to parent and children + // that are not of concrete types. + if let Some(parent) = nodes[i].parent { + if nodes[parent].ty.as_ref().map(|t| t.is_concrete()) != Some(true) { + todo.push(parent); + } + } + for &j in &nodes[i].children { + if nodes[j].ty.as_ref().map(|t| t.is_concrete()) != Some(true) { + todo.push(j); + } + } + } + } nodes[i].ty = this_ty; changed = true; } } + // Remove old tasks. + for i in (0..todo_len).rev() {todo.swap_remove(i);} + // There must be a change to continue checking, + // even with type refinement, because the todo-list + // waits for other changes to happen. if !changed { break; } } diff --git a/src/ty.rs b/src/ty.rs index 5defd44d..d8ab067b 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -138,6 +138,33 @@ impl Type { } } + /// Returns `true` if the type is concrete. + pub fn is_concrete(&self) -> bool { + match *self { + Type::Unreachable | + Type::Void | + Type::Bool | + Type::F64 | + Type::Vec4 | + Type::Mat4 | + Type::Str | + Type::Link | + Type::Object => true, + Type::Array(ref t) | + Type::Option(ref t) | + Type::Result(ref t) | + Type::Secret(ref t) | + Type::Thread(ref t) | + Type::In(ref t) | + Type::AdHoc(_, ref t) => t.is_concrete(), + Type::Closure(ref dfn) => { + dfn.tys.iter().all(|ty| ty.is_concrete()) && + dfn.ret.is_concrete() + } + Type::Any => false, + } + } + /// Returns an array type with an `any` as inner type. pub fn array() -> Type {Type::Array(Box::new(Type::Any))} diff --git a/tests/lib.rs b/tests/lib.rs index 9be67d76..58c40111 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -221,6 +221,8 @@ fn test_typechk() { test_src("source/typechk/mat4_2.dyon"); test_src("source/typechk/ind_arr.dyon"); test_fail_src("source/typechk/norm.dyon"); + test_fail_src("source/typechk/refinement.dyon"); + test_fail_src("source/typechk/refinement_2.dyon"); } #[test] From 288f05548dbd454f73a883347a7e050bd03de170 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 16 Sep 2019 06:12:06 +0200 Subject: [PATCH 02/83] Skip meta data content of extra type information --- src/lifetime/node.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 62440f23..a08be6f8 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -333,7 +333,9 @@ pub fn convert_meta_data( Kind::TyArg | Kind::TyRet => { // Parse extra type information. let convert = Convert::new(&data[i..]); - if let Ok((_, val)) = Type::from_meta_data(kind_name, convert, ignored) { + if let Ok((range, val)) = Type::from_meta_data(kind_name, convert, ignored) { + // Skip content of type meta data until end node. + skip = Some(range.next_offset() + i - 1); Some(val) } else { None From 15eafdc81367de5c53eee8de9bf201152d932d32 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 16 Sep 2019 06:13:50 +0200 Subject: [PATCH 03/83] Do not propagate refineent changes to node children --- src/lifetime/typecheck.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 1f12a43d..47019b21 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -583,11 +583,6 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> todo.push(parent); } } - for &j in &nodes[i].children { - if nodes[j].ty.as_ref().map(|t| t.is_concrete()) != Some(true) { - todo.push(j); - } - } } } nodes[i].ty = this_ty; From fa9ac22a68dcd20c1a498574bd1178b69bb66876 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 16 Sep 2019 07:05:28 +0200 Subject: [PATCH 04/83] Make refinement propagate across item declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_3.dyon” --- source/typechk/refinement_3.dyon | 9 +++++++++ src/lifetime/typecheck.rs | 30 +++++++++++++++++++++++++++--- tests/lib.rs | 1 + 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 source/typechk/refinement_3.dyon diff --git a/source/typechk/refinement_3.dyon b/source/typechk/refinement_3.dyon new file mode 100644 index 00000000..bb66fd7a --- /dev/null +++ b/source/typechk/refinement_3.dyon @@ -0,0 +1,9 @@ +fn add(a, b) -> {return a + b} +(vec4, vec4) -> vec4 + +fn check_f64(_: f64) {} + +fn main() { + b := add((1, 2), (3, 4)) + check_f64(b) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 47019b21..cce74975 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -286,6 +286,28 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> this_ty = Some(Type::Void); Some(right_ty.clone()) } + (&Some(ref left_ty), &Some(ref right_ty)) => { + if right_ty.goes_with(left_ty) { + if !nodes[left].children.is_empty() { + // Tell the item that it needs refinement. + let it = nodes[left].children[0]; + todo.push(it); + // Tell all nodes that uses the item as declaration that + // they need refinement. + for j in it+1..nodes.len() { + if let Some(decl) = nodes[j].declaration { + if decl == it {todo.push(j)} + } + } + } + this_ty = Some(Type::Void); + Some(right_ty.clone()) + } else { + // TODO: Type conflict between left and refined right. + // Might be caught by later rules. + continue + } + } _ => { continue } }; changed = true; @@ -576,10 +598,12 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> if let (&Some(ref old_ty), &Some(ref new_ty)) = (&nodes[i].ty, &this_ty) { if old_ty != new_ty { - // If type was refined, propagate changes to parent and children - // that are not of concrete types. + // If type was refined, propagate changes to parent. if let Some(parent) = nodes[i].parent { - if nodes[parent].ty.as_ref().map(|t| t.is_concrete()) != Some(true) { + // If the type of the parent is not set, + // then there is no need to add it to the todo-list, + // since it will be covered by the default loop. + if nodes[parent].ty.is_some() { todo.push(parent); } } diff --git a/tests/lib.rs b/tests/lib.rs index 58c40111..cce07238 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -223,6 +223,7 @@ fn test_typechk() { test_fail_src("source/typechk/norm.dyon"); test_fail_src("source/typechk/refinement.dyon"); test_fail_src("source/typechk/refinement_2.dyon"); + test_fail_src("source/typechk/refinement_3.dyon"); } #[test] From 30c0eb172c5321beb03ee82e41078a3eea4bbf8e Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 16 Sep 2019 14:47:55 +0200 Subject: [PATCH 05/83] Remove condition for refinement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_4.dyon” --- source/typechk/refinement_4.dyon | 23 ++++++++++++ src/lifetime/typecheck.rs | 64 +++++++++++++++----------------- tests/lib.rs | 1 + 3 files changed, 54 insertions(+), 34 deletions(-) create mode 100644 source/typechk/refinement_4.dyon diff --git a/source/typechk/refinement_4.dyon b/source/typechk/refinement_4.dyon new file mode 100644 index 00000000..ba0ca520 --- /dev/null +++ b/source/typechk/refinement_4.dyon @@ -0,0 +1,23 @@ +// Turn a name string into a hero object. +fn hero(name: str) -> {} {return clone({name: name})} +// Simple refinement types are specified after function declaration. +// This provides the type checker with extra type information. +// +// Refine `Super str` to `SuperHero {}` object. +(Super str) -> SuperHero {} +// Refine `Duper str` to `DuperHero {}` object. +(Duper str) -> DuperHero {} + +// Create ad-hoc type `Super str`. +fn new_super(name: str) -> Super str {return clone(name)} +// Create ad-hoc type `Duper str`. +fn new_duper(name: str) -> Duper str {return clone(name)} + +// Check for ad-hoc type `SuperHero`. +fn check_super_hero(_: SuperHero) {} +// Check for ad-hoc type `DuperHero`. +fn check_duper_hero(_: DuperHero) {} + +fn main() { + check_duper_hero(hero(new_super("Mr. X"))) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index cce74975..699c9415 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -190,45 +190,41 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::Call => { if let Some(decl) = nodes[i].declaration { - if nodes[i].ty.is_none() || - !nodes[i].ty.as_ref().unwrap().is_concrete() || - todo.binary_search(&i).is_ok() { - // Refine types using extra type information. - let mut found = false; - for &ty in nodes[decl].children.iter() - .filter(|&&ty| nodes[ty].kind == Kind::Ty) + // Refine types using extra type information. + let mut found = false; + for &ty in nodes[decl].children.iter() + .filter(|&&ty| nodes[ty].kind == Kind::Ty) + { + let mut all = true; + for (arg_expr, &ty_arg) in nodes[i].children.iter() + .filter(|&&arg| nodes[arg].kind == Kind::CallArg && + !nodes[arg].children.is_empty()) + .map(|&arg| nodes[arg].children[0]) + .zip(nodes[ty].children.iter() + .filter(|&&ty_arg| nodes[ty_arg].kind == Kind::TyArg)) { - let mut all = true; - for (arg_expr, &ty_arg) in nodes[i].children.iter() - .filter(|&&arg| nodes[arg].kind == Kind::CallArg && - !nodes[arg].children.is_empty()) - .map(|&arg| nodes[arg].children[0]) - .zip(nodes[ty].children.iter() - .filter(|&&ty_arg| nodes[ty_arg].kind == Kind::TyArg)) - { - let found_arg = if let (&Some(ref a), &Some(ref b)) = - (&nodes[arg_expr].ty, &nodes[ty_arg].ty) {a.goes_with(b)} - else {false}; - if !found_arg { - all = false; - break; - } - } - if all { - if let Some(&ind) = nodes[ty].children.iter() - .filter(|&&ty| nodes[ty].kind == Kind::TyRet) - .next() { - this_ty = nodes[ind].ty.clone(); - found = true; - break; - } + let found_arg = if let (&Some(ref a), &Some(ref b)) = + (&nodes[arg_expr].ty, &nodes[ty_arg].ty) {a.goes_with(b)} + else {false}; + if !found_arg { + all = false; + break; } } - if !found { - // Delay completion of this call until extra type information matches. - todo.push(i) + if all { + if let Some(&ind) = nodes[ty].children.iter() + .filter(|&&ty| nodes[ty].kind == Kind::TyRet) + .next() { + this_ty = nodes[ind].ty.clone(); + found = true; + break; + } } } + if !found { + // Delay completion of this call until extra type information matches. + todo.push(i); + } // If the type has not been refined, fall back to default type signature. if this_ty.is_none() && nodes[i].ty.is_none() { diff --git a/tests/lib.rs b/tests/lib.rs index cce07238..0fa2a9ce 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -224,6 +224,7 @@ fn test_typechk() { test_fail_src("source/typechk/refinement.dyon"); test_fail_src("source/typechk/refinement_2.dyon"); test_fail_src("source/typechk/refinement_3.dyon"); + test_fail_src("source/typechk/refinement_4.dyon"); } #[test] From fb282edd37cdbda85b80c3080f0c692699389961 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 16 Sep 2019 15:50:35 +0200 Subject: [PATCH 06/83] Handle ambiguity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed `Type::is_concrete` - Added “typechk/refinement_5.dyon” --- source/typechk/refinement_5.dyon | 24 +++++++++++++++++ src/lifetime/typecheck.rs | 9 +++++-- src/ty.rs | 44 ++++++++++++-------------------- tests/lib.rs | 1 + 4 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 source/typechk/refinement_5.dyon diff --git a/source/typechk/refinement_5.dyon b/source/typechk/refinement_5.dyon new file mode 100644 index 00000000..03b04bf3 --- /dev/null +++ b/source/typechk/refinement_5.dyon @@ -0,0 +1,24 @@ +// Turn a name string into a hero object. +fn hero(name: str) -> {} {return clone({name: name})} +// Simple refinement types are specified after function declaration. +// This provides the type checker with extra type information. +// +// Refine `Super str` to `SuperHero {}` object. +(Super str) -> SuperHero {} +// Refine `Duper str` to `DuperHero {}` object. +(Duper str) -> DuperHero {} + +// Create ad-hoc type `Super str`. +fn new_super(name: str) -> Super str {return clone(name)} +// Create ad-hoc type `Duper str`. +fn new_duper(name: str) -> Duper str {return clone(name)} + +// Check for ad-hoc type `SuperHero`. +fn check_super_hero(_: SuperHero) {} +// Check for ad-hoc type `DuperHero`. +fn check_duper_hero(_: DuperHero) {} + +fn main() { + check_super_hero(hero("Mr. X")) + check_duper_hero(hero("Mr. X")) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 699c9415..81cd6daa 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -192,7 +192,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> if let Some(decl) = nodes[i].declaration { // Refine types using extra type information. let mut found = false; - for &ty in nodes[decl].children.iter() + 'outer: for &ty in nodes[decl].children.iter() .filter(|&&ty| nodes[ty].kind == Kind::Ty) { let mut all = true; @@ -204,7 +204,12 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> .filter(|&&ty_arg| nodes[ty_arg].kind == Kind::TyArg)) { let found_arg = if let (&Some(ref a), &Some(ref b)) = - (&nodes[arg_expr].ty, &nodes[ty_arg].ty) {a.goes_with(b)} + (&nodes[arg_expr].ty, &nodes[ty_arg].ty) { + if a.goes_with(b) { + if a.ambiguous(b) {break 'outer} + true + } else {false} + } else {false}; if !found_arg { all = false; diff --git a/src/ty.rs b/src/ty.rs index d8ab067b..a2eb1f0f 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -138,33 +138,6 @@ impl Type { } } - /// Returns `true` if the type is concrete. - pub fn is_concrete(&self) -> bool { - match *self { - Type::Unreachable | - Type::Void | - Type::Bool | - Type::F64 | - Type::Vec4 | - Type::Mat4 | - Type::Str | - Type::Link | - Type::Object => true, - Type::Array(ref t) | - Type::Option(ref t) | - Type::Result(ref t) | - Type::Secret(ref t) | - Type::Thread(ref t) | - Type::In(ref t) | - Type::AdHoc(_, ref t) => t.is_concrete(), - Type::Closure(ref dfn) => { - dfn.tys.iter().all(|ty| ty.is_concrete()) && - dfn.ret.is_concrete() - } - Type::Any => false, - } - } - /// Returns an array type with an `any` as inner type. pub fn array() -> Type {Type::Array(Box::new(Type::Any))} @@ -183,6 +156,21 @@ impl Type { /// Returns an in-type with an `any` as inner type. pub fn in_ty() -> Type {Type::In(Box::new(Type::Any))} + /// Returns `true` if a type is ambiguous relative to a refinement type. + /// + /// For example, the type `str` is ambiguous with ad-hoc type `Foo str`. + /// If more was known about the `str` type with further refinement, + /// then it might turn out to be `Bar str` which would not go with `Foo str`. + pub fn ambiguous(&self, refine: &Type) -> bool { + use self::Type::*; + + match (self, refine) { + (&AdHoc(ref xa, ref xb), &AdHoc(ref ya, ref yb)) if xa == ya => xb.ambiguous(yb), + (x, &AdHoc(_, ref y)) if x.goes_with(y) => true, + _ => false + } + } + /// Returns `true` if a type goes with another type (directional check). /// /// - `bool` (argument) goes with `sec[bool]` (value) @@ -290,7 +278,7 @@ impl Type { ty.goes_with(other) } } - // Bool, F64, Text, Vec4, AdHoc. + // Bool, F64, Text, Vec4. x if x == other => { true } _ if *other == Type::Any => { true } _ => { false } diff --git a/tests/lib.rs b/tests/lib.rs index 0fa2a9ce..411ad8bf 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -225,6 +225,7 @@ fn test_typechk() { test_fail_src("source/typechk/refinement_2.dyon"); test_fail_src("source/typechk/refinement_3.dyon"); test_fail_src("source/typechk/refinement_4.dyon"); + test_src("source/typechk/refinement_5.dyon"); } #[test] From 7470d32690975e505b91e14a800a7dcb7186eeed Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 14:18:41 +0200 Subject: [PATCH 07/83] Delay premature error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed bug `a.goes_with(b)` => `b.goes_with(a)` - Added “typechk/refinement_6.dyon” - Added “typechk/refinement_7.dyon” --- source/typechk/refinement_6.dyon | 8 ++++++++ source/typechk/refinement_7.dyon | 8 ++++++++ src/lifetime/typecheck.rs | 29 ++++++++++++++++++++++++----- tests/lib.rs | 2 ++ 4 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 source/typechk/refinement_6.dyon create mode 100644 source/typechk/refinement_7.dyon diff --git a/source/typechk/refinement_6.dyon b/source/typechk/refinement_6.dyon new file mode 100644 index 00000000..b77c6ca0 --- /dev/null +++ b/source/typechk/refinement_6.dyon @@ -0,0 +1,8 @@ +fn foo(a: bool) -> bool {return clone(a)} +(sec[bool]) -> sec[bool] + +fn check_sec(a: sec[bool]) {} + +fn main() { + check_sec(foo(explain_why(true, ""))) +} diff --git a/source/typechk/refinement_7.dyon b/source/typechk/refinement_7.dyon new file mode 100644 index 00000000..5ce81965 --- /dev/null +++ b/source/typechk/refinement_7.dyon @@ -0,0 +1,8 @@ +fn foo(a: bool) -> bool {return clone(a)} +(sec[bool]) -> sec[bool] + +fn check_sec(a: sec[bool]) {} + +fn main() { + check_sec(foo(true)) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 81cd6daa..103ea46b 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -46,8 +46,13 @@ use ast::UseLookup; /// After type propagation, all blocks in the `if` expression should have some type information, /// but no further propagation is necessary, so it only need to check for consistency. pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> Result<(), Range> { + use std::collections::HashMap; + // Keep an extra todo-list for nodes that are affected by type refinement. let mut todo: Vec = vec![]; + // Keep an extra delay-errors map for nodes that should not report an error after all, + // if the type refined turned out to match. + let mut delay_errs: HashMap> = HashMap::new(); // Type propagation. let mut changed; loop { @@ -93,6 +98,11 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> let ch = nodes[i].children[0]; let expr_type = nodes[ch].ty.as_ref().map(|ty| nodes[i].inner_type(&ty)); if let Some(parent) = nodes[i].parent { + // Remove previous delay errors. + if delay_errs.contains_key(&i) { + delay_errs.remove(&i); + } + // Take into account swizzling for the declared argument position. let j = { let mut sum = 0; @@ -146,10 +156,13 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> match (&expr_type, &nodes[arg].ty) { (&Some(ref ch_ty), &Some(ref arg_ty)) => { if !arg_ty.goes_with(ch_ty) { - return Err(nodes[i].source.wrap( - format!("Type mismatch (#100):\n\ - Expected `{}`, found `{}`", - arg_ty.description(), ch_ty.description()))); + if !delay_errs.contains_key(&i) { + delay_errs.insert(i, nodes[i].source.wrap( + format!("Type mismatch (#100):\n\ + Expected `{}`, found `{}`", + arg_ty.description(), ch_ty.description()))); + } + continue 'node; } } (&None, _) | (_, &None) => {} @@ -205,7 +218,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> { let found_arg = if let (&Some(ref a), &Some(ref b)) = (&nodes[arg_expr].ty, &nodes[ty_arg].ty) { - if a.goes_with(b) { + if b.goes_with(a) { if a.ambiguous(b) {break 'outer} true } else {false} @@ -614,6 +627,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> changed = true; } } + // Remove old tasks. for i in (0..todo_len).rev() {todo.swap_remove(i);} // There must be a change to continue checking, @@ -622,6 +636,11 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> if !changed { break; } } + // Report one delayed error, if any. + if delay_errs.len() > 0 { + return Err(delay_errs.values().next().unwrap().clone()) + } + // After type propagation. for i in 0..nodes.len() { let kind = nodes[i].kind; diff --git a/tests/lib.rs b/tests/lib.rs index 411ad8bf..f4b02d04 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -226,6 +226,8 @@ fn test_typechk() { test_fail_src("source/typechk/refinement_3.dyon"); test_fail_src("source/typechk/refinement_4.dyon"); test_src("source/typechk/refinement_5.dyon"); + test_src("source/typechk/refinement_6.dyon"); + test_fail_src("source/typechk/refinement_7.dyon"); } #[test] From 17091b67d67341505ac8de6433f020b48d7fccd2 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 14:51:49 +0200 Subject: [PATCH 08/83] Make `std` namespace for standard external functions Closes https://github.com/PistonDevelopers/dyon/issues/639 --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1c0eea5b..c2910997 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -361,6 +361,7 @@ impl Module { use dyon_std::*; let mut m = Module::empty(); + m.ns("std"); m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); @@ -524,6 +525,7 @@ impl Module { m.add_str("wait_next", wait_next, Dfn::nl(vec![Type::in_ty()], Any)); m.add_str("next", next, Dfn::nl(vec![Type::in_ty()], Type::option())); + m.no_ns(); m } From 6019448310bc4a8093c4b1227ae80e5f181692a6 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:05:40 +0200 Subject: [PATCH 09/83] Make `f64` ambiguous with `any` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Swap order of `Type::ambiguous` - Added “typechk/refinement_8.dyon” - Added “typechk/refinement_9.dyon” --- source/typechk/refinement_8.dyon | 7 +++++++ source/typechk/refinement_9.dyon | 11 +++++++++++ src/lifetime/typecheck.rs | 2 +- src/ty.rs | 10 ++++++---- tests/lib.rs | 2 ++ 5 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 source/typechk/refinement_8.dyon create mode 100644 source/typechk/refinement_9.dyon diff --git a/source/typechk/refinement_8.dyon b/source/typechk/refinement_8.dyon new file mode 100644 index 00000000..bb9ae16c --- /dev/null +++ b/source/typechk/refinement_8.dyon @@ -0,0 +1,7 @@ +fn check_f64(_: f64) {} +fn foo(a) -> {return clone(a)} +([]) -> [f64] + +fn main() { + check_f64(foo([1, 2, 3])) +} diff --git a/source/typechk/refinement_9.dyon b/source/typechk/refinement_9.dyon new file mode 100644 index 00000000..5e723a5a --- /dev/null +++ b/source/typechk/refinement_9.dyon @@ -0,0 +1,11 @@ +fn check_f64(_: f64) {} +fn foo(a) -> {return clone(a)} +([f64]) -> [f64] +fn bar(a: [f64]) -> [] {return clone(a)} + +fn main() { + // `bar` returns an ambiguous array type `[]`, + // so `foo` should not refine the type, + // which should make `check_f64` not detect a type error. + check_f64(foo(bar([1, 2, 3]))) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 103ea46b..b55f4df5 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -219,7 +219,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> let found_arg = if let (&Some(ref a), &Some(ref b)) = (&nodes[arg_expr].ty, &nodes[ty_arg].ty) { if b.goes_with(a) { - if a.ambiguous(b) {break 'outer} + if b.ambiguous(a) {break 'outer} true } else {false} } diff --git a/src/ty.rs b/src/ty.rs index a2eb1f0f..4769608c 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -156,17 +156,19 @@ impl Type { /// Returns an in-type with an `any` as inner type. pub fn in_ty() -> Type {Type::In(Box::new(Type::Any))} - /// Returns `true` if a type is ambiguous relative to a refinement type. + /// Returns `true` if a type is ambiguous relative to a refinement type (directional check). /// - /// For example, the type `str` is ambiguous with ad-hoc type `Foo str`. + /// For example, the type ad-hoc type `Foo str` is ambiguous with type `str`. /// If more was known about the `str` type with further refinement, - /// then it might turn out to be `Bar str` which would not go with `Foo str`. + /// then it might turn out to be `Bar str`, which triggers a collision. pub fn ambiguous(&self, refine: &Type) -> bool { use self::Type::*; match (self, refine) { (&AdHoc(ref xa, ref xb), &AdHoc(ref ya, ref yb)) if xa == ya => xb.ambiguous(yb), - (x, &AdHoc(_, ref y)) if x.goes_with(y) => true, + (&AdHoc(_, ref x), y) if x.goes_with(y) => true, + (&Array(ref x), &Array(ref y)) if x.ambiguous(y) => true, + (&F64, &Any) => true, _ => false } } diff --git a/tests/lib.rs b/tests/lib.rs index f4b02d04..67632f02 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -228,6 +228,8 @@ fn test_typechk() { test_src("source/typechk/refinement_5.dyon"); test_src("source/typechk/refinement_6.dyon"); test_fail_src("source/typechk/refinement_7.dyon"); + test_fail_src("source/typechk/refinement_8.dyon"); + test_src("source/typechk/refinement_9.dyon"); } #[test] From 10ac60ef3e7a95c5380c8c1af5c1f14e86b0dfe6 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:14:45 +0200 Subject: [PATCH 10/83] Make `bool` ambiguous with `any` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_10.dyon” - Added “typechk/refinement_11.dyon” --- source/typechk/refinement_10.dyon | 7 +++++++ source/typechk/refinement_11.dyon | 11 +++++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 21 insertions(+) create mode 100644 source/typechk/refinement_10.dyon create mode 100644 source/typechk/refinement_11.dyon diff --git a/source/typechk/refinement_10.dyon b/source/typechk/refinement_10.dyon new file mode 100644 index 00000000..1bc91b31 --- /dev/null +++ b/source/typechk/refinement_10.dyon @@ -0,0 +1,7 @@ +fn check_bool(_: bool) {} +fn foo(a) -> {return clone(a)} +([]) -> [bool] + +fn main() { + check_bool(foo([true, true, false])) +} diff --git a/source/typechk/refinement_11.dyon b/source/typechk/refinement_11.dyon new file mode 100644 index 00000000..a0ec5349 --- /dev/null +++ b/source/typechk/refinement_11.dyon @@ -0,0 +1,11 @@ +fn check_bool(_: bool) {} +fn foo(a) -> {return clone(a)} +([bool]) -> [bool] +fn bar(a: [bool]) -> [] {return clone(a)} + +fn main() { + // `bar` returns an ambiguous array type `[]`, + // so `foo` should not refine type type, + // which should make `check_bool` not detect a type error. + check_bool(foo(bar([true, true, false]))) +} diff --git a/src/ty.rs b/src/ty.rs index 4769608c..3301cdf2 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -168,6 +168,7 @@ impl Type { (&AdHoc(ref xa, ref xb), &AdHoc(ref ya, ref yb)) if xa == ya => xb.ambiguous(yb), (&AdHoc(_, ref x), y) if x.goes_with(y) => true, (&Array(ref x), &Array(ref y)) if x.ambiguous(y) => true, + (&Bool, &Any) => true, (&F64, &Any) => true, _ => false } diff --git a/tests/lib.rs b/tests/lib.rs index 67632f02..09738f5a 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -230,6 +230,8 @@ fn test_typechk() { test_fail_src("source/typechk/refinement_7.dyon"); test_fail_src("source/typechk/refinement_8.dyon"); test_src("source/typechk/refinement_9.dyon"); + test_fail_src("source/typechk/refinement_10.dyon"); + test_src("source/typechk/refinement_11.dyon"); } #[test] From afac0dc4f1f2be3eebd2fcc3edebec0bfc7f87e1 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:20:29 +0200 Subject: [PATCH 11/83] Make `str` ambiguous with `any` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_12.dyon” - Added “typechk/refinement_13.dyon” --- source/typechk/refinement_12.dyon | 7 +++++++ source/typechk/refinement_13.dyon | 8 ++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 18 insertions(+) create mode 100644 source/typechk/refinement_12.dyon create mode 100644 source/typechk/refinement_13.dyon diff --git a/source/typechk/refinement_12.dyon b/source/typechk/refinement_12.dyon new file mode 100644 index 00000000..fd93245c --- /dev/null +++ b/source/typechk/refinement_12.dyon @@ -0,0 +1,7 @@ +fn check_str(_: str) {} +fn foo(a) -> {return clone(a)} +([]) -> [str] + +fn main() { + check_str(foo(["one", "two", "three"])) +} diff --git a/source/typechk/refinement_13.dyon b/source/typechk/refinement_13.dyon new file mode 100644 index 00000000..4d38b58e --- /dev/null +++ b/source/typechk/refinement_13.dyon @@ -0,0 +1,8 @@ +fn check_str(_: str) {} +fn foo(a) -> {return clone(a)} +([str]) -> [str] +fn bar(a: [str]) -> [] {return clone(a)} + +fn main() { + check_str(foo(bar(["one", "two", "three"]))) +} diff --git a/src/ty.rs b/src/ty.rs index 3301cdf2..573892f8 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -170,6 +170,7 @@ impl Type { (&Array(ref x), &Array(ref y)) if x.ambiguous(y) => true, (&Bool, &Any) => true, (&F64, &Any) => true, + (&Str, &Any) => true, _ => false } } diff --git a/tests/lib.rs b/tests/lib.rs index 09738f5a..cc3f0e9c 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -232,6 +232,8 @@ fn test_typechk() { test_src("source/typechk/refinement_9.dyon"); test_fail_src("source/typechk/refinement_10.dyon"); test_src("source/typechk/refinement_11.dyon"); + test_fail_src("source/typechk/refinement_12.dyon"); + test_src("source/typechk/refinement_13.dyon"); } #[test] From 9647ecb22150d41ca5bdc7976bfa406233132b14 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:26:15 +0200 Subject: [PATCH 12/83] Make `mat4` ambiguous with `any` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_14.dyon” - Added “typechk/refinement_15.dyon” --- source/typechk/refinement_14.dyon | 7 +++++++ source/typechk/refinement_15.dyon | 8 ++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 18 insertions(+) create mode 100644 source/typechk/refinement_14.dyon create mode 100644 source/typechk/refinement_15.dyon diff --git a/source/typechk/refinement_14.dyon b/source/typechk/refinement_14.dyon new file mode 100644 index 00000000..c165a5f3 --- /dev/null +++ b/source/typechk/refinement_14.dyon @@ -0,0 +1,7 @@ +fn check_mat4(_: mat4) {} +fn foo(a) -> {return clone(a)} +([]) -> [mat4] + +fn main() { + check_mat4(foo([mat4 {1,;}])) +} diff --git a/source/typechk/refinement_15.dyon b/source/typechk/refinement_15.dyon new file mode 100644 index 00000000..78dbe742 --- /dev/null +++ b/source/typechk/refinement_15.dyon @@ -0,0 +1,8 @@ +fn check_mat4(_: mat4) {} +fn foo(a) -> {return clone(a)} +([mat4]) -> [mat4] +fn bar(a: [mat4]) -> [] {return clone(a)} + +fn main() { + check_mat4(foo(bar([mat4 {1,;}]))) +} diff --git a/src/ty.rs b/src/ty.rs index 573892f8..9187397b 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -171,6 +171,7 @@ impl Type { (&Bool, &Any) => true, (&F64, &Any) => true, (&Str, &Any) => true, + (&Mat4, &Any) => true, _ => false } } diff --git a/tests/lib.rs b/tests/lib.rs index cc3f0e9c..163360d4 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -234,6 +234,8 @@ fn test_typechk() { test_src("source/typechk/refinement_11.dyon"); test_fail_src("source/typechk/refinement_12.dyon"); test_src("source/typechk/refinement_13.dyon"); + test_fail_src("source/typechk/refinement_14.dyon"); + test_src("source/typechk/refinement_15.dyon"); } #[test] From 79de1d3c431805f3bb215e2e51f597fac45b8768 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:31:27 +0200 Subject: [PATCH 13/83] Make `vec4` ambiguous with `any` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_16.dyon” - Added “typechk/refinement_17.dyon” --- source/typechk/refinement_16.dyon | 7 +++++++ source/typechk/refinement_17.dyon | 8 ++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 18 insertions(+) create mode 100644 source/typechk/refinement_16.dyon create mode 100644 source/typechk/refinement_17.dyon diff --git a/source/typechk/refinement_16.dyon b/source/typechk/refinement_16.dyon new file mode 100644 index 00000000..fdfbf86d --- /dev/null +++ b/source/typechk/refinement_16.dyon @@ -0,0 +1,7 @@ +fn check_vec4(_: vec4) {} +fn foo(a) -> {return clone(a)} +([]) -> [vec4] + +fn main() { + check_vec4(foo([(1, 0)])) +} diff --git a/source/typechk/refinement_17.dyon b/source/typechk/refinement_17.dyon new file mode 100644 index 00000000..08613624 --- /dev/null +++ b/source/typechk/refinement_17.dyon @@ -0,0 +1,8 @@ +fn check_vec4(_: vec4) {} +fn foo(a) -> {return clone(a)} +([vec4]) -> [vec4] +fn bar(a: [vec4]) -> [] {return clone(a)} + +fn main() { + check_vec4(foo(bar([(1, 0)]))) +} diff --git a/src/ty.rs b/src/ty.rs index 9187397b..e90dde4a 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -171,6 +171,7 @@ impl Type { (&Bool, &Any) => true, (&F64, &Any) => true, (&Str, &Any) => true, + (&Vec4, &Any) => true, (&Mat4, &Any) => true, _ => false } diff --git a/tests/lib.rs b/tests/lib.rs index 163360d4..60bca4df 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -236,6 +236,8 @@ fn test_typechk() { test_src("source/typechk/refinement_13.dyon"); test_fail_src("source/typechk/refinement_14.dyon"); test_src("source/typechk/refinement_15.dyon"); + test_fail_src("source/typechk/refinement_16.dyon"); + test_src("source/typechk/refinement_17.dyon"); } #[test] From abb26ec670b02e642f47a1221ca14ab83f01facc Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:40:24 +0200 Subject: [PATCH 14/83] Make `link` ambiguous with `any` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_18.dyon” - Added “typechk/refinement_19.dyon” --- source/typechk/refinement_18.dyon | 7 +++++++ source/typechk/refinement_19.dyon | 8 ++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 18 insertions(+) create mode 100644 source/typechk/refinement_18.dyon create mode 100644 source/typechk/refinement_19.dyon diff --git a/source/typechk/refinement_18.dyon b/source/typechk/refinement_18.dyon new file mode 100644 index 00000000..375025e6 --- /dev/null +++ b/source/typechk/refinement_18.dyon @@ -0,0 +1,7 @@ +fn check_link(_: link) {} +fn foo(a) -> {return clone(a)} +([]) -> [link] + +fn main() { + check_link(foo([link {"hi"}])) +} diff --git a/source/typechk/refinement_19.dyon b/source/typechk/refinement_19.dyon new file mode 100644 index 00000000..5fe62bd0 --- /dev/null +++ b/source/typechk/refinement_19.dyon @@ -0,0 +1,8 @@ +fn check_link(_: link) {} +fn foo(a) -> {return clone(a)} +([link]) -> [link] +fn bar(a: [link]) -> [] {return clone(a)} + +fn main() { + check_link(foo(bar([link {"hi"}]))) +} diff --git a/src/ty.rs b/src/ty.rs index e90dde4a..8b4765a2 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -173,6 +173,7 @@ impl Type { (&Str, &Any) => true, (&Vec4, &Any) => true, (&Mat4, &Any) => true, + (&Link, &Any) => true, _ => false } } diff --git a/tests/lib.rs b/tests/lib.rs index 60bca4df..b438f36b 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -238,6 +238,8 @@ fn test_typechk() { test_src("source/typechk/refinement_15.dyon"); test_fail_src("source/typechk/refinement_16.dyon"); test_src("source/typechk/refinement_17.dyon"); + test_fail_src("source/typechk/refinement_18.dyon"); + test_src("source/typechk/refinement_19.dyon"); } #[test] From e7bcd06e60030ef4a232554fcd2ec356cbf2c22d Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:49:05 +0200 Subject: [PATCH 15/83] Added ambiguity check for `opt` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_20.dyon” - Added “typechk/refinement_21.dyon” --- source/typechk/refinement_20.dyon | 7 +++++++ source/typechk/refinement_21.dyon | 8 ++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 18 insertions(+) create mode 100644 source/typechk/refinement_20.dyon create mode 100644 source/typechk/refinement_21.dyon diff --git a/source/typechk/refinement_20.dyon b/source/typechk/refinement_20.dyon new file mode 100644 index 00000000..5dfacc23 --- /dev/null +++ b/source/typechk/refinement_20.dyon @@ -0,0 +1,7 @@ +fn check(_: f64) {} +fn foo(a) -> {return clone(a)} +(opt) -> opt[f64] + +fn main() { + check(foo(some(1))) +} diff --git a/source/typechk/refinement_21.dyon b/source/typechk/refinement_21.dyon new file mode 100644 index 00000000..c1128661 --- /dev/null +++ b/source/typechk/refinement_21.dyon @@ -0,0 +1,8 @@ +fn check(_: f64) {} +fn foo(a) -> {return clone(a)} +(opt[f64]) -> opt[f64] +fn bar(a: opt[f64]) -> opt {return clone(a)} + +fn main() { + check(foo(bar(some(1)))) +} diff --git a/src/ty.rs b/src/ty.rs index 8b4765a2..4ea8893c 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -168,6 +168,7 @@ impl Type { (&AdHoc(ref xa, ref xb), &AdHoc(ref ya, ref yb)) if xa == ya => xb.ambiguous(yb), (&AdHoc(_, ref x), y) if x.goes_with(y) => true, (&Array(ref x), &Array(ref y)) if x.ambiguous(y) => true, + (&Option(ref x), &Option(ref y)) if x.ambiguous(y) => true, (&Bool, &Any) => true, (&F64, &Any) => true, (&Str, &Any) => true, diff --git a/tests/lib.rs b/tests/lib.rs index b438f36b..bd666f85 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -240,6 +240,8 @@ fn test_typechk() { test_src("source/typechk/refinement_17.dyon"); test_fail_src("source/typechk/refinement_18.dyon"); test_src("source/typechk/refinement_19.dyon"); + test_fail_src("source/typechk/refinement_20.dyon"); + test_src("source/typechk/refinement_21.dyon"); } #[test] From ff0233d26d2d12b11abc71f7f40fe66dad041a87 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 17:57:47 +0200 Subject: [PATCH 16/83] Added ambiguity check for `res` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_22.dyon” - Added “typechk/refinement_23.dyon” --- source/typechk/refinement_22.dyon | 7 +++++++ source/typechk/refinement_23.dyon | 8 ++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 18 insertions(+) create mode 100644 source/typechk/refinement_22.dyon create mode 100644 source/typechk/refinement_23.dyon diff --git a/source/typechk/refinement_22.dyon b/source/typechk/refinement_22.dyon new file mode 100644 index 00000000..baa09396 --- /dev/null +++ b/source/typechk/refinement_22.dyon @@ -0,0 +1,7 @@ +fn check(_: f64) {} +fn foo(a) -> {return clone(a)} +(res) -> res[f64] + +fn main() { + check(foo(ok(1))) +} diff --git a/source/typechk/refinement_23.dyon b/source/typechk/refinement_23.dyon new file mode 100644 index 00000000..3f9ce8e2 --- /dev/null +++ b/source/typechk/refinement_23.dyon @@ -0,0 +1,8 @@ +fn check(_: f64) {} +fn foo(a) -> {return clone(a)} +(res[f64]) -> res[f64] +fn bar(a: res[f64]) -> res {return clone(a)} + +fn main() { + check(foo(bar(ok(1)))) +} diff --git a/src/ty.rs b/src/ty.rs index 4ea8893c..e9695779 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -169,6 +169,7 @@ impl Type { (&AdHoc(_, ref x), y) if x.goes_with(y) => true, (&Array(ref x), &Array(ref y)) if x.ambiguous(y) => true, (&Option(ref x), &Option(ref y)) if x.ambiguous(y) => true, + (&Result(ref x), &Result(ref y)) if x.ambiguous(y) => true, (&Bool, &Any) => true, (&F64, &Any) => true, (&Str, &Any) => true, diff --git a/tests/lib.rs b/tests/lib.rs index bd666f85..6b85d2aa 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -242,6 +242,8 @@ fn test_typechk() { test_src("source/typechk/refinement_19.dyon"); test_fail_src("source/typechk/refinement_20.dyon"); test_src("source/typechk/refinement_21.dyon"); + test_fail_src("source/typechk/refinement_22.dyon"); + test_src("source/typechk/refinement_23.dyon"); } #[test] From 91bc45456c560f74301ad0cf0c6958e6b192da72 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 18:15:42 +0200 Subject: [PATCH 17/83] Do not allow "sec" as ad-hoc type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “syntax/secret_fail.dyon” --- assets/syntax.txt | 2 +- source/syntax/secret_fail.dyon | 6 ++++++ tests/lib.rs | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 source/syntax/secret_fail.dyon diff --git a/assets/syntax.txt b/assets/syntax.txt index cea37431..d4457940 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -214,7 +214,7 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" ["in" ?w "[" ?w type:"in" ?w "]"] "in":"in_any" closure_type:"closure_type" - [.._seps!:"ad_hoc" ?[?w type:"ad_hoc_ty"]] + [!"sec" .._seps!:"ad_hoc" ?[?w type:"ad_hoc_ty"]] } 101 closure_type = ["\\(" ?w .s?.(, type:"cl_arg") ?w ")" ?w "->" ?w type:"cl_ret"] diff --git a/source/syntax/secret_fail.dyon b/source/syntax/secret_fail.dyon new file mode 100644 index 00000000..b904390a --- /dev/null +++ b/source/syntax/secret_fail.dyon @@ -0,0 +1,6 @@ +// Must use `sec[bool]` or `sec[f64]`. +fn check(_: sec) {} + +fn main() { + check(explain_where(2, "")) +} diff --git a/tests/lib.rs b/tests/lib.rs index 6b85d2aa..3db0504a 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -135,6 +135,7 @@ fn test_syntax() { test_fail_src("source/syntax/div_fail_1.dyon"); test_src("source/syntax/continue_call.dyon"); test_src("source/syntax/mat4_1.dyon"); + test_fail_src("source/syntax/secret_fail.dyon"); } #[test] From 9b367e063c5d93effd3fa2483e30974ace561910 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 19:37:48 +0200 Subject: [PATCH 18/83] Report error if function has extra type information without returning something MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/void_refinement.dyon” --- source/typechk/void_refinement.dyon | 5 +++++ src/lifetime/mod.rs | 13 +++++++++++++ tests/lib.rs | 1 + 3 files changed, 19 insertions(+) create mode 100644 source/typechk/void_refinement.dyon diff --git a/source/typechk/void_refinement.dyon b/source/typechk/void_refinement.dyon new file mode 100644 index 00000000..b4544897 --- /dev/null +++ b/source/typechk/void_refinement.dyon @@ -0,0 +1,5 @@ +fn foo(_) {} +(bool) -> bool + +fn main() { +} diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index ad5509a2..3d4dd072 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -204,6 +204,19 @@ pub fn check( .map(|(i, _)| i) .collect(); + // Check that functions with extra type information returns something. + for &f in &functions { + if nodes[f].ty == Some(Type::Void) { + for &ch in &nodes[f].children { + if nodes[ch].kind == Kind::Ty { + return Err(nodes[ch].source.wrap( + format!("`{}` has extra type information but does not return anything", + nodes[f].name().expect("Expected name")))) + } + } + } + } + // Link items to their declaration. for &i in &items { // When `return` is used as variable one does not need to link. diff --git a/tests/lib.rs b/tests/lib.rs index 3db0504a..6dfc283b 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -245,6 +245,7 @@ fn test_typechk() { test_src("source/typechk/refinement_21.dyon"); test_fail_src("source/typechk/refinement_22.dyon"); test_src("source/typechk/refinement_23.dyon"); + test_fail_src("source/typechk/void_refinement.dyon"); } #[test] From f59e01e39fcdf57ddd712759a5596e168edee40d Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 20:18:26 +0200 Subject: [PATCH 19/83] Report error on wrong number of arguments in extra type information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/args_refinement.dyon” --- source/typechk/args_refinement.dyon | 5 +++ src/lifetime/mod.rs | 48 ++++++++++++++++++++--------- tests/lib.rs | 1 + 3 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 source/typechk/args_refinement.dyon diff --git a/source/typechk/args_refinement.dyon b/source/typechk/args_refinement.dyon new file mode 100644 index 00000000..beea1e9d --- /dev/null +++ b/source/typechk/args_refinement.dyon @@ -0,0 +1,5 @@ +fn foo(a: bool) -> {return clone(a)} +(bool, bool) -> bool + +fn main() { +} diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 3d4dd072..6302b0fb 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -87,8 +87,9 @@ pub fn check( let functions: Vec = nodes.iter().enumerate() .filter(|&(_, n)| n.kind == Kind::Fn).map(|(i, _)| i).collect(); - // Stores functions arguments with same index as `functions`. - let mut function_args = Vec::with_capacity(functions.len()); + // Stores number of functions arguments with same index as `functions`. + // To look up number of arguments, use `.enumerate()` on the loop. + let mut function_args: Vec = Vec::with_capacity(functions.len()); // Collect indices to call nodes. let calls: Vec = nodes.iter().enumerate() @@ -204,19 +205,6 @@ pub fn check( .map(|(i, _)| i) .collect(); - // Check that functions with extra type information returns something. - for &f in &functions { - if nodes[f].ty == Some(Type::Void) { - for &ch in &nodes[f].children { - if nodes[ch].kind == Kind::Ty { - return Err(nodes[ch].source.wrap( - format!("`{}` has extra type information but does not return anything", - nodes[f].name().expect("Expected name")))) - } - } - } - } - // Link items to their declaration. for &i in &items { // When `return` is used as variable one does not need to link. @@ -393,6 +381,36 @@ pub fn check( function_args.push(n); } + // Check extra type information. + for (i, &f) in functions.iter().enumerate() { + if nodes[f].ty == Some(Type::Void) { + for &ch in &nodes[f].children { + if nodes[ch].kind == Kind::Ty { + return Err(nodes[ch].source.wrap( + format!("`{}` has extra type information but does not return anything", + nodes[f].name().expect("Expected name")))) + } + } + } else { + let n = function_args[i]; + for &ch in &nodes[f].children { + if nodes[ch].kind == Kind::Ty { + let mut count = 0; + for &ty_ch in &nodes[ch].children { + if nodes[ty_ch].kind == Kind::TyArg { + count += 1; + } + } + if count != n { + return Err(nodes[ch].source.wrap( + format!("Expected {} number of arguments, found {}", + n, count))) + } + } + } + } + } + // Check for duplicate functions and build name to index map. let mut function_lookup: HashMap, usize> = HashMap::new(); for (i, &f) in functions.iter().enumerate() { diff --git a/tests/lib.rs b/tests/lib.rs index 6dfc283b..4280d554 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -246,6 +246,7 @@ fn test_typechk() { test_fail_src("source/typechk/refinement_22.dyon"); test_src("source/typechk/refinement_23.dyon"); test_fail_src("source/typechk/void_refinement.dyon"); + test_fail_src("source/typechk/args_refinement.dyon"); } #[test] From 069874ccd8bb4cca30ccdff62cd8645a69c70126 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 20:43:53 +0200 Subject: [PATCH 20/83] Check that extra type information arguments work with function arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refine_type_fail.dyon” --- source/typechk/refine_type_fail.dyon | 5 +++++ src/lifetime/mod.rs | 20 +++++++++++++++++++- tests/lib.rs | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 source/typechk/refine_type_fail.dyon diff --git a/source/typechk/refine_type_fail.dyon b/source/typechk/refine_type_fail.dyon new file mode 100644 index 00000000..9bd58faf --- /dev/null +++ b/source/typechk/refine_type_fail.dyon @@ -0,0 +1,5 @@ +fn foo(a: bool) -> {return clone(a)} +(str) -> bool + +fn main() { +} diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 6302b0fb..f070010d 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -396,8 +396,26 @@ pub fn check( for &ch in &nodes[f].children { if nodes[ch].kind == Kind::Ty { let mut count = 0; - for &ty_ch in &nodes[ch].children { + let mut arg = 0; + for &ty_ch in nodes[ch].children.iter() { if nodes[ty_ch].kind == Kind::TyArg { + if arg < n { + if let Some(ref ty_arg_ty) = nodes[ty_ch].ty { + while nodes[nodes[f].children[arg]].kind != Kind::Arg { + arg += 1; + } + if arg < n { + if let Some(ref arg_ty) = nodes[nodes[f].children[arg]].ty { + if !arg_ty.goes_with(ty_arg_ty) { + return Err(nodes[ty_ch].source.wrap( + format!("The type `{}` does not work with `{}`", + ty_arg_ty.description(), arg_ty.description()))) + } + } + } + } + arg += 1; + } count += 1; } } diff --git a/tests/lib.rs b/tests/lib.rs index 4280d554..b7775e37 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -247,6 +247,7 @@ fn test_typechk() { test_src("source/typechk/refinement_23.dyon"); test_fail_src("source/typechk/void_refinement.dyon"); test_fail_src("source/typechk/args_refinement.dyon"); + test_fail_src("source/typechk/refine_type_fail.dyon"); } #[test] From 1688e3c6543419649d81d0c36adb3f19de1aed33 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 20:51:50 +0200 Subject: [PATCH 21/83] Added "norm" to "lib.dyon" --- src/lib.dyon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.dyon b/src/lib.dyon index dfee199c..81f14b09 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -229,6 +229,9 @@ fn min(array: [f64]) -> f64 { ... } /// Returns NaN if array is empty. fn max(array: [f64]) -> f64 { ... } +/// Returns the length of 4D vector. +fn norm(v: vec4) -> f64 { ... } + /// Returns x component of 4D vector. fn x(v: vec4) -> f64 { ... } From 47953c4cb1d289da7fe25e487414a115a8f92969 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 22:07:49 +0200 Subject: [PATCH 22/83] Added ambiguity check for `thr` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refinement_24.dyon” - Added “typechk/refinement_25.dyon” --- source/typechk/refinement_24.dyon | 10 ++++++++++ source/typechk/refinement_25.dyon | 10 ++++++++++ src/ty.rs | 1 + tests/lib.rs | 2 ++ 4 files changed, 23 insertions(+) create mode 100644 source/typechk/refinement_24.dyon create mode 100644 source/typechk/refinement_25.dyon diff --git a/source/typechk/refinement_24.dyon b/source/typechk/refinement_24.dyon new file mode 100644 index 00000000..c842eda9 --- /dev/null +++ b/source/typechk/refinement_24.dyon @@ -0,0 +1,10 @@ +fn check(_: thr[f64]) {} + +fn do_something() -> bool {return true} +fn foo() -> thr[bool] {return go do_something()} +fn bar(a) -> {return clone(a)} +(thr[bool]) -> thr[bool] + +fn main() { + check(bar(foo())) +} diff --git a/source/typechk/refinement_25.dyon b/source/typechk/refinement_25.dyon new file mode 100644 index 00000000..1744061d --- /dev/null +++ b/source/typechk/refinement_25.dyon @@ -0,0 +1,10 @@ +fn check(_: thr[f64]) {} + +fn do_something() -> bool {return true} +fn foo() -> thr[any] {return go do_something()} +fn bar(a) -> {return clone(a)} +(thr[bool]) -> thr[bool] + +fn main() { + check(bar(foo())) +} diff --git a/src/ty.rs b/src/ty.rs index e9695779..c3464a2a 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -170,6 +170,7 @@ impl Type { (&Array(ref x), &Array(ref y)) if x.ambiguous(y) => true, (&Option(ref x), &Option(ref y)) if x.ambiguous(y) => true, (&Result(ref x), &Result(ref y)) if x.ambiguous(y) => true, + (&Thread(ref x), &Thread(ref y)) if x.ambiguous(y) => true, (&Bool, &Any) => true, (&F64, &Any) => true, (&Str, &Any) => true, diff --git a/tests/lib.rs b/tests/lib.rs index b7775e37..2ecee60b 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -245,6 +245,8 @@ fn test_typechk() { test_src("source/typechk/refinement_21.dyon"); test_fail_src("source/typechk/refinement_22.dyon"); test_src("source/typechk/refinement_23.dyon"); + test_fail_src("source/typechk/refinement_24.dyon"); + test_src("source/typechk/refinement_25.dyon"); test_fail_src("source/typechk/void_refinement.dyon"); test_fail_src("source/typechk/args_refinement.dyon"); test_fail_src("source/typechk/refine_type_fail.dyon"); From d981874b8e3b7d0f43abf3f7711dd45ad88ac3c0 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 17 Sep 2019 23:38:36 +0200 Subject: [PATCH 23/83] Added ambiguity check for `in` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added ambiguity check for wrapper types - Fixed doc comment on `Type::ambiguous` - Added “typechk/refinement_26.dyon” - Added “typechk/refinement_27.dyon” --- source/typechk/refinement_26.dyon | 10 ++++++++++ source/typechk/refinement_27.dyon | 10 ++++++++++ src/ty.rs | 8 +++++++- tests/lib.rs | 2 ++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 source/typechk/refinement_26.dyon create mode 100644 source/typechk/refinement_27.dyon diff --git a/source/typechk/refinement_26.dyon b/source/typechk/refinement_26.dyon new file mode 100644 index 00000000..7299ef3b --- /dev/null +++ b/source/typechk/refinement_26.dyon @@ -0,0 +1,10 @@ +fn check(_: in[[bool]]) {} + +fn foo(a: f64) {} +fn bar(a) -> {return clone(a)} +(in[[f64]]) -> in[[f64]] +fn baz() -> in[[f64]] {return in foo} + +fn main() { + check(bar(baz())) +} diff --git a/source/typechk/refinement_27.dyon b/source/typechk/refinement_27.dyon new file mode 100644 index 00000000..88872d2e --- /dev/null +++ b/source/typechk/refinement_27.dyon @@ -0,0 +1,10 @@ +fn check(_: in[[bool]]) {} + +fn foo(a: f64) {} +fn bar(a) -> {return clone(a)} +(in[[f64]]) -> in[[f64]] +fn baz() -> in {return in foo} + +fn main() { + check(bar(baz())) +} diff --git a/src/ty.rs b/src/ty.rs index c3464a2a..09c29112 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -156,7 +156,7 @@ impl Type { /// Returns an in-type with an `any` as inner type. pub fn in_ty() -> Type {Type::In(Box::new(Type::Any))} - /// Returns `true` if a type is ambiguous relative to a refinement type (directional check). + /// Returns `true` if a type to be refined is ambiguous relative to this type (directional check). /// /// For example, the type ad-hoc type `Foo str` is ambiguous with type `str`. /// If more was known about the `str` type with further refinement, @@ -171,12 +171,18 @@ impl Type { (&Option(ref x), &Option(ref y)) if x.ambiguous(y) => true, (&Result(ref x), &Result(ref y)) if x.ambiguous(y) => true, (&Thread(ref x), &Thread(ref y)) if x.ambiguous(y) => true, + (&In(ref x), &In(ref y)) if x.ambiguous(y) => true, (&Bool, &Any) => true, (&F64, &Any) => true, (&Str, &Any) => true, (&Vec4, &Any) => true, (&Mat4, &Any) => true, (&Link, &Any) => true, + (&Array(_), &Any) => true, + (&Option(_), &Any) => true, + (&Result(_), &Any) => true, + (&Thread(_), &Any) => true, + (&In(_), &Any) => true, _ => false } } diff --git a/tests/lib.rs b/tests/lib.rs index 2ecee60b..352bff20 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -247,6 +247,8 @@ fn test_typechk() { test_src("source/typechk/refinement_23.dyon"); test_fail_src("source/typechk/refinement_24.dyon"); test_src("source/typechk/refinement_25.dyon"); + test_fail_src("source/typechk/refinement_26.dyon"); + test_src("source/typechk/refinement_27.dyon"); test_fail_src("source/typechk/void_refinement.dyon"); test_fail_src("source/typechk/args_refinement.dyon"); test_fail_src("source/typechk/refine_type_fail.dyon"); From f0894773f18a0524847a5bc4f3c6880a27f2d1fe Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 00:08:24 +0200 Subject: [PATCH 24/83] Check return type of extra type information MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/refine_type_fail_2.dyon” --- source/typechk/refine_type_fail_2.dyon | 5 +++++ src/lifetime/mod.rs | 10 +++++++++- tests/lib.rs | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 source/typechk/refine_type_fail_2.dyon diff --git a/source/typechk/refine_type_fail_2.dyon b/source/typechk/refine_type_fail_2.dyon new file mode 100644 index 00000000..a2e9545c --- /dev/null +++ b/source/typechk/refine_type_fail_2.dyon @@ -0,0 +1,5 @@ +fn foo(a: bool) -> bool {return clone(a)} +(bool) -> str + +fn main() { +} diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index f070010d..57a2f193 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -391,7 +391,7 @@ pub fn check( nodes[f].name().expect("Expected name")))) } } - } else { + } else if let Some(ref ret_type) = nodes[f].ty { let n = function_args[i]; for &ch in &nodes[f].children { if nodes[ch].kind == Kind::Ty { @@ -417,6 +417,14 @@ pub fn check( arg += 1; } count += 1; + } else if nodes[ty_ch].kind == Kind::TyRet { + if let Some(ref ty_ret) = nodes[ty_ch].ty { + if !ret_type.goes_with(ty_ret) { + return Err(nodes[ty_ch].source.wrap( + format!("The type `{}` does not work with `{}`", + ty_ret.description(), ret_type.description()))) + } + } } } if count != n { diff --git a/tests/lib.rs b/tests/lib.rs index 352bff20..3a0b1ee5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -252,6 +252,7 @@ fn test_typechk() { test_fail_src("source/typechk/void_refinement.dyon"); test_fail_src("source/typechk/args_refinement.dyon"); test_fail_src("source/typechk/refine_type_fail.dyon"); + test_fail_src("source/typechk/refine_type_fail_2.dyon"); } #[test] From bebd9d1e42392fe20eeec637deaa55b2647ae2d7 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 01:18:38 +0200 Subject: [PATCH 25/83] Make closure calls work with ad-hoc types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/closure_ad_hoc.dyon” --- source/typechk/closure_ad_hoc.dyon | 5 +++++ src/lifetime/typecheck.rs | 15 +++++---------- src/ty.rs | 12 ++++++++++++ tests/lib.rs | 1 + 4 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 source/typechk/closure_ad_hoc.dyon diff --git a/source/typechk/closure_ad_hoc.dyon b/source/typechk/closure_ad_hoc.dyon new file mode 100644 index 00000000..5b9855cd --- /dev/null +++ b/source/typechk/closure_ad_hoc.dyon @@ -0,0 +1,5 @@ +fn foo() -> Foo \() -> bool {return \() = true} +fn main() { + a := foo() + println(\a()) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index b55f4df5..dc47dcaf 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -267,18 +267,13 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> if nodes[item].item_ids() { continue 'node; } if let Some(decl) = nodes[item].declaration { if let Some(ref ty) = nodes[decl].ty { - match *ty { - Type::Closure(ref ty) => { - this_ty = Some(ty.ret.clone()); - } - Type::Any => { - this_ty = Some(Type::Any); - } - _ => return Err(nodes[item].source.wrap( + if let Some(ty) = ty.closure_ret_ty() { + this_ty = Some(ty); + } else { + return Err(nodes[item].source.wrap( format!("Type mismatch (#250):\n\ Expected `closure`, found `{}`", - ty.description()), - )) + ty.description()))) } } } diff --git a/src/ty.rs b/src/ty.rs index 09c29112..70510752 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -187,6 +187,18 @@ impl Type { } } + /// Returns `true` if the type can be a closure, `false` otherwise. + pub fn closure_ret_ty(&self) -> Option { + use self::Type::*; + + match *self { + Closure(ref ty) => Some(ty.ret.clone()), + AdHoc(_, ref x) => x.closure_ret_ty(), + Any => Some(Type::Any), + _ => None + } + } + /// Returns `true` if a type goes with another type (directional check). /// /// - `bool` (argument) goes with `sec[bool]` (value) diff --git a/tests/lib.rs b/tests/lib.rs index 3a0b1ee5..8efb200c 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -253,6 +253,7 @@ fn test_typechk() { test_fail_src("source/typechk/args_refinement.dyon"); test_fail_src("source/typechk/refine_type_fail.dyon"); test_fail_src("source/typechk/refine_type_fail_2.dyon"); + test_src("source/typechk/closure_ad_hoc.dyon"); } #[test] From 566c8420feeb395adaf1b07d25e9e99c0a00b91a Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 11:04:51 +0200 Subject: [PATCH 26/83] Added `not` as standard external function --- src/dyon_std/mod.rs | 10 ++++++++++ src/lib.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 4867ef2f..8e61b7fd 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -14,6 +14,16 @@ const HTTP_SUPPORT_DISABLED: &'static str = "Http support is disabled"; #[cfg(not(feature = "file"))] const FILE_SUPPORT_DISABLED: &'static str = "File support is disabled"; +pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { + let b = rt.stack.pop().expect(TINVOTS); + let b = match *rt.resolve(&b) { + Variable::Bool(ref b, ref sec) => Variable::Bool(!b, sec.clone()), + ref x => return Err(rt.expected_arg(0, x, "bool")) + }; + rt.stack.push(b); + Ok(()) +} + dyon_fn!{fn x(v: Vec4) -> f64 {f64::from(v.0[0])}} dyon_fn!{fn y(v: Vec4) -> f64 {f64::from(v.0[1])}} dyon_fn!{fn z(v: Vec4) -> f64 {f64::from(v.0[2])}} diff --git a/src/lib.rs b/src/lib.rs index c2910997..7f7fc886 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -362,6 +362,7 @@ impl Module { let mut m = Module::empty(); m.ns("std"); + m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); From 6a53ce2890bd5d038289831d1840ccabd4562da3 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 11:22:17 +0200 Subject: [PATCH 27/83] Convert `!a` into `not(a)` - Added `UnOpEpression::into_expression` --- src/ast/mod.rs | 16 +++++++++++++++- src/runtime/mod.rs | 8 +------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index ecc60c73..189ec201 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1095,7 +1095,7 @@ impl Expression { } else if let Ok((range, val)) = UnOpExpression::from_meta_data( file, source, convert, ignored) { convert.update(range); - result = Some(Expression::UnOp(Box::new(val))); + result = Some(val.into_expression()); } else if let Ok((range, val)) = Mul::from_meta_data( file, source, convert, ignored) { convert.update(range); @@ -2972,6 +2972,20 @@ impl UnOpExpression { })) } + fn into_expression(self) -> Expression { + match self.op { + UnOp::Not => Expression::Call(Box::new(Call { + alias: None, + name: Arc::new("not".into()), + args: vec![self.expr], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range + })), + UnOp::Neg => Expression::UnOp(Box::new(self)) + } + } + fn resolve_locals( &self, relative: usize, diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index a92d8b4f..27f157d1 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2489,12 +2489,6 @@ impl Runtime { _ => return self.err(unop.source_range, "Expected something from unary argument") }; let v = match *self.resolve(&val) { - Variable::Bool(b, ref sec) => { - Variable::Bool(match unop.op { - ast::UnOp::Not => !b, - _ => return self.err(unop.source_range, "Unknown boolean unary operator") - }, sec.clone()) - } Variable::F64(v, ref sec) => { Variable::F64(match unop.op { ast::UnOp::Neg => -v, @@ -2519,7 +2513,7 @@ impl Runtime { } } _ => return self.err(unop.source_range, - "Invalid type for unary operator, expected bool, f64, vec4 or mat4") + "Invalid type for unary operator, expected f64, vec4 or mat4") }; Ok((Some(v), Flow::Continue)) } From 801ca832ab61dc12222616387eda045f789ae841 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 11:26:03 +0200 Subject: [PATCH 28/83] Write `not(a)` as `!a` --- src/write.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/write.rs b/src/write.rs index 04288b4d..2cbe2fb0 100644 --- a/src/write.rs +++ b/src/write.rs @@ -205,6 +205,8 @@ fn write_expr( E::Call(ref call) => { if &**call.name == "norm" && call.args.len() == 1 { write_norm(w, rt, &call.args[0], tabs)? + } else if &**call.name == "not" && call.args.len() == 1 { + write_not(w, rt, &call.args[0], tabs)? } else { write_call(w, rt, call, tabs)? } @@ -423,6 +425,16 @@ fn write_binop( Ok(()) } +fn write_not( + w: &mut W, + rt: &Runtime, + expr: &ast::Expression, + tabs: u32, +) -> Result<(), io::Error> { + write!(w, "!")?; + write_expr(w, rt, &expr, tabs) +} + fn write_unop( w: &mut W, rt: &Runtime, From 0610573ba22323506c49aab8a0bdba5ec36cd747 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 11:46:31 +0200 Subject: [PATCH 29/83] Rewrite `!a` as `not(a)` in type checker - Added `Kind::Not` --- assets/syntax.txt | 2 +- src/ast/mod.rs | 6 +++--- src/lifetime/kind.rs | 2 ++ src/lifetime/mod.rs | 8 ++++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/assets/syntax.txt b/assets/syntax.txt index d4457940..7e1ebacb 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -155,7 +155,7 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" 64 , = [?w "," ?w] 65 arr = {array:"array" array_fill:"array_fill"} 66 items = {mat4:"mat4" vec4:"vec4" link:"link" grab:"grab" try_expr:"try_expr" - ["(" ?w expr ?w ")"] unop_not:"unop" norm:"norm" + ["(" ?w expr ?w ")"] unop_not:"not" norm:"norm" text go:"go" call_closure:"call_closure" named_call_closure:"named_call_closure" call:"call" named_call:"named_call" diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 189ec201..835a6bf1 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1093,7 +1093,7 @@ impl Expression { convert.update(range); result = Some(val.into_expression()); } else if let Ok((range, val)) = UnOpExpression::from_meta_data( - file, source, convert, ignored) { + "not", file, source, convert, ignored) { convert.update(range); result = Some(val.into_expression()); } else if let Ok((range, val)) = Mul::from_meta_data( @@ -1932,7 +1932,7 @@ impl Mul { convert.update(range); break; } else if let Ok((range, val)) = UnOpExpression::from_meta_data( - file, source, convert, ignored) { + "unop", file, source, convert, ignored) { convert.update(range); items.push(Expression::UnOp(Box::new(val))); } else if let Ok((range, val)) = Pow::from_meta_data( @@ -2930,13 +2930,13 @@ pub struct UnOpExpression { impl UnOpExpression { /// Creates unary operator expression from meta data. pub fn from_meta_data( + node: &str, file: &Arc, source: &Arc, mut convert: Convert, ignored: &mut Vec) -> Result<(Range, UnOpExpression), ()> { let start = convert; - let node = "unop"; let start_range = convert.start_node(node)?; convert.update(start_range); diff --git a/src/lifetime/kind.rs b/src/lifetime/kind.rs index ffa3a81d..522662b6 100644 --- a/src/lifetime/kind.rs +++ b/src/lifetime/kind.rs @@ -67,6 +67,7 @@ pub enum Kind { Break, Continue, Norm, + Not, UnOp, Vec4, X, @@ -177,6 +178,7 @@ impl Kind { "break" => Kind::Break, "continue" => Kind::Continue, "norm" => Kind::Norm, + "not" => Kind::Not, "unop" => Kind::UnOp, "vec4" => Kind::Vec4, "x" => Kind::X, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 57a2f193..a0fb3ae2 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -39,6 +39,14 @@ pub fn check( nodes[ch].kind = Kind::CallArg; } } + Kind::Not => { + if nodes[i].children.len() == 1 { + nodes[i].kind = Kind::Call; + nodes[i].names.push(Arc::new("not".into())); + let ch = nodes[i].children[0]; + nodes[ch].kind = Kind::CallArg; + } + } _ => {} } } From 1021361e89506b689471fb16b80587501688a5f2 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 11:50:14 +0200 Subject: [PATCH 30/83] Added `not` to "lib.dyon" --- src/lib.dyon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.dyon b/src/lib.dyon index 81f14b09..dd3f75b2 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -232,6 +232,9 @@ fn max(array: [f64]) -> f64 { ... } /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } +/// Logical NOT. +fn not(b: bool) -> bool { ... } + /// Returns x component of 4D vector. fn x(v: vec4) -> f64 { ... } From 69112372935136a91fe31039ab2cd7fa2d584162 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 11:59:15 +0200 Subject: [PATCH 31/83] Added `neg` as standard external function --- src/dyon_std/mod.rs | 17 +++++++++++++++++ src/lib.rs | 1 + 2 files changed, 18 insertions(+) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 8e61b7fd..f266b04e 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -24,6 +24,23 @@ pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { Ok(()) } +pub(crate) fn neg(rt: &mut Runtime) -> Result<(), String> { + let n = rt.stack.pop().expect(TINVOTS); + let n = match *rt.resolve(&n) { + Variable::F64(v, ref sec) => Variable::F64(-v, sec.clone()), + Variable::Vec4(v) => Variable::Vec4([-v[0], -v[1], -v[2], -v[3]]), + Variable::Mat4(ref m) => Variable::Mat4(Box::new([ + [-m[0][0], -m[0][1], -m[0][2], -m[0][3]], + [-m[1][0], -m[1][1], -m[1][2], -m[1][3]], + [-m[2][0], -m[2][1], -m[2][2], -m[2][3]], + [-m[3][0], -m[3][1], -m[3][2], -m[3][3]], + ])), + ref x => return Err(rt.expected_arg(0, x, "f64, vec4 or mat4")) + }; + rt.stack.push(n); + Ok(()) +} + dyon_fn!{fn x(v: Vec4) -> f64 {f64::from(v.0[0])}} dyon_fn!{fn y(v: Vec4) -> f64 {f64::from(v.0[1])}} dyon_fn!{fn z(v: Vec4) -> f64 {f64::from(v.0[2])}} diff --git a/src/lib.rs b/src/lib.rs index 7f7fc886..8efd0e69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -363,6 +363,7 @@ impl Module { let mut m = Module::empty(); m.ns("std"); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); + m.add_str("neg", neg, Dfn::nl(vec![Any], Any)); m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); From 3f36cdf6b91a60a738cd72e6513ff4be14f5c7a0 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 12:04:56 +0200 Subject: [PATCH 32/83] Added `neg` to "lib.dyon" --- src/lib.dyon | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.dyon b/src/lib.dyon index dd3f75b2..3ceec0c9 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -235,6 +235,12 @@ fn norm(v: vec4) -> f64 { ... } /// Logical NOT. fn not(b: bool) -> bool { ... } +/// Negation. +fn neg(v: any) -> any { ... } + (f64) -> f64 + (vec4) -> vec4 + (mat4) -> mat4 + /// Returns x component of 4D vector. fn x(v: vec4) -> f64 { ... } From 4cdd6ab31c1416eac5b02a86942fcf9e10ce8d5c Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 12:42:04 +0200 Subject: [PATCH 33/83] Use `-a` as `neg(a)` - Removed `Expression::UnOp` - Added `Node::rewrite_unop` - Added `Kind::Neg` - Removed `Kind::UnOp` - Removed `Runtime::unop` --- assets/syntax.txt | 2 +- src/ast/infer_len.rs | 4 ---- src/ast/mod.rs | 45 ++++++++++++--------------------------- src/ast/replace.rs | 8 ------- src/grab.rs | 10 --------- src/lifetime/kind.rs | 4 ++-- src/lifetime/mod.rs | 25 ++++++---------------- src/lifetime/node.rs | 12 ++++++----- src/lifetime/typecheck.rs | 2 +- src/runtime/mod.rs | 36 ------------------------------- src/write.rs | 22 ++++++------------- 11 files changed, 38 insertions(+), 132 deletions(-) diff --git a/assets/syntax.txt b/assets/syntax.txt index 7e1ebacb..87368e8d 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -231,7 +231,7 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" 203 / = [wn "/":"/" !"/" ?w] 204 % = [wn "%":"%" ?w] 205 pow = [lexpr:"base" wn {"^" "⊻" ["xor" w]} ?w lexpr:"exp"] -206 mul = .s!({* / %} {unop_neg:"unop" pow:"pow" lexpr:"val"}) +206 mul = .s!({* / %} {unop_neg:"neg" pow:"pow" lexpr:"val"}) 207 mul_expr = {mul:"mul"} 208 add = .s!({+ -} mul_expr:"expr") diff --git a/src/ast/infer_len.rs b/src/ast/infer_len.rs index 277935b8..812f440d 100644 --- a/src/ast/infer_len.rs +++ b/src/ast/infer_len.rs @@ -214,10 +214,6 @@ fn infer_expr( let right = infer_expr(&cmp_expr.right, name, decls); if right.is_some() { return right; } } - UnOp(ref unop_expr) => { - let res = infer_expr(&unop_expr.expr, name, decls); - if res.is_some() { return res; } - } Variable(_) => {} Try(ref expr) => { let res = infer_expr(expr, name, decls); diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 835a6bf1..6f77dca6 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1006,8 +1006,6 @@ pub enum Expression { If(Box), /// Compare expression. Compare(Box), - /// Unary operator expression. - UnOp(Box), /// Variable. /// /// This means it contains no members that depends on other expressions. @@ -1366,7 +1364,6 @@ impl Expression { LinkIn(ref for_in_expr) => for_in_expr.source_range, If(ref if_expr) => if_expr.source_range, Compare(ref comp) => comp.source_range, - UnOp(ref unop) => unop.source_range, Variable(ref range_var) => range_var.0, Try(ref expr) => expr.source_range(), Swizzle(ref swizzle) => swizzle.source_range, @@ -1467,8 +1464,6 @@ impl Expression { if_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), Compare(ref comp) => comp.resolve_locals(relative, stack, closure_stack, module, use_lookup), - UnOp(ref unop) => - unop.resolve_locals(relative, stack, closure_stack, module, use_lookup), Variable(_) => {} Try(ref expr) => expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), @@ -1932,9 +1927,9 @@ impl Mul { convert.update(range); break; } else if let Ok((range, val)) = UnOpExpression::from_meta_data( - "unop", file, source, convert, ignored) { + "neg", file, source, convert, ignored) { convert.update(range); - items.push(Expression::UnOp(Box::new(val))); + items.push(val.into_expression()); } else if let Ok((range, val)) = Pow::from_meta_data( file, source, convert, ignored) { convert.update(range); @@ -2973,30 +2968,18 @@ impl UnOpExpression { } fn into_expression(self) -> Expression { - match self.op { - UnOp::Not => Expression::Call(Box::new(Call { - alias: None, - name: Arc::new("not".into()), - args: vec![self.expr], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range - })), - UnOp::Neg => Expression::UnOp(Box::new(self)) - } - } - - fn resolve_locals( - &self, - relative: usize, - stack: &mut Vec>>, - closure_stack: &mut Vec, - module: &Module, - use_lookup: &UseLookup, - ) { - let st = stack.len(); - self.expr.resolve_locals(relative, stack, closure_stack, module, use_lookup); - stack.truncate(st); + let name = match self.op { + UnOp::Not => "not", + UnOp::Neg => "neg" + }; + Expression::Call(Box::new(Call { + alias: None, + name: Arc::new(name.into()), + args: vec![self.expr], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range + })) } } diff --git a/src/ast/replace.rs b/src/ast/replace.rs index d19e0df3..1535454e 100644 --- a/src/ast/replace.rs +++ b/src/ast/replace.rs @@ -21,7 +21,6 @@ use super::{ Link, Object, Swizzle, - UnOpExpression, Vec4, Mat4, TryExpr, @@ -327,13 +326,6 @@ pub fn number(expr: &Expression, name: &Arc, val: f64) -> Expression { source_range: cmp_expr.source_range, })) } - E::UnOp(ref unop_expr) => { - E::UnOp(Box::new(UnOpExpression { - op: unop_expr.op, - expr: number(&unop_expr.expr, name, val), - source_range: unop_expr.source_range, - })) - } E::Variable(_) => expr.clone(), E::Try(ref expr) => E::Try(Box::new(number(expr, name, val))), E::Swizzle(ref swizzle_expr) => { diff --git a/src/grab.rs b/src/grab.rs index 14087f73..f7e2315e 100644 --- a/src/grab.rs +++ b/src/grab.rs @@ -300,16 +300,6 @@ pub fn grab_expr( } x => x, }, - E::UnOp(ref unop) => { - Ok((Grabbed::Expression(E::UnOp(Box::new(ast::UnOpExpression { - op: unop.op, - expr: match grab_expr(level, rt, &unop.expr, side) { - Ok((Grabbed::Expression(x), Flow::Continue)) => x, - x => return x, - }, - source_range: unop.source_range, - }))), Flow::Continue)) - } E::Vec4(ref vec4) => { Ok((Grabbed::Expression(E::Vec4(Box::new(ast::Vec4 { args: { diff --git a/src/lifetime/kind.rs b/src/lifetime/kind.rs index 522662b6..7a6aba84 100644 --- a/src/lifetime/kind.rs +++ b/src/lifetime/kind.rs @@ -68,7 +68,7 @@ pub enum Kind { Continue, Norm, Not, - UnOp, + Neg, Vec4, X, Y, @@ -179,7 +179,7 @@ impl Kind { "continue" => Kind::Continue, "norm" => Kind::Norm, "not" => Kind::Not, - "unop" => Kind::UnOp, + "neg" => Kind::Neg, "vec4" => Kind::Vec4, "x" => Kind::X, "y" => Kind::Y, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index a0fb3ae2..841bf87a 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -30,24 +30,13 @@ pub fn check( // Rewrite graph for syntax sugar that corresponds to function calls. for i in 0..nodes.len() { - match nodes[i].kind { - Kind::Norm => { - if nodes[i].children.len() == 1 { - nodes[i].kind = Kind::Call; - nodes[i].names.push(Arc::new("norm".into())); - let ch = nodes[i].children[0]; - nodes[ch].kind = Kind::CallArg; - } - } - Kind::Not => { - if nodes[i].children.len() == 1 { - nodes[i].kind = Kind::Call; - nodes[i].names.push(Arc::new("not".into())); - let ch = nodes[i].children[0]; - nodes[ch].kind = Kind::CallArg; - } - } - _ => {} + if nodes[i].children.len() == 1 { + Node::rewrite_unop(i, match nodes[i].kind { + Kind::Norm => Arc::new("norm".into()), + Kind::Not => Arc::new("not".into()), + Kind::Neg => Arc::new("neg".into()), + _ => continue + }, &mut nodes) } } diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index a08be6f8..2d6773f7 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -54,6 +54,13 @@ impl Node { else { Some(&self.names[0]) } } + pub fn rewrite_unop(i: usize, name: Arc, nodes: &mut [Node]) { + nodes[i].kind = Kind::Call; + nodes[i].names.push(name); + let ch = nodes[i].children[0]; + nodes[ch].kind = Kind::CallArg; + } + #[allow(dead_code)] pub fn print(&self, nodes: &[Node], indent: u32) { for _ in 0..indent { print!(" ") } @@ -210,11 +217,6 @@ impl Node { (Kind::CallClosure, Kind::Item) => { continue } (_, Kind::Item) => {} (_, Kind::Norm) => {} - (_, Kind::UnOp) => { - // The result of all unary operators does not depend - // on the lifetime of the argument. - continue - } (_, Kind::Compare) => { // The result of all compare operators does not depend // on the lifetime of the arguments. diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index dc47dcaf..5e30600b 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -367,7 +367,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::Return | Kind::Val | Kind::Expr | Kind::Cond | Kind::Exp | Kind::Base | Kind::Left | Kind::Right | - Kind::ElseIfCond | Kind::UnOp | Kind::Grab + Kind::ElseIfCond | Kind::Grab => { // TODO: Report error for expected unary operator. if nodes[i].children.is_empty() { continue 'node; } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 27f157d1..56d023b0 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -512,7 +512,6 @@ impl Runtime { self.call_internal(call, loader) } Item(ref item) => self.item(item, side), - UnOp(ref unop) => self.unop(unop, side), BinOp(ref binop) => self.binop(binop, side), Assign(ref assign) => self.assign(assign.op, &assign.left, &assign.right), Vec4(ref vec4) => self.vec4(vec4, side), @@ -2482,41 +2481,6 @@ impl Runtime { [x[3], y[3], z[3], w[3]], ]))), Flow::Continue)) } - fn unop(&mut self, unop: &ast::UnOpExpression, side: Side) -> FlowResult { - let val = match self.expression(&unop.expr, side)? { - (Some(x), Flow::Continue) => x, - (x, Flow::Return) => return Ok((x, Flow::Return)), - _ => return self.err(unop.source_range, "Expected something from unary argument") - }; - let v = match *self.resolve(&val) { - Variable::F64(v, ref sec) => { - Variable::F64(match unop.op { - ast::UnOp::Neg => -v, - _ => return self.err(unop.source_range, "Unknown number unary operator") - }, sec.clone()) - } - Variable::Vec4(v) => { - Variable::Vec4(match unop.op { - ast::UnOp::Neg => [-v[0], -v[1], -v[2], -v[3]], - _ => return self.err(unop.source_range, "Unknown vec4 unary operator") - }) - } - Variable::Mat4(ref m) => { - match unop.op { - ast::UnOp::Neg => Variable::Mat4(Box::new([ - [-m[0][0], -m[0][1], -m[0][2], -m[0][3]], - [-m[1][0], -m[1][1], -m[1][2], -m[1][3]], - [-m[2][0], -m[2][1], -m[2][2], -m[2][3]], - [-m[3][0], -m[3][1], -m[3][2], -m[3][3]], - ])), - _ => return self.err(unop.source_range, "Unknown mat4 unary operator") - } - } - _ => return self.err(unop.source_range, - "Invalid type for unary operator, expected f64, vec4 or mat4") - }; - Ok((Some(v), Flow::Continue)) - } fn binop(&mut self, binop: &ast::BinOpExpression, side: Side) -> FlowResult { use ast::BinOp::*; diff --git a/src/write.rs b/src/write.rs index 2cbe2fb0..6f78bd91 100644 --- a/src/write.rs +++ b/src/write.rs @@ -207,6 +207,8 @@ fn write_expr( write_norm(w, rt, &call.args[0], tabs)? } else if &**call.name == "not" && call.args.len() == 1 { write_not(w, rt, &call.args[0], tabs)? + } else if &**call.name == "neg" && call.args.len() == 1 { + write_neg(w, rt, &call.args[0], tabs)? } else { write_call(w, rt, call, tabs)? } @@ -321,7 +323,6 @@ fn write_expr( write_for_in(w, rt, for_in, tabs)?; } E::If(ref if_expr) => write_if(w, rt, if_expr, tabs)?, - E::UnOp(ref unop) => write_unop(w, rt, unop, tabs)?, E::Try(ref expr) => { write_expr(w, rt, expr, tabs)?; write!(w, "?")?; @@ -435,25 +436,14 @@ fn write_not( write_expr(w, rt, &expr, tabs) } -fn write_unop( +fn write_neg( w: &mut W, rt: &Runtime, - unop: &ast::UnOpExpression, + expr: &ast::Expression, tabs: u32, ) -> Result<(), io::Error> { - use ast::UnOp::*; - - match unop.op { - Not => { - write!(w, "!")?; - write_expr(w, rt, &unop.expr, tabs)?; - } - Neg => { - write!(w, "-")?; - write_expr(w, rt, &unop.expr, tabs)?; - } - } - Ok(()) + write!(w, "-")?; + write_expr(w, rt, &expr, tabs) } fn write_item( From 0baf934d07068a46ed148d714d95c38f7b3cc098 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 13:27:26 +0200 Subject: [PATCH 34/83] Report error if refinement failed without ambiguity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes https://github.com/PistonDevelopers/dyon/issues/644 - Added “typechk/refine_closed_fail_1.dyon” - Added “typechk/refine_closed_fail_2.dyon” - Added “typechk/refine_closed_pass_1.dyon” --- source/typechk/refine_closed_fail_1.dyon | 7 +++++ source/typechk/refine_closed_fail_2.dyon | 7 +++++ source/typechk/refine_closed_pass_1.dyon | 7 +++++ src/lifetime/typecheck.rs | 36 ++++++++++++++++++++++-- tests/lib.rs | 3 ++ 5 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 source/typechk/refine_closed_fail_1.dyon create mode 100644 source/typechk/refine_closed_fail_2.dyon create mode 100644 source/typechk/refine_closed_pass_1.dyon diff --git a/source/typechk/refine_closed_fail_1.dyon b/source/typechk/refine_closed_fail_1.dyon new file mode 100644 index 00000000..53a54ce1 --- /dev/null +++ b/source/typechk/refine_closed_fail_1.dyon @@ -0,0 +1,7 @@ +fn foo(a) -> {return clone(a)} +(bool) -> bool +(f64) -> f64 + +fn main() { + println(foo("hi")) +} diff --git a/source/typechk/refine_closed_fail_2.dyon b/source/typechk/refine_closed_fail_2.dyon new file mode 100644 index 00000000..b4dabac2 --- /dev/null +++ b/source/typechk/refine_closed_fail_2.dyon @@ -0,0 +1,7 @@ +fn foo(a, b) -> {return clone(a)} +(bool, bool) -> bool +(f64, bool) -> f64 + +fn main() { + println(foo(1, 2)) +} diff --git a/source/typechk/refine_closed_pass_1.dyon b/source/typechk/refine_closed_pass_1.dyon new file mode 100644 index 00000000..962912fc --- /dev/null +++ b/source/typechk/refine_closed_pass_1.dyon @@ -0,0 +1,7 @@ +fn foo(a) -> {return clone(a)} +(bool) -> bool +(any) -> f64 + +fn main() { + println(foo(1)) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 5e30600b..a56df76a 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -205,9 +205,12 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> if let Some(decl) = nodes[i].declaration { // Refine types using extra type information. let mut found = false; + let mut ambiguous = false; + let mut count = 0; 'outer: for &ty in nodes[decl].children.iter() .filter(|&&ty| nodes[ty].kind == Kind::Ty) { + count += 1; let mut all = true; for (arg_expr, &ty_arg) in nodes[i].children.iter() .filter(|&&arg| nodes[arg].kind == Kind::CallArg && @@ -216,10 +219,17 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> .zip(nodes[ty].children.iter() .filter(|&&ty_arg| nodes[ty_arg].kind == Kind::TyArg)) { + if nodes[arg_expr].ty.is_none() { + ambiguous = true; + break 'outer; + } let found_arg = if let (&Some(ref a), &Some(ref b)) = (&nodes[arg_expr].ty, &nodes[ty_arg].ty) { if b.goes_with(a) { - if b.ambiguous(a) {break 'outer} + if b.ambiguous(a) { + ambiguous = true; + break 'outer; + } true } else {false} } @@ -240,8 +250,28 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } } if !found { - // Delay completion of this call until extra type information matches. - todo.push(i); + if ambiguous { + // Delay completion of this call until extra type information matches. + todo.push(i); + } else if count > 0 { + use std::io::Write; + + let mut buf: Vec = vec![]; + write!(&mut buf, "Type mismatch (#230):\nThe argument type `").unwrap(); + for (i, arg) in nodes[i].children.iter() + .filter(|&&arg| nodes[arg].kind == Kind::CallArg && + !nodes[arg].children.is_empty()) + .map(|&arg| nodes[arg].children[0]) + .enumerate() { + if let Some(ref arg_ty) = nodes[arg].ty { + if i != 0 {write!(&mut buf, ", ").unwrap()}; + write!(&mut buf, "{}", arg_ty.description()).unwrap(); + } + } + write!(&mut buf, "` does not work with `{}`", + nodes[i].name().expect("Expected name")).unwrap(); + return Err(nodes[i].source.wrap(String::from_utf8(buf).unwrap())) + } } // If the type has not been refined, fall back to default type signature. diff --git a/tests/lib.rs b/tests/lib.rs index 8efb200c..38724a2e 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -254,6 +254,9 @@ fn test_typechk() { test_fail_src("source/typechk/refine_type_fail.dyon"); test_fail_src("source/typechk/refine_type_fail_2.dyon"); test_src("source/typechk/closure_ad_hoc.dyon"); + test_fail_src("source/typechk/refine_closed_fail_1.dyon"); + test_fail_src("source/typechk/refine_closed_fail_2.dyon"); + test_src("source/typechk/refine_closed_pass_1.dyon"); } #[test] From 0428c2344a787d9aa03906284778776fdc16c2c6 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 17:12:07 +0200 Subject: [PATCH 35/83] Added `Dfn::ext` --- examples/call.rs | 14 +++----------- src/lib.rs | 15 ++++++++++----- src/lifetime/typecheck.rs | 3 ++- src/prelude.rs | 8 ++++++-- src/ty.rs | 2 +- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/examples/call.rs b/examples/call.rs index d8894e92..4dd94036 100644 --- a/examples/call.rs +++ b/examples/call.rs @@ -2,22 +2,14 @@ extern crate dyon; use std::sync::Arc; -use dyon::{load_str, error, Call, Module, Dfn, Lt, Type, Runtime, RustObject}; +use dyon::{load_str, error, Call, Module, Dfn, Type, Runtime, RustObject}; fn main() { let mut module = Module::new(); // Add functions to read `a` and `b` from `RustArgs`. - module.add(Arc::new("a_of".into()), a_of, Dfn { - lts: vec![Lt::Default], - tys: vec![Type::Any], - ret: Type::F64 - }); - module.add(Arc::new("b_of".into()), b_of, Dfn { - lts: vec![Lt::Default], - tys: vec![Type::Any], - ret: Type::F64 - }); + module.add(Arc::new("a_of".into()), a_of, Dfn::nl(vec![Type::Any], Type::F64)); + module.add(Arc::new("b_of".into()), b_of, Dfn::nl(vec![Type::Any], Type::F64)); error(load_str("main.dyon", Arc::new(r#" fn add_args(a: f64, b: f64) { diff --git a/src/lib.rs b/src/lib.rs index 8efd0e69..cf321cf9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -485,28 +485,33 @@ impl Module { m.add_str("push_ref(mut,_)", push_ref, Dfn { lts: vec![Lt::Default, Lt::Arg(0)], tys: vec![Type::array(), Any], - ret: Void + ret: Void, + ext: vec![], }); m.add_str("insert_ref(mut,_,_)", insert_ref, Dfn { lts: vec![Lt::Default, Lt::Default, Lt::Arg(0)], tys: vec![Type::array(), F64, Any], - ret: Void + ret: Void, + ext: vec![], }); m.add_str("push(mut,_)", push, Dfn::nl(vec![Type::array(), Any], Void)); m.add_str("insert(mut,_,_)", insert, Dfn { lts: vec![Lt::Default; 3], tys: vec![Type::array(), F64, Any], - ret: Void + ret: Void, + ext: vec![], }); m.add_str("pop(mut)", pop, Dfn { lts: vec![Lt::Return], tys: vec![Type::array()], - ret: Any + ret: Any, + ext: vec![], }); m.add_str("remove(mut,_)", remove, Dfn { lts: vec![Lt::Return, Lt::Default], tys: vec![Type::array(), F64], - ret: Any + ret: Any, + ext: vec![], }); m.add_str("reverse(mut)", reverse, Dfn::nl(vec![Type::array()], Void)); m.add_str("clear(mut)", clear, Dfn::nl(vec![Type::array()], Void)); diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index a56df76a..90aeb84c 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -627,7 +627,8 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> this_ty = Some(Type::Closure(Box::new(Dfn { lts, tys, - ret: ret.unwrap() + ret: ret.unwrap(), + ext: vec![], }))); } } diff --git a/src/prelude.rs b/src/prelude.rs index 9f6bfc67..62aed20b 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -32,15 +32,18 @@ pub struct Dfn { pub tys: Vec, /// Return type of function. pub ret: Type, + /// Extra type information. + pub ext: Vec<(Vec, Type)>, } impl Dfn { - /// Creates a new function signature with no lifetime. + /// Creates a new function signature with no lifetime or refinement. pub fn nl(args: Vec, ret: Type) -> Dfn { Dfn { lts: vec![Lt::Default; args.len()], tys: args, - ret + ret, + ext: vec![], } } @@ -70,6 +73,7 @@ impl Dfn { lts, tys, ret: f.ret.clone(), + ext: vec![], } } diff --git a/src/ty.rs b/src/ty.rs index 70510752..3df2e85a 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -536,7 +536,7 @@ impl Type { convert.update(range); let range = convert.end_node("closure_type")?; convert.update(range); - ty = Some(Type::Closure(Box::new(Dfn { lts, tys, ret }))); + ty = Some(Type::Closure(Box::new(Dfn { lts, tys, ret, ext: vec![] }))); } else { let range = convert.ignore(); convert.update(range); From 80e9cc0ab324ac085fe494755e10d45d001a65df Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 17:26:38 +0200 Subject: [PATCH 36/83] Moved refine call to its own module --- src/lifetime/typecheck.rs | 73 ++-------------------------- src/lifetime/typecheck/refine.rs | 82 ++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 70 deletions(-) create mode 100644 src/lifetime/typecheck/refine.rs diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 90aeb84c..25875704 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -5,6 +5,8 @@ use Prelude; use Type; use ast::UseLookup; +mod refine; + /// Runs type checking. /// /// The type checking consists of 2 steps: @@ -203,76 +205,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::Call => { if let Some(decl) = nodes[i].declaration { - // Refine types using extra type information. - let mut found = false; - let mut ambiguous = false; - let mut count = 0; - 'outer: for &ty in nodes[decl].children.iter() - .filter(|&&ty| nodes[ty].kind == Kind::Ty) - { - count += 1; - let mut all = true; - for (arg_expr, &ty_arg) in nodes[i].children.iter() - .filter(|&&arg| nodes[arg].kind == Kind::CallArg && - !nodes[arg].children.is_empty()) - .map(|&arg| nodes[arg].children[0]) - .zip(nodes[ty].children.iter() - .filter(|&&ty_arg| nodes[ty_arg].kind == Kind::TyArg)) - { - if nodes[arg_expr].ty.is_none() { - ambiguous = true; - break 'outer; - } - let found_arg = if let (&Some(ref a), &Some(ref b)) = - (&nodes[arg_expr].ty, &nodes[ty_arg].ty) { - if b.goes_with(a) { - if b.ambiguous(a) { - ambiguous = true; - break 'outer; - } - true - } else {false} - } - else {false}; - if !found_arg { - all = false; - break; - } - } - if all { - if let Some(&ind) = nodes[ty].children.iter() - .filter(|&&ty| nodes[ty].kind == Kind::TyRet) - .next() { - this_ty = nodes[ind].ty.clone(); - found = true; - break; - } - } - } - if !found { - if ambiguous { - // Delay completion of this call until extra type information matches. - todo.push(i); - } else if count > 0 { - use std::io::Write; - - let mut buf: Vec = vec![]; - write!(&mut buf, "Type mismatch (#230):\nThe argument type `").unwrap(); - for (i, arg) in nodes[i].children.iter() - .filter(|&&arg| nodes[arg].kind == Kind::CallArg && - !nodes[arg].children.is_empty()) - .map(|&arg| nodes[arg].children[0]) - .enumerate() { - if let Some(ref arg_ty) = nodes[arg].ty { - if i != 0 {write!(&mut buf, ", ").unwrap()}; - write!(&mut buf, "{}", arg_ty.description()).unwrap(); - } - } - write!(&mut buf, "` does not work with `{}`", - nodes[i].name().expect("Expected name")).unwrap(); - return Err(nodes[i].source.wrap(String::from_utf8(buf).unwrap())) - } - } + refine::declaration(i, decl, nodes, &mut todo, &mut this_ty)?; // If the type has not been refined, fall back to default type signature. if this_ty.is_none() && nodes[i].ty.is_none() { diff --git a/src/lifetime/typecheck/refine.rs b/src/lifetime/typecheck/refine.rs new file mode 100644 index 00000000..e45ac58b --- /dev/null +++ b/src/lifetime/typecheck/refine.rs @@ -0,0 +1,82 @@ +use super::*; + +pub(crate) fn declaration( + i: usize, + decl: usize, + nodes: &[Node], + todo: &mut Vec, + this_ty: &mut Option +) -> Result<(), Range> { + // Refine types using extra type information. + let mut found = false; + let mut ambiguous = false; + let mut count = 0; + 'outer: for &ty in nodes[decl].children.iter() + .filter(|&&ty| nodes[ty].kind == Kind::Ty) + { + count += 1; + let mut all = true; + for (arg_expr, &ty_arg) in nodes[i].children.iter() + .filter(|&&arg| nodes[arg].kind == Kind::CallArg && + !nodes[arg].children.is_empty()) + .map(|&arg| nodes[arg].children[0]) + .zip(nodes[ty].children.iter() + .filter(|&&ty_arg| nodes[ty_arg].kind == Kind::TyArg)) + { + if nodes[arg_expr].ty.is_none() { + ambiguous = true; + break 'outer; + } + let found_arg = if let (&Some(ref a), &Some(ref b)) = + (&nodes[arg_expr].ty, &nodes[ty_arg].ty) { + if b.goes_with(a) { + if b.ambiguous(a) { + ambiguous = true; + break 'outer; + } + true + } else {false} + } + else {false}; + if !found_arg { + all = false; + break; + } + } + if all { + if let Some(&ind) = nodes[ty].children.iter() + .filter(|&&ty| nodes[ty].kind == Kind::TyRet) + .next() { + *this_ty = nodes[ind].ty.clone(); + found = true; + break; + } + } + } + if !found { + if ambiguous { + // Delay completion of this call until extra type information matches. + todo.push(i); + } else if count > 0 { + use std::io::Write; + + let mut buf: Vec = vec![]; + write!(&mut buf, "Type mismatch (#230):\nThe argument type `").unwrap(); + for (i, arg) in nodes[i].children.iter() + .filter(|&&arg| nodes[arg].kind == Kind::CallArg && + !nodes[arg].children.is_empty()) + .map(|&arg| nodes[arg].children[0]) + .enumerate() { + if let Some(ref arg_ty) = nodes[arg].ty { + if i != 0 {write!(&mut buf, ", ").unwrap()}; + write!(&mut buf, "{}", arg_ty.description()).unwrap(); + } + } + write!(&mut buf, "` does not work with `{}`", + nodes[i].name().expect("Expected name")).unwrap(); + return Err(nodes[i].source.wrap(String::from_utf8(buf).unwrap())) + } + } + + Ok(()) +} From de9ddc2625b2935242541033392cb3f86683e63e Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 18:10:44 +0200 Subject: [PATCH 37/83] Added extra type information to `neg` --- src/lib.rs | 9 ++- src/lifetime/typecheck.rs | 24 +++++++- src/lifetime/typecheck/refine.rs | 100 ++++++++++++++++++++++++------- 3 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cf321cf9..dcf35f1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -363,7 +363,14 @@ impl Module { let mut m = Module::empty(); m.ns("std"); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); - m.add_str("neg", neg, Dfn::nl(vec![Any], Any)); + m.add_str("neg", neg, Dfn{ + lts: vec![Lt::Default], tys: vec![Any], ret: Any, + ext: vec![ + (vec![F64], F64), + (vec![Vec4], Vec4), + (vec![Mat4], Mat4), + ] + }); m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 25875704..51faabf5 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -219,10 +219,30 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> // External functions are treated as loaded in prelude. if let Some(&FnAlias::Loaded(f)) = use_lookup.aliases.get(alias) .and_then(|map| map.get(nodes[i].name().unwrap())) { - this_ty = Some(prelude.list[f].ret.clone()); + let f = &prelude.list[f]; + if f.ext.len() == 0 { + this_ty = Some(f.ret.clone()); + } else { + refine::prelude(i, f, nodes, &mut todo, &mut this_ty)?; + + // If the type has not been refined, fall back to default type signature. + if this_ty.is_none() && nodes[i].ty.is_none() { + this_ty = Some(f.ret.clone()); + } + } } } else if let Some(&f) = prelude.functions.get(nodes[i].name().unwrap()) { - this_ty = Some(prelude.list[f].ret.clone()); + let f = &prelude.list[f]; + if f.ext.len() == 0 { + this_ty = Some(f.ret.clone()); + } else { + refine::prelude(i, f, nodes, &mut todo, &mut this_ty)?; + + // If the type has not been refined, fall back to default type signature. + if this_ty.is_none() && nodes[i].ty.is_none() { + this_ty = Some(f.ret.clone()); + } + } } } Kind::CallClosure => { diff --git a/src/lifetime/typecheck/refine.rs b/src/lifetime/typecheck/refine.rs index e45ac58b..3f57101a 100644 --- a/src/lifetime/typecheck/refine.rs +++ b/src/lifetime/typecheck/refine.rs @@ -1,4 +1,40 @@ use super::*; +use prelude::Dfn; + +fn report( + i: usize, + found: bool, + ambiguous: bool, + count: usize, + nodes: &[Node], + todo: &mut Vec +) -> Result<(), Range> { + if !found { + if ambiguous { + // Delay completion of this call until extra type information matches. + todo.push(i); + } else if count > 0 { + use std::io::Write; + + let mut buf: Vec = vec![]; + write!(&mut buf, "Type mismatch (#230):\nThe argument type `").unwrap(); + for (i, arg) in nodes[i].children.iter() + .filter(|&&arg| nodes[arg].kind == Kind::CallArg && + !nodes[arg].children.is_empty()) + .map(|&arg| nodes[arg].children[0]) + .enumerate() { + if let Some(ref arg_ty) = nodes[arg].ty { + if i != 0 {write!(&mut buf, ", ").unwrap()}; + write!(&mut buf, "{}", arg_ty.description()).unwrap(); + } + } + write!(&mut buf, "` does not work with `{}`", + nodes[i].name().expect("Expected name")).unwrap(); + return Err(nodes[i].source.wrap(String::from_utf8(buf).unwrap())) + } + } + Ok(()) +} pub(crate) fn declaration( i: usize, @@ -53,30 +89,54 @@ pub(crate) fn declaration( } } } - if !found { - if ambiguous { - // Delay completion of this call until extra type information matches. - todo.push(i); - } else if count > 0 { - use std::io::Write; - let mut buf: Vec = vec![]; - write!(&mut buf, "Type mismatch (#230):\nThe argument type `").unwrap(); - for (i, arg) in nodes[i].children.iter() - .filter(|&&arg| nodes[arg].kind == Kind::CallArg && - !nodes[arg].children.is_empty()) - .map(|&arg| nodes[arg].children[0]) - .enumerate() { - if let Some(ref arg_ty) = nodes[arg].ty { - if i != 0 {write!(&mut buf, ", ").unwrap()}; - write!(&mut buf, "{}", arg_ty.description()).unwrap(); + report(i, found, ambiguous, count, nodes, todo) +} + + +pub(crate) fn prelude( + i: usize, + f: &Dfn, + nodes: &[Node], + todo: &mut Vec, + this_ty: &mut Option +) -> Result<(), Range> { + // Refine types using extra type information. + let mut found = false; + let mut ambiguous = false; + 'outer: for ty in &f.ext { + let mut all = true; + for (arg_expr, ty_arg) in nodes[i].children.iter() + .filter(|&&arg| nodes[arg].kind == Kind::CallArg && + !nodes[arg].children.is_empty()) + .map(|&arg| nodes[arg].children[0]) + .zip(ty.0.iter()) + { + if nodes[arg_expr].ty.is_none() { + ambiguous = true; + break 'outer; + } + let found_arg = if let Some(ref a) = nodes[arg_expr].ty { + if ty_arg.goes_with(a) { + if ty_arg.ambiguous(a) { + ambiguous = true; + break 'outer; + } + true + } else {false} } + else {false}; + if !found_arg { + all = false; + break; } - write!(&mut buf, "` does not work with `{}`", - nodes[i].name().expect("Expected name")).unwrap(); - return Err(nodes[i].source.wrap(String::from_utf8(buf).unwrap())) + } + if all { + *this_ty = Some(ty.1.clone()); + found = true; + break; } } - Ok(()) + report(i, found, ambiguous, f.ext.len(), nodes, todo) } From a8695343446444881047c772d4288c41f1baebff Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 18:17:16 +0200 Subject: [PATCH 38/83] Use `RETURN_TYPE` because it has the same name --- src/runtime/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 56d023b0..392b51d0 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -92,7 +92,6 @@ pub struct Runtime { /// When a current object is used, the runtime searches backwards /// until it finds the last current variable with the name. pub current_stack: Vec<(Arc, usize)>, - ret: Arc, pub(crate) rng: rand::rngs::StdRng, /// External functions can choose to report an error on an argument. pub arg_err_index: Cell>, @@ -309,7 +308,6 @@ impl Runtime { call_stack: vec![], local_stack: vec![], current_stack: vec![], - ret: Arc::new("return".into()), rng: rand::rngs::StdRng::from_entropy(), arg_err_index: Cell::new(None), } @@ -857,7 +855,6 @@ impl Runtime { current_len: 0, }], rng: self.rng.clone(), - ret: self.ret.clone(), arg_err_index: Cell::new(None), }; let handle: JoinHandle> = thread::spawn(move || { @@ -941,7 +938,8 @@ impl Runtime { self.push_fn(call.item.name.clone(), env.relative, Some(f.file.clone()), st, lc, cu); if f.returns() { - self.local_stack.push((self.ret.clone(), st - 1)); + // Use return type because it has the same name. + self.local_stack.push((RETURN_TYPE.clone(), st - 1)); } for (i, arg) in f.args.iter().enumerate() { // Do not resolve locals to keep fixed length from end of stack. @@ -1154,7 +1152,8 @@ impl Runtime { self.push_fn(call.name.clone(), new_index, Some(f.file.clone()), st, lc, cu); if f.returns() { - self.local_stack.push((self.ret.clone(), st - 1)); + // Use return type because it has same name. + self.local_stack.push((RETURN_TYPE.clone(), st - 1)); } for (i, arg) in f.args.iter().enumerate() { // Do not resolve locals to keep fixed length from end of stack. From 8abd60940d1d64f1048a19d3cf243748f05549fd Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 18:33:06 +0200 Subject: [PATCH 39/83] Reuse `Arc` pointers with lazy static --- src/ast/mod.rs | 17 ++++++++++------- src/lib.rs | 6 ++++++ src/lifetime/mod.rs | 6 +++--- src/runtime/mod.rs | 3 ++- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6f77dca6..21cd83c3 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -479,7 +479,8 @@ impl Function { let mut stack: Vec>> = vec![]; let mut closure_stack: Vec = vec![]; if self.returns() { - stack.push(Some(Arc::new("return".into()))); + // Use return type because it has the same name. + stack.push(Some(crate::runtime::RETURN_TYPE.clone())); } for arg in &self.args { stack.push(Some(arg.name.clone())); @@ -589,7 +590,8 @@ impl Closure { let cs = closure_stack.len(); closure_stack.push(stack.len()); if self.returns() { - stack.push(Some(Arc::new("return".into()))); + // Use return type because it has the same name. + stack.push(Some(crate::runtime::RETURN_TYPE.clone())); } for arg in &self.args { stack.push(Some(arg.name.clone())); @@ -2790,7 +2792,8 @@ impl CallClosure { let st = stack.len(); self.item.resolve_locals(relative, stack, closure_stack, module, use_lookup); // All closures must return a value. - stack.push(Some(Arc::new("return".into()))); + // Use return type because it has the same name. + stack.push(Some(crate::runtime::RETURN_TYPE.clone())); for arg in &self.args { let arg_st = stack.len(); arg.resolve_locals(relative, stack, closure_stack, module, use_lookup); @@ -2875,7 +2878,7 @@ impl Norm { args: vec![self.expr], custom_source: None, f_index: Cell::new(FnIndex::None), - name: Arc::new("norm".into()), + name: crate::NORM.clone(), source_range: self.source_range, })) } @@ -2969,12 +2972,12 @@ impl UnOpExpression { fn into_expression(self) -> Expression { let name = match self.op { - UnOp::Not => "not", - UnOp::Neg => "neg" + UnOp::Not => crate::NOT.clone(), + UnOp::Neg => crate::NEG.clone() }; Expression::Call(Box::new(Call { alias: None, - name: Arc::new(name.into()), + name: name, args: vec![self.expr], custom_source: None, f_index: Cell::new(FnIndex::None), diff --git a/src/lib.rs b/src/lib.rs index dcf35f1c..96cc00c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,12 @@ pub use mat4::Mat4; /// A common error message when there is no value on the stack. pub const TINVOTS: &str = "There is no value on the stack"; +lazy_static!{ + pub(crate) static ref NOT: Arc = Arc::new("not".into()); + pub(crate) static ref NEG: Arc = Arc::new("neg".into()); + pub(crate) static ref NORM: Arc = Arc::new("norm".into()); +} + /// Type alias for Dyon arrays. pub type Array = Arc>; /// Type alias for Dyon objects. diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 841bf87a..179eca1a 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -32,9 +32,9 @@ pub fn check( for i in 0..nodes.len() { if nodes[i].children.len() == 1 { Node::rewrite_unop(i, match nodes[i].kind { - Kind::Norm => Arc::new("norm".into()), - Kind::Not => Arc::new("not".into()), - Kind::Neg => Arc::new("neg".into()), + Kind::Norm => crate::NORM.clone(), + Kind::Not => crate::NOT.clone(), + Kind::Neg => crate::NEG.clone(), _ => continue }, &mut nodes) } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 392b51d0..d6f16243 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -75,6 +75,7 @@ lazy_static! { pub(crate) static ref THREAD_TYPE: Arc = Arc::new("thread".into()); pub(crate) static ref CLOSURE_TYPE: Arc = Arc::new("closure".into()); pub(crate) static ref IN_TYPE: Arc = Arc::new("in".into()); + pub(crate) static ref MAIN: Arc = Arc::new("main".into()); } /// Stores data needed for running a Dyon program. @@ -746,7 +747,7 @@ impl Runtime { use std::mem::replace; let old_module = replace(&mut self.module, module.clone()); - let name: Arc = Arc::new("main".into()); + let name: Arc = MAIN.clone(); let call = ast::Call { alias: None, name: name.clone(), From af99af9878dc6ea7056db6a28385f3f97cc2f5db Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 18:43:17 +0200 Subject: [PATCH 40/83] Removed `UnOp` --- assets/syntax.txt | 4 +-- src/ast/mod.rs | 66 +++++++++++++---------------------------------- 2 files changed, 20 insertions(+), 50 deletions(-) diff --git a/assets/syntax.txt b/assets/syntax.txt index 87368e8d..16f3f15f 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -98,8 +98,8 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" ?[, arg_expr:"z" ?[, arg_expr:"w"]] ?,] 26 text = .t?:"text" 27 bool = [{"true":"bool" "false":!"bool"} !.._seps!] -28 unop_not = [{"!":"!" "¬":"!"} ?w lexpr:"expr"] -29 unop_neg = ["-":"-" ?w mul_expr:"expr"] +28 unop_not = [{"!" "¬"} ?w lexpr:"expr"] +29 unop_neg = ["-" ?w mul_expr:"expr"] 30 norm = ["|" ?w expr:"expr" ?w "|"] 31 item = [?"~":"current" ?w .._seps!:"name" ?[wn "?":"try_item"] ?item_extra:"item_extra"] diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 21cd83c3..a6c41487 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1095,7 +1095,7 @@ impl Expression { } else if let Ok((range, val)) = UnOpExpression::from_meta_data( "not", file, source, convert, ignored) { convert.update(range); - result = Some(val.into_expression()); + result = Some(val); } else if let Ok((range, val)) = Mul::from_meta_data( file, source, convert, ignored) { convert.update(range); @@ -1931,7 +1931,7 @@ impl Mul { } else if let Ok((range, val)) = UnOpExpression::from_meta_data( "neg", file, source, convert, ignored) { convert.update(range); - items.push(val.into_expression()); + items.push(val); } else if let Ok((range, val)) = Pow::from_meta_data( file, source, convert, ignored) { convert.update(range); @@ -2124,15 +2124,6 @@ impl BinOp { } } -/// Unary operator. -#[derive(Debug, Copy, Clone)] -pub enum UnOp { - /// Logical not. - Not, - /// Negation. - Neg, -} - /// An item id. /// /// This is the thing that's inside the square brackets, e.g. `foo[i]`. @@ -2915,15 +2906,7 @@ impl BinOpExpression { } /// Unary operator expression. -#[derive(Debug, Clone)] -pub struct UnOpExpression { - /// Unary operator. - pub op: UnOp, - /// Expression argument. - pub expr: Expression, - /// The range in source. - pub source_range: Range, -} +pub struct UnOpExpression; impl UnOpExpression { /// Creates unary operator expression from meta data. @@ -2933,23 +2916,16 @@ impl UnOpExpression { source: &Arc, mut convert: Convert, ignored: &mut Vec) - -> Result<(Range, UnOpExpression), ()> { + -> Result<(Range, Expression), ()> { let start = convert; let start_range = convert.start_node(node)?; convert.update(start_range); - let mut unop: Option = None; let mut expr: Option = None; loop { if let Ok(range) = convert.end_node(node) { convert.update(range); break; - } else if let Ok((range, _)) = convert.meta_bool("!") { - convert.update(range); - unop = Some(UnOp::Not); - } else if let Ok((range, _)) = convert.meta_bool("-") { - convert.update(range); - unop = Some(UnOp::Neg); } else if let Ok((range, val)) = Expression::from_meta_data( file, source, "expr", convert, ignored) { convert.update(range); @@ -2961,27 +2937,21 @@ impl UnOpExpression { } } - let unop = unop.ok_or(())?; let expr = expr.ok_or(())?; - Ok((convert.subtract(start), UnOpExpression { - op: unop, - expr, - source_range: convert.source(start).unwrap() - })) - } - - fn into_expression(self) -> Expression { - let name = match self.op { - UnOp::Not => crate::NOT.clone(), - UnOp::Neg => crate::NEG.clone() - }; - Expression::Call(Box::new(Call { - alias: None, - name: name, - args: vec![self.expr], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range + Ok((convert.subtract(start), { + let name = match node { + "not" => crate::NOT.clone(), + "neg" => crate::NEG.clone(), + _ => return Err(()) + }; + Expression::Call(Box::new(Call { + alias: None, + name: name, + args: vec![expr], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: convert.source(start).unwrap() + })) })) } } From 498ad2ad0aa67487bd909dc9a27b71d8d00a39a4 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 18 Sep 2019 19:45:03 +0200 Subject: [PATCH 41/83] Made `dot` standard external function --- src/ast/mod.rs | 22 ++++++++++++++++++---- src/dyon_std/mod.rs | 16 ++++++++++++++++ src/lib.dyon | 6 ++++++ src/lib.rs | 11 +++++++++++ src/runtime/mod.rs | 10 +++------- src/write.rs | 18 +++++++++++------- 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a6c41487..0923b1cf 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1888,12 +1888,12 @@ impl Add { let op = self.ops.pop().unwrap(); let last = self.items.pop().unwrap(); let source_range = self.source_range; - Expression::BinOp(Box::new(BinOpExpression { + BinOpExpression { op, left: self.into_expression(), right: last, source_range - })) + }.into_expression() } } } @@ -1982,12 +1982,12 @@ impl Mul { let op = self.ops.pop().expect("Expected a binary operation"); let last = self.items.pop().expect("Expected argument"); let source_range = self.source_range; - Expression::BinOp(Box::new(BinOpExpression { + BinOpExpression { op, left: self.into_expression(), right: last, source_range, - })) + }.into_expression() } } } @@ -2903,6 +2903,20 @@ impl BinOpExpression { self.right.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); } + + fn into_expression(self) -> Expression { + match self.op { + BinOp::Dot => Expression::Call(Box::new(Call { + alias: None, + name: crate::DOT.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), + _ => Expression::BinOp(Box::new(self)) + } + } } /// Unary operator expression. diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index f266b04e..89dd7b02 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -41,6 +41,22 @@ pub(crate) fn neg(rt: &mut Runtime) -> Result<(), String> { Ok(()) } +pub(crate) fn dot(rt: &mut Runtime) -> Result<(), String> { + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&Variable::Vec4(a), &Variable::Vec4(b)) => vecmath::vec4_dot(a, b) as f64, + (&Variable::Vec4(a), &Variable::F64(b, _)) | + (&Variable::F64(b, _), &Variable::Vec4(a)) => { + let b = b as f32; + (a[0] * b + a[1] * b + a[2] * b + a[3] * b) as f64 + } + _ => return Err("Expected (vec4, vec4), (vec4, f64) or (f64, vec4)".into()) + }; + rt.stack.push(Variable::f64(r)); + Ok(()) +} + dyon_fn!{fn x(v: Vec4) -> f64 {f64::from(v.0[0])}} dyon_fn!{fn y(v: Vec4) -> f64 {f64::from(v.0[1])}} dyon_fn!{fn z(v: Vec4) -> f64 {f64::from(v.0[2])}} diff --git a/src/lib.dyon b/src/lib.dyon index 3ceec0c9..743c0513 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -241,6 +241,12 @@ fn neg(v: any) -> any { ... } (vec4) -> vec4 (mat4) -> mat4 +/// Returns dot product. +fn dot(a: any, b: any) -> f64 { ... } + (vec4, vec4) -> vec4 + (vec4, f64) -> vec4 + (f64, vec4) -> vec4 + /// Returns x component of 4D vector. fn x(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index 96cc00c4..73e199ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,7 @@ pub use mat4::Mat4; pub const TINVOTS: &str = "There is no value on the stack"; lazy_static!{ + pub(crate) static ref DOT: Arc = Arc::new("dot".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); pub(crate) static ref NEG: Arc = Arc::new("neg".into()); pub(crate) static ref NORM: Arc = Arc::new("norm".into()); @@ -377,6 +378,16 @@ impl Module { (vec![Mat4], Mat4), ] }); + m.add_str("dot", dot, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: F64, + ext: vec![ + (vec![Vec4, Vec4], F64), + (vec![Vec4, F64], F64), + (vec![F64, Vec4], F64), + ] + }); m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index d6f16243..d222448a 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2530,8 +2530,6 @@ impl Runtime { Add => Variable::Vec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]]), Sub => Variable::Vec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]]), Mul => Variable::Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]), - Dot => Variable::f64(f64::from(a[0] * b[0] + a[1] * b[1] + - a[2] * b[2] + a[3] * b[3])), Cross => Variable::Vec4([a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0]), @@ -2539,7 +2537,7 @@ impl Runtime { Rem => Variable::Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), Pow => Variable::Vec4([a[0].powf(b[0]), a[1].powf(b[1]), a[2].powf(b[2]), a[3].powf(b[3])]), - AndAlso | OrElse => return Err(self.module.error(binop.source_range, + AndAlso | OrElse | Dot => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `vec4` and `vec4`", self.stack_trace(), binop.op.symbol_bool()), self)), @@ -2551,7 +2549,6 @@ impl Runtime { Add => Variable::Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]), Sub => Variable::Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b]), Mul => Variable::Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]), - Dot => Variable::f64(f64::from(a[0] * b + a[1] * b + a[2] * b + a[3] * b)), Cross => return Err(self.module.error(binop.source_range, &format!("{}\nExpected two vec4 for `{:?}`", self.stack_trace(), binop.op.symbol()), self)), @@ -2559,7 +2556,7 @@ impl Runtime { Rem => Variable::Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]), Pow => Variable::Vec4([a[0].powf(b), a[1].powf(b), a[2].powf(b), a[3].powf(b)]), - AndAlso | OrElse => return Err(self.module.error(binop.source_range, + AndAlso | OrElse | Dot => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `vec4` and `f64`", self.stack_trace(), binop.op.symbol_bool()), self)), @@ -2571,7 +2568,6 @@ impl Runtime { Add => Variable::Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]), Sub => Variable::Vec4([a - b[0], a - b[1], a - b[2], a - b[3]]), Mul => Variable::Vec4([a * b[0], a * b[1], a * b[2], a * b[3]]), - Dot => Variable::f64(f64::from(a * b[0] + a * b[1] + a * b[2] + a * b[3])), Div => Variable::Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]), Rem => Variable::Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]), Pow => Variable::Vec4([a.powf(b[0]), a.powf(b[1]), @@ -2579,7 +2575,7 @@ impl Runtime { Cross => return Err(self.module.error(binop.source_range, &format!("{}\nExpected two vec4 for `{:?}`", self.stack_trace(), binop.op.symbol()), self)), - AndAlso | OrElse => return Err(self.module.error(binop.source_range, + AndAlso | OrElse | Dot => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `f64` and `vec4`", self.stack_trace(), binop.op.symbol_bool()), self)), diff --git a/src/write.rs b/src/write.rs index 6f78bd91..c47e501b 100644 --- a/src/write.rs +++ b/src/write.rs @@ -194,7 +194,7 @@ fn write_expr( use ast::Expression as E; match *expr { - E::BinOp(ref binop) => write_binop(w, rt, binop, tabs)?, + E::BinOp(ref binop) => write_binop(w, rt, binop.op, &binop.left, &binop.right, tabs)?, E::Item(ref item) => write_item(w, rt, item, tabs)?, E::Variable(ref range_var) => write_variable(w, rt, &range_var.1, EscapeString::Json, tabs)?, @@ -209,6 +209,8 @@ fn write_expr( write_not(w, rt, &call.args[0], tabs)? } else if &**call.name == "neg" && call.args.len() == 1 { write_neg(w, rt, &call.args[0], tabs)? + } else if &**call.name == "dot" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Dot, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } @@ -402,24 +404,26 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo fn write_binop( w: &mut W, rt: &Runtime, - binop: &ast::BinOpExpression, + op: ast::BinOp, + left: &ast::Expression, + right: &ast::Expression, tabs: u32, ) -> Result<(), io::Error> { - let left_needs_parens = binop_needs_parens(binop.op, &binop.left, false); - let right_needs_parens = binop_needs_parens(binop.op, &binop.right, true); + let left_needs_parens = binop_needs_parens(op, left, false); + let right_needs_parens = binop_needs_parens(op, right, true); if left_needs_parens { write!(w, "(")?; } - write_expr(w, rt, &binop.left, tabs)?; + write_expr(w, rt, left, tabs)?; if left_needs_parens { write!(w, ")")?; } - write!(w, " {} ", binop.op.symbol())?; + write!(w, " {} ", op.symbol())?; if right_needs_parens { write!(w, "(")?; } - write_expr(w, rt, &binop.right, tabs)?; + write_expr(w, rt, right, tabs)?; if right_needs_parens { write!(w, ")")?; } From 21c8f1c9a312f437cc7ff5972635c98b3c7aac5b Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Thu, 19 Sep 2019 09:40:32 +0200 Subject: [PATCH 42/83] Rewrite multiple binary operators into nested ones - Added efficient normalizing algorithm based on Group Theory --- src/lifetime/mod.rs | 78 ++++++++++++++++++++++++++++++++++++--- src/lifetime/normalize.rs | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 src/lifetime/normalize.rs diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 179eca1a..6af4dbbb 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -18,6 +18,7 @@ mod kind; mod node; mod lt; mod typecheck; +mod normalize; /// Checks lifetime constraints and does type checking. /// Returns refined return types of functions to put in AST. @@ -28,18 +29,83 @@ pub fn check( let mut nodes: Vec = vec![]; convert_meta_data(&mut nodes, data)?; + // Rewrite multiple binary operators into nested ones. + for i in 0..nodes.len() { + if nodes[i].binops.len() <= 1 {continue}; + + let new_child = nodes.len(); + let mut parent = nodes[i].parent; + for n in (2..nodes[i].children.len()).rev() { + // The right argument of the last call + // is the last item among the children. + // The left argument of the last call + // is the result of the recursion. + // The last call gets at the top. + // This means that it gets pushed first. + // The left argument points to the next node to be pushed, + // except the last push which points to original node for reuse. + let id = nodes.len(); + let right = nodes[i].children[n]; + nodes[right].parent = Some(id); + nodes.push(Node { + kind: nodes[i].kind, + names: vec![], + ty: None, + declaration: None, + alias: None, + mutable: false, + try: false, + grab_level: 0, + source: nodes[i].source, + start: nodes[i].start, + end: nodes[i].end, + lifetime: None, + op: None, + binops: vec![nodes[i].binops[n-1]], + lts: vec![], + parent, + children: vec![ + if n == 2 {i} else {id + 1}, + right + ] + }); + parent = Some(id); + } + + // Remove all children from original node except the two first. + nodes[i].children.truncate(2); + // Remove all binary operators from original node except the first. + nodes[i].binops.truncate(1); + if let Some(old_parent) = nodes[i].parent { + // Find the node among the children of the parent, + // but do not rely on binary search because it might be unsorted. + // Unsorted children is due to possible inference from other rewrites. + for j in 0..nodes[old_parent].children.len() { + if nodes[old_parent].children[j] == i { + nodes[old_parent].children[j] = new_child; + break; + } + } + } + // Change parent of original node. + nodes[i].parent = parent; + } + // Rewrite graph for syntax sugar that corresponds to function calls. for i in 0..nodes.len() { if nodes[i].children.len() == 1 { - Node::rewrite_unop(i, match nodes[i].kind { - Kind::Norm => crate::NORM.clone(), - Kind::Not => crate::NOT.clone(), - Kind::Neg => crate::NEG.clone(), - _ => continue - }, &mut nodes) + match nodes[i].kind { + Kind::Norm => Node::rewrite_unop(i, crate::NORM.clone(), &mut nodes), + Kind::Not => Node::rewrite_unop(i, crate::NOT.clone(), &mut nodes), + Kind::Neg => Node::rewrite_unop(i, crate::NEG.clone(), &mut nodes), + _ => {} + } } } + // After graph rewrite, the graph might be unnormalized. + normalize::fix(&mut nodes); + // Add mutability information to function names. for i in 0..nodes.len() { match nodes[i].kind { diff --git a/src/lifetime/normalize.rs b/src/lifetime/normalize.rs new file mode 100644 index 00000000..b1a210dc --- /dev/null +++ b/src/lifetime/normalize.rs @@ -0,0 +1,71 @@ +use super::*; + +/// Normalize directed acyclic graph such that all children are sorted in memory, +/// and no child is stored before its parent. +pub fn fix(nodes: &mut [Node]) { + // This problem can be solving efficiently using Group Theory. + // This avoid the need for cloning nodes into a new array, + // while performing the minimum work to get a normalized graph. + // + // Create a group generator that is modified by swapping to find a solution. + // The group generator keeps track of indices, such that child-parent relations + // do not have to change until later. + // + // Use the order in the generator to detect whether a swap has been performed. + // The condition for swapping `a, b` is `gen[a] > gen[b]`. + let mut gen: Vec = (0..nodes.len()).collect(); + loop { + let mut changed = false; + for i in 0..nodes.len() { + for j in 0..nodes[i].children.len() { + let a = nodes[i].children[j]; + // Store child after its parent. + if gen[i] > gen[a] { + gen.swap(i, a); + changed = true; + } + // Check all pairs of children. + for k in j+1..nodes[i].children.len() { + let b = nodes[i].children[k]; + + // Store children in sorted order. + if gen[a] > gen[b] { + gen.swap(a, b); + changed = true; + } + } + } + } + if !changed {break} + } + + // Update the graph data with the new indices from the generator. + // Do this before performing the actual swapping, + // since the generator maps from old indices to new indices. + for i in 0..nodes.len() { + nodes[i].parent = nodes[i].parent.map(|p| gen[p]); + for ch in &mut nodes[i].children {*ch = gen[*ch]} + } + + // Swap nodes using the group generator as guide. + // When swapping has been performed, update the generator to keep track of state. + // This is because multiple swaps sharing elements might require multiple steps. + // + // The order which swaps are retraced might be different than the solving phase: + // + // `a, b, c` => `a, (c, b)` => `(c, a), b` => `c, a, b` (solving phase) + // `c, a, b` => `(b), a, (c)` => `(a, b), c` => `a, b, c` (retrace phase) + // + // However, since the generator solution is produced by swapping operations, + // it is guaranteed to be restorable to the identity generator when retracing. + // + // There is no need to loop more than once because each index is stored uniquely by lookup, + // such that if `g[i] = i` then there exists no `j != i` such that `g[j] = i`. + for i in 0..nodes.len() { + while gen[i] != i { + let j = gen[i]; + nodes.swap(i, j); + gen.swap(i, j); + } + } +} From ad82d86fba112ad31c6d646a20aeb41b1cb325b5 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Thu, 19 Sep 2019 10:02:41 +0200 Subject: [PATCH 43/83] Make normalizing algorithm generic --- src/lifetime/normalize.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/lifetime/normalize.rs b/src/lifetime/normalize.rs index b1a210dc..8ef1b708 100644 --- a/src/lifetime/normalize.rs +++ b/src/lifetime/normalize.rs @@ -3,6 +3,16 @@ use super::*; /// Normalize directed acyclic graph such that all children are sorted in memory, /// and no child is stored before its parent. pub fn fix(nodes: &mut [Node]) { + sort(nodes, |n| &mut n.parent, |n| &mut n.children) +} + +/// Performs in-memory topological sort on a directed acyclic graph where +/// order is determined by every child being greater than their parent, +/// and every sibling being greater than previous siblings. +pub fn sort(nodes: &mut [T], parent: P, children: C) + where P: Fn(&mut T) -> &mut Option, + C: Fn(&mut T) -> &mut [usize] +{ // This problem can be solving efficiently using Group Theory. // This avoid the need for cloning nodes into a new array, // while performing the minimum work to get a normalized graph. @@ -17,16 +27,17 @@ pub fn fix(nodes: &mut [Node]) { loop { let mut changed = false; for i in 0..nodes.len() { - for j in 0..nodes[i].children.len() { - let a = nodes[i].children[j]; + let children = children(&mut nodes[i]); + for j in 0..children.len() { + let a = children[j]; // Store child after its parent. if gen[i] > gen[a] { gen.swap(i, a); changed = true; } // Check all pairs of children. - for k in j+1..nodes[i].children.len() { - let b = nodes[i].children[k]; + for k in j+1..children.len() { + let b = children[k]; // Store children in sorted order. if gen[a] > gen[b] { @@ -43,8 +54,9 @@ pub fn fix(nodes: &mut [Node]) { // Do this before performing the actual swapping, // since the generator maps from old indices to new indices. for i in 0..nodes.len() { - nodes[i].parent = nodes[i].parent.map(|p| gen[p]); - for ch in &mut nodes[i].children {*ch = gen[*ch]} + let p = parent(&mut nodes[i]); + *p = p.map(|p| gen[p]); + for ch in children(&mut nodes[i]) {*ch = gen[*ch]} } // Swap nodes using the group generator as guide. From 8fd52424383b339b1887177c747071805001a5cf Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Thu, 19 Sep 2019 13:24:28 +0200 Subject: [PATCH 44/83] Use "advancedresearch-tree_mem_sort" for normalizing after rewriting --- Cargo.toml | 1 + src/lib.rs | 1 + src/lifetime/normalize.rs | 78 +-------------------------------------- 3 files changed, 3 insertions(+), 77 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e8415a1..c42eac06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ read_color = "1.0.0" read_token = "1.0.0" lazy_static = "1.0.0" vecmath = "1.0.0" +advancedresearch-tree_mem_sort = "0.1.1" [dependencies.reqwest] version = "0.4.0" diff --git a/src/lib.rs b/src/lib.rs index 73e199ce..a23eeae7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ extern crate reqwest; #[macro_use] extern crate lazy_static; extern crate vecmath; +extern crate tree_mem_sort; use std::any::Any; use std::fmt; diff --git a/src/lifetime/normalize.rs b/src/lifetime/normalize.rs index 8ef1b708..18262590 100644 --- a/src/lifetime/normalize.rs +++ b/src/lifetime/normalize.rs @@ -3,81 +3,5 @@ use super::*; /// Normalize directed acyclic graph such that all children are sorted in memory, /// and no child is stored before its parent. pub fn fix(nodes: &mut [Node]) { - sort(nodes, |n| &mut n.parent, |n| &mut n.children) -} - -/// Performs in-memory topological sort on a directed acyclic graph where -/// order is determined by every child being greater than their parent, -/// and every sibling being greater than previous siblings. -pub fn sort(nodes: &mut [T], parent: P, children: C) - where P: Fn(&mut T) -> &mut Option, - C: Fn(&mut T) -> &mut [usize] -{ - // This problem can be solving efficiently using Group Theory. - // This avoid the need for cloning nodes into a new array, - // while performing the minimum work to get a normalized graph. - // - // Create a group generator that is modified by swapping to find a solution. - // The group generator keeps track of indices, such that child-parent relations - // do not have to change until later. - // - // Use the order in the generator to detect whether a swap has been performed. - // The condition for swapping `a, b` is `gen[a] > gen[b]`. - let mut gen: Vec = (0..nodes.len()).collect(); - loop { - let mut changed = false; - for i in 0..nodes.len() { - let children = children(&mut nodes[i]); - for j in 0..children.len() { - let a = children[j]; - // Store child after its parent. - if gen[i] > gen[a] { - gen.swap(i, a); - changed = true; - } - // Check all pairs of children. - for k in j+1..children.len() { - let b = children[k]; - - // Store children in sorted order. - if gen[a] > gen[b] { - gen.swap(a, b); - changed = true; - } - } - } - } - if !changed {break} - } - - // Update the graph data with the new indices from the generator. - // Do this before performing the actual swapping, - // since the generator maps from old indices to new indices. - for i in 0..nodes.len() { - let p = parent(&mut nodes[i]); - *p = p.map(|p| gen[p]); - for ch in children(&mut nodes[i]) {*ch = gen[*ch]} - } - - // Swap nodes using the group generator as guide. - // When swapping has been performed, update the generator to keep track of state. - // This is because multiple swaps sharing elements might require multiple steps. - // - // The order which swaps are retraced might be different than the solving phase: - // - // `a, b, c` => `a, (c, b)` => `(c, a), b` => `c, a, b` (solving phase) - // `c, a, b` => `(b), a, (c)` => `(a, b), c` => `a, b, c` (retrace phase) - // - // However, since the generator solution is produced by swapping operations, - // it is guaranteed to be restorable to the identity generator when retracing. - // - // There is no need to loop more than once because each index is stored uniquely by lookup, - // such that if `g[i] = i` then there exists no `j != i` such that `g[j] = i`. - for i in 0..nodes.len() { - while gen[i] != i { - let j = gen[i]; - nodes.swap(i, j); - gen.swap(i, j); - } - } + tree_mem_sort::sort(nodes, |n| &mut n.parent, |n| &mut n.children) } From bd1ed8703b632021ba78736adff8c9e794501550 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Fri, 20 Sep 2019 06:38:28 +0200 Subject: [PATCH 45/83] Rewrite `dot` in type checker --- src/lifetime/mod.rs | 9 ++++++++- src/lifetime/node.rs | 13 ++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 6af4dbbb..675135ba 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -32,7 +32,7 @@ pub fn check( // Rewrite multiple binary operators into nested ones. for i in 0..nodes.len() { if nodes[i].binops.len() <= 1 {continue}; - + let new_child = nodes.len(); let mut parent = nodes[i].parent; for n in (2..nodes[i].children.len()).rev() { @@ -100,6 +100,13 @@ pub fn check( Kind::Neg => Node::rewrite_unop(i, crate::NEG.clone(), &mut nodes), _ => {} } + } else if nodes[i].binops.len() == 1 { + use ast::BinOp::*; + + match nodes[i].binops[0] { + Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), + _ => {} + } } } diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 2d6773f7..8ca750de 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -61,6 +61,16 @@ impl Node { nodes[ch].kind = Kind::CallArg; } + pub fn rewrite_binop(i: usize, name: Arc, nodes: &mut [Node]) { + nodes[i].kind = Kind::Call; + nodes[i].names.push(name); + nodes[i].binops.clear(); + let left = nodes[i].children[0]; + let right = nodes[i].children[1]; + nodes[left].kind = Kind::CallArg; + nodes[right].kind = Kind::CallArg; + } + #[allow(dead_code)] pub fn print(&self, nodes: &[Node], indent: u32) { for _ in 0..indent { print!(" ") } @@ -252,7 +262,8 @@ impl Node { // The result of array fill does not depend on `n`. continue } - (Kind::Call, Kind::CallArg) | (Kind::CallClosure, Kind::CallArg) => { + (Kind::Call, Kind::CallArg) | (Kind::CallClosure, Kind::CallArg) | + (Kind::CallArg, Kind::CallArg) => { // If there is no return lifetime on the declared argument, // there is no need to check it, because the computed value // does not depend on the lifetime of that argument. From ec96e0088f75b2c1ab0b175755a5c64dc8460d12 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Fri, 20 Sep 2019 06:54:34 +0200 Subject: [PATCH 46/83] Make `cross` standard external function --- src/ast/mod.rs | 8 ++++++++ src/dyon_std/mod.rs | 6 ++++++ src/lib.dyon | 9 ++++++--- src/lib.rs | 2 ++ src/lifetime/mod.rs | 3 ++- src/runtime/mod.rs | 15 +++------------ src/write.rs | 2 ++ 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0923b1cf..3e84c4ff 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2914,6 +2914,14 @@ impl BinOpExpression { f_index: Cell::new(FnIndex::None), source_range: self.source_range, })), + BinOp::Cross => Expression::Call(Box::new(Call { + alias: None, + name: crate::CROSS.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), _ => Expression::BinOp(Box::new(self)) } } diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 89dd7b02..36c3b7a4 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -57,6 +57,12 @@ pub(crate) fn dot(rt: &mut Runtime) -> Result<(), String> { Ok(()) } +dyon_fn!{fn cross(a: Vec4, b: Vec4) -> Vec4 { + Vec4([a.0[1] * b.0[2] - a.0[2] * b.0[1], + a.0[2] * b.0[0] - a.0[0] * b.0[2], + a.0[0] * b.0[1] - a.0[1] * b.0[0], 0.0]) +}} + dyon_fn!{fn x(v: Vec4) -> f64 {f64::from(v.0[0])}} dyon_fn!{fn y(v: Vec4) -> f64 {f64::from(v.0[1])}} dyon_fn!{fn z(v: Vec4) -> f64 {f64::from(v.0[2])}} diff --git a/src/lib.dyon b/src/lib.dyon index 743c0513..3343a306 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -243,9 +243,12 @@ fn neg(v: any) -> any { ... } /// Returns dot product. fn dot(a: any, b: any) -> f64 { ... } - (vec4, vec4) -> vec4 - (vec4, f64) -> vec4 - (f64, vec4) -> vec4 + (vec4, vec4) -> f64 + (vec4, f64) -> f64 + (f64, vec4) -> f64 + +/// Returns cross product. +fn cross(a: vec4, b: vec4) -> vec4 { ... } /// Returns x component of 4D vector. fn x(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index a23eeae7..3a865105 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub const TINVOTS: &str = "There is no value on the stack"; lazy_static!{ pub(crate) static ref DOT: Arc = Arc::new("dot".into()); + pub(crate) static ref CROSS: Arc = Arc::new("cross".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); pub(crate) static ref NEG: Arc = Arc::new("neg".into()); pub(crate) static ref NORM: Arc = Arc::new("norm".into()); @@ -389,6 +390,7 @@ impl Module { (vec![F64, Vec4], F64), ] }); + m.add_str("cross", cross, Dfn::nl(vec![Vec4, Vec4], Vec4)); m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 675135ba..feed027a 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -100,11 +100,12 @@ pub fn check( Kind::Neg => Node::rewrite_unop(i, crate::NEG.clone(), &mut nodes), _ => {} } - } else if nodes[i].binops.len() == 1 { + } else if nodes[i].binops.len() == 1 && nodes[i].children.len() == 2 { use ast::BinOp::*; match nodes[i].binops[0] { Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), + Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), _ => {} } } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index d222448a..63a7d808 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2530,14 +2530,11 @@ impl Runtime { Add => Variable::Vec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]]), Sub => Variable::Vec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]]), Mul => Variable::Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]), - Cross => Variable::Vec4([a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0], 0.0]), Div => Variable::Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), Rem => Variable::Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), Pow => Variable::Vec4([a[0].powf(b[0]), a[1].powf(b[1]), a[2].powf(b[2]), a[3].powf(b[3])]), - AndAlso | OrElse | Dot => return Err(self.module.error(binop.source_range, + AndAlso | OrElse | Dot | Cross => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `vec4` and `vec4`", self.stack_trace(), binop.op.symbol_bool()), self)), @@ -2549,14 +2546,11 @@ impl Runtime { Add => Variable::Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]), Sub => Variable::Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b]), Mul => Variable::Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]), - Cross => return Err(self.module.error(binop.source_range, - &format!("{}\nExpected two vec4 for `{:?}`", - self.stack_trace(), binop.op.symbol()), self)), Div => Variable::Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]), Rem => Variable::Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]), Pow => Variable::Vec4([a[0].powf(b), a[1].powf(b), a[2].powf(b), a[3].powf(b)]), - AndAlso | OrElse | Dot => return Err(self.module.error(binop.source_range, + AndAlso | OrElse | Dot | Cross => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `vec4` and `f64`", self.stack_trace(), binop.op.symbol_bool()), self)), @@ -2572,10 +2566,7 @@ impl Runtime { Rem => Variable::Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]), Pow => Variable::Vec4([a.powf(b[0]), a.powf(b[1]), a.powf(b[2]), a.powf(b[3])]), - Cross => return Err(self.module.error(binop.source_range, - &format!("{}\nExpected two vec4 for `{:?}`", - self.stack_trace(), binop.op.symbol()), self)), - AndAlso | OrElse | Dot => return Err(self.module.error(binop.source_range, + AndAlso | OrElse | Dot | Cross => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `f64` and `vec4`", self.stack_trace(), binop.op.symbol_bool()), self)), diff --git a/src/write.rs b/src/write.rs index c47e501b..3e983222 100644 --- a/src/write.rs +++ b/src/write.rs @@ -211,6 +211,8 @@ fn write_expr( write_neg(w, rt, &call.args[0], tabs)? } else if &**call.name == "dot" && call.args.len() == 2 { write_binop(w, rt, ast::BinOp::Dot, &call.args[0], &call.args[1], tabs)? + } else if &**call.name == "cross" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Cross, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } From 120404687510bedcd97c7d6c7eab2d86cbd0a714 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Fri, 20 Sep 2019 08:03:48 +0200 Subject: [PATCH 47/83] Made `add` standard external function --- src/ast/mod.rs | 8 ++++++ src/dyon_std/mod.rs | 52 +++++++++++++++++++++++++++++++++ src/lib.dyon | 13 +++++++++ src/lib.rs | 18 ++++++++++++ src/lifetime/mod.rs | 1 + src/lifetime/node.rs | 68 ++++++++++++++++++++++++++++++++++++++++---- src/runtime/mod.rs | 52 ++++----------------------------- 7 files changed, 160 insertions(+), 52 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 3e84c4ff..247f4f07 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2906,6 +2906,14 @@ impl BinOpExpression { fn into_expression(self) -> Expression { match self.op { + BinOp::Add => Expression::Call(Box::new(Call { + alias: None, + name: crate::ADD.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), BinOp::Dot => Expression::Call(Box::new(Call { alias: None, name: crate::DOT.clone(), diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 36c3b7a4..39f49dbe 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -14,6 +14,58 @@ const HTTP_SUPPORT_DISABLED: &'static str = "Http support is disabled"; #[cfg(not(feature = "file"))] const FILE_SUPPORT_DISABLED: &'static str = "File support is disabled"; +pub(crate) fn add(rt: &mut Runtime) -> Result<(), String> { + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => + Variable::F64(a + b, sec.clone()), + (&Variable::Vec4(a), &Variable::Vec4(b)) => + Variable::Vec4(vecmath::vec4_add(a, b)), + (&Variable::Vec4(a), &Variable::F64(b, _)) => { + let b = b as f32; + Variable::Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]) + } + (&Variable::F64(a, _), &Variable::Vec4(b)) => { + let a = a as f32; + Variable::Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]) + } + (&Variable::Mat4(ref a), &Variable::Mat4(ref b)) => + Variable::Mat4(Box::new(vecmath::mat4_add(**a, **b))), + (&Variable::F64(a, _), &Variable::Mat4(ref b)) => { + let a = a as f32; + Variable::Mat4(Box::new([ + [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], + [b[1][0] + a, b[1][1] + a, b[1][2] + a, b[1][3] + a], + [b[2][0] + a, b[2][1] + a, b[2][2] + a, b[2][3] + a], + [b[3][0] + a, b[3][1] + a, b[3][2] + a, b[3][3] + a] + ])) + } + (&Variable::Mat4(ref b), &Variable::F64(a, _)) => { + let a = a as f32; + Variable::Mat4(Box::new([ + [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], + [b[1][0] + a, b[1][1] + a, b[1][2] + a, b[1][3] + a], + [b[2][0] + a, b[2][1] + a, b[2][2] + a, b[2][3] + a], + [b[3][0] + a, b[3][1] + a, b[3][2] + a, b[3][3] + a] + ])) + } + (&Variable::Bool(a, ref sec), &Variable::Bool(b, _)) => + Variable::Bool(a || b, sec.clone()), + (&Variable::Str(ref a), &Variable::Str(ref b)) => { + let mut res = String::with_capacity(a.len() + b.len()); + res.push_str(a); + res.push_str(b); + Variable::Str(Arc::new(res)) + } + (&Variable::Link(ref a), &Variable::Link(ref b)) => + Variable::Link(Box::new(a.add(b))), + _ => return Err("Expected `f64`, `vec4`, `mat4`, `bool`, `str` or `link`".into()) + }; + rt.stack.push(r); + Ok(()) +} + pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { let b = rt.stack.pop().expect(TINVOTS); let b = match *rt.resolve(&b) { diff --git a/src/lib.dyon b/src/lib.dyon index 3343a306..6a514980 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -229,6 +229,19 @@ fn min(array: [f64]) -> f64 { ... } /// Returns NaN if array is empty. fn max(array: [f64]) -> f64 { ... } +/// Addition. +fn add(a: any, b: any) -> any { ... } + (f64, f64) -> f64 + (vec4, vec4) -> vec4 + (vec4, f64) -> vec4 + (f64, vec4) -> vec4 + (mat4, mat4) -> mat4 + (f64, mat4) -> mat4 + (mat4, f64) -> mat4 + (bool, bool) -> bool + (str, str) -> str + (link, link) -> link + /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index 3a865105..ee103bcc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,7 @@ pub use mat4::Mat4; pub const TINVOTS: &str = "There is no value on the stack"; lazy_static!{ + pub(crate) static ref ADD: Arc = Arc::new("add".into()); pub(crate) static ref DOT: Arc = Arc::new("dot".into()); pub(crate) static ref CROSS: Arc = Arc::new("cross".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); @@ -371,6 +372,23 @@ impl Module { let mut m = Module::empty(); m.ns("std"); + m.add_str("add", add, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![F64, F64], F64), + (vec![Vec4, Vec4], Vec4), + (vec![Vec4, F64], Vec4), + (vec![F64, Vec4], Vec4), + (vec![Mat4, Mat4], Mat4), + (vec![F64, Mat4], Mat4), + (vec![Mat4, F64], Mat4), + (vec![Bool, Bool], Bool), + (vec![Str, Str], Str), + (vec![Link, Link], Link), + ] + }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index feed027a..e04fb1f2 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -104,6 +104,7 @@ pub fn check( use ast::BinOp::*; match nodes[i].binops[0] { + Add => Node::rewrite_binop(i, crate::ADD.clone(), &mut nodes), Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), _ => {} diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 8ca750de..3b7e58af 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -61,14 +61,60 @@ impl Node { nodes[ch].kind = Kind::CallArg; } - pub fn rewrite_binop(i: usize, name: Arc, nodes: &mut [Node]) { + pub fn rewrite_binop(i: usize, name: Arc, nodes: &mut Vec) { nodes[i].kind = Kind::Call; nodes[i].names.push(name); nodes[i].binops.clear(); - let left = nodes[i].children[0]; - let right = nodes[i].children[1]; - nodes[left].kind = Kind::CallArg; - nodes[right].kind = Kind::CallArg; + + let old_left = nodes[i].children[0]; + let old_right = nodes[i].children[1]; + + let left = nodes.len(); + nodes.push(Node { + kind: Kind::CallArg, + names: vec![], + ty: None, + declaration: None, + alias: None, + mutable: false, + try: false, + grab_level: 0, + source: nodes[old_left].source, + start: nodes[old_left].start, + end: nodes[old_left].end, + lifetime: None, + op: None, + binops: vec![], + lts: vec![], + parent: Some(i), + children: vec![old_left], + }); + let right = nodes.len(); + nodes.push(Node { + kind: Kind::CallArg, + names: vec![], + ty: None, + declaration: None, + alias: None, + mutable: false, + try: false, + grab_level: 0, + source: nodes[old_right].source, + start: nodes[old_right].start, + end: nodes[old_right].end, + lifetime: None, + op: None, + binops: vec![], + lts: vec![], + parent: Some(i), + children: vec![old_right], + }); + + nodes[old_left].parent = Some(left); + nodes[old_right].parent = Some(right); + + nodes[i].children[0] = left; + nodes[i].children[1] = right; } #[allow(dead_code)] @@ -516,6 +562,18 @@ pub fn convert_meta_data( let i = *parents.last().unwrap(); nodes[i].binops.push(BinOp::AndAlso); } + "+" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::Add); + } + "-" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::Sub); + } + "||" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::OrElse); + } _ => {} } } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 63a7d808..daa429a1 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2513,7 +2513,6 @@ impl Runtime { let v = match (self.resolve(&left), self.resolve(&right)) { (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => { Variable::F64(match binop.op { - Add => a + b, Sub => a - b, Mul => a * b, Div => a / b, @@ -2527,14 +2526,13 @@ impl Runtime { } (&Variable::Vec4(a), &Variable::Vec4(b)) => { match binop.op { - Add => Variable::Vec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]]), Sub => Variable::Vec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]]), Mul => Variable::Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]), Div => Variable::Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), Rem => Variable::Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), Pow => Variable::Vec4([a[0].powf(b[0]), a[1].powf(b[1]), a[2].powf(b[2]), a[3].powf(b[3])]), - AndAlso | OrElse | Dot | Cross => return Err(self.module.error(binop.source_range, + _ => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `vec4` and `vec4`", self.stack_trace(), binop.op.symbol_bool()), self)), @@ -2543,14 +2541,13 @@ impl Runtime { (&Variable::Vec4(a), &Variable::F64(b, _)) => { let b = b as f32; match binop.op { - Add => Variable::Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]), Sub => Variable::Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b]), Mul => Variable::Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]), Div => Variable::Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]), Rem => Variable::Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]), Pow => Variable::Vec4([a[0].powf(b), a[1].powf(b), a[2].powf(b), a[3].powf(b)]), - AndAlso | OrElse | Dot | Cross => return Err(self.module.error(binop.source_range, + _ => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `vec4` and `f64`", self.stack_trace(), binop.op.symbol_bool()), self)), @@ -2559,24 +2556,22 @@ impl Runtime { (&Variable::F64(a, _), &Variable::Vec4(b)) => { let a = a as f32; match binop.op { - Add => Variable::Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]), Sub => Variable::Vec4([a - b[0], a - b[1], a - b[2], a - b[3]]), Mul => Variable::Vec4([a * b[0], a * b[1], a * b[2], a * b[3]]), Div => Variable::Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]), Rem => Variable::Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]), Pow => Variable::Vec4([a.powf(b[0]), a.powf(b[1]), a.powf(b[2]), a.powf(b[3])]), - AndAlso | OrElse | Dot | Cross => return Err(self.module.error(binop.source_range, + _ => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `f64` and `vec4`", self.stack_trace(), binop.op.symbol_bool()), self)), } } (&Variable::Mat4(ref a), &Variable::Mat4(ref b)) => { - use vecmath::{mat4_add, mat4_sub, col_mat4_mul}; + use vecmath::{mat4_sub, col_mat4_mul}; match binop.op { - Add => Variable::Mat4(Box::new(mat4_add(**a, **b))), Sub => Variable::Mat4(Box::new(mat4_sub(**a, **b))), Mul => Variable::Mat4(Box::new(col_mat4_mul(**a, **b))), _ => return Err(self.module.error(binop.source_range, @@ -2588,12 +2583,6 @@ impl Runtime { (&Variable::F64(a, _), &Variable::Mat4(ref b)) => { let a = a as f32; match binop.op { - Add => Variable::Mat4(Box::new([ - [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], - [b[1][0] + a, b[1][1] + a, b[1][2] + a, b[1][3] + a], - [b[2][0] + a, b[2][1] + a, b[2][2] + a, b[2][3] + a], - [b[3][0] + a, b[3][1] + a, b[3][2] + a, b[3][3] + a] - ])), Sub => Variable::Mat4(Box::new([ [a - b[0][0], a - b[0][1], a - b[0][2], a - b[0][3]], [a - b[1][0], a - b[1][1], a - b[1][2], a - b[1][3]], @@ -2615,12 +2604,6 @@ impl Runtime { (&Variable::Mat4(ref b), &Variable::F64(a, _)) => { let a = a as f32; match binop.op { - Add => Variable::Mat4(Box::new([ - [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], - [b[1][0] + a, b[1][1] + a, b[1][2] + a, b[1][3] + a], - [b[2][0] + a, b[2][1] + a, b[2][2] + a, b[2][3] + a], - [b[3][0] + a, b[3][1] + a, b[3][2] + a, b[3][3] + a] - ])), Sub => Variable::Mat4(Box::new([ [b[0][0] - a, b[0][1] - a, b[0][2] - a, b[0][3] - a], [b[1][0] - a, b[1][1] - a, b[1][2] - a, b[1][3] - a], @@ -2652,7 +2635,7 @@ impl Runtime { } (&Variable::Bool(a, ref sec), &Variable::Bool(b, _)) => { Variable::Bool(match binop.op { - Add | OrElse => a || b, + OrElse => a || b, // Boolean subtraction with lazy precedence. Sub => a && !b, Mul | AndAlso => a && b, @@ -2663,31 +2646,6 @@ impl Runtime { binop.op.symbol_bool()), self)) }, sec.clone()) } - (&Variable::Str(ref a), &Variable::Str(ref b)) => { - match binop.op { - Add => { - let mut res = String::with_capacity(a.len() + b.len()); - res.push_str(a); - res.push_str(b); - Variable::Str(Arc::new(res)) - } - _ => return self.err(binop.source_range, - "This operation can not be used with strings") - } - } - (&Variable::Str(_), _) => - return self.err(binop.source_range, - "The right argument must be a string. \ - Try the `str` function"), - (&Variable::Link(ref a), &Variable::Link(ref b)) => { - match binop.op { - Add => { - Variable::Link(Box::new(a.add(b))) - } - _ => return self.err(binop.source_range, - "This operation can not be used with links") - } - } _ => return Err(self.module.error(binop.source_range, &format!( "{}\nInvalid type for binary operator `{:?}`, \ expected numbers, vec4s, bools or strings", From 54ab7db8e2fc0f168182ffd845b05e5f4320b03a Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 21 Sep 2019 02:48:51 +0200 Subject: [PATCH 48/83] Added support for ad-hoc variables in refinement types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/PistonDevelopers/dyon/issues/645 Use the semantics that none ad-hoc type is quantified over by `all`. - Added `Type::all_ext` - Added `Type::bind_ty_vars` - Added `Type::insert_var` - Added `Type::insert_none_var` - Added `T` lazy static string - Added some refine quantifier tests - Updated `add` in “lib.dyon” - Updated `add` extra type information - Added support for type quantifier in syntax - Push type variable names to “ty” node --- assets/syntax.txt | 6 +- source/typechk/refine_quantifier_fail_1.dyon | 10 +++ source/typechk/refine_quantifier_fail_2.dyon | 11 +++ source/typechk/refine_quantifier_pass_1.dyon | 10 +++ source/typechk/refine_quantifier_pass_2.dyon | 8 ++ source/typechk/refine_quantifier_pass_3.dyon | 11 +++ source/typechk/refine_quantifier_pass_4.dyon | 15 ++++ source/typechk/refine_quantifier_pass_5.dyon | 12 +++ source/typechk/refine_quantifier_pass_6.dyon | 18 ++++ src/lib.dyon | 20 ++--- src/lib.rs | 33 +++---- src/lifetime/node.rs | 5 ++ src/lifetime/typecheck/refine.rs | 34 +++++++- src/prelude.rs | 4 +- src/ty.rs | 90 ++++++++++++++++++++ tests/lib.rs | 7 ++ 16 files changed, 263 insertions(+), 31 deletions(-) create mode 100644 source/typechk/refine_quantifier_fail_1.dyon create mode 100644 source/typechk/refine_quantifier_fail_2.dyon create mode 100644 source/typechk/refine_quantifier_pass_1.dyon create mode 100644 source/typechk/refine_quantifier_pass_2.dyon create mode 100644 source/typechk/refine_quantifier_pass_3.dyon create mode 100644 source/typechk/refine_quantifier_pass_4.dyon create mode 100644 source/typechk/refine_quantifier_pass_5.dyon create mode 100644 source/typechk/refine_quantifier_pass_6.dyon diff --git a/assets/syntax.txt b/assets/syntax.txt index 16f3f15f..328c336b 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -20,7 +20,11 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" } ?w block:"block" ?.l([?w ty:"ty" ?w])] [.."("!:"name" ?w "(" ?w args ?w ")" ?w ?currents ?w "=" ?w expr:"expr"] } -4 ty = ["(" ?w .s?.(, type:"ty_arg") ?w ")" .w? "->" ?w type:"ty_ret"] +4 ty = { + ty_var + ["(" ?w .s?.(, type:"ty_arg") ?w ")" .w? "->" ?w type:"ty_ret"] +} +5 ty_var = ["all" ?w .s?.(, .._seps!:"ty_var") ?w "{" ?w ty ?w "}"] 4 args = .s?.(, arg:"arg") 5 arg = [?"mut":"mut" ?w .._seps!:"name" ?[?w ":" ?w ?["'" ?w .._seps!:"lifetime"] ?w ?type:"type"]] diff --git a/source/typechk/refine_quantifier_fail_1.dyon b/source/typechk/refine_quantifier_fail_1.dyon new file mode 100644 index 00000000..4dcb3eac --- /dev/null +++ b/source/typechk/refine_quantifier_fail_1.dyon @@ -0,0 +1,10 @@ +fn foo(a: any, b: any) -> any {return a + b} + all T { (T f64, T f64) -> T f64 } + +fn km(a: f64) -> km f64 {return clone(a)} + +fn check_km(a: km f64) {} + +fn main() { + check_km(foo(km(2), 3)) +} diff --git a/source/typechk/refine_quantifier_fail_2.dyon b/source/typechk/refine_quantifier_fail_2.dyon new file mode 100644 index 00000000..df0e6c5e --- /dev/null +++ b/source/typechk/refine_quantifier_fail_2.dyon @@ -0,0 +1,11 @@ +fn foo(a: any, b: any) -> any {return a + b} + all T { (T f64, T f64) -> T f64 } + +fn km(a: f64) -> km f64 {return clone(a)} +fn m(a: f64) -> m f64 {return clone(a)} + +fn check_km(a: km f64) {} + +fn main() { + check_km(foo(m(2), km(3))) +} diff --git a/source/typechk/refine_quantifier_pass_1.dyon b/source/typechk/refine_quantifier_pass_1.dyon new file mode 100644 index 00000000..5333af29 --- /dev/null +++ b/source/typechk/refine_quantifier_pass_1.dyon @@ -0,0 +1,10 @@ +fn foo(a: any, b: any) -> any {return a + b} + all T { (T f64, T f64) -> T f64 } + +fn km(a: f64) -> km f64 {return clone(a)} + +fn check_km(a: km f64) {} + +fn main() { + check_km(foo(km(2), km(3))) +} diff --git a/source/typechk/refine_quantifier_pass_2.dyon b/source/typechk/refine_quantifier_pass_2.dyon new file mode 100644 index 00000000..08414ed9 --- /dev/null +++ b/source/typechk/refine_quantifier_pass_2.dyon @@ -0,0 +1,8 @@ +fn foo(a: any, b: any) -> any {return a + b} + all T { (T f64, T f64) -> T f64 } + +fn check_f64(a: f64) {} + +fn main() { + check_f64(foo(2, 3)) +} diff --git a/source/typechk/refine_quantifier_pass_3.dyon b/source/typechk/refine_quantifier_pass_3.dyon new file mode 100644 index 00000000..f452ab3e --- /dev/null +++ b/source/typechk/refine_quantifier_pass_3.dyon @@ -0,0 +1,11 @@ +fn foo(a: any, b: any) -> any {return a + b} + all T { (T f64, T f64) -> T f64 } + +fn km(a: f64) -> km f64 {return clone(a)} + +fn check_km(a: km f64) {} + +fn main() { + // Allow no ad-hoc type before the first one. + check_km(foo(2, km(3))) +} diff --git a/source/typechk/refine_quantifier_pass_4.dyon b/source/typechk/refine_quantifier_pass_4.dyon new file mode 100644 index 00000000..cea2618a --- /dev/null +++ b/source/typechk/refine_quantifier_pass_4.dyon @@ -0,0 +1,15 @@ +fn km(a: any) -> km any {return clone(a)} + (vec4) -> km vec4 + (f64) -> km f64 +fn m(a: any) -> m any {return clone(a)} + (vec4) -> m vec4 + (f64) -> m f64 +fn foo(a: any, b: any) -> any {return a + b} + all T { (T vec4, T vec4) -> T vec4 } + all T { (T vec4, T f64) -> T vec4 } + +fn check_m(a: m vec4) {} + +fn main() { + check_m(foo(m((2, 0)), m(2))) +} diff --git a/source/typechk/refine_quantifier_pass_5.dyon b/source/typechk/refine_quantifier_pass_5.dyon new file mode 100644 index 00000000..8d7912a4 --- /dev/null +++ b/source/typechk/refine_quantifier_pass_5.dyon @@ -0,0 +1,12 @@ +fn m(a: any) -> m any {return clone(a)} + (vec4) -> m vec4 + (f64) -> m f64 +fn foo(a: any, b: any) -> any {return a + b} + all T { (T vec4, T vec4) -> T vec4 } + all T { (T vec4, f64) -> T vec4 } + +fn check_m(a: m vec4) {} + +fn main() { + check_m(foo(m((2, 0)), 2)) +} diff --git a/source/typechk/refine_quantifier_pass_6.dyon b/source/typechk/refine_quantifier_pass_6.dyon new file mode 100644 index 00000000..1df14d0e --- /dev/null +++ b/source/typechk/refine_quantifier_pass_6.dyon @@ -0,0 +1,18 @@ +fn km(a: any) -> km any {return clone(a)} + (vec4) -> km vec4 + (f64) -> km f64 +fn m(a: any) -> m any {return clone(a)} + (vec4) -> m vec4 + (f64) -> m f64 + +fn foo(a: any, b: any) -> any {return a + b} + all T { (T f64, T f64) -> T f64 } + all T { (T vec4, T vec4) -> T vec4 } + all T { (T vec4, T f64) -> T vec4 } + (f64, vec4) -> vec4 + +fn check_m(a: m vec4) {} + +fn main() { + check_m(foo(m(2), km((1, 0)))) +} diff --git a/src/lib.dyon b/src/lib.dyon index 6a514980..f45b82ad 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -231,16 +231,16 @@ fn max(array: [f64]) -> f64 { ... } /// Addition. fn add(a: any, b: any) -> any { ... } - (f64, f64) -> f64 - (vec4, vec4) -> vec4 - (vec4, f64) -> vec4 - (f64, vec4) -> vec4 - (mat4, mat4) -> mat4 - (f64, mat4) -> mat4 - (mat4, f64) -> mat4 - (bool, bool) -> bool - (str, str) -> str - (link, link) -> link + all T { (T f64, T f64) -> T f64 } + all T { (T vec4, T vec4) -> T vec4 } + all T { (T vec4, T f64) -> T vec4 } + all T { (T f64, T vec4) -> T vec4 } + all T { (T mat4, T mat4) -> T mat4 } + all T { (T f64, T mat4) -> T mat4 } + all T { (T mat4, T f64) -> T mat4 } + all T { (T bool, T bool) -> T bool } + all T { (T str, T str) -> T str } + all T { (T link, T link) -> T link } /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index ee103bcc..4d456349 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,6 +61,7 @@ lazy_static!{ pub(crate) static ref NOT: Arc = Arc::new("not".into()); pub(crate) static ref NEG: Arc = Arc::new("neg".into()); pub(crate) static ref NORM: Arc = Arc::new("norm".into()); + pub(crate) static ref T: Arc = Arc::new("T".into()); } /// Type alias for Dyon arrays. @@ -377,25 +378,25 @@ impl Module { tys: vec![Any; 2], ret: Any, ext: vec![ - (vec![F64, F64], F64), - (vec![Vec4, Vec4], Vec4), - (vec![Vec4, F64], Vec4), - (vec![F64, Vec4], Vec4), - (vec![Mat4, Mat4], Mat4), - (vec![F64, Mat4], Mat4), - (vec![Mat4, F64], Mat4), - (vec![Bool, Bool], Bool), - (vec![Str, Str], Str), - (vec![Link, Link], Link), + Type::all_ext(vec![F64, F64], F64), + Type::all_ext(vec![Vec4, Vec4], Vec4), + Type::all_ext(vec![Vec4, F64], Vec4), + Type::all_ext(vec![F64, Vec4], Vec4), + Type::all_ext(vec![Mat4, Mat4], Mat4), + Type::all_ext(vec![F64, Mat4], Mat4), + Type::all_ext(vec![Mat4, F64], Mat4), + Type::all_ext(vec![Bool, Bool], Bool), + Type::all_ext(vec![Str, Str], Str), + Type::all_ext(vec![Link, Link], Link), ] }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, ext: vec![ - (vec![F64], F64), - (vec![Vec4], Vec4), - (vec![Mat4], Mat4), + (vec![], vec![F64], F64), + (vec![], vec![Vec4], Vec4), + (vec![], vec![Mat4], Mat4), ] }); m.add_str("dot", dot, Dfn { @@ -403,9 +404,9 @@ impl Module { tys: vec![Any; 2], ret: F64, ext: vec![ - (vec![Vec4, Vec4], F64), - (vec![Vec4, F64], F64), - (vec![F64, Vec4], F64), + (vec![], vec![Vec4, Vec4], F64), + (vec![], vec![Vec4, F64], F64), + (vec![], vec![F64, Vec4], F64), ] }); m.add_str("cross", cross, Dfn::nl(vec![Vec4, Vec4], Vec4)); diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 3b7e58af..1f594f1e 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -473,6 +473,11 @@ pub fn convert_meta_data( let i = *parents.last().unwrap(); nodes[i].ty = Some(Type::Vec4); } + "ty_var" => { + // Use names as a way of storing type variables. + let i = *parents.last().unwrap(); + nodes[i].names.push(val.clone()); + } _ => {} } } diff --git a/src/lifetime/typecheck/refine.rs b/src/lifetime/typecheck/refine.rs index 3f57101a..fdf0911a 100644 --- a/src/lifetime/typecheck/refine.rs +++ b/src/lifetime/typecheck/refine.rs @@ -1,6 +1,8 @@ use super::*; use prelude::Dfn; +use std::sync::Arc; + fn report( i: usize, found: bool, @@ -52,6 +54,7 @@ pub(crate) fn declaration( { count += 1; let mut all = true; + let mut ty_vars: Vec>> = vec![None; nodes[ty].names.len()]; for (arg_expr, &ty_arg) in nodes[i].children.iter() .filter(|&&arg| nodes[arg].kind == Kind::CallArg && !nodes[arg].children.is_empty()) @@ -65,6 +68,8 @@ pub(crate) fn declaration( } let found_arg = if let (&Some(ref a), &Some(ref b)) = (&nodes[arg_expr].ty, &nodes[ty_arg].ty) { + let b = b.bind_ty_vars(a, &nodes[ty].names, &mut ty_vars) + .map_err(|err| nodes[arg_expr].source.wrap(err))?; if b.goes_with(a) { if b.ambiguous(a) { ambiguous = true; @@ -83,7 +88,18 @@ pub(crate) fn declaration( if let Some(&ind) = nodes[ty].children.iter() .filter(|&&ty| nodes[ty].kind == Kind::TyRet) .next() { - *this_ty = nodes[ind].ty.clone(); + + let mut new_ty = nodes[ind].ty.clone(); + if let Some(ref mut new_ty) = new_ty { + for i in 0..nodes[ty].names.len() { + if let Some(ref val) = ty_vars[i] { + new_ty.insert_var(&nodes[ty].names[i], val); + } else { + new_ty.insert_none_var(&nodes[ty].names[i]); + } + } + } + *this_ty = new_ty; found = true; break; } @@ -106,17 +122,20 @@ pub(crate) fn prelude( let mut ambiguous = false; 'outer: for ty in &f.ext { let mut all = true; + let mut ty_vars: Vec>> = vec![None; ty.0.len()]; for (arg_expr, ty_arg) in nodes[i].children.iter() .filter(|&&arg| nodes[arg].kind == Kind::CallArg && !nodes[arg].children.is_empty()) .map(|&arg| nodes[arg].children[0]) - .zip(ty.0.iter()) + .zip(ty.1.iter()) { if nodes[arg_expr].ty.is_none() { ambiguous = true; break 'outer; } let found_arg = if let Some(ref a) = nodes[arg_expr].ty { + let ty_arg = ty_arg.bind_ty_vars(a, &ty.0, &mut ty_vars) + .map_err(|err| nodes[arg_expr].source.wrap(err))?; if ty_arg.goes_with(a) { if ty_arg.ambiguous(a) { ambiguous = true; @@ -132,7 +151,16 @@ pub(crate) fn prelude( } } if all { - *this_ty = Some(ty.1.clone()); + let mut new_ty = ty.2.clone(); + for i in 0..ty.0.len() { + if let Some(ref val) = ty_vars[i] { + new_ty.insert_var(&ty.0[i], val); + } else { + new_ty.insert_none_var(&ty.0[i]); + } + } + + *this_ty = Some(new_ty); found = true; break; } diff --git a/src/prelude.rs b/src/prelude.rs index 62aed20b..2c2bc289 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -33,7 +33,9 @@ pub struct Dfn { /// Return type of function. pub ret: Type, /// Extra type information. - pub ext: Vec<(Vec, Type)>, + /// + /// Stores type variables, argument types, return type. + pub ext: Vec<(Vec>, Vec, Type)>, } impl Dfn { diff --git a/src/ty.rs b/src/ty.rs index 3df2e85a..e848debf 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -47,6 +47,17 @@ pub enum Type { } impl Type { + /// Returns an extension quantified over ad-hoc types. + /// + /// For example, `(vec4, vec4) -> vec4` becomes `all T { (T vec4, T vec4) -> T vec4 }`. + pub fn all_ext(args: Vec, ret: Type) -> (Vec>, Vec, Type) { + use crate::T; + use Type::AdHoc; + + (vec![T.clone()], args.into_iter().map(|arg| AdHoc(T.clone(), Box::new(arg))).collect(), + AdHoc(T.clone(), Box::new(ret))) + } + /// Returns description of the type. pub fn description(&self) -> String { use Type::*; @@ -156,6 +167,85 @@ impl Type { /// Returns an in-type with an `any` as inner type. pub fn in_ty() -> Type {Type::In(Box::new(Type::Any))} + /// Binds refinement type variables. + /// + /// Returns the type argument to compare to. + pub fn bind_ty_vars( + &self, + refine: &Type, + names: &[Arc], + ty_vars: &mut Vec>> + ) -> Result { + if names.len() == 0 {return Ok(self.clone())}; + match (self, refine) { + (&Type::AdHoc(ref a_name, ref a_inner_ty), + &Type::AdHoc(ref b_name, ref b_inner_ty)) => { + for i in 0..names.len() { + if a_name == &names[i] { + let new_inner = a_inner_ty.bind_ty_vars(b_inner_ty, names, ty_vars)?; + if let Some(ref existing_name) = ty_vars[i] { + if existing_name != b_name && + new_inner.goes_with(b_inner_ty) && + !new_inner.ambiguous(b_inner_ty) + { + return Err(format!("Type mismatch (#1500): Expected `{}`, found `{}`", + existing_name, b_name)) + } else { + return Ok(Type::AdHoc(existing_name.clone(), + Box::new(new_inner))) + } + } else { + ty_vars[i] = Some(b_name.clone()); + return Ok(Type::AdHoc(b_name.clone(), + Box::new(a_inner_ty.bind_ty_vars(b_inner_ty, names, ty_vars)?))) + } + } + } + Ok(Type::AdHoc(a_name.clone(), + Box::new(a_inner_ty.bind_ty_vars(b_inner_ty, names, ty_vars)?))) + } + (&Type::AdHoc(ref a_name, ref a_inner_ty), ref b) => { + for i in 0..names.len() { + if a_name == &names[i] { + let new_inner = a_inner_ty.bind_ty_vars(refine, names, ty_vars)?; + if let Some(ref n) = ty_vars[i] { + if new_inner.goes_with(b) && !new_inner.ambiguous(b) { + return Err(format!( + "Type mismatch (#1600): Expected `{}`, found no ad-hoc type", n)) + } + } else {break} + } + } + a_inner_ty.bind_ty_vars(refine, names, ty_vars) + } + _ => Ok(self.clone()) + } + } + + /// Inserts variable name, replacing ad-hoc type name. + pub fn insert_var(&mut self, name: &Arc, val: &Arc) { + match *self { + Type::AdHoc(ref mut n, ref mut inner_ty) => { + if n == name { + *n = val.clone(); + } + inner_ty.insert_var(name, val) + } + _ => {} + } + } + + /// Inserts a none ad-hoc variable. + pub fn insert_none_var(&mut self, name: &Arc) { + match *self { + Type::AdHoc(_, ref mut inner_ty) => { + inner_ty.insert_none_var(name); + *self = (**inner_ty).clone(); + } + _ => {} + } + } + /// Returns `true` if a type to be refined is ambiguous relative to this type (directional check). /// /// For example, the type ad-hoc type `Foo str` is ambiguous with type `str`. diff --git a/tests/lib.rs b/tests/lib.rs index 38724a2e..dfe2adcc 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -257,6 +257,13 @@ fn test_typechk() { test_fail_src("source/typechk/refine_closed_fail_1.dyon"); test_fail_src("source/typechk/refine_closed_fail_2.dyon"); test_src("source/typechk/refine_closed_pass_1.dyon"); + test_src("source/typechk/refine_quantifier_pass_1.dyon"); + test_src("source/typechk/refine_quantifier_pass_2.dyon"); + test_src("source/typechk/refine_quantifier_pass_3.dyon"); + test_src("source/typechk/refine_quantifier_pass_4.dyon"); + test_src("source/typechk/refine_quantifier_pass_5.dyon"); + test_fail_src("source/typechk/refine_quantifier_fail_1.dyon"); + test_fail_src("source/typechk/refine_quantifier_fail_2.dyon"); } #[test] From eea34636f8269eded712367cbecd2c483d671418 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 21 Sep 2019 11:58:47 +0200 Subject: [PATCH 49/83] Made `sub` standard external function --- src/ast/mod.rs | 8 +++++ src/dyon_std/mod.rs | 79 +++++++++++++++++++++++++++++++++------------ src/lib.dyon | 13 ++++++++ src/lib.rs | 16 +++++++++ src/lifetime/mod.rs | 1 + src/runtime/mod.rs | 21 +----------- src/write.rs | 4 +++ 7 files changed, 102 insertions(+), 40 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 247f4f07..db834976 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2914,6 +2914,14 @@ impl BinOpExpression { f_index: Cell::new(FnIndex::None), source_range: self.source_range, })), + BinOp::Sub => Expression::Call(Box::new(Call { + alias: None, + name: crate::SUB.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), BinOp::Dot => Expression::Call(Box::new(Call { alias: None, name: crate::DOT.clone(), diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 39f49dbe..b00a6088 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -15,57 +15,96 @@ const HTTP_SUPPORT_DISABLED: &'static str = "Http support is disabled"; const FILE_SUPPORT_DISABLED: &'static str = "File support is disabled"; pub(crate) fn add(rt: &mut Runtime) -> Result<(), String> { + use Variable::*; + let b = rt.stack.pop().expect(TINVOTS); let a = rt.stack.pop().expect(TINVOTS); let r = match (rt.resolve(&a), rt.resolve(&b)) { - (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => - Variable::F64(a + b, sec.clone()), - (&Variable::Vec4(a), &Variable::Vec4(b)) => - Variable::Vec4(vecmath::vec4_add(a, b)), - (&Variable::Vec4(a), &Variable::F64(b, _)) => { + (&F64(a, ref sec), &F64(b, _)) => F64(a + b, sec.clone()), + (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_add(a, b)), + (&Vec4(a), &F64(b, _)) => { let b = b as f32; - Variable::Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]) + Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]) } - (&Variable::F64(a, _), &Variable::Vec4(b)) => { + (&F64(a, _), &Vec4(b)) => { let a = a as f32; - Variable::Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]) + Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]) } - (&Variable::Mat4(ref a), &Variable::Mat4(ref b)) => - Variable::Mat4(Box::new(vecmath::mat4_add(**a, **b))), - (&Variable::F64(a, _), &Variable::Mat4(ref b)) => { + (&Mat4(ref a), &Mat4(ref b)) => Mat4(Box::new(vecmath::mat4_add(**a, **b))), + (&F64(a, _), &Mat4(ref b)) => { let a = a as f32; - Variable::Mat4(Box::new([ + Mat4(Box::new([ [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], [b[1][0] + a, b[1][1] + a, b[1][2] + a, b[1][3] + a], [b[2][0] + a, b[2][1] + a, b[2][2] + a, b[2][3] + a], [b[3][0] + a, b[3][1] + a, b[3][2] + a, b[3][3] + a] ])) } - (&Variable::Mat4(ref b), &Variable::F64(a, _)) => { + (&Mat4(ref b), &F64(a, _)) => { let a = a as f32; - Variable::Mat4(Box::new([ + Mat4(Box::new([ [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], [b[1][0] + a, b[1][1] + a, b[1][2] + a, b[1][3] + a], [b[2][0] + a, b[2][1] + a, b[2][2] + a, b[2][3] + a], [b[3][0] + a, b[3][1] + a, b[3][2] + a, b[3][3] + a] ])) } - (&Variable::Bool(a, ref sec), &Variable::Bool(b, _)) => - Variable::Bool(a || b, sec.clone()), - (&Variable::Str(ref a), &Variable::Str(ref b)) => { + (&Bool(a, ref sec), &Bool(b, _)) => Bool(a || b, sec.clone()), + (&Str(ref a), &Str(ref b)) => { let mut res = String::with_capacity(a.len() + b.len()); res.push_str(a); res.push_str(b); - Variable::Str(Arc::new(res)) + Str(Arc::new(res)) } - (&Variable::Link(ref a), &Variable::Link(ref b)) => - Variable::Link(Box::new(a.add(b))), + (&Link(ref a), &Link(ref b)) => Link(Box::new(a.add(b))), _ => return Err("Expected `f64`, `vec4`, `mat4`, `bool`, `str` or `link`".into()) }; rt.stack.push(r); Ok(()) } +pub(crate) fn sub(rt: &mut Runtime) -> Result<(), String> { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&F64(a, ref sec), &F64(b, _)) => F64(a - b, sec.clone()), + (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_sub(a, b)), + (&Vec4(a), &F64(b, _)) => { + let b = b as f32; + Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b]) + } + (&F64(a, _), &Vec4(b)) => { + let a = a as f32; + Vec4([a - b[0], a - b[1], a - b[2], a - b[3]]) + } + (&Mat4(ref a), &Mat4(ref b)) => Mat4(Box::new(vecmath::mat4_sub(**a, **b))), + (&F64(a, _), &Mat4(ref b)) => { + let a = a as f32; + Mat4(Box::new([ + [a - b[0][0], a - b[0][1], a - b[0][2], a - b[0][3]], + [a - b[1][0], a - b[1][1], a - b[1][2], a - b[1][3]], + [a - b[2][0], a - b[2][1], a - b[2][2], a - b[2][3]], + [a - b[3][0], a - b[3][1], a - b[3][2], a - b[3][3]] + ])) + } + (&Mat4(ref b), &F64(a, _)) => { + let a = a as f32; + Mat4(Box::new([ + [b[0][0] - a, b[0][1] - a, b[0][2] - a, b[0][3] - a], + [b[1][0] - a, b[1][1] - a, b[1][2] - a, b[1][3] - a], + [b[2][0] - a, b[2][1] - a, b[2][2] - a, b[2][3] - a], + [b[3][0] - a, b[3][1] - a, b[3][2] - a, b[3][3] - a] + ])) + } + (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && !b, sec.clone()), + _ => return Err("Expected `f64` or `vec4`".into()) + }; + rt.stack.push(r); + Ok(()) +} + pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { let b = rt.stack.pop().expect(TINVOTS); let b = match *rt.resolve(&b) { diff --git a/src/lib.dyon b/src/lib.dyon index f45b82ad..87e8e074 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -1,3 +1,5 @@ +ns std + /// Returns an array of derived information for the truth value of `var`. /// This can be used with the value of `∃`/`any` and `∀`/`all` loops. fn why(var: sec[bool]) -> [any] { ... } @@ -242,6 +244,17 @@ fn add(a: any, b: any) -> any { ... } all T { (T str, T str) -> T str } all T { (T link, T link) -> T link } +/// Subtraction. +fn sub(a: any, b: any) -> any { ... } + all T { (T f64, T f64) -> T f64 } + all T { (T vec4, T vec4) -> T vec4 } + all T { (T vec4, T f64) -> T vec4 } + all T { (T f64, T vec4) -> T vec4 } + all T { (T mat4, T mat4) -> T mat4 } + all T { (T f64, T mat4) -> T mat4 } + all T { (T mat4, T f64) -> T mat4 } + all T { (T bool, T bool) -> T bool } + /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index 4d456349..1bd54335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub const TINVOTS: &str = "There is no value on the stack"; lazy_static!{ pub(crate) static ref ADD: Arc = Arc::new("add".into()); + pub(crate) static ref SUB: Arc = Arc::new("sub".into()); pub(crate) static ref DOT: Arc = Arc::new("dot".into()); pub(crate) static ref CROSS: Arc = Arc::new("cross".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); @@ -390,6 +391,21 @@ impl Module { Type::all_ext(vec![Link, Link], Link), ] }); + m.add_str("sub", sub, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + Type::all_ext(vec![F64, F64], F64), + Type::all_ext(vec![Vec4, Vec4], Vec4), + Type::all_ext(vec![Vec4, F64], Vec4), + Type::all_ext(vec![F64, Vec4], Vec4), + Type::all_ext(vec![Mat4, Mat4], Mat4), + Type::all_ext(vec![F64, Mat4], Mat4), + Type::all_ext(vec![Mat4, F64], Mat4), + Type::all_ext(vec![Bool, Bool], Bool), + ] + }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index e04fb1f2..f7e9ae0d 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -105,6 +105,7 @@ pub fn check( match nodes[i].binops[0] { Add => Node::rewrite_binop(i, crate::ADD.clone(), &mut nodes), + Sub => Node::rewrite_binop(i, crate::SUB.clone(), &mut nodes), Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), _ => {} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index daa429a1..f5ea1bf7 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2513,7 +2513,6 @@ impl Runtime { let v = match (self.resolve(&left), self.resolve(&right)) { (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => { Variable::F64(match binop.op { - Sub => a - b, Mul => a * b, Div => a / b, Rem => a % b, @@ -2526,7 +2525,6 @@ impl Runtime { } (&Variable::Vec4(a), &Variable::Vec4(b)) => { match binop.op { - Sub => Variable::Vec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]]), Mul => Variable::Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]), Div => Variable::Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), Rem => Variable::Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), @@ -2541,7 +2539,6 @@ impl Runtime { (&Variable::Vec4(a), &Variable::F64(b, _)) => { let b = b as f32; match binop.op { - Sub => Variable::Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b]), Mul => Variable::Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]), Div => Variable::Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]), Rem => Variable::Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]), @@ -2556,7 +2553,6 @@ impl Runtime { (&Variable::F64(a, _), &Variable::Vec4(b)) => { let a = a as f32; match binop.op { - Sub => Variable::Vec4([a - b[0], a - b[1], a - b[2], a - b[3]]), Mul => Variable::Vec4([a * b[0], a * b[1], a * b[2], a * b[3]]), Div => Variable::Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]), Rem => Variable::Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]), @@ -2569,10 +2565,9 @@ impl Runtime { } } (&Variable::Mat4(ref a), &Variable::Mat4(ref b)) => { - use vecmath::{mat4_sub, col_mat4_mul}; + use vecmath::col_mat4_mul; match binop.op { - Sub => Variable::Mat4(Box::new(mat4_sub(**a, **b))), Mul => Variable::Mat4(Box::new(col_mat4_mul(**a, **b))), _ => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown operator `{:?}` for `mat4` and `mat4`", @@ -2583,12 +2578,6 @@ impl Runtime { (&Variable::F64(a, _), &Variable::Mat4(ref b)) => { let a = a as f32; match binop.op { - Sub => Variable::Mat4(Box::new([ - [a - b[0][0], a - b[0][1], a - b[0][2], a - b[0][3]], - [a - b[1][0], a - b[1][1], a - b[1][2], a - b[1][3]], - [a - b[2][0], a - b[2][1], a - b[2][2], a - b[2][3]], - [a - b[3][0], a - b[3][1], a - b[3][2], a - b[3][3]] - ])), Mul => Variable::Mat4(Box::new([ [b[0][0] * a, b[0][1] * a, b[0][2] * a, b[0][3] * a], [b[1][0] * a, b[1][1] * a, b[1][2] * a, b[1][3] * a], @@ -2604,12 +2593,6 @@ impl Runtime { (&Variable::Mat4(ref b), &Variable::F64(a, _)) => { let a = a as f32; match binop.op { - Sub => Variable::Mat4(Box::new([ - [b[0][0] - a, b[0][1] - a, b[0][2] - a, b[0][3] - a], - [b[1][0] - a, b[1][1] - a, b[1][2] - a, b[1][3] - a], - [b[2][0] - a, b[2][1] - a, b[2][2] - a, b[2][3] - a], - [b[3][0] - a, b[3][1] - a, b[3][2] - a, b[3][3] - a] - ])), Mul => Variable::Mat4(Box::new([ [b[0][0] * a, b[0][1] * a, b[0][2] * a, b[0][3] * a], [b[1][0] * a, b[1][1] * a, b[1][2] * a, b[1][3] * a], @@ -2636,8 +2619,6 @@ impl Runtime { (&Variable::Bool(a, ref sec), &Variable::Bool(b, _)) => { Variable::Bool(match binop.op { OrElse => a || b, - // Boolean subtraction with lazy precedence. - Sub => a && !b, Mul | AndAlso => a && b, Pow => a ^ b, _ => return Err(self.module.error(binop.source_range, diff --git a/src/write.rs b/src/write.rs index 3e983222..5c8f8ecc 100644 --- a/src/write.rs +++ b/src/write.rs @@ -213,6 +213,10 @@ fn write_expr( write_binop(w, rt, ast::BinOp::Dot, &call.args[0], &call.args[1], tabs)? } else if &**call.name == "cross" && call.args.len() == 2 { write_binop(w, rt, ast::BinOp::Cross, &call.args[0], &call.args[1], tabs)? + } else if &**call.name == "add" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Add, &call.args[0], &call.args[1], tabs)? + } else if &**call.name == "sub" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Sub, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } From 758bffdaea0bd8346f3fd604477cffc51e2953f8 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 21 Sep 2019 12:43:01 +0200 Subject: [PATCH 50/83] Made `mul` standard external function --- src/ast/mod.rs | 8 +++++++ src/dyon_std/mod.rs | 49 +++++++++++++++++++++++++------------- src/lib.dyon | 12 ++++++++++ src/lib.rs | 17 +++++++++++++ src/lifetime/mod.rs | 1 + src/runtime/mod.rs | 58 +-------------------------------------------- src/write.rs | 2 ++ 7 files changed, 74 insertions(+), 73 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index db834976..4c00db3c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2922,6 +2922,14 @@ impl BinOpExpression { f_index: Cell::new(FnIndex::None), source_range: self.source_range, })), + BinOp::Mul => Expression::Call(Box::new(Call { + alias: None, + name: crate::MUL.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), BinOp::Dot => Expression::Call(Box::new(Call { alias: None, name: crate::DOT.clone(), diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index b00a6088..c4fc5308 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -22,25 +22,12 @@ pub(crate) fn add(rt: &mut Runtime) -> Result<(), String> { let r = match (rt.resolve(&a), rt.resolve(&b)) { (&F64(a, ref sec), &F64(b, _)) => F64(a + b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_add(a, b)), - (&Vec4(a), &F64(b, _)) => { + (&Vec4(a), &F64(b, _)) | (&F64(b, _), &Vec4(a)) => { let b = b as f32; Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]) } - (&F64(a, _), &Vec4(b)) => { - let a = a as f32; - Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]) - } (&Mat4(ref a), &Mat4(ref b)) => Mat4(Box::new(vecmath::mat4_add(**a, **b))), - (&F64(a, _), &Mat4(ref b)) => { - let a = a as f32; - Mat4(Box::new([ - [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], - [b[1][0] + a, b[1][1] + a, b[1][2] + a, b[1][3] + a], - [b[2][0] + a, b[2][1] + a, b[2][2] + a, b[2][3] + a], - [b[3][0] + a, b[3][1] + a, b[3][2] + a, b[3][3] + a] - ])) - } - (&Mat4(ref b), &F64(a, _)) => { + (&F64(a, _), &Mat4(ref b)) | (&Mat4(ref b), &F64(a, _)) => { let a = a as f32; Mat4(Box::new([ [b[0][0] + a, b[0][1] + a, b[0][2] + a, b[0][3] + a], @@ -99,7 +86,37 @@ pub(crate) fn sub(rt: &mut Runtime) -> Result<(), String> { ])) } (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && !b, sec.clone()), - _ => return Err("Expected `f64` or `vec4`".into()) + _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) + }; + rt.stack.push(r); + Ok(()) +} + +pub(crate) fn mul(rt: &mut Runtime) -> Result<(), String> { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&F64(a, ref sec), &F64(b, _)) => F64(a * b, sec.clone()), + (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_mul(a, b)), + (&Vec4(a), &F64(b, _)) | (&F64(b, _), &Vec4(a)) => { + let b = b as f32; + Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]) + } + (&Mat4(ref a), &Mat4(ref b)) => Mat4(Box::new(vecmath::col_mat4_mul(**a, **b))), + (&F64(a, _), &Mat4(ref b)) | (&Mat4(ref b), &F64(a, _)) => { + let a = a as f32; + Mat4(Box::new([ + [b[0][0] * a, b[0][1] * a, b[0][2] * a, b[0][3] * a], + [b[1][0] * a, b[1][1] * a, b[1][2] * a, b[1][3] * a], + [b[2][0] * a, b[2][1] * a, b[2][2] * a, b[2][3] * a], + [b[3][0] * a, b[3][1] * a, b[3][2] * a, b[3][3] * a] + ])) + } + (&Mat4(ref a), &Vec4(b)) => Vec4(vecmath::col_mat4_transform(**a, b)), + (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && b, sec.clone()), + _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) }; rt.stack.push(r); Ok(()) diff --git a/src/lib.dyon b/src/lib.dyon index 87e8e074..b5905f33 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -255,6 +255,18 @@ fn sub(a: any, b: any) -> any { ... } all T { (T mat4, T f64) -> T mat4 } all T { (T bool, T bool) -> T bool } +/// Multiplication. +fn mul(a: any, b: any) -> any { ... } + (f64, f64) -> f64 + (vec4, vec4) -> vec4 + (vec4, f64) -> vec4 + (f64, vec4) -> vec4 + (mat4, mat4) -> mat4 + (f64, mat4) -> mat4 + (mat4, f64) -> mat4 + (mat4, vec4) -> vec4 + all T { (T bool, T bool) -> T bool } + /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index 1bd54335..30983eb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ pub const TINVOTS: &str = "There is no value on the stack"; lazy_static!{ pub(crate) static ref ADD: Arc = Arc::new("add".into()); pub(crate) static ref SUB: Arc = Arc::new("sub".into()); + pub(crate) static ref MUL: Arc = Arc::new("mul".into()); pub(crate) static ref DOT: Arc = Arc::new("dot".into()); pub(crate) static ref CROSS: Arc = Arc::new("cross".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); @@ -406,6 +407,22 @@ impl Module { Type::all_ext(vec![Bool, Bool], Bool), ] }); + m.add_str("mul", mul, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + (vec![], vec![Mat4, Mat4], Mat4), + (vec![], vec![F64, Mat4], Mat4), + (vec![], vec![Mat4, F64], Mat4), + (vec![], vec![Mat4, Vec4], Vec4), + Type::all_ext(vec![Bool, Bool], Bool), + ] + }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index f7e9ae0d..1387c8e1 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -106,6 +106,7 @@ pub fn check( match nodes[i].binops[0] { Add => Node::rewrite_binop(i, crate::ADD.clone(), &mut nodes), Sub => Node::rewrite_binop(i, crate::SUB.clone(), &mut nodes), + Mul => Node::rewrite_binop(i, crate::MUL.clone(), &mut nodes), Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), _ => {} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index f5ea1bf7..8ccffc05 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2513,7 +2513,6 @@ impl Runtime { let v = match (self.resolve(&left), self.resolve(&right)) { (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => { Variable::F64(match binop.op { - Mul => a * b, Div => a / b, Rem => a % b, Pow => a.powf(b), @@ -2525,7 +2524,6 @@ impl Runtime { } (&Variable::Vec4(a), &Variable::Vec4(b)) => { match binop.op { - Mul => Variable::Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]), Div => Variable::Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), Rem => Variable::Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), Pow => Variable::Vec4([a[0].powf(b[0]), a[1].powf(b[1]), @@ -2539,7 +2537,6 @@ impl Runtime { (&Variable::Vec4(a), &Variable::F64(b, _)) => { let b = b as f32; match binop.op { - Mul => Variable::Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]), Div => Variable::Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]), Rem => Variable::Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]), Pow => Variable::Vec4([a[0].powf(b), a[1].powf(b), @@ -2553,7 +2550,6 @@ impl Runtime { (&Variable::F64(a, _), &Variable::Vec4(b)) => { let a = a as f32; match binop.op { - Mul => Variable::Vec4([a * b[0], a * b[1], a * b[2], a * b[3]]), Div => Variable::Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]), Rem => Variable::Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]), Pow => Variable::Vec4([a.powf(b[0]), a.powf(b[1]), @@ -2564,62 +2560,10 @@ impl Runtime { binop.op.symbol_bool()), self)), } } - (&Variable::Mat4(ref a), &Variable::Mat4(ref b)) => { - use vecmath::col_mat4_mul; - - match binop.op { - Mul => Variable::Mat4(Box::new(col_mat4_mul(**a, **b))), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown operator `{:?}` for `mat4` and `mat4`", - self.stack_trace(), - binop.op.symbol_bool()), self)), - } - } - (&Variable::F64(a, _), &Variable::Mat4(ref b)) => { - let a = a as f32; - match binop.op { - Mul => Variable::Mat4(Box::new([ - [b[0][0] * a, b[0][1] * a, b[0][2] * a, b[0][3] * a], - [b[1][0] * a, b[1][1] * a, b[1][2] * a, b[1][3] * a], - [b[2][0] * a, b[2][1] * a, b[2][2] * a, b[2][3] * a], - [b[3][0] * a, b[3][1] * a, b[3][2] * a, b[3][3] * a] - ])), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown operator `{:?}` for `f64` and `mat4`", - self.stack_trace(), - binop.op.symbol_bool()), self)), - } - } - (&Variable::Mat4(ref b), &Variable::F64(a, _)) => { - let a = a as f32; - match binop.op { - Mul => Variable::Mat4(Box::new([ - [b[0][0] * a, b[0][1] * a, b[0][2] * a, b[0][3] * a], - [b[1][0] * a, b[1][1] * a, b[1][2] * a, b[1][3] * a], - [b[2][0] * a, b[2][1] * a, b[2][2] * a, b[2][3] * a], - [b[3][0] * a, b[3][1] * a, b[3][2] * a, b[3][3] * a] - ])), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown operator `{:?}` for `f64` and `mat4`", - self.stack_trace(), - binop.op.symbol_bool()), self)), - } - } - (&Variable::Mat4(ref a), &Variable::Vec4(b)) => { - use vecmath::col_mat4_transform; - - match binop.op { - Mul => Variable::Vec4(col_mat4_transform(**a, b)), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown operator `{:?}` for `mat4` and `vec4`", - self.stack_trace(), - binop.op.symbol_bool()), self)), - } - } (&Variable::Bool(a, ref sec), &Variable::Bool(b, _)) => { Variable::Bool(match binop.op { OrElse => a || b, - Mul | AndAlso => a && b, + AndAlso => a && b, Pow => a ^ b, _ => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown boolean operator `{:?}`", diff --git a/src/write.rs b/src/write.rs index 5c8f8ecc..9f8ef7da 100644 --- a/src/write.rs +++ b/src/write.rs @@ -217,6 +217,8 @@ fn write_expr( write_binop(w, rt, ast::BinOp::Add, &call.args[0], &call.args[1], tabs)? } else if &**call.name == "sub" && call.args.len() == 2 { write_binop(w, rt, ast::BinOp::Sub, &call.args[0], &call.args[1], tabs)? + } else if &**call.name == "mul" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Mul, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } From abfee351cbb81e15736756c74a0721ea9eb98d3b Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 21 Sep 2019 13:02:39 +0200 Subject: [PATCH 51/83] Make `div` standard external function --- src/ast/mod.rs | 8 ++++++++ src/dyon_std/mod.rs | 22 ++++++++++++++++++++++ src/lib.dyon | 7 +++++++ src/lib.rs | 12 ++++++++++++ src/lifetime/mod.rs | 1 + src/runtime/mod.rs | 4 ---- src/write.rs | 2 ++ 7 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4c00db3c..e7529467 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2930,6 +2930,14 @@ impl BinOpExpression { f_index: Cell::new(FnIndex::None), source_range: self.source_range, })), + BinOp::Div => Expression::Call(Box::new(Call { + alias: None, + name: crate::DIV.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), BinOp::Dot => Expression::Call(Box::new(Call { alias: None, name: crate::DOT.clone(), diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index c4fc5308..5b8c48e3 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -122,6 +122,28 @@ pub(crate) fn mul(rt: &mut Runtime) -> Result<(), String> { Ok(()) } +pub(crate) fn div(rt: &mut Runtime) -> Result<(), String> { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&F64(a, ref sec), &F64(b, _)) => F64(a * b, sec.clone()), + (&Vec4(a), &Vec4(b)) => Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), + (&Vec4(a), &F64(b, _)) => { + let b = b as f32; + Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]) + } + (&F64(a, _), &Vec4(b)) => { + let a = a as f32; + Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]) + } + _ => return Err("Expected `f64` or `vec4`".into()) + }; + rt.stack.push(r); + Ok(()) +} + pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { let b = rt.stack.pop().expect(TINVOTS); let b = match *rt.resolve(&b) { diff --git a/src/lib.dyon b/src/lib.dyon index b5905f33..d20f1444 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -267,6 +267,13 @@ fn mul(a: any, b: any) -> any { ... } (mat4, vec4) -> vec4 all T { (T bool, T bool) -> T bool } +/// Division. +fn div(a: any, b: any) -> any { ... } + (f64, f64) -> f64 + (vec4, vec4) -> vec4 + (vec4, f64) -> vec4 + (f64, vec4) -> vec4 + /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index 30983eb4..391ecf30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,7 @@ lazy_static!{ pub(crate) static ref ADD: Arc = Arc::new("add".into()); pub(crate) static ref SUB: Arc = Arc::new("sub".into()); pub(crate) static ref MUL: Arc = Arc::new("mul".into()); + pub(crate) static ref DIV: Arc = Arc::new("div".into()); pub(crate) static ref DOT: Arc = Arc::new("dot".into()); pub(crate) static ref CROSS: Arc = Arc::new("cross".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); @@ -423,6 +424,17 @@ impl Module { Type::all_ext(vec![Bool, Bool], Bool), ] }); + m.add_str("div", div, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + ] + }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 1387c8e1..6d44151f 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -107,6 +107,7 @@ pub fn check( Add => Node::rewrite_binop(i, crate::ADD.clone(), &mut nodes), Sub => Node::rewrite_binop(i, crate::SUB.clone(), &mut nodes), Mul => Node::rewrite_binop(i, crate::MUL.clone(), &mut nodes), + Div => Node::rewrite_binop(i, crate::DIV.clone(), &mut nodes), Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), _ => {} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 8ccffc05..b421b67f 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2513,7 +2513,6 @@ impl Runtime { let v = match (self.resolve(&left), self.resolve(&right)) { (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => { Variable::F64(match binop.op { - Div => a / b, Rem => a % b, Pow => a.powf(b), _ => return Err(self.module.error(binop.source_range, @@ -2524,7 +2523,6 @@ impl Runtime { } (&Variable::Vec4(a), &Variable::Vec4(b)) => { match binop.op { - Div => Variable::Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), Rem => Variable::Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), Pow => Variable::Vec4([a[0].powf(b[0]), a[1].powf(b[1]), a[2].powf(b[2]), a[3].powf(b[3])]), @@ -2537,7 +2535,6 @@ impl Runtime { (&Variable::Vec4(a), &Variable::F64(b, _)) => { let b = b as f32; match binop.op { - Div => Variable::Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]), Rem => Variable::Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]), Pow => Variable::Vec4([a[0].powf(b), a[1].powf(b), a[2].powf(b), a[3].powf(b)]), @@ -2550,7 +2547,6 @@ impl Runtime { (&Variable::F64(a, _), &Variable::Vec4(b)) => { let a = a as f32; match binop.op { - Div => Variable::Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]), Rem => Variable::Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]), Pow => Variable::Vec4([a.powf(b[0]), a.powf(b[1]), a.powf(b[2]), a.powf(b[3])]), diff --git a/src/write.rs b/src/write.rs index 9f8ef7da..f44a5e79 100644 --- a/src/write.rs +++ b/src/write.rs @@ -219,6 +219,8 @@ fn write_expr( write_binop(w, rt, ast::BinOp::Sub, &call.args[0], &call.args[1], tabs)? } else if &**call.name == "mul" && call.args.len() == 2 { write_binop(w, rt, ast::BinOp::Mul, &call.args[0], &call.args[1], tabs)? + } else if &**call.name == "div" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Div, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } From f2b5aeabb4f57a1766cadac9682b62ce8a386064 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 21 Sep 2019 13:23:32 +0200 Subject: [PATCH 52/83] Make `rem` standard external function --- src/ast/mod.rs | 8 ++++++++ src/dyon_std/mod.rs | 22 ++++++++++++++++++++++ src/lib.dyon | 7 +++++++ src/lib.rs | 12 ++++++++++++ src/lifetime/mod.rs | 1 + src/runtime/mod.rs | 4 ---- src/write.rs | 2 ++ 7 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index e7529467..f6063bac 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2938,6 +2938,14 @@ impl BinOpExpression { f_index: Cell::new(FnIndex::None), source_range: self.source_range, })), + BinOp::Rem => Expression::Call(Box::new(Call { + alias: None, + name: crate::REM.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), BinOp::Dot => Expression::Call(Box::new(Call { alias: None, name: crate::DOT.clone(), diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 5b8c48e3..9f5cf3d1 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -144,6 +144,28 @@ pub(crate) fn div(rt: &mut Runtime) -> Result<(), String> { Ok(()) } +pub(crate) fn rem(rt: &mut Runtime) -> Result<(), String> { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&F64(a, ref sec), &F64(b, _)) => F64(a % b, sec.clone()), + (&Vec4(a), &Vec4(b)) => Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), + (&Vec4(a), &F64(b, _)) => { + let b = b as f32; + Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]) + } + (&F64(a, _), &Vec4(b)) => { + let a = a as f32; + Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]) + } + _ => return Err("Expected `f64` or `vec4`".into()) + }; + rt.stack.push(r); + Ok(()) +} + pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { let b = rt.stack.pop().expect(TINVOTS); let b = match *rt.resolve(&b) { diff --git a/src/lib.dyon b/src/lib.dyon index d20f1444..4d0101e1 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -274,6 +274,13 @@ fn div(a: any, b: any) -> any { ... } (vec4, f64) -> vec4 (f64, vec4) -> vec4 +/// Division reminder. +fn rem(a: any, b: any) -> any { ... } + (f64, f64) -> f64 + (vec4, vec4) -> vec4 + (vec4, f64) -> vec4 + (f64, vec4) -> vec4 + /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index 391ecf30..db1c4c7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,7 @@ lazy_static!{ pub(crate) static ref SUB: Arc = Arc::new("sub".into()); pub(crate) static ref MUL: Arc = Arc::new("mul".into()); pub(crate) static ref DIV: Arc = Arc::new("div".into()); + pub(crate) static ref REM: Arc = Arc::new("rem".into()); pub(crate) static ref DOT: Arc = Arc::new("dot".into()); pub(crate) static ref CROSS: Arc = Arc::new("cross".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); @@ -435,6 +436,17 @@ impl Module { (vec![], vec![F64, Vec4], Vec4), ] }); + m.add_str("rem", rem, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + ] + }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 6d44151f..22f84766 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -108,6 +108,7 @@ pub fn check( Sub => Node::rewrite_binop(i, crate::SUB.clone(), &mut nodes), Mul => Node::rewrite_binop(i, crate::MUL.clone(), &mut nodes), Div => Node::rewrite_binop(i, crate::DIV.clone(), &mut nodes), + Rem => Node::rewrite_binop(i, crate::REM.clone(), &mut nodes), Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), _ => {} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index b421b67f..c1aa22db 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2513,7 +2513,6 @@ impl Runtime { let v = match (self.resolve(&left), self.resolve(&right)) { (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => { Variable::F64(match binop.op { - Rem => a % b, Pow => a.powf(b), _ => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown number operator `{:?}`", @@ -2523,7 +2522,6 @@ impl Runtime { } (&Variable::Vec4(a), &Variable::Vec4(b)) => { match binop.op { - Rem => Variable::Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), Pow => Variable::Vec4([a[0].powf(b[0]), a[1].powf(b[1]), a[2].powf(b[2]), a[3].powf(b[3])]), _ => return Err(self.module.error(binop.source_range, @@ -2535,7 +2533,6 @@ impl Runtime { (&Variable::Vec4(a), &Variable::F64(b, _)) => { let b = b as f32; match binop.op { - Rem => Variable::Vec4([a[0] % b, a[1] % b, a[2] % b, a[3] % b]), Pow => Variable::Vec4([a[0].powf(b), a[1].powf(b), a[2].powf(b), a[3].powf(b)]), _ => return Err(self.module.error(binop.source_range, @@ -2547,7 +2544,6 @@ impl Runtime { (&Variable::F64(a, _), &Variable::Vec4(b)) => { let a = a as f32; match binop.op { - Rem => Variable::Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]), Pow => Variable::Vec4([a.powf(b[0]), a.powf(b[1]), a.powf(b[2]), a.powf(b[3])]), _ => return Err(self.module.error(binop.source_range, diff --git a/src/write.rs b/src/write.rs index f44a5e79..427b1cec 100644 --- a/src/write.rs +++ b/src/write.rs @@ -221,6 +221,8 @@ fn write_expr( write_binop(w, rt, ast::BinOp::Mul, &call.args[0], &call.args[1], tabs)? } else if &**call.name == "div" && call.args.len() == 2 { write_binop(w, rt, ast::BinOp::Div, &call.args[0], &call.args[1], tabs)? + } else if &**call.name == "rem" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Rem, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } From a46afd4c5bc0c4a36a12add42308f70e55b37e9d Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 21 Sep 2019 13:46:49 +0200 Subject: [PATCH 53/83] Make `pow` standard external function --- src/ast/mod.rs | 12 ++++++++++-- src/dyon_std/mod.rs | 24 ++++++++++++++++++++++++ src/lib.dyon | 8 ++++++++ src/lib.rs | 13 +++++++++++++ src/lifetime/mod.rs | 1 + src/runtime/mod.rs | 42 ------------------------------------------ src/write.rs | 2 ++ 7 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index f6063bac..a74482c1 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2051,12 +2051,12 @@ impl Pow { } fn into_expression(self) -> Expression { - Expression::BinOp(Box::new(BinOpExpression { + BinOpExpression { op: BinOp::Pow, left: self.base, right: self.exp, source_range: self.source_range, - })) + }.into_expression() } } @@ -2946,6 +2946,14 @@ impl BinOpExpression { f_index: Cell::new(FnIndex::None), source_range: self.source_range, })), + BinOp::Pow => Expression::Call(Box::new(Call { + alias: None, + name: crate::POW.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), BinOp::Dot => Expression::Call(Box::new(Call { alias: None, name: crate::DOT.clone(), diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 9f5cf3d1..4fb45f6e 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -166,6 +166,30 @@ pub(crate) fn rem(rt: &mut Runtime) -> Result<(), String> { Ok(()) } +pub(crate) fn pow(rt: &mut Runtime) -> Result<(), String> { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&F64(a, ref sec), &F64(b, _)) => F64(a.powf(b), sec.clone()), + (&Vec4(a), &Vec4(b)) => Vec4([a[0].powf(b[0]), a[1].powf(b[1]), + a[2].powf(b[2]), a[3].powf(b[3])]), + (&Vec4(a), &F64(b, _)) => { + let b = b as f32; + Vec4([a[0].powf(b), a[1].powf(b), a[2].powf(b), a[3].powf(b)]) + } + (&F64(a, _), &Vec4(b)) => { + let a = a as f32; + Vec4([a.powf(b[0]), a.powf(b[1]), a.powf(b[2]), a.powf(b[3])]) + } + (&Bool(a, ref sec), &Bool(ref b, _)) => Bool(a ^ b, sec.clone()), + _ => return Err("Expected `f64`, `vec4` or `bool`".into()) + }; + rt.stack.push(r); + Ok(()) +} + pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { let b = rt.stack.pop().expect(TINVOTS); let b = match *rt.resolve(&b) { diff --git a/src/lib.dyon b/src/lib.dyon index 4d0101e1..459e7134 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -281,6 +281,14 @@ fn rem(a: any, b: any) -> any { ... } (vec4, f64) -> vec4 (f64, vec4) -> vec4 +/// Power operator. +fn pow(a: any, b: any) -> any { ... } + (f64, f64) -> f64 + (vec4, vec4) -> vec4 + (vec4, f64) -> vec4 + (f64, vec4) -> vec4 + all T { (T bool, T bool) -> T bool } + /// Returns the length of 4D vector. fn norm(v: vec4) -> f64 { ... } diff --git a/src/lib.rs b/src/lib.rs index db1c4c7d..cf352855 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,7 @@ lazy_static!{ pub(crate) static ref MUL: Arc = Arc::new("mul".into()); pub(crate) static ref DIV: Arc = Arc::new("div".into()); pub(crate) static ref REM: Arc = Arc::new("rem".into()); + pub(crate) static ref POW: Arc = Arc::new("pow".into()); pub(crate) static ref DOT: Arc = Arc::new("dot".into()); pub(crate) static ref CROSS: Arc = Arc::new("cross".into()); pub(crate) static ref NOT: Arc = Arc::new("not".into()); @@ -447,6 +448,18 @@ impl Module { (vec![], vec![F64, Vec4], Vec4), ] }); + m.add_str("pow", pow, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + Type::all_ext(vec![Bool, Bool], Bool), + ] + }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 22f84766..ff221142 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -109,6 +109,7 @@ pub fn check( Mul => Node::rewrite_binop(i, crate::MUL.clone(), &mut nodes), Div => Node::rewrite_binop(i, crate::DIV.clone(), &mut nodes), Rem => Node::rewrite_binop(i, crate::REM.clone(), &mut nodes), + Pow => Node::rewrite_binop(i, crate::POW.clone(), &mut nodes), Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), _ => {} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index c1aa22db..0a848f1a 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2511,52 +2511,10 @@ impl Runtime { _ => return self.err(binop.source_range, "Expected something from right argument") }; let v = match (self.resolve(&left), self.resolve(&right)) { - (&Variable::F64(a, ref sec), &Variable::F64(b, _)) => { - Variable::F64(match binop.op { - Pow => a.powf(b), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown number operator `{:?}`", - self.stack_trace(), - binop.op.symbol()), self)) - }, sec.clone()) - } - (&Variable::Vec4(a), &Variable::Vec4(b)) => { - match binop.op { - Pow => Variable::Vec4([a[0].powf(b[0]), a[1].powf(b[1]), - a[2].powf(b[2]), a[3].powf(b[3])]), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown operator `{:?}` for `vec4` and `vec4`", - self.stack_trace(), - binop.op.symbol_bool()), self)), - } - } - (&Variable::Vec4(a), &Variable::F64(b, _)) => { - let b = b as f32; - match binop.op { - Pow => Variable::Vec4([a[0].powf(b), a[1].powf(b), - a[2].powf(b), a[3].powf(b)]), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown operator `{:?}` for `vec4` and `f64`", - self.stack_trace(), - binop.op.symbol_bool()), self)), - } - } - (&Variable::F64(a, _), &Variable::Vec4(b)) => { - let a = a as f32; - match binop.op { - Pow => Variable::Vec4([a.powf(b[0]), a.powf(b[1]), - a.powf(b[2]), a.powf(b[3])]), - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown operator `{:?}` for `f64` and `vec4`", - self.stack_trace(), - binop.op.symbol_bool()), self)), - } - } (&Variable::Bool(a, ref sec), &Variable::Bool(b, _)) => { Variable::Bool(match binop.op { OrElse => a || b, AndAlso => a && b, - Pow => a ^ b, _ => return Err(self.module.error(binop.source_range, &format!("{}\nUnknown boolean operator `{:?}`", self.stack_trace(), diff --git a/src/write.rs b/src/write.rs index 427b1cec..78a1678b 100644 --- a/src/write.rs +++ b/src/write.rs @@ -223,6 +223,8 @@ fn write_expr( write_binop(w, rt, ast::BinOp::Div, &call.args[0], &call.args[1], tabs)? } else if &**call.name == "rem" && call.args.len() == 2 { write_binop(w, rt, ast::BinOp::Rem, &call.args[0], &call.args[1], tabs)? + } else if &**call.name == "pow" && call.args.len() == 2 { + write_binop(w, rt, ast::BinOp::Pow, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } From 704e879538cb95ff6169fb83d5dd2fbe0c4c3623 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 22 Sep 2019 11:28:29 +0200 Subject: [PATCH 54/83] Added support for lazy invariants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/PistonDevelopers/dyon/issues/640 - Use fake grab expressions to precompute lazy invariant values - Added lazy invariant fake grap expressions - Added lazy invariant `ok(_)` pattern - Added lazy invariant `err(_)` pattern - Added lazy invariant `some(_)` pattern - Added test for lazy invariant - Removed `Expression::BinOp` - Added `ast::Lazy` - Added `ast::Arg::lazy` - Added `FnIndex::ExternalLazy` - Removed `BinOpExpression::resolve_locals` - Made `and_also` standard external function - Made `or_else` standard external function - Changed external function signature to return `Result, String>` - Returns value directly from external functions whenever possible - Made `unwrap_or` lazy - Use static pointers for external lazy invariants - Added `LAZY_UNWRAP_OR` - Added `LAZY_AND` - Added `LAZY_OR` - Added `LAZY_NO` - Rewrite binary operators in type checker - Delay errors in type checker for external functions - Treat `Kind::Add`, `Kind::Mul`, `Kind::Pow` as expressions in type checker - Updated `dyon_fn!` macro to return value directly when possible - Removed `Runtime::binop` - Added ambiguity check for `sec[_] vs any` - Added `standard_binop` to “write.rs” --- assets/syntax.txt | 6 +- source/syntax/lazy_pass_1.dyon | 8 + source/syntax/lazy_pass_2.dyon | 8 + source/syntax/lazy_pass_3.dyon | 8 + source/syntax/lazy_pass_4.dyon | 8 + source/syntax/lazy_pass_5.dyon | 8 + source/syntax/lazy_pass_6.dyon | 9 + source/syntax/lazy_pass_7.dyon | 12 ++ source/syntax/lazy_pass_8.dyon | 11 + src/ast/infer_len.rs | 6 - src/ast/mod.rs | 89 +++++--- src/ast/replace.rs | 9 - src/dyon_std/mod.rs | 378 +++++++++++++++------------------ src/grab.rs | 14 -- src/lib.dyon | 16 +- src/lib.rs | 88 ++++++-- src/lifetime/mod.rs | 27 +-- src/lifetime/typecheck.rs | 112 ++-------- src/macros.rs | 18 +- src/prelude.rs | 5 + src/runtime/mod.rs | 144 ++++++++----- src/ty.rs | 5 +- src/write.rs | 59 ++--- tests/lib.rs | 8 + 24 files changed, 584 insertions(+), 472 deletions(-) create mode 100644 source/syntax/lazy_pass_1.dyon create mode 100644 source/syntax/lazy_pass_2.dyon create mode 100644 source/syntax/lazy_pass_3.dyon create mode 100644 source/syntax/lazy_pass_4.dyon create mode 100644 source/syntax/lazy_pass_5.dyon create mode 100644 source/syntax/lazy_pass_6.dyon create mode 100644 source/syntax/lazy_pass_7.dyon create mode 100644 source/syntax/lazy_pass_8.dyon diff --git a/assets/syntax.txt b/assets/syntax.txt index 328c336b..6a68620e 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -27,7 +27,11 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" 5 ty_var = ["all" ?w .s?.(, .._seps!:"ty_var") ?w "{" ?w ty ?w "}"] 4 args = .s?.(, arg:"arg") 5 arg = [?"mut":"mut" ?w .._seps!:"name" ?[?w ":" ?w - ?["'" ?w .._seps!:"lifetime"] ?w ?type:"type"]] + ?["'" ?w .._seps!:"lifetime"] ?w ?type:"type"] + ?[?w "=>" ?w .s!.([?w "|" ?w] { + "ok(_)":"ok(_)" "err(_)":"err(_)" "some(_)":"some(_)" lazy:"grab"})]] +// Fake grab expression to reuse code for lazy invariants. +5 lazy = expr:"expr" 6 imm_arg = [!"mut " .._seps!:"name" ?[?w ":" ?w !"'" ?type:"type"]] 7 closure = ["\\(" ?w .s?.(, imm_arg:"arg") ?w ")" ?w ?currents ?w "=" ?w expr:"expr"] diff --git a/source/syntax/lazy_pass_1.dyon b/source/syntax/lazy_pass_1.dyon new file mode 100644 index 00000000..a65e5ebd --- /dev/null +++ b/source/syntax/lazy_pass_1.dyon @@ -0,0 +1,8 @@ +fn foo(a: bool => false, b: bool) -> bool {return a && b} + +fn main() { + println(foo(false, { + println("this is a test") + false + })) +} diff --git a/source/syntax/lazy_pass_2.dyon b/source/syntax/lazy_pass_2.dyon new file mode 100644 index 00000000..9dc88ac2 --- /dev/null +++ b/source/syntax/lazy_pass_2.dyon @@ -0,0 +1,8 @@ +fn foo(a: f64 => 0, b: f64) -> f64 {return a * b} + +fn main() { + println(foo(0, { + println("this is a test") + 2 + })) +} diff --git a/source/syntax/lazy_pass_3.dyon b/source/syntax/lazy_pass_3.dyon new file mode 100644 index 00000000..2a5b536e --- /dev/null +++ b/source/syntax/lazy_pass_3.dyon @@ -0,0 +1,8 @@ +fn foo(a: [f64] => [], b: [f64]) -> [f64] {return clone(a)} + +fn main() { + println(foo([], { + println("this is a test") + [1, 2] + })) +} diff --git a/source/syntax/lazy_pass_4.dyon b/source/syntax/lazy_pass_4.dyon new file mode 100644 index 00000000..8bbb2714 --- /dev/null +++ b/source/syntax/lazy_pass_4.dyon @@ -0,0 +1,8 @@ +fn foo(a: str => "hi", b: str) -> str {return clone(a)} + +fn main() { + println(foo("hi", { + println("this is a test") + "hello" + })) +} diff --git a/source/syntax/lazy_pass_5.dyon b/source/syntax/lazy_pass_5.dyon new file mode 100644 index 00000000..b34fde31 --- /dev/null +++ b/source/syntax/lazy_pass_5.dyon @@ -0,0 +1,8 @@ +fn foo(a: res[str] => ok(_), b: str) -> str {return unwrap_or(a, b)} + +fn main() { + println(foo(ok("hi"), { + println("this is a test") + "hello" + })) +} diff --git a/source/syntax/lazy_pass_6.dyon b/source/syntax/lazy_pass_6.dyon new file mode 100644 index 00000000..50520b0e --- /dev/null +++ b/source/syntax/lazy_pass_6.dyon @@ -0,0 +1,9 @@ +fn foo(a: res[str] => ok(_), b: str) -> str {return unwrap_or(a, b)} + +fn main() { + a := ok("hi") + println(foo(a, { + println("this is a test") + "hello" + })) +} diff --git a/source/syntax/lazy_pass_7.dyon b/source/syntax/lazy_pass_7.dyon new file mode 100644 index 00000000..fc280826 --- /dev/null +++ b/source/syntax/lazy_pass_7.dyon @@ -0,0 +1,12 @@ +fn foo(a: res[str] => err(_), b: str) -> any { + return if is_err(a) {unwrap_err(a)} + else {clone(b)} +} + +fn main() { + a := err("hi") + println(foo(a, { + println("this is a test") + "hello" + })) +} diff --git a/source/syntax/lazy_pass_8.dyon b/source/syntax/lazy_pass_8.dyon new file mode 100644 index 00000000..9eacd0d7 --- /dev/null +++ b/source/syntax/lazy_pass_8.dyon @@ -0,0 +1,11 @@ +fn foo(a: any => ok(_) | some(_), b: str) -> str { + return unwrap_or(a, b) +} + +fn main() { + a := ok("hi") + println(foo(a, { + println("this is a test") + "hello" + })) +} diff --git a/src/ast/infer_len.rs b/src/ast/infer_len.rs index 812f440d..932c7ad0 100644 --- a/src/ast/infer_len.rs +++ b/src/ast/infer_len.rs @@ -50,12 +50,6 @@ fn infer_expr( let res = infer_item(item, name, decls); if res.is_some() { return res; } } - BinOp(ref binop_expr) => { - let left = infer_expr(&binop_expr.left, name, decls); - if left.is_some() { return left; } - let right = infer_expr(&binop_expr.right, name, decls); - if right.is_some() { return right; } - } Assign(ref assign_expr) => { let left = infer_expr(&assign_expr.left, name, decls); if left.is_some() { return left; } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a74482c1..401f643d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -393,7 +393,7 @@ impl Function { convert.update(range); name = Some(val); } else if let Ok((range, val)) = Arg::from_meta_data( - convert, ignored) { + file, source, convert, ignored) { convert.update(range); args.push(val); } else if let Ok((range, val)) = Current::from_meta_data( @@ -534,7 +534,7 @@ impl Closure { convert.update(range); break; } else if let Ok((range, val)) = Arg::from_meta_data( - convert, ignored) { + file, source, convert, ignored) { convert.update(range); args.push(val); } else if let Ok((range, val)) = Current::from_meta_data( @@ -746,6 +746,24 @@ impl TryExpr { } } +/// Lazy invariant. +#[derive(Debug, Clone, PartialEq)] +pub enum Lazy { + /// Return a variable if result of argument is equal to the variable. + Variable(Variable), + /// Unwrap the Ok result. + UnwrapOk, + /// Unwrap the Err result. + UnwrapErr, + /// Unwrap the Some option. + UnwrapSome, +} + +/// This is requires because `UnsafeRef(*mut Variable)` can not be sent across threads. +/// The lack of `UnsafeRef` variant when sending across threads is guaranteed at language level. +/// The interior of `UnsafeRef` can not be accessed outside this library. +unsafe impl Sync for Lazy {} + /// Function argument. #[derive(Debug, Clone)] pub struct Arg { @@ -755,6 +773,8 @@ pub struct Arg { pub lifetime: Option>, /// The type of the argument. pub ty: Type, + /// Lazy invariant. + pub lazy: Vec, /// The range in source. pub source_range: Range, /// Whether the argument is mutable. @@ -763,8 +783,12 @@ pub struct Arg { impl Arg { /// Creates function argument from meta data. - pub fn from_meta_data(mut convert: Convert, ignored: &mut Vec) - -> Result<(Range, Arg), ()> { + pub fn from_meta_data( + file: &Arc, + source: &Arc, + mut convert: Convert, + ignored: &mut Vec + ) -> Result<(Range, Arg), ()> { let start = convert; let node = "arg"; let start_range = convert.start_node(node)?; @@ -774,6 +798,7 @@ impl Arg { let mut lifetime: Option> = None; let mut ty: Option = None; let mut mutable = false; + let mut lazy: Vec = vec![]; loop { if let Ok(range) = convert.end_node(node) { convert.update(range); @@ -791,6 +816,20 @@ impl Arg { "type", convert, ignored) { convert.update(range); ty = Some(val); + } else if let Ok((range, val)) = Grab::from_meta_data(file, source, convert, ignored) { + convert.update(range); + if let Some(val) = val.precompute() { + lazy.push(Lazy::Variable(val)); + } else {return Err(())} + } else if let Ok((range, _)) = convert.meta_bool("ok(_)") { + convert.update(range); + lazy.push(Lazy::UnwrapOk); + } else if let Ok((range, _)) = convert.meta_bool("err(_)") { + convert.update(range); + lazy.push(Lazy::UnwrapErr); + } else if let Ok((range, _)) = convert.meta_bool("some(_)") { + convert.update(range); + lazy.push(Lazy::UnwrapSome); } else { let range = convert.ignore(); convert.update(range); @@ -807,6 +846,7 @@ impl Arg { name, lifetime, ty, + lazy, source_range: convert.source(start).unwrap(), mutable, })) @@ -954,8 +994,6 @@ pub enum Expression { Call(Box), /// Item expression. Item(Box), - /// Binary operator expression. - BinOp(Box), /// Assignment expression. Assign(Box), /// 4D vector expression. @@ -1339,7 +1377,6 @@ impl Expression { Go(ref go) => go.source_range, Call(ref call) => call.source_range, Item(ref it) => it.source_range, - BinOp(ref binop) => binop.source_range, Assign(ref assign) => assign.source_range, Vec4(ref vec4) => vec4.source_range, Mat4(ref mat4) => mat4.source_range, @@ -1412,8 +1449,6 @@ impl Expression { call.resolve_locals(relative, stack, closure_stack, module, use_lookup), Item(ref it) => it.resolve_locals(relative, stack, closure_stack, module, use_lookup), - BinOp(ref binop) => - binop.resolve_locals(relative, stack, closure_stack, module, use_lookup), Assign(ref assign) => assign.resolve_locals(relative, stack, closure_stack, module, use_lookup), Vec4(ref vec4) => @@ -2596,7 +2631,9 @@ impl Call { stack.push(None); } } - FnIndex::ExternalVoid(_) | FnIndex::ExternalReturn(_) => { + FnIndex::ExternalVoid(_) | + FnIndex::ExternalReturn(_) | + FnIndex::ExternalLazy(_, _) => { // Don't push return since last value in block // is used as return value. } @@ -2889,21 +2926,6 @@ pub struct BinOpExpression { } impl BinOpExpression { - fn resolve_locals( - &self, - relative: usize, - stack: &mut Vec>>, - closure_stack: &mut Vec, - module: &Module, - use_lookup: &UseLookup, - ) { - let st = stack.len(); - self.left.resolve_locals(relative, stack, closure_stack, module, use_lookup); - stack.truncate(st); - self.right.resolve_locals(relative, stack, closure_stack, module, use_lookup); - stack.truncate(st); - } - fn into_expression(self) -> Expression { match self.op { BinOp::Add => Expression::Call(Box::new(Call { @@ -2970,7 +2992,22 @@ impl BinOpExpression { f_index: Cell::new(FnIndex::None), source_range: self.source_range, })), - _ => Expression::BinOp(Box::new(self)) + BinOp::AndAlso => Expression::Call(Box::new(Call { + alias: None, + name: crate::AND_ALSO.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), + BinOp::OrElse => Expression::Call(Box::new(Call { + alias: None, + name: crate::OR_ELSE.clone(), + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })), } } } diff --git a/src/ast/replace.rs b/src/ast/replace.rs index 1535454e..024a4e1b 100644 --- a/src/ast/replace.rs +++ b/src/ast/replace.rs @@ -6,7 +6,6 @@ use super::{ ArrayFill, Assign, Block, - BinOpExpression, Call, CallClosure, Compare, @@ -44,14 +43,6 @@ pub fn number(expr: &Expression, name: &Arc, val: f64) -> Expression { source_range: link_expr.source_range, })) } - E::BinOp(ref bin_op_expr) => { - E::BinOp(Box::new(BinOpExpression { - op: bin_op_expr.op, - left: number(&bin_op_expr.left, name, val), - right: number(&bin_op_expr.right, name, val), - source_range: bin_op_expr.source_range, - })) - } E::Item(ref item) => { if &item.name == name { E::Variable(Box::new((item.source_range, Variable::f64(val)))) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 4fb45f6e..1577fb11 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -14,7 +14,31 @@ const HTTP_SUPPORT_DISABLED: &'static str = "Http support is disabled"; #[cfg(not(feature = "file"))] const FILE_SUPPORT_DISABLED: &'static str = "File support is disabled"; -pub(crate) fn add(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn and_also(rt: &mut Runtime) -> Result, String> { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && b, sec.clone()), + _ => return Err("Expected `bool`".into()) + }; + Ok(Some(r)) +} + +pub(crate) fn or_else(rt: &mut Runtime) -> Result, String> { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&Bool(a, ref sec), &Bool(b, _)) => Bool(a || b, sec.clone()), + _ => return Err("Expected `bool`".into()) + }; + Ok(Some(r)) +} + +pub(crate) fn add(rt: &mut Runtime) -> Result, String> { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -46,11 +70,10 @@ pub(crate) fn add(rt: &mut Runtime) -> Result<(), String> { (&Link(ref a), &Link(ref b)) => Link(Box::new(a.add(b))), _ => return Err("Expected `f64`, `vec4`, `mat4`, `bool`, `str` or `link`".into()) }; - rt.stack.push(r); - Ok(()) + Ok(Some(r)) } -pub(crate) fn sub(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn sub(rt: &mut Runtime) -> Result, String> { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -88,11 +111,10 @@ pub(crate) fn sub(rt: &mut Runtime) -> Result<(), String> { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && !b, sec.clone()), _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) }; - rt.stack.push(r); - Ok(()) + Ok(Some(r)) } -pub(crate) fn mul(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn mul(rt: &mut Runtime) -> Result, String> { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -118,11 +140,10 @@ pub(crate) fn mul(rt: &mut Runtime) -> Result<(), String> { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && b, sec.clone()), _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) }; - rt.stack.push(r); - Ok(()) + Ok(Some(r)) } -pub(crate) fn div(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn div(rt: &mut Runtime) -> Result, String> { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -140,11 +161,10 @@ pub(crate) fn div(rt: &mut Runtime) -> Result<(), String> { } _ => return Err("Expected `f64` or `vec4`".into()) }; - rt.stack.push(r); - Ok(()) + Ok(Some(r)) } -pub(crate) fn rem(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn rem(rt: &mut Runtime) -> Result, String> { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -162,11 +182,10 @@ pub(crate) fn rem(rt: &mut Runtime) -> Result<(), String> { } _ => return Err("Expected `f64` or `vec4`".into()) }; - rt.stack.push(r); - Ok(()) + Ok(Some(r)) } -pub(crate) fn pow(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn pow(rt: &mut Runtime) -> Result, String> { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -186,21 +205,19 @@ pub(crate) fn pow(rt: &mut Runtime) -> Result<(), String> { (&Bool(a, ref sec), &Bool(ref b, _)) => Bool(a ^ b, sec.clone()), _ => return Err("Expected `f64`, `vec4` or `bool`".into()) }; - rt.stack.push(r); - Ok(()) + Ok(Some(r)) } -pub(crate) fn not(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn not(rt: &mut Runtime) -> Result, String> { let b = rt.stack.pop().expect(TINVOTS); let b = match *rt.resolve(&b) { Variable::Bool(ref b, ref sec) => Variable::Bool(!b, sec.clone()), ref x => return Err(rt.expected_arg(0, x, "bool")) }; - rt.stack.push(b); - Ok(()) + Ok(Some(b)) } -pub(crate) fn neg(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn neg(rt: &mut Runtime) -> Result, String> { let n = rt.stack.pop().expect(TINVOTS); let n = match *rt.resolve(&n) { Variable::F64(v, ref sec) => Variable::F64(-v, sec.clone()), @@ -213,11 +230,10 @@ pub(crate) fn neg(rt: &mut Runtime) -> Result<(), String> { ])), ref x => return Err(rt.expected_arg(0, x, "f64, vec4 or mat4")) }; - rt.stack.push(n); - Ok(()) + Ok(Some(n)) } -pub(crate) fn dot(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn dot(rt: &mut Runtime) -> Result, String> { let b = rt.stack.pop().expect(TINVOTS); let a = rt.stack.pop().expect(TINVOTS); let r = match (rt.resolve(&a), rt.resolve(&b)) { @@ -229,8 +245,7 @@ pub(crate) fn dot(rt: &mut Runtime) -> Result<(), String> { } _ => return Err("Expected (vec4, vec4), (vec4, f64) or (f64, vec4)".into()) }; - rt.stack.push(Variable::f64(r)); - Ok(()) + Ok(Some(Variable::f64(r))) } dyon_fn!{fn cross(a: Vec4, b: Vec4) -> Vec4 { @@ -245,13 +260,12 @@ dyon_fn!{fn z(v: Vec4) -> f64 {f64::from(v.0[2])}} dyon_fn!{fn w(v: Vec4) -> f64 {f64::from(v.0[3])}} dyon_fn!{fn norm(v: Vec4) -> f32 {vecmath::vec4_len(v.0)}} -pub(crate) fn s(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn s(rt: &mut Runtime) -> Result, String> { let ind: f64 = rt.pop().expect(TINVOTS); let ind = ind as usize; if ind >= 4 {return Err(format!("Index out of bounds `{}`", ind))}; let v: [f32; 4] = rt.pop_vec4().expect(TINVOTS); - rt.push(f64::from(v[ind])); - Ok(()) + Ok(Some(Variable::f64(f64::from(v[ind])))) } dyon_fn!{fn det(m: Mat4) -> f64 {f64::from(vecmath::mat4_det(m.0))}} @@ -323,13 +337,12 @@ dyon_fn!{fn ry(m: Mat4) -> Vec4 {Vec4([m.0[0][1], m.0[1][1], m.0[2][1], m.0[3][1 dyon_fn!{fn rz(m: Mat4) -> Vec4 {Vec4([m.0[0][2], m.0[1][2], m.0[2][2], m.0[3][2]])}} dyon_fn!{fn rw(m: Mat4) -> Vec4 {Vec4([m.0[0][3], m.0[1][3], m.0[2][3], m.0[3][3]])}} -pub(crate) fn rv(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn rv(rt: &mut Runtime) -> Result, String> { let ind: f64 = rt.pop().expect(TINVOTS); let ind = ind as usize; if ind >= 4 {return Err(format!("Index out of bounds `{}`", ind))}; let m: [[f32; 4]; 4] = rt.pop_mat4().expect(TINVOTS); - rt.stack.push(Variable::Vec4([m[0][ind], m[1][ind], m[2][ind], m[3][ind]])); - Ok(()) + Ok(Some(Variable::Vec4([m[0][ind], m[1][ind], m[2][ind], m[3][ind]]))) } dyon_fn!{fn cx(m: Mat4) -> Vec4 {Vec4(m.0[0])}} @@ -337,23 +350,21 @@ dyon_fn!{fn cy(m: Mat4) -> Vec4 {Vec4(m.0[1])}} dyon_fn!{fn cz(m: Mat4) -> Vec4 {Vec4(m.0[2])}} dyon_fn!{fn cw(m: Mat4) -> Vec4 {Vec4(m.0[3])}} -pub(crate) fn cv(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn cv(rt: &mut Runtime) -> Result, String> { let ind: f64 = rt.pop().expect(TINVOTS); let ind = ind as usize; if ind >= 4 {return Err(format!("Index out of bounds `{}`", ind))}; let m: [[f32; 4]; 4] = rt.pop_mat4().expect(TINVOTS); - rt.stack.push(Variable::Vec4(m[ind])); - Ok(()) + Ok(Some(Variable::Mat4(Box::new(m)))) } -pub(crate) fn clone(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn clone(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = rt.resolve(&v).deep_clone(&rt.stack); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn why(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn why(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Array(Arc::new(match rt.resolve(&v) { &Variable::Bool(true, Some(ref sec)) => { @@ -375,11 +386,10 @@ pub(crate) fn why(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "bool")) })); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn _where(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn _where(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Array(Arc::new(match rt.resolve(&v) { &Variable::F64(val, Some(ref sec)) => { @@ -402,11 +412,10 @@ pub(crate) fn _where(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "f64")) })); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn explain_why(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn explain_why(rt: &mut Runtime) -> Result, String> { let why = rt.stack.pop().expect(TINVOTS); let val = rt.stack.pop().expect(TINVOTS); let (val, why) = match rt.resolve(&val) { @@ -422,11 +431,10 @@ pub(crate) fn explain_why(rt: &mut Runtime) -> Result<(), String> { ), x => return Err(rt.expected_arg(0, x, "bool")) }; - rt.stack.push(Variable::Bool(val, Some(why))); - Ok(()) + Ok(Some(Variable::Bool(val, Some(why)))) } -pub(crate) fn explain_where(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn explain_where(rt: &mut Runtime) -> Result, String> { let wh = rt.stack.pop().expect(TINVOTS); let val = rt.stack.pop().expect(TINVOTS); let (val, wh) = match rt.resolve(&val) { @@ -442,25 +450,24 @@ pub(crate) fn explain_where(rt: &mut Runtime) -> Result<(), String> { ), x => return Err(rt.expected_arg(0, x, "bool")) }; - rt.stack.push(Variable::F64(val, Some(wh))); - Ok(()) + Ok(Some(Variable::F64(val, Some(wh)))) } -pub(crate) fn println(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn println(rt: &mut Runtime) -> Result, String> { use write::{print_variable, EscapeString}; let x = rt.stack.pop().expect(TINVOTS); print_variable(rt, &x, EscapeString::None); println!(); - Ok(()) + Ok(None) } -pub(crate) fn print(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn print(rt: &mut Runtime) -> Result, String> { use write::{print_variable, EscapeString}; let x = rt.stack.pop().expect(TINVOTS); print_variable(rt, &x, EscapeString::None); - Ok(()) + Ok(None) } dyon_fn!{fn sqrt(a: f64) -> f64 {a.sqrt()}} @@ -488,67 +495,61 @@ dyon_fn!{fn sleep(v: f64) { sleep(Duration::new(secs, nanos)); }} -pub(crate) fn head(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn head(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Option(match rt.resolve(&v) { &Variable::Link(ref link) => link.head(), x => return Err(rt.expected_arg(0, x, "link")) }); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn tip(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn tip(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Option(match rt.resolve(&v) { &Variable::Link(ref link) => link.tip(), x => return Err(rt.expected_arg(0, x, "link")) }); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn tail(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn tail(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Link(Box::new(match rt.resolve(&v) { &Variable::Link(ref link) => link.tail(), x => return Err(rt.expected_arg(0, x, "link")) })); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn neck(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn neck(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Link(Box::new(match rt.resolve(&v) { &Variable::Link(ref link) => link.neck(), x => return Err(rt.expected_arg(0, x, "link")) })); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn is_empty(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn is_empty(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::bool(match rt.resolve(&v) { &Variable::Link(ref link) => link.is_empty(), x => return Err(rt.expected_arg(0, x, "link")) }); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn random(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn random(rt: &mut Runtime) -> Result, String> { use rand::Rng; let v: f64 = rt.rng.gen(); - rt.push(v); - Ok(()) + Ok(Some(Variable::f64(v))) } dyon_fn!{fn tau() -> f64 {6.283_185_307_179_586}} -pub(crate) fn len(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn len(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = { let arr = match rt.resolve(&v) { @@ -557,11 +558,10 @@ pub(crate) fn len(rt: &mut Runtime) -> Result<(), String> { }; Variable::f64(arr.len() as f64) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn push_ref(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn push_ref(rt: &mut Runtime) -> Result, String> { let item = rt.stack.pop().expect(TINVOTS); let v = rt.stack.pop().expect(TINVOTS); @@ -584,10 +584,10 @@ pub(crate) fn push_ref(rt: &mut Runtime) -> Result<(), String> { "Expected reference to array".into() }) } - Ok(()) + Ok(None) } -pub(crate) fn insert_ref(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn insert_ref(rt: &mut Runtime) -> Result, String> { let item = rt.stack.pop().expect(TINVOTS); let index = rt.stack.pop().expect(TINVOTS); let index = match rt.resolve(&index) { @@ -621,10 +621,10 @@ pub(crate) fn insert_ref(rt: &mut Runtime) -> Result<(), String> { "Expected reference to array".into() }) } - Ok(()) + Ok(None) } -pub(crate) fn push(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn push(rt: &mut Runtime) -> Result, String> { let item = rt.stack.pop().expect(TINVOTS); let item = rt.resolve(&item).deep_clone(&rt.stack); let v = rt.stack.pop().expect(TINVOTS); @@ -648,10 +648,10 @@ pub(crate) fn push(rt: &mut Runtime) -> Result<(), String> { "Expected reference to array".into() }) } - Ok(()) + Ok(None) } -pub(crate) fn insert(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn insert(rt: &mut Runtime) -> Result, String> { let item = rt.stack.pop().expect(TINVOTS); let item = rt.resolve(&item).deep_clone(&rt.stack); let index = rt.stack.pop().expect(TINVOTS); @@ -686,10 +686,10 @@ pub(crate) fn insert(rt: &mut Runtime) -> Result<(), String> { "Expected reference to array".into() }) } - Ok(()) + Ok(None) } -pub(crate) fn pop(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn pop(rt: &mut Runtime) -> Result, String> { let arr = rt.stack.pop().expect(TINVOTS); let mut v: Option = None; if let Variable::Ref(ind) = arr { @@ -718,11 +718,10 @@ pub(crate) fn pop(rt: &mut Runtime) -> Result<(), String> { }), Some(val) => val }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn remove(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn remove(rt: &mut Runtime) -> Result, String> { let index = rt.stack.pop().expect(TINVOTS); let index = match rt.resolve(&index) { &Variable::F64(index, _) => index, @@ -738,8 +737,7 @@ pub(crate) fn remove(rt: &mut Runtime) -> Result<(), String> { } if let Variable::Array(ref mut arr) = rt.stack[ind] { let v = Arc::make_mut(arr).remove(index as usize); - rt.stack.push(v); - return Ok(()); + return Ok(Some(v)); }; return Err({ rt.arg_err_index.set(Some(0)); @@ -753,7 +751,7 @@ pub(crate) fn remove(rt: &mut Runtime) -> Result<(), String> { } } -pub(crate) fn reverse(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn reverse(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); if let Variable::Ref(ind) = v { let ok = if let Variable::Array(ref mut arr) = rt.stack[ind] { @@ -774,10 +772,10 @@ pub(crate) fn reverse(rt: &mut Runtime) -> Result<(), String> { "Expected reference to array".into() }) } - Ok(()) + Ok(None) } -pub(crate) fn clear(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn clear(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); if let Variable::Ref(ind) = v { let ok = if let Variable::Array(ref mut arr) = rt.stack[ind] { @@ -798,10 +796,10 @@ pub(crate) fn clear(rt: &mut Runtime) -> Result<(), String> { "Expected reference to array".into() }) } - Ok(()) + Ok(None) } -pub(crate) fn swap(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn swap(rt: &mut Runtime) -> Result, String> { let j = rt.stack.pop().expect(TINVOTS); let i = rt.stack.pop().expect(TINVOTS); let j = match rt.resolve(&j) { @@ -832,10 +830,10 @@ pub(crate) fn swap(rt: &mut Runtime) -> Result<(), String> { "Expected reference to array".into() }) } - Ok(()) + Ok(None) } -pub(crate) fn read_line(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn read_line(rt: &mut Runtime) -> Result, String> { use std::io::{self, Write}; use std::error::Error; @@ -845,15 +843,14 @@ pub(crate) fn read_line(rt: &mut Runtime) -> Result<(), String> { Ok(_) => None, Err(error) => Some(error) }; - rt.push(if let Some(error) = error { + Ok(Some(if let Some(error) = error { return Err(error.description().into()) } else { Variable::Str(Arc::new(input)) - }); - Ok(()) + })) } -pub(crate) fn read_number(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn read_number(rt: &mut Runtime) -> Result, String> { use std::io::{self, Write}; use std::error::Error; @@ -873,8 +870,7 @@ pub(crate) fn read_number(rt: &mut Runtime) -> Result<(), String> { Err(_) => println!("{}", err), } }; - rt.push(rv); - Ok(()) + Ok(Some(Variable::f64(rv))) } dyon_fn!{fn parse_number(text: Arc) -> Option {text.trim().parse::().ok()}} @@ -882,24 +878,22 @@ dyon_fn!{fn trim(v: Arc) -> Arc {Arc::new(v.trim().into())}} dyon_fn!{fn trim_left(v: Arc) -> Arc {Arc::new(v.trim_start().into())}} dyon_fn!{fn trim_right(v: Arc) -> Arc {Arc::new(v.trim_end().into())}} -pub(crate) fn _str(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn _str(rt: &mut Runtime) -> Result, String> { use write::{write_variable, EscapeString}; let v = rt.stack.pop().expect(TINVOTS); let mut buf: Vec = vec![]; write_variable(&mut buf, rt, rt.resolve(&v), EscapeString::None, 0).unwrap(); - rt.push(String::from_utf8(buf).unwrap()); - Ok(()) + Ok(Some(Variable::Str(Arc::new(String::from_utf8(buf).unwrap())))) } -pub(crate) fn json_string(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn json_string(rt: &mut Runtime) -> Result, String> { use write::{write_variable, EscapeString}; let v = rt.stack.pop().expect(TINVOTS); let mut buf: Vec = vec![]; write_variable(&mut buf, rt, rt.resolve(&v), EscapeString::Json, 0).unwrap(); - rt.stack.push(Variable::Str(Arc::new(String::from_utf8(buf).unwrap()))); - Ok(()) + Ok(Some(Variable::Str(Arc::new(String::from_utf8(buf).unwrap())))) } dyon_fn!{fn str__color(v: Vec4) -> Arc { @@ -952,7 +946,7 @@ dyon_fn!{fn linear_to_srgb__color(v: Vec4) -> Vec4 { Vec4([to_srgb(v[0]), to_srgb(v[1]), to_srgb(v[2]), v[3]]) }} -pub(crate) fn _typeof(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn _typeof(rt: &mut Runtime) -> Result, String> { use crate::runtime::*; use crate::Variable::*; @@ -976,23 +970,22 @@ pub(crate) fn _typeof(rt: &mut Runtime) -> Result<(), String> { Closure(_, _) => CLOSURE_TYPE.clone(), In(_) => IN_TYPE.clone(), }); - rt.stack.push(t); - Ok(()) + Ok(Some(t)) } -pub(crate) fn debug(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn debug(rt: &mut Runtime) -> Result, String> { println!("Stack {:#?}", rt.stack); println!("Locals {:#?}", rt.local_stack); println!("Currents {:#?}", rt.current_stack); - Ok(()) + Ok(None) } -pub(crate) fn backtrace(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn backtrace(rt: &mut Runtime) -> Result, String> { println!("{:#?}", rt.call_stack); - Ok(()) + Ok(None) } -pub(crate) fn load(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn load(rt: &mut Runtime) -> Result, String> { use load; let v = rt.stack.pop().expect(TINVOTS); @@ -1016,11 +1009,10 @@ pub(crate) fn load(rt: &mut Runtime) -> Result<(), String> { return Err(rt.expected_arg(0, x, "string")); } }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result, String> { use load; let modules = rt.stack.pop().expect(TINVOTS); @@ -1075,11 +1067,10 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "str")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result, String> { let modules = rt.stack.pop().expect(TINVOTS); let source = rt.stack.pop().expect(TINVOTS); let source = match rt.resolve(&source) { @@ -1136,11 +1127,10 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result<(), String> Variable::RustObject(Arc::new( Mutex::new(Arc::new(new_module))))))) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn _call(rt: &mut Runtime) -> Result, String> { // Use the source from calling function. let source = rt.module.functions[rt.call_stack.last().unwrap().index].source.clone(); let args = rt.stack.pop().expect(TINVOTS); @@ -1184,7 +1174,9 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { })?; } FnIndex::None | - FnIndex::ExternalVoid(_) | FnIndex::ExternalReturn(_) => + FnIndex::ExternalVoid(_) | + FnIndex::ExternalReturn(_) | + FnIndex::ExternalLazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) } // Use empty range instead of `call.source_range` (from when it was intrinsic). @@ -1205,10 +1197,10 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { None => return Err(rt.expected_arg(0, x, "Module")) } - Ok(()) + Ok(None) } -pub(crate) fn call_ret(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn call_ret(rt: &mut Runtime) -> Result, String> { // Use the source from calling function. let source = rt.module.functions[rt.call_stack.last().unwrap().index].source.clone(); let args = rt.stack.pop().expect(TINVOTS); @@ -1252,7 +1244,9 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result<(), String> { })?; } FnIndex::None | - FnIndex::ExternalVoid(_) | FnIndex::ExternalReturn(_) => + FnIndex::ExternalVoid(_) | + FnIndex::ExternalReturn(_) | + FnIndex::ExternalLazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) } // Use empty range instead of `call.source_range` (from when it was intrinsic). @@ -1275,18 +1269,16 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result<(), String> { None => return Err(rt.expected_arg(0, x, "Module")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn functions(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn functions(rt: &mut Runtime) -> Result, String> { // List available functions in scope. let v = Variable::Array(Arc::new(functions::list_functions(&rt.module))); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn functions__module(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn functions__module(rt: &mut Runtime) -> Result, String> { // List available functions in scope. let m = rt.stack.pop().expect(TINVOTS); let x = rt.resolve(&m); @@ -1302,64 +1294,58 @@ pub(crate) fn functions__module(rt: &mut Runtime) -> Result<(), String> { }; let v = Variable::Array(Arc::new(functions)); - rt.push(v); - Ok(()) + Ok(Some(v)) } dyon_fn!{fn none() -> Option {None}} -pub(crate) fn some(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn some(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Option(Some(Box::new( rt.resolve(&v).deep_clone(&rt.stack) ))); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn ok(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn ok(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Result(Ok(Box::new( rt.resolve(&v).deep_clone(&rt.stack) ))); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn err(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn err(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Result(Err(Box::new( Error { message: rt.resolve(&v).deep_clone(&rt.stack), trace: vec![] }))); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn is_err(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn is_err(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Result(Err(_)) => Variable::bool(true), &Variable::Result(Ok(_)) => Variable::bool(false), x => return Err(rt.expected_arg(0, x, "result")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn is_ok(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn is_ok(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Result(Err(_)) => Variable::bool(false), &Variable::Result(Ok(_)) => Variable::bool(true), x => return Err(rt.expected_arg(0, x, "result")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn min(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn min(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Array(ref arr) => { @@ -1373,11 +1359,10 @@ pub(crate) fn min(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "array")) }; - rt.stack.push(Variable::f64(v)); - Ok(()) + Ok(Some(Variable::f64(v))) } -pub(crate) fn max(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn max(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Array(ref arr) => { @@ -1391,11 +1376,10 @@ pub(crate) fn max(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "array")) }; - rt.stack.push(Variable::f64(v)); - Ok(()) + Ok(Some(Variable::f64(v))) } -pub(crate) fn unwrap(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn unwrap(rt: &mut Runtime) -> Result, String> { use write::{write_variable, EscapeString}; // Return value does not depend on lifetime of argument since @@ -1430,11 +1414,10 @@ pub(crate) fn unwrap(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "some(_) or ok(_)")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result, String> { // Return value does not depend on lifetime of argument since // `ok(x)` and `some(x)` perform a deep clone. let def = rt.stack.pop().expect(TINVOTS); @@ -1446,18 +1429,16 @@ pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result<(), String> { &Variable::Result(Err(_)) => rt.resolve(&def).clone(), x => return Err(rt.expected_arg(0, x, "some(_) or ok(_)")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn unwrap_err(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn unwrap_err(rt: &mut Runtime) -> Result, String> { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Result(Err(ref err)) => err.message.clone(), x => return Err(rt.expected_arg(0, x, "err(_)")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } dyon_fn!{fn dir__angle(val: f64) -> Vec4 {Vec4([val.cos() as f32, val.sin() as f32, 0.0, 0.0])}} @@ -1498,7 +1479,7 @@ dyon_fn!{fn syntax__in_string(name: Arc, text: Arc) -> Variable }) }} -pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result, String> { use piston_meta::Syntax; let text = rt.stack.pop().expect(TINVOTS); @@ -1529,8 +1510,7 @@ pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result<(), String> { trace: vec![] })) }); - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } dyon_fn!{fn download__url_file(url: Arc, file: Arc) -> Variable { @@ -1568,7 +1548,7 @@ dyon_fn!{fn save__string_file(text: Arc, file: Arc) -> Variable }} #[cfg(not(feature = "file"))] -pub(crate) fn save__string_file(_: &mut Runtime) -> Result<(), String> { +pub(crate) fn save__string_file(_: &mut Runtime) -> Result, String> { Err(FILE_SUPPORT_DISABLED.into()) } @@ -1601,7 +1581,7 @@ dyon_fn!{fn load_string__file(file: Arc) -> Variable { }} #[cfg(not(feature = "file"))] -pub(crate) fn load_string__file(_: &mut Runtime) -> Result<(), String> { +pub(crate) fn load_string__file(_: &mut Runtime) -> Result, String> { Err(FILE_SUPPORT_DISABLED.into()) } @@ -1619,7 +1599,7 @@ dyon_fn!{fn load_string__url(url: Arc) -> Variable { }) }} -pub(crate) fn join__thread(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn join__thread(rt: &mut Runtime) -> Result, String> { let thread = rt.stack.pop().expect(TINVOTS); let handle_res = Thread::invalidate_handle(rt, thread); let v = Variable::Result({ @@ -1648,8 +1628,7 @@ pub(crate) fn join__thread(rt: &mut Runtime) -> Result<(), String> { } } }); - rt.push(v); - Ok(()) + Ok(Some(v)) } dyon_fn!{fn load_data__file(file: Arc) -> Variable { @@ -1682,7 +1661,7 @@ dyon_fn!{fn load_data__string(text: Arc) -> Variable { Variable::Result(res) }} -pub(crate) fn args_os(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn args_os(rt: &mut Runtime) -> Result, String> { let mut arr: Vec = vec![]; for arg in ::std::env::args_os() { if let Ok(t) = arg.into_string() { @@ -1691,12 +1670,11 @@ pub(crate) fn args_os(rt: &mut Runtime) -> Result<(), String> { return Err("Invalid unicode in os argument".into()); } } - rt.stack.push(Variable::Array(Arc::new(arr))); - Ok(()) + Ok(Some(Variable::Array(Arc::new(arr)))) } #[cfg(feature = "file")] -pub(crate) fn save__data_file(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn save__data_file(rt: &mut Runtime) -> Result, String> { use std::error::Error; use std::fs::File; use std::io::BufWriter; @@ -1730,16 +1708,15 @@ pub(crate) fn save__data_file(rt: &mut Runtime) -> Result<(), String> { })) } }; - rt.stack.push(Variable::Result(res)); - Ok(()) + Ok(Some(Variable::Result(res))) } #[cfg(not(feature = "file"))] -pub(crate) fn save__data_file(_: &mut Runtime) -> Result<(), String> { +pub(crate) fn save__data_file(_: &mut Runtime) -> Result, String> { Err(FILE_SUPPORT_DISABLED.into()) } -pub(crate) fn json_from_meta_data(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn json_from_meta_data(rt: &mut Runtime) -> Result, String> { use std::error::Error; let meta_data = rt.stack.pop().expect(TINVOTS); @@ -1753,11 +1730,10 @@ pub(crate) fn json_from_meta_data(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "array")) }; - rt.stack.push(Variable::Str(Arc::new(json))); - Ok(()) + Ok(Some(Variable::Str(Arc::new(json)))) } -pub(crate) fn errstr__string_start_len_msg(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn errstr__string_start_len_msg(rt: &mut Runtime) -> Result, String> { use piston_meta::ParseErrorHandler; let msg = rt.stack.pop().expect(TINVOTS); @@ -1785,11 +1761,10 @@ pub(crate) fn errstr__string_start_len_msg(rt: &mut Runtime) -> Result<(), Strin ParseErrorHandler::new(&source) .write_msg(&mut buf, Range::new(start, len), &msg) .unwrap(); - rt.stack.push(Variable::Str(Arc::new(String::from_utf8(buf).unwrap()))); - Ok(()) + Ok(Some(Variable::Str(Arc::new(String::from_utf8(buf).unwrap())))) } -pub(crate) fn has(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn has(rt: &mut Runtime) -> Result, String> { let key = rt.stack.pop().expect(TINVOTS); let key = match rt.resolve(&key) { &Variable::Str(ref t) => t.clone(), @@ -1800,11 +1775,10 @@ pub(crate) fn has(rt: &mut Runtime) -> Result<(), String> { &Variable::Object(ref obj) => obj.contains_key(&key), x => return Err(rt.expected_arg(0, x, "object")) }; - rt.stack.push(Variable::bool(res)); - Ok(()) + Ok(Some(Variable::bool(res))) } -pub(crate) fn keys(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn keys(rt: &mut Runtime) -> Result, String> { let obj = rt.stack.pop().expect(TINVOTS); let res = Variable::Array(Arc::new(match rt.resolve(&obj) { &Variable::Object(ref obj) => { @@ -1812,11 +1786,10 @@ pub(crate) fn keys(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "object")) })); - rt.stack.push(res); - Ok(()) + Ok(Some(res)) } -pub(crate) fn chars(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn chars(rt: &mut Runtime) -> Result, String> { let t = rt.stack.pop().expect(TINVOTS); let t = match rt.resolve(&t) { &Variable::Str(ref t) => t.clone(), @@ -1829,8 +1802,7 @@ pub(crate) fn chars(rt: &mut Runtime) -> Result<(), String> { Variable::Str(Arc::new(s)) }) .collect::>(); - rt.stack.push(Variable::Array(Arc::new(res))); - Ok(()) + Ok(Some(Variable::Array(Arc::new(res)))) } dyon_fn!{fn now() -> f64 { @@ -1849,7 +1821,7 @@ dyon_fn!{fn now() -> f64 { dyon_fn!{fn is_nan(v: f64) -> bool {v.is_nan()}} -pub(crate) fn wait_next(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn wait_next(rt: &mut Runtime) -> Result, String> { use std::error::Error; let v = rt.stack.pop().expect(TINVOTS); @@ -1866,11 +1838,10 @@ pub(crate) fn wait_next(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "in")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } -pub(crate) fn next(rt: &mut Runtime) -> Result<(), String> { +pub(crate) fn next(rt: &mut Runtime) -> Result, String> { use std::error::Error; let v = rt.stack.pop().expect(TINVOTS); @@ -1887,6 +1858,5 @@ pub(crate) fn next(rt: &mut Runtime) -> Result<(), String> { } x => return Err(rt.expected_arg(0, x, "in")) }; - rt.stack.push(v); - Ok(()) + Ok(Some(v)) } diff --git a/src/grab.rs b/src/grab.rs index f7e2315e..dacb436f 100644 --- a/src/grab.rs +++ b/src/grab.rs @@ -56,20 +56,6 @@ pub fn grab_expr( x => return x, }))), Flow::Continue)) } - E::BinOp(ref binop_expr) => { - Ok((Grabbed::Expression(E::BinOp(Box::new(ast::BinOpExpression { - op: binop_expr.op, - left: match grab_expr(level, rt, &binop_expr.left, side) { - Ok((Grabbed::Expression(x), Flow::Continue)) => x, - x => return x, - }, - right: match grab_expr(level, rt, &binop_expr.right, side) { - Ok((Grabbed::Expression(x), Flow::Continue)) => x, - x => return x, - }, - source_range: binop_expr.source_range, - }))), Flow::Continue)) - } E::ReturnVoid(_) | E::Break(_) | E::Continue(_) | diff --git a/src/lib.dyon b/src/lib.dyon index 459e7134..24e704ac 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -1,5 +1,15 @@ ns std +/// Lazy AND (`&&`). +fn and_also(a: bool => false, b: bool) -> any { ... } + (sec[bool], bool) -> sec[bool] + (bool, bool) -> bool + +/// Lazy OR (`||`). +fn or_else(a: bool => true, b: bool) -> any { ... } + (sec[bool], bool) -> sec[bool] + (bool, bool) -> bool + /// Returns an array of derived information for the truth value of `var`. /// This can be used with the value of `∃`/`any` and `∀`/`all` loops. fn why(var: sec[bool]) -> [any] { ... } @@ -209,7 +219,11 @@ fn unwrap(var: any) -> any { ... } fn unwrap_err(var: any) -> any { ... } /// Unwraps value or using a default. -fn unwrap_or(var: any, def: any) -> any { ... } +/// +/// This function uses a lazy invariant in the first argument. +/// This means that if the lazy invariant matches, +/// the second argument is not evaluated. +fn unwrap_or(var: any => ok(_) | some(_), def: any) -> any { ... } /// Creates `ok(var)` variant of option values. fn ok(var: any) -> res[any] { ... } diff --git a/src/lib.rs b/src/lib.rs index cf352855..6b75a450 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,11 +50,14 @@ pub use ty::Type; pub use link::Link; pub use vec4::Vec4; pub use mat4::Mat4; +pub use ast::Lazy; /// A common error message when there is no value on the stack. pub const TINVOTS: &str = "There is no value on the stack"; lazy_static!{ + pub(crate) static ref AND_ALSO: Arc = Arc::new("and_also".into()); + pub(crate) static ref OR_ELSE: Arc = Arc::new("or_else".into()); pub(crate) static ref ADD: Arc = Arc::new("add".into()); pub(crate) static ref SUB: Arc = Arc::new("sub".into()); pub(crate) static ref MUL: Arc = Arc::new("mul".into()); @@ -69,6 +72,18 @@ lazy_static!{ pub(crate) static ref T: Arc = Arc::new("T".into()); } +/// Type alias for lazy invariants of external functions. +pub type LazyInvariant = &'static [&'static [Lazy]]; + +/// Lazy invariant to unwrap first argument. +pub static LAZY_UNWRAP_OR: LazyInvariant = &[&[Lazy::UnwrapOk, Lazy::UnwrapSome]]; +/// Lazy invariant for `&&`. +pub static LAZY_AND: LazyInvariant = &[&[Lazy::Variable(Variable::Bool(false, None))]]; +/// Lazy invariant for `||`. +pub static LAZY_OR: LazyInvariant = &[&[Lazy::Variable(Variable::Bool(true, None))]]; +/// Lazy invariant that no arguments have lazy invariants. +pub static LAZY_NO: LazyInvariant = &[]; + /// Type alias for Dyon arrays. pub type Array = Arc>; /// Type alias for Dyon objects. @@ -313,11 +328,13 @@ pub enum FnIndex { ExternalVoid(FnExternalRef), /// Extern function with return value. ExternalReturn(FnExternalRef), + /// Extern function with return value and lazy invariant. + ExternalLazy(FnExternalRef, LazyInvariant), } /// Used to store direct reference to external function. #[derive(Copy)] -pub struct FnExternalRef(pub fn(&mut Runtime) -> Result<(), String>); +pub struct FnExternalRef(pub fn(&mut Runtime) -> Result, String>); impl Clone for FnExternalRef { fn clone(&self) -> FnExternalRef { @@ -334,7 +351,7 @@ impl fmt::Debug for FnExternalRef { struct FnExternal { namespace: Arc>>, name: Arc, - f: fn(&mut Runtime) -> Result<(), String>, + f: fn(&mut Runtime) -> Result, String>, p: Dfn, } @@ -378,6 +395,26 @@ impl Module { let mut m = Module::empty(); m.ns("std"); + m.add_str("and_also", and_also, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Bool, Bool], + ret: Any, + ext: vec![ + (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), + (vec![], vec![Bool; 2], Bool) + ], + lazy: LAZY_AND + }); + m.add_str("or_else", or_else, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Bool, Bool], + ret: Any, + ext: vec![ + (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), + (vec![], vec![Bool; 2], Bool) + ], + lazy: LAZY_OR + }); m.add_str("add", add, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], @@ -393,7 +430,8 @@ impl Module { Type::all_ext(vec![Bool, Bool], Bool), Type::all_ext(vec![Str, Str], Str), Type::all_ext(vec![Link, Link], Link), - ] + ], + lazy: LAZY_NO }); m.add_str("sub", sub, Dfn { lts: vec![Lt::Default; 2], @@ -408,7 +446,8 @@ impl Module { Type::all_ext(vec![F64, Mat4], Mat4), Type::all_ext(vec![Mat4, F64], Mat4), Type::all_ext(vec![Bool, Bool], Bool), - ] + ], + lazy: LAZY_NO }); m.add_str("mul", mul, Dfn { lts: vec![Lt::Default; 2], @@ -424,7 +463,8 @@ impl Module { (vec![], vec![Mat4, F64], Mat4), (vec![], vec![Mat4, Vec4], Vec4), Type::all_ext(vec![Bool, Bool], Bool), - ] + ], + lazy: LAZY_NO }); m.add_str("div", div, Dfn { lts: vec![Lt::Default; 2], @@ -435,7 +475,8 @@ impl Module { (vec![], vec![Vec4, Vec4], Vec4), (vec![], vec![Vec4, F64], Vec4), (vec![], vec![F64, Vec4], Vec4), - ] + ], + lazy: LAZY_NO }); m.add_str("rem", rem, Dfn { lts: vec![Lt::Default; 2], @@ -446,7 +487,8 @@ impl Module { (vec![], vec![Vec4, Vec4], Vec4), (vec![], vec![Vec4, F64], Vec4), (vec![], vec![F64, Vec4], Vec4), - ] + ], + lazy: LAZY_NO }); m.add_str("pow", pow, Dfn { lts: vec![Lt::Default; 2], @@ -458,7 +500,8 @@ impl Module { (vec![], vec![Vec4, F64], Vec4), (vec![], vec![F64, Vec4], Vec4), Type::all_ext(vec![Bool, Bool], Bool), - ] + ], + lazy: LAZY_NO }); m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); m.add_str("neg", neg, Dfn{ @@ -467,7 +510,8 @@ impl Module { (vec![], vec![F64], F64), (vec![], vec![Vec4], Vec4), (vec![], vec![Mat4], Mat4), - ] + ], + lazy: LAZY_NO }); m.add_str("dot", dot, Dfn { lts: vec![Lt::Default; 2], @@ -477,7 +521,8 @@ impl Module { (vec![], vec![Vec4, Vec4], F64), (vec![], vec![Vec4, F64], F64), (vec![], vec![F64, Vec4], F64), - ] + ], + lazy: LAZY_NO }); m.add_str("cross", cross, Dfn::nl(vec![Vec4, Vec4], Vec4)); m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); @@ -603,12 +648,14 @@ impl Module { tys: vec![Type::array(), Any], ret: Void, ext: vec![], + lazy: LAZY_NO }); m.add_str("insert_ref(mut,_,_)", insert_ref, Dfn { lts: vec![Lt::Default, Lt::Default, Lt::Arg(0)], tys: vec![Type::array(), F64, Any], ret: Void, ext: vec![], + lazy: LAZY_NO }); m.add_str("push(mut,_)", push, Dfn::nl(vec![Type::array(), Any], Void)); m.add_str("insert(mut,_,_)", insert, Dfn { @@ -616,23 +663,32 @@ impl Module { tys: vec![Type::array(), F64, Any], ret: Void, ext: vec![], + lazy: LAZY_NO }); m.add_str("pop(mut)", pop, Dfn { lts: vec![Lt::Return], tys: vec![Type::array()], ret: Any, ext: vec![], + lazy: LAZY_NO }); m.add_str("remove(mut,_)", remove, Dfn { lts: vec![Lt::Return, Lt::Default], tys: vec![Type::array(), F64], ret: Any, ext: vec![], + lazy: LAZY_NO }); m.add_str("reverse(mut)", reverse, Dfn::nl(vec![Type::array()], Void)); m.add_str("clear(mut)", clear, Dfn::nl(vec![Type::array()], Void)); m.add_str("swap(mut,_,_)", swap, Dfn::nl(vec![Type::array(), F64, F64], Void)); - m.add_str("unwrap_or", unwrap_or, Dfn::nl(vec![Any; 2], Any)); + m.add_str("unwrap_or", unwrap_or, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![], + lazy: LAZY_UNWRAP_OR + }); m.add_str("unwrap_err", unwrap_err, Dfn::nl(vec![Any], Any)); m.add_str("meta__syntax_in_string", meta__syntax_in_string, Dfn::nl(vec![Any, Str, Str], @@ -679,7 +735,11 @@ impl Module { for f in self.ext_prelude.iter().rev() { if &f.name == name { return if f.p.returns() { - FnIndex::ExternalReturn(FnExternalRef(f.f)) + if f.p.lazy == LAZY_NO { + FnIndex::ExternalReturn(FnExternalRef(f.f)) + } else { + FnIndex::ExternalLazy(FnExternalRef(f.f), f.p.lazy) + } } else { FnIndex::ExternalVoid(FnExternalRef(f.f)) }; @@ -716,7 +776,7 @@ impl Module { pub fn add( &mut self, name: Arc, - f: fn(&mut Runtime) -> Result<(), String>, + f: fn(&mut Runtime) -> Result, String>, prelude_function: Dfn ) { self.ext_prelude.push(FnExternal { @@ -731,7 +791,7 @@ impl Module { pub fn add_str( &mut self, name: &str, - f: fn(&mut Runtime) -> Result<(), String>, + f: fn(&mut Runtime) -> Result, String>, prelude_function: Dfn ) { self.ext_prelude.push(FnExternal { diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index ff221142..308be1c6 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -102,18 +102,21 @@ pub fn check( } } else if nodes[i].binops.len() == 1 && nodes[i].children.len() == 2 { use ast::BinOp::*; - - match nodes[i].binops[0] { - Add => Node::rewrite_binop(i, crate::ADD.clone(), &mut nodes), - Sub => Node::rewrite_binop(i, crate::SUB.clone(), &mut nodes), - Mul => Node::rewrite_binop(i, crate::MUL.clone(), &mut nodes), - Div => Node::rewrite_binop(i, crate::DIV.clone(), &mut nodes), - Rem => Node::rewrite_binop(i, crate::REM.clone(), &mut nodes), - Pow => Node::rewrite_binop(i, crate::POW.clone(), &mut nodes), - Dot => Node::rewrite_binop(i, crate::DOT.clone(), &mut nodes), - Cross => Node::rewrite_binop(i, crate::CROSS.clone(), &mut nodes), - _ => {} - } + + Node::rewrite_binop(i, match nodes[i].binops[0] { + Add => crate::ADD.clone(), + Sub => crate::SUB.clone(), + Mul => crate::MUL.clone(), + Div => crate::DIV.clone(), + Rem => crate::REM.clone(), + Pow => crate::POW.clone(), + Dot => crate::DOT.clone(), + Cross => crate::CROSS.clone(), + AndAlso => crate::AND_ALSO.clone(), + OrElse => crate::OR_ELSE.clone(), + }, &mut nodes); + } else if nodes[i].kind == Kind::Pow && nodes[i].children.len() == 2 { + Node::rewrite_binop(i, crate::POW.clone(), &mut nodes); } } diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 51faabf5..33ed590f 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -178,11 +178,14 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> let f = &prelude.list[f]; if let Some(ref ty) = expr_type { if !f.tys[j].goes_with(ty) { - return Err(nodes[i].source.wrap( - format!("Type mismatch (#150):\n\ - Expected `{}`, found `{}`", - f.tys[j].description(), ty.description()) - )) + if !delay_errs.contains_key(&i) { + delay_errs.insert(i, nodes[i].source.wrap( + format!("Type mismatch (#150):\n\ + Expected `{}`, found `{}`", + f.tys[j].description(), ty.description()) + )); + } + continue 'node; } } } @@ -191,11 +194,14 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> let f = &prelude.list[f]; if let Some(ref ty) = expr_type { if !f.tys[j].goes_with(ty) { - return Err(nodes[i].source.wrap( - format!("Type mismatch (#200):\n\ - Expected `{}`, found `{}`", - f.tys[j].description(), ty.description()) - )) + if !delay_errs.contains_key(&i) { + delay_errs.insert(i, nodes[i].source.wrap( + format!("Type mismatch (#200):\n\ + Expected `{}`, found `{}`", + f.tys[j].description(), ty.description()) + )); + } + continue 'node; } } } @@ -350,7 +356,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::Return | Kind::Val | Kind::Expr | Kind::Cond | Kind::Exp | Kind::Base | Kind::Left | Kind::Right | - Kind::ElseIfCond | Kind::Grab + Kind::ElseIfCond | Kind::Grab | Kind::Add | Kind::Mul | Kind::Pow => { // TODO: Report error for expected unary operator. if nodes[i].children.is_empty() { continue 'node; } @@ -396,89 +402,6 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> this_ty = Some(nodes[i].inner_type(&ty)); } } - Kind::Add => { - // Require type to be inferred from all children. - let mut it_ty: Option = None; - for &ch in &nodes[i].children { - if nodes[ch].item_ids() { continue 'node; } - if let Some(ref ty) = nodes[ch].ty { - it_ty = if let Some(ref it) = it_ty { - match it.add(ty) { - None => return Err(nodes[ch].source.wrap( - format!("Type mismatch (#400):\n\ - Binary operator can not be used with `{}` and `{}`", - it.description(), ty.description()))), - x => x - } - } else { - Some(ty.clone()) - } - } else { - continue 'node; - } - } - this_ty = it_ty; - } - Kind::Mul => { - if nodes[i].binops.len() + 1 != nodes[i].children.len() { - return Err(nodes[i].source.wrap( - "Type mismatch (#450):\n\ - Missing binary operator for node when converting meta data".to_string() - )) - } - - // Require type to be inferred from all children. - let mut bin_ind = 0; - let mut it_ty: Option = None; - for &ch in &nodes[i].children { - if nodes[ch].item_ids() { continue 'node; } - if let Some(ref ty) = nodes[ch].ty { - it_ty = if let Some(ref it) = it_ty { - match it.mul(ty, nodes[i].binops[bin_ind]) { - None => return Err(nodes[ch].source.wrap( - format!("Type mismatch (#500):\n\ - Binary operator can not be used with `{}` and `{}`", - it.description(), ty.description()))), - x => { - bin_ind += 1; - x - } - } - } else { - Some(ty.clone()) - } - } else { - continue 'node; - } - } - this_ty = it_ty; - } - Kind::Pow => { - let base = match nodes[i].find_child_by_kind(nodes, Kind::Base) { - None => continue 'node, - Some(x) => x - }; - let exp = match nodes[i].find_child_by_kind(nodes, Kind::Exp) { - None => continue 'node, - Some(x) => x - }; - if nodes[base].item_ids() || nodes[exp].item_ids() { - continue 'node; - } - if let Some(ref base_ty) = nodes[base].ty { - if let Some(ref exp_ty) = nodes[exp].ty { - if let Some(ty) = base_ty.pow(exp_ty) { - this_ty = Some(ty); - } else { - return Err(nodes[i].source.wrap( - format!("Type mismatch (#600):\n\ - Binary operator can not be used \ - with `{}` and `{}`", base_ty.description(), - exp_ty.description()))); - } - } - } - } Kind::Compare => { let left = match nodes[i].find_child_by_kind(nodes, Kind::Left) { None => continue 'node, @@ -582,6 +505,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> tys, ret: ret.unwrap(), ext: vec![], + lazy: crate::LAZY_NO, }))); } } diff --git a/src/macros.rs b/src/macros.rs index a570e239..86dbfaef 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,51 +24,49 @@ macro_rules! dyon_fn_pop { macro_rules! dyon_fn { (fn $name:ident () -> $rt:ty $b:block) => { #[allow(non_snake_case)] - pub fn $name(rt: &mut $crate::Runtime) -> Result<(), String> { + pub fn $name(_rt: &mut $crate::Runtime) -> Result, String> { fn inner() -> $rt { $b } - rt.push(inner()); - Ok(()) + Ok(Some($crate::embed::PushVariable::push_var(&inner()))) } }; (fn $name:ident ($($arg:tt : $t:ty),+) -> $rt:ty $b:block) => { dyon_macro_items!{ #[allow(non_snake_case)] - pub fn $name(rt: &mut $crate::Runtime) -> Result<(), String> { + pub fn $name(rt: &mut $crate::Runtime) -> Result, String> { fn inner($($arg: $t),+) -> $rt { $b } dyon_fn_pop!(rt $($arg: $t),+); - rt.push(inner($($arg),+)); - Ok(()) + Ok(Some($crate::embed::PushVariable::push_var(&inner($($arg),+)))) } } }; (fn $name:ident () $b:block) => { #[allow(non_snake_case)] - pub fn $name(_: &mut $crate::Runtime) -> Result<(), String> { + pub fn $name(_: &mut $crate::Runtime) -> Result, String> { fn inner() { $b } inner(); - Ok(()) + Ok(None) } }; (fn $name:ident ($($arg:tt : $t:ty),+) $b:block) => { dyon_macro_items!{ #[allow(non_snake_case)] - pub fn $name(rt: &mut $crate::Runtime) -> Result<(), String> { + pub fn $name(rt: &mut $crate::Runtime) -> Result, String> { fn inner($($arg: $t),+) { $b } dyon_fn_pop!(rt $($arg: $t),+); inner($($arg),+); - Ok(()) + Ok(None) } } }; diff --git a/src/prelude.rs b/src/prelude.rs index 2c2bc289..63db4f94 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use ast; use Module; use Type; +use Lazy; /// Argument lifetime constraint. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -36,6 +37,8 @@ pub struct Dfn { /// /// Stores type variables, argument types, return type. pub ext: Vec<(Vec>, Vec, Type)>, + /// Stores lazy invariants. + pub lazy: &'static [&'static [Lazy]], } impl Dfn { @@ -46,6 +49,7 @@ impl Dfn { tys: args, ret, ext: vec![], + lazy: crate::LAZY_NO, } } @@ -76,6 +80,7 @@ impl Dfn { tys, ret: f.ret.clone(), ext: vec![], + lazy: crate::LAZY_NO, } } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 0a848f1a..158688b5 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -98,6 +98,8 @@ pub struct Runtime { pub arg_err_index: Cell>, } +unsafe impl Send for Runtime {} + impl Default for Runtime { fn default() -> Runtime {Runtime::new()} } @@ -511,7 +513,6 @@ impl Runtime { self.call_internal(call, loader) } Item(ref item) => self.item(item, side), - BinOp(ref binop) => self.binop(binop, side), Assign(ref assign) => self.assign(assign.op, &assign.left, &assign.right), Vec4(ref vec4) => self.vec4(vec4, side), Mat4(ref mat4) => self.mat4(mat4, side), @@ -1053,7 +1054,7 @@ impl Runtime { Expression did not return a value.") }; } - (f)(self).map_err(|err| { + if let Some(x) = (f)(self).map_err(|err| { let range = if let Some(ind) = self.arg_err_index.get() { self.arg_err_index.set(None); call.args[ind].source_range() @@ -1061,8 +1062,64 @@ impl Runtime { call.source_range }; self.module.error(range, &err, self) - })?; - Ok((Some(self.stack.pop().expect(TINVOTS)), Flow::Continue)) + })? { + Ok((Some(x), Flow::Continue)) + } else { + Ok((Some(self.stack.pop().expect(TINVOTS)), Flow::Continue)) + } + } + FnIndex::ExternalLazy(FnExternalRef(f), lazy) => { + for (i, arg) in call.args.iter().enumerate() { + match self.expression(arg, Side::Right)? { + (Some(x), Flow::Continue) => { + use ast::Lazy; + // Return immediately if equal to lazy invariant. + if let Some(&ls) = lazy.get(i) { + for lazy in ls { + match *lazy { + Lazy::Variable(ref val) => { + if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} + } + Lazy::UnwrapOk => { + if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } + } + Lazy::UnwrapErr => { + if let &Variable::Result(Err(ref x)) = self.resolve(&x) { + return Ok((Some(x.message.clone()), Flow::Continue)) + } + } + Lazy::UnwrapSome => { + if let &Variable::Option(Some(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } + } + } + } + } + + self.stack.push(x) + } + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(arg.source_range(), + "Expected something. \ + Expression did not return a value.") + }; + } + if let Some(x) = (f)(self).map_err(|err| { + let range = if let Some(ind) = self.arg_err_index.get() { + self.arg_err_index.set(None); + call.args[ind].source_range() + } else { + call.source_range + }; + self.module.error(range, &err, self) + })? { + Ok((Some(x), Flow::Continue)) + } else { + Ok((Some(self.stack.pop().expect(TINVOTS)), Flow::Continue)) + } } FnIndex::Loaded(f_index) => { use std::sync::atomic::Ordering; @@ -1091,9 +1148,36 @@ impl Runtime { let lc = self.local_stack.len(); let cu = self.current_stack.len(); - for arg in &call.args { + for (i, arg) in call.args.iter().enumerate() { match self.expression(arg, Side::Right)? { - (Some(x), Flow::Continue) => self.stack.push(x), + (Some(x), Flow::Continue) => { + use ast::Lazy; + // Return immediately if equal to lazy invariant. + for lazy in &f.args[i].lazy { + match *lazy { + Lazy::Variable(ref val) => { + if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} + } + Lazy::UnwrapOk => { + if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } + } + Lazy::UnwrapErr => { + if let &Variable::Result(Err(ref x)) = self.resolve(&x) { + return Ok((Some(x.message.clone()), Flow::Continue)) + } + } + Lazy::UnwrapSome => { + if let &Variable::Option(Some(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } + } + } + } + + self.stack.push(x) + } (None, Flow::Continue) => {} (x, Flow::Return) => { return Ok((x, Flow::Return)); } _ => return self.err(arg.source_range(), @@ -2481,55 +2565,7 @@ impl Runtime { [x[3], y[3], z[3], w[3]], ]))), Flow::Continue)) } - fn binop(&mut self, binop: &ast::BinOpExpression, side: Side) -> FlowResult { - use ast::BinOp::*; - - let left = match self.expression(&binop.left, side)? { - (Some(x), Flow::Continue) => x, - (x, Flow::Return) => return Ok((x, Flow::Return)), - _ => return self.err(binop.source_range, "Expected something from left argument") - }; - // Check lazy boolean expressions. - match binop.op { - OrElse => { - if let Variable::Bool(true, ref sec) = *self.resolve(&left) { - return Ok((Some(Variable::Bool(true, sec.clone())), Flow::Continue)); - } - } - AndAlso => { - if let Variable::Bool(false, ref sec) = *self.resolve(&left) { - return Ok((Some(Variable::Bool(false, sec.clone())), Flow::Continue)); - } - } - _ => {} - } - - let right = match self.expression(&binop.right, side)? { - (Some(x), Flow::Continue) => x, - (x, Flow::Return) => return Ok((x, Flow::Return)), - _ => return self.err(binop.source_range, "Expected something from right argument") - }; - let v = match (self.resolve(&left), self.resolve(&right)) { - (&Variable::Bool(a, ref sec), &Variable::Bool(b, _)) => { - Variable::Bool(match binop.op { - OrElse => a || b, - AndAlso => a && b, - _ => return Err(self.module.error(binop.source_range, - &format!("{}\nUnknown boolean operator `{:?}`", - self.stack_trace(), - binop.op.symbol_bool()), self)) - }, sec.clone()) - } - _ => return Err(self.module.error(binop.source_range, &format!( - "{}\nInvalid type for binary operator `{:?}`, \ - expected numbers, vec4s, bools or strings", - self.stack_trace(), - binop.op.symbol()), self)) - }; - - Ok((Some(v), Flow::Continue)) - } pub(crate) fn stack_trace(&self) -> String {stack_trace(&self.call_stack)} } diff --git a/src/ty.rs b/src/ty.rs index e848debf..0954a301 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -272,6 +272,7 @@ impl Type { (&Option(_), &Any) => true, (&Result(_), &Any) => true, (&Thread(_), &Any) => true, + (&Secret(_), &Any) => true, (&In(_), &Any) => true, _ => false } @@ -626,7 +627,9 @@ impl Type { convert.update(range); let range = convert.end_node("closure_type")?; convert.update(range); - ty = Some(Type::Closure(Box::new(Dfn { lts, tys, ret, ext: vec![] }))); + ty = Some(Type::Closure(Box::new( + Dfn { lts, tys, ret, ext: vec![], lazy: crate::LAZY_NO } + ))); } else { let range = convert.ignore(); convert.update(range); diff --git a/src/write.rs b/src/write.rs index 78a1678b..59801577 100644 --- a/src/write.rs +++ b/src/write.rs @@ -194,7 +194,6 @@ fn write_expr( use ast::Expression as E; match *expr { - E::BinOp(ref binop) => write_binop(w, rt, binop.op, &binop.left, &binop.right, tabs)?, E::Item(ref item) => write_item(w, rt, item, tabs)?, E::Variable(ref range_var) => write_variable(w, rt, &range_var.1, EscapeString::Json, tabs)?, @@ -209,22 +208,8 @@ fn write_expr( write_not(w, rt, &call.args[0], tabs)? } else if &**call.name == "neg" && call.args.len() == 1 { write_neg(w, rt, &call.args[0], tabs)? - } else if &**call.name == "dot" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Dot, &call.args[0], &call.args[1], tabs)? - } else if &**call.name == "cross" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Cross, &call.args[0], &call.args[1], tabs)? - } else if &**call.name == "add" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Add, &call.args[0], &call.args[1], tabs)? - } else if &**call.name == "sub" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Sub, &call.args[0], &call.args[1], tabs)? - } else if &**call.name == "mul" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Mul, &call.args[0], &call.args[1], tabs)? - } else if &**call.name == "div" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Div, &call.args[0], &call.args[1], tabs)? - } else if &**call.name == "rem" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Rem, &call.args[0], &call.args[1], tabs)? - } else if &**call.name == "pow" && call.args.len() == 2 { - write_binop(w, rt, ast::BinOp::Pow, &call.args[0], &call.args[1], tabs)? + } else if let Some(op) = standard_binop(call) { + write_binop(w, rt, op, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } @@ -402,14 +387,16 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo match *expr { E::Compare(_) => true, - E::BinOp(ref binop) => { - match (op.precedence(), binop.op.precedence()) { - (3, _) => true, - (2, 1) => true, - (2, 2) if right => true, - (1, 1) if right => true, - _ => false - } + E::Call(ref call) => { + if let Some(binop_op) = standard_binop(call) { + match (op.precedence(), binop_op.precedence()) { + (3, _) => true, + (2, 1) => true, + (2, 2) if right => true, + (1, 1) if right => true, + _ => false + } + } else {false} } _ => false } @@ -726,11 +713,31 @@ fn write_for( Ok(()) } +fn standard_binop(call: &ast::Call) -> Option { + use ast::BinOp::*; + + if call.args.len() != 2 {return None}; + + Some(match &**call.name { + "dot" => Dot, + "cross" => Cross, + "add" => Add, + "sub" => Sub, + "mul" => Mul, + "div" => Div, + "rem" => Rem, + "pow" => Pow, + "and_also" => AndAlso, + "or_else" => OrElse, + _ => return None + }) +} + fn compare_needs_parent(expr: &ast::Expression) -> bool { use ast::Expression as E; match *expr { - E::BinOp(_) => true, + E::Call(ref call) => standard_binop(call).is_some(), _ => false } } diff --git a/tests/lib.rs b/tests/lib.rs index dfe2adcc..2390bdea 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -136,6 +136,14 @@ fn test_syntax() { test_src("source/syntax/continue_call.dyon"); test_src("source/syntax/mat4_1.dyon"); test_fail_src("source/syntax/secret_fail.dyon"); + test_src("source/syntax/lazy_pass_1.dyon"); + test_src("source/syntax/lazy_pass_2.dyon"); + test_src("source/syntax/lazy_pass_3.dyon"); + test_src("source/syntax/lazy_pass_4.dyon"); + test_src("source/syntax/lazy_pass_5.dyon"); + test_src("source/syntax/lazy_pass_6.dyon"); + test_src("source/syntax/lazy_pass_7.dyon"); + test_src("source/syntax/lazy_pass_8.dyon"); } #[test] From a1d07deea7800e2a7f0c0d75051bc62728f9e3c2 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sun, 22 Sep 2019 11:36:31 +0200 Subject: [PATCH 55/83] Removed `Type::add`, `Type::mul` and `Type::pow` - Fixed two warnings --- src/dyon_std/mod.rs | 4 +- src/ty.rs | 105 -------------------------------------------- 2 files changed, 2 insertions(+), 107 deletions(-) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 1577fb11..6e4fc77d 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -833,7 +833,7 @@ pub(crate) fn swap(rt: &mut Runtime) -> Result, String> { Ok(None) } -pub(crate) fn read_line(rt: &mut Runtime) -> Result, String> { +pub(crate) fn read_line(_rt: &mut Runtime) -> Result, String> { use std::io::{self, Write}; use std::error::Error; @@ -1661,7 +1661,7 @@ dyon_fn!{fn load_data__string(text: Arc) -> Variable { Variable::Result(res) }} -pub(crate) fn args_os(rt: &mut Runtime) -> Result, String> { +pub(crate) fn args_os(_rt: &mut Runtime) -> Result, String> { let mut arr: Vec = vec![]; for arg in ::std::env::args_os() { if let Ok(t) = arg.into_string() { diff --git a/src/ty.rs b/src/ty.rs index 0954a301..2f491350 100644 --- a/src/ty.rs +++ b/src/ty.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use piston_meta::bootstrap::Convert; use range::Range; use Dfn; -use ast::BinOp; /// Stores a Dyon type. #[derive(Debug, Clone, PartialEq)] @@ -404,46 +403,6 @@ impl Type { } } - /// Infers type from the `+` operator. - pub fn add(&self, other: &Type) -> Option { - use self::Type::*; - - match (self, other) { - (&AdHoc(ref name, ref ty), &AdHoc(ref other_name, ref other_ty)) => { - if name != other_name { return None; } - if !ty.goes_with(other_ty) { return None; } - if let Some(new_ty) = ty.add(other_ty) { - Some(AdHoc(name.clone(), Box::new(new_ty))) - } else { - None - } - } - (&Void, _) | (_, &Void) => None, - (&Array(_), _) | (_, &Array(_)) => None, - (&Bool, &Bool) => Some(Bool), - (&Secret(ref a), &Secret(ref b)) - if **a == Type::Bool && **b == Type::Bool => - Some(Secret(Box::new(Bool))), - (&Secret(ref a), &Bool) if **a == Type::Bool => Some(Secret(Box::new(Bool))), - (&Bool, &Secret(ref b)) if **b == Type::Bool => Some(Bool), - (&F64, &F64) => Some(F64), - (&Mat4, &Mat4) => Some(Mat4), - (&F64, &Mat4) | (&Mat4, &F64) => Some(Mat4), - (&Secret(ref a), &Secret(ref b)) - if **a == Type::F64 && **b == Type::F64 => - Some(Secret(Box::new(F64))), - (&Secret(ref a), &F64) if **a == Type::F64 => Some(Secret(Box::new(F64))), - (&F64, &Secret(ref b)) if **b == Type::F64 => Some(F64), - (&Str, &Str) => Some(Str), - (&Vec4, &F64) => Some(Vec4), - (&F64, &Vec4) => Some(Vec4), - (&Vec4, &Vec4) => Some(Vec4), - (&Any, x) if x != &Type::Void => Some(Any), - (x, &Any) if x != &Type::Void => Some(Any), - _ => None - } - } - /// Infers type from the `+=` operator. pub fn add_assign(&self, other: &Type) -> bool { use self::Type::*; @@ -460,70 +419,6 @@ impl Type { } } - /// Infers type from the `*` binary operator. - pub fn mul(&self, other: &Type, binop: BinOp) -> Option { - use self::Type::*; - - match (self, other) { - (&Void, _) | (_, &Void) => None, - (&Array(_), _) | (_, &Array(_)) => None, - (&Bool, &Bool) => Some(Bool), - (&Secret(ref a), &Secret(ref b)) - if **a == Type::Bool && **b == Type::Bool => - Some(Secret(Box::new(Bool))), - (&Secret(ref a), &Bool) if **a == Type::Bool => Some(Secret(Box::new(Bool))), - (&Bool, &Secret(ref b)) if **b == Type::Bool => Some(Bool), - (&F64, &F64) => Some(F64), - (&Mat4, &Mat4) => Some(Mat4), - (&F64, &Mat4) | (&Mat4, &F64) => Some(Mat4), - (&Mat4, &Vec4) => Some(Vec4), - (&Secret(ref a), &Secret(ref b)) - if **a == Type::F64 && **b == Type::F64 => - Some(Secret(Box::new(F64))), - (&Secret(ref a), &F64) if **a == Type::F64 => Some(Secret(Box::new(F64))), - (&F64, &Secret(ref b)) if **b == Type::F64 => Some(F64), - (&Vec4, &F64) => Some(Vec4), - (&F64, &Vec4) => Some(Vec4), - (&Vec4, &Vec4) => { - if let BinOp::Dot = binop { - Some(F64) - } else { - Some(Vec4) - } - } - (&Any, x) if x != &Type::Void => Some(Any), - (x, &Any) if x != &Type::Void => Some(Any), - _ => None - } - } - - /// Infers type from the `^` binary operator. - pub fn pow(&self, other: &Type) -> Option { - use self::Type::*; - - match (self, other) { - (&Void, _) | (_, &Void) => None, - (&Array(_), _) | (_, &Array(_)) => None, - (&Bool, &Bool) => Some(Bool), - (&Secret(ref a), &Secret(ref b)) - if **a == Type::Bool && **b == Type::Bool => - Some(Secret(Box::new(Bool))), - (&Secret(ref a), &Bool) if **a == Type::Bool => Some(Secret(Box::new(Bool))), - (&Bool, &Secret(ref b)) if **b == Type::Bool => Some(Bool), - (&F64, &F64) => Some(F64), - (&Secret(ref a), &Secret(ref b)) - if **a == Type::F64 && **b == Type::F64 => - Some(Secret(Box::new(F64))), - (&Secret(ref a), &F64) if **a == Type::F64 => Some(Secret(Box::new(F64))), - (&F64, &Secret(ref b)) if **b == Type::F64 => Some(F64), - (&Vec4, &F64) | (&F64, &Vec4) => Some(Vec4), - (&Vec4, &Vec4) => Some(Vec4), - (&Any, x) if x != &Type::Void => Some(Any), - (x, &Any) if x != &Type::Void => Some(Any), - _ => None - } - } - /// Converts meta data into a type. pub fn from_meta_data(node: &str, mut convert: Convert, ignored: &mut Vec) -> Result<(Range, Type), ()> { From 453eab9ea67847d499ba29dfd5ea41fbeb1f1cfc Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 11:52:53 +0200 Subject: [PATCH 56/83] Reduced code in `BinaryExpression::into_expression` --- src/ast/mod.rs | 103 ++++++++++--------------------------------------- 1 file changed, 21 insertions(+), 82 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 401f643d..e41d9f43 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2927,88 +2927,27 @@ pub struct BinOpExpression { impl BinOpExpression { fn into_expression(self) -> Expression { - match self.op { - BinOp::Add => Expression::Call(Box::new(Call { - alias: None, - name: crate::ADD.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::Sub => Expression::Call(Box::new(Call { - alias: None, - name: crate::SUB.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::Mul => Expression::Call(Box::new(Call { - alias: None, - name: crate::MUL.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::Div => Expression::Call(Box::new(Call { - alias: None, - name: crate::DIV.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::Rem => Expression::Call(Box::new(Call { - alias: None, - name: crate::REM.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::Pow => Expression::Call(Box::new(Call { - alias: None, - name: crate::POW.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::Dot => Expression::Call(Box::new(Call { - alias: None, - name: crate::DOT.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::Cross => Expression::Call(Box::new(Call { - alias: None, - name: crate::CROSS.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::AndAlso => Expression::Call(Box::new(Call { - alias: None, - name: crate::AND_ALSO.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - BinOp::OrElse => Expression::Call(Box::new(Call { - alias: None, - name: crate::OR_ELSE.clone(), - args: vec![self.left, self.right], - custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, - })), - } + use self::BinOp::*; + + Expression::Call(Box::new(Call { + alias: None, + name: match self.op { + Add => crate::ADD.clone(), + Sub => crate::SUB.clone(), + Mul => crate::MUL.clone(), + Div => crate::DIV.clone(), + Rem => crate::REM.clone(), + Pow => crate::POW.clone(), + Dot => crate::DOT.clone(), + Cross => crate::CROSS.clone(), + AndAlso => crate::AND_ALSO.clone(), + OrElse => crate::OR_ELSE.clone(), + }, + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })) } } From 4f073a6914436dbf58a4680d820afdde2b3c6a00 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 11:53:21 +0200 Subject: [PATCH 57/83] Moved lazy invariants from `Arg` to `Function` --- src/ast/mod.rs | 20 +++++++++++++------- src/runtime/mod.rs | 34 ++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index e41d9f43..c7418ada 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -345,6 +345,8 @@ pub struct Function { pub source: Arc, /// Function arguments. pub args: Vec, + /// Lazy invariants. + pub lazy_inv: Vec>, /// Current object references. pub currents: Vec, /// Function block. @@ -385,6 +387,7 @@ impl Function { let mut block: Option = None; let mut expr: Option = None; let mut ret: Option = None; + let mut lazy_inv: Vec> = vec![]; loop { if let Ok(range) = convert.end_node(node) { convert.update(range); @@ -392,10 +395,11 @@ impl Function { } else if let Ok((range, val)) = convert.meta_string("name") { convert.update(range); name = Some(val); - } else if let Ok((range, val)) = Arg::from_meta_data( + } else if let Ok((range, val, lazy)) = Arg::from_meta_data( file, source, convert, ignored) { convert.update(range); args.push(val); + lazy_inv.push(lazy); } else if let Ok((range, val)) = Current::from_meta_data( convert, ignored) { convert.update(range); @@ -453,6 +457,10 @@ impl Function { name = Arc::new(name_plus_args); } let ret = ret.ok_or(())?; + // Remove empty lazy invariants. + while let Some(true) = lazy_inv.last().map(|lz| lz.len() == 0) { + lazy_inv.pop(); + } Ok((convert.subtract(start), Function { namespace: namespace.clone(), resolved: Arc::new(AtomicBool::new(false)), @@ -460,6 +468,7 @@ impl Function { file: file.clone(), source: source.clone(), args, + lazy_inv, currents, block, ret, @@ -533,7 +542,7 @@ impl Closure { if let Ok(range) = convert.end_node(node) { convert.update(range); break; - } else if let Ok((range, val)) = Arg::from_meta_data( + } else if let Ok((range, val, _)) = Arg::from_meta_data( file, source, convert, ignored) { convert.update(range); args.push(val); @@ -773,8 +782,6 @@ pub struct Arg { pub lifetime: Option>, /// The type of the argument. pub ty: Type, - /// Lazy invariant. - pub lazy: Vec, /// The range in source. pub source_range: Range, /// Whether the argument is mutable. @@ -788,7 +795,7 @@ impl Arg { source: &Arc, mut convert: Convert, ignored: &mut Vec - ) -> Result<(Range, Arg), ()> { + ) -> Result<(Range, Arg, Vec), ()> { let start = convert; let node = "arg"; let start_range = convert.start_node(node)?; @@ -846,10 +853,9 @@ impl Arg { name, lifetime, ty, - lazy, source_range: convert.source(start).unwrap(), mutable, - })) + }, lazy)) } } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 158688b5..41e43d13 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1153,24 +1153,26 @@ impl Runtime { (Some(x), Flow::Continue) => { use ast::Lazy; // Return immediately if equal to lazy invariant. - for lazy in &f.args[i].lazy { - match *lazy { - Lazy::Variable(ref val) => { - if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} - } - Lazy::UnwrapOk => { - if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { - return Ok((Some((**x).clone()), Flow::Continue)) + if let Some(lz) = f.lazy_inv.get(i) { + for lazy in lz { + match *lazy { + Lazy::Variable(ref val) => { + if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} } - } - Lazy::UnwrapErr => { - if let &Variable::Result(Err(ref x)) = self.resolve(&x) { - return Ok((Some(x.message.clone()), Flow::Continue)) + Lazy::UnwrapOk => { + if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } } - } - Lazy::UnwrapSome => { - if let &Variable::Option(Some(ref x)) = self.resolve(&x) { - return Ok((Some((**x).clone()), Flow::Continue)) + Lazy::UnwrapErr => { + if let &Variable::Result(Err(ref x)) = self.resolve(&x) { + return Ok((Some(x.message.clone()), Flow::Continue)) + } + } + Lazy::UnwrapSome => { + if let &Variable::Option(Some(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } } } } From 63d4f76d370c49fb268f5dd33e916458a35567a1 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 11:59:20 +0200 Subject: [PATCH 58/83] Moved `Module` to its own module --- src/lib.rs | 439 +------------------------------------------------- src/module.rs | 438 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 440 insertions(+), 437 deletions(-) create mode 100644 src/module.rs diff --git a/src/lib.rs b/src/lib.rs index 6b75a450..725b5937 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ pub mod macros; mod vec4; mod mat4; mod write; +mod module; mod grab; mod dyon_std; @@ -51,6 +52,7 @@ pub use link::Link; pub use vec4::Vec4; pub use mat4::Mat4; pub use ast::Lazy; +pub use module::Module; /// A common error message when there is no value on the stack. pub const TINVOTS: &str = "There is no value on the stack"; @@ -366,443 +368,6 @@ impl Clone for FnExternal { } } -/// Stores functions for a Dyon module. -#[derive(Clone)] -pub struct Module { - functions: Vec, - ext_prelude: Vec, - register_namespace: Arc>>, -} - -impl Default for Module { - fn default() -> Module {Module::new()} -} - -impl Module { - /// Creates a new empty module. - pub fn empty() -> Module { - Module { - functions: vec![], - ext_prelude: vec![], - register_namespace: Arc::new(vec![]), - } - } - - /// Creates a new module with standard library. - pub fn new() -> Module { - use Type::*; - use dyon_std::*; - - let mut m = Module::empty(); - m.ns("std"); - m.add_str("and_also", and_also, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Bool, Bool], - ret: Any, - ext: vec![ - (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), - (vec![], vec![Bool; 2], Bool) - ], - lazy: LAZY_AND - }); - m.add_str("or_else", or_else, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Bool, Bool], - ret: Any, - ext: vec![ - (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), - (vec![], vec![Bool; 2], Bool) - ], - lazy: LAZY_OR - }); - m.add_str("add", add, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: Any, - ext: vec![ - Type::all_ext(vec![F64, F64], F64), - Type::all_ext(vec![Vec4, Vec4], Vec4), - Type::all_ext(vec![Vec4, F64], Vec4), - Type::all_ext(vec![F64, Vec4], Vec4), - Type::all_ext(vec![Mat4, Mat4], Mat4), - Type::all_ext(vec![F64, Mat4], Mat4), - Type::all_ext(vec![Mat4, F64], Mat4), - Type::all_ext(vec![Bool, Bool], Bool), - Type::all_ext(vec![Str, Str], Str), - Type::all_ext(vec![Link, Link], Link), - ], - lazy: LAZY_NO - }); - m.add_str("sub", sub, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: Any, - ext: vec![ - Type::all_ext(vec![F64, F64], F64), - Type::all_ext(vec![Vec4, Vec4], Vec4), - Type::all_ext(vec![Vec4, F64], Vec4), - Type::all_ext(vec![F64, Vec4], Vec4), - Type::all_ext(vec![Mat4, Mat4], Mat4), - Type::all_ext(vec![F64, Mat4], Mat4), - Type::all_ext(vec![Mat4, F64], Mat4), - Type::all_ext(vec![Bool, Bool], Bool), - ], - lazy: LAZY_NO - }); - m.add_str("mul", mul, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: Any, - ext: vec![ - (vec![], vec![F64, F64], F64), - (vec![], vec![Vec4, Vec4], Vec4), - (vec![], vec![Vec4, F64], Vec4), - (vec![], vec![F64, Vec4], Vec4), - (vec![], vec![Mat4, Mat4], Mat4), - (vec![], vec![F64, Mat4], Mat4), - (vec![], vec![Mat4, F64], Mat4), - (vec![], vec![Mat4, Vec4], Vec4), - Type::all_ext(vec![Bool, Bool], Bool), - ], - lazy: LAZY_NO - }); - m.add_str("div", div, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: Any, - ext: vec![ - (vec![], vec![F64, F64], F64), - (vec![], vec![Vec4, Vec4], Vec4), - (vec![], vec![Vec4, F64], Vec4), - (vec![], vec![F64, Vec4], Vec4), - ], - lazy: LAZY_NO - }); - m.add_str("rem", rem, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: Any, - ext: vec![ - (vec![], vec![F64, F64], F64), - (vec![], vec![Vec4, Vec4], Vec4), - (vec![], vec![Vec4, F64], Vec4), - (vec![], vec![F64, Vec4], Vec4), - ], - lazy: LAZY_NO - }); - m.add_str("pow", pow, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: Any, - ext: vec![ - (vec![], vec![F64, F64], F64), - (vec![], vec![Vec4, Vec4], Vec4), - (vec![], vec![Vec4, F64], Vec4), - (vec![], vec![F64, Vec4], Vec4), - Type::all_ext(vec![Bool, Bool], Bool), - ], - lazy: LAZY_NO - }); - m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); - m.add_str("neg", neg, Dfn{ - lts: vec![Lt::Default], tys: vec![Any], ret: Any, - ext: vec![ - (vec![], vec![F64], F64), - (vec![], vec![Vec4], Vec4), - (vec![], vec![Mat4], Mat4), - ], - lazy: LAZY_NO - }); - m.add_str("dot", dot, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: F64, - ext: vec![ - (vec![], vec![Vec4, Vec4], F64), - (vec![], vec![Vec4, F64], F64), - (vec![], vec![F64, Vec4], F64), - ], - lazy: LAZY_NO - }); - m.add_str("cross", cross, Dfn::nl(vec![Vec4, Vec4], Vec4)); - m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); - m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); - m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); - m.add_str("w", w, Dfn::nl(vec![Vec4], F64)); - m.add_str("norm", norm, Dfn::nl(vec![Vec4], F64)); - m.add_str("det", det, Dfn::nl(vec![Mat4], F64)); - m.add_str("inv", inv, Dfn::nl(vec![Mat4], Mat4)); - m.add_str("mov", mov, Dfn::nl(vec![Vec4], Mat4)); - m.add_str("rot__axis_angle", rot__axis_angle, Dfn::nl(vec![Vec4, F64], Mat4)); - m.add_str("ortho__pos_right_up_forward", ortho__pos_right_up_forward, - Dfn::nl(vec![Vec4; 4], Mat4)); - m.add_str("proj__fov_near_far_ar", proj__fov_near_far_ar, Dfn::nl(vec![F64; 4], Mat4)); - m.add_str("mvp__model_view_projection", mvp__model_view_projection, - Dfn::nl(vec![Mat4; 3], Mat4)); - m.add_str("scale", scale, Dfn::nl(vec![Vec4], Mat4)); - m.add_str("rx", rx, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("ry", ry, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("rz", rz, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("rw", rw, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("cx", cx, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("cy", cy, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("cz", cz, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("cw", cw, Dfn::nl(vec![Mat4], Vec4)); - m.add_str("cv", cv, Dfn::nl(vec![Mat4, F64], Vec4)); - m.add_str("clone", clone, Dfn::nl(vec![Any], Any)); - m.add_str("rv", rv, Dfn::nl(vec![Mat4, Type::F64], Vec4)); - m.add_str("s", s, Dfn::nl(vec![Vec4, F64], F64)); - m.add_str("println", println, Dfn::nl(vec![Any], Void)); - m.add_str("print", print, Dfn::nl(vec![Any], Void)); - m.add_str("sqrt", sqrt, Dfn::nl(vec![F64], F64)); - m.add_str("sin", sin, Dfn::nl(vec![F64], F64)); - m.add_str("asin", asin, Dfn::nl(vec![F64], F64)); - m.add_str("cos", cos, Dfn::nl(vec![F64], F64)); - m.add_str("acos", acos, Dfn::nl(vec![F64], F64)); - m.add_str("tan", tan, Dfn::nl(vec![F64], F64)); - m.add_str("atan", atan, Dfn::nl(vec![F64], F64)); - m.add_str("atan2", atan2, Dfn::nl(vec![F64; 2], F64)); - m.add_str("exp", exp, Dfn::nl(vec![F64], F64)); - m.add_str("ln", ln, Dfn::nl(vec![F64], F64)); - m.add_str("log2", log2, Dfn::nl(vec![F64], F64)); - m.add_str("log10", log10, Dfn::nl(vec![F64], F64)); - m.add_str("round", round, Dfn::nl(vec![F64], F64)); - m.add_str("abs", abs, Dfn::nl(vec![F64], F64)); - m.add_str("floor", floor, Dfn::nl(vec![F64], F64)); - m.add_str("ceil", ceil, Dfn::nl(vec![F64], F64)); - m.add_str("sleep", sleep, Dfn::nl(vec![F64], Void)); - m.add_str("random", random, Dfn::nl(vec![], F64)); - m.add_str("tau", tau, Dfn::nl(vec![], F64)); - m.add_str("read_line", read_line, Dfn::nl(vec![], Str)); - m.add_str("read_number", read_number, Dfn::nl(vec![Str], F64)); - m.add_str("parse_number", parse_number, Dfn::nl(vec![Str], Option(Box::new(Type::F64)))); - m.add_str("trim", trim, Dfn::nl(vec![Str], Str)); - m.add_str("trim_left", trim_left, Dfn::nl(vec![Str], Str)); - m.add_str("trim_right", trim_right, Dfn::nl(vec![Str], Str)); - m.add_str("str", _str, Dfn::nl(vec![Any], Str)); - m.add_str("json_string", json_string, Dfn::nl(vec![Str], Str)); - m.add_str("str__color", str__color, Dfn::nl(vec![Vec4], Str)); - m.add_str("srgb_to_linear__color", srgb_to_linear__color, Dfn::nl(vec![Vec4], Vec4)); - m.add_str("linear_to_srgb__color", linear_to_srgb__color, Dfn::nl(vec![Vec4], Vec4)); - m.add_str("typeof", _typeof, Dfn::nl(vec![Any], Str)); - m.add_str("debug", debug, Dfn::nl(vec![], Void)); - m.add_str("backtrace", backtrace, Dfn::nl(vec![], Void)); - m.add_str("none", none, Dfn::nl(vec![], Type::option())); - m.add_str("some", some, Dfn::nl(vec![Any], Type::option())); - m.add_str("ok", ok, Dfn::nl(vec![Any], Type::result())); - m.add_str("err", err, Dfn::nl(vec![Any], Type::result())); - m.add_str("dir__angle", dir__angle, Dfn::nl(vec![F64], Vec4)); - m.add_str("load__meta_file", load__meta_file, Dfn::nl(vec![Str; 2], - Type::Result(Box::new(Type::Array(Box::new(Type::array())))) - )); - m.add_str("load__meta_url", load__meta_url, Dfn::nl(vec![Str; 2], - Type::Result(Box::new(Type::Array(Box::new(Type::array())))) - )); - m.add_str("syntax__in_string", syntax__in_string, - Dfn::nl(vec![Type::Str; 2], Type::Result(Box::new(Any)))); - m.add_str("download__url_file", download__url_file, - Dfn::nl(vec![Type::Str; 2], Type::Result(Box::new(Str)))); - m.add_str("save__string_file", save__string_file, - Dfn::nl(vec![Type::Str; 2], Type::Result(Box::new(Str)))); - m.add_str("load_string__file", load_string__file, - Dfn::nl(vec![Str], Type::Result(Box::new(Str)))); - m.add_str("load_string__url", load_string__url, - Dfn::nl(vec![Str], Type::Result(Box::new(Str)))); - m.add_str("join__thread", join__thread, - Dfn::nl(vec![Type::thread()], Type::Result(Box::new(Any)))); - m.add_str("load_data__file", load_data__file, - Dfn::nl(vec![Str], Type::Result(Box::new(Any)))); - m.add_str("load_data__string", load_data__string, - Dfn::nl(vec![Str], Type::Result(Box::new(Any)))); - m.add_str("args_os", args_os, Dfn::nl(vec![], Type::Array(Box::new(Str)))); - m.add_str("now", now, Dfn::nl(vec![], F64)); - m.add_str("is_nan", is_nan, Dfn::nl(vec![F64], Bool)); - m.add_str("load", load, Dfn::nl(vec![Str], Type::result())); - m.add_str("load__source_imports", load__source_imports, - Dfn::nl(vec![Str, Type::array()], Type::result())); - m.add_str("module__in_string_imports", module__in_string_imports, - Dfn::nl(vec![Str, Str, Type::array()], Type::result())); - m.add_str("call", _call, Dfn::nl(vec![Any, Str, Type::array()], Void)); - m.add_str("call_ret", call_ret, Dfn::nl(vec![Any, Str, Type::array()], Any)); - m.add_str("functions", functions, Dfn::nl(vec![], Any)); - m.add_str("functions__module", functions__module, Dfn::nl(vec![Any], Any)); - m.add_str("is_err", is_err, Dfn::nl(vec![Type::result()], Bool)); - m.add_str("is_ok", is_ok, Dfn::nl(vec![Type::result()], Bool)); - m.add_str("min", min, Dfn::nl(vec![Type::Array(Box::new(F64))], F64)); - m.add_str("max", max, Dfn::nl(vec![Type::Array(Box::new(F64))], F64)); - m.add_str("unwrap", unwrap, Dfn::nl(vec![Any], Any)); - m.add_str("why", why, Dfn::nl(vec![Type::Secret(Box::new(Bool))], Type::array())); - m.add_str("where", _where, Dfn::nl(vec![Type::Secret(Box::new(F64))], Type::array())); - m.add_str("explain_why", explain_why, Dfn::nl(vec![Bool, Any], - Type::Secret(Box::new(Bool)))); - m.add_str("explain_where", explain_where, Dfn::nl(vec![F64, Any], - Type::Secret(Box::new(F64)))); - m.add_str("head", head, Dfn::nl(vec![Link], Any)); - m.add_str("tip", tip, Dfn::nl(vec![Link], Type::Option(Box::new(Any)))); - m.add_str("tail", tail, Dfn::nl(vec![Link], Link)); - m.add_str("neck", neck, Dfn::nl(vec![Link], Link)); - m.add_str("is_empty", is_empty, Dfn::nl(vec![Link], Bool)); - m.add_str("len", len, Dfn::nl(vec![Type::array()], F64)); - m.add_str("push_ref(mut,_)", push_ref, Dfn { - lts: vec![Lt::Default, Lt::Arg(0)], - tys: vec![Type::array(), Any], - ret: Void, - ext: vec![], - lazy: LAZY_NO - }); - m.add_str("insert_ref(mut,_,_)", insert_ref, Dfn { - lts: vec![Lt::Default, Lt::Default, Lt::Arg(0)], - tys: vec![Type::array(), F64, Any], - ret: Void, - ext: vec![], - lazy: LAZY_NO - }); - m.add_str("push(mut,_)", push, Dfn::nl(vec![Type::array(), Any], Void)); - m.add_str("insert(mut,_,_)", insert, Dfn { - lts: vec![Lt::Default; 3], - tys: vec![Type::array(), F64, Any], - ret: Void, - ext: vec![], - lazy: LAZY_NO - }); - m.add_str("pop(mut)", pop, Dfn { - lts: vec![Lt::Return], - tys: vec![Type::array()], - ret: Any, - ext: vec![], - lazy: LAZY_NO - }); - m.add_str("remove(mut,_)", remove, Dfn { - lts: vec![Lt::Return, Lt::Default], - tys: vec![Type::array(), F64], - ret: Any, - ext: vec![], - lazy: LAZY_NO - }); - m.add_str("reverse(mut)", reverse, Dfn::nl(vec![Type::array()], Void)); - m.add_str("clear(mut)", clear, Dfn::nl(vec![Type::array()], Void)); - m.add_str("swap(mut,_,_)", swap, Dfn::nl(vec![Type::array(), F64, F64], Void)); - m.add_str("unwrap_or", unwrap_or, Dfn { - lts: vec![Lt::Default; 2], - tys: vec![Any; 2], - ret: Any, - ext: vec![], - lazy: LAZY_UNWRAP_OR - }); - m.add_str("unwrap_err", unwrap_err, Dfn::nl(vec![Any], Any)); - m.add_str("meta__syntax_in_string", - meta__syntax_in_string, Dfn::nl(vec![Any, Str, Str], - Type::Result(Box::new(Type::Array(Box::new(Type::array())))))); - m.add_str("save__data_file", save__data_file, Dfn::nl(vec![Any, Str], Str)); - m.add_str("json_from_meta_data", json_from_meta_data, - Dfn::nl(vec![Type::Array(Box::new(Type::array()))], Str)); - m.add_str("errstr__string_start_len_msg", - errstr__string_start_len_msg, Dfn::nl(vec![Str, F64, F64, Str], Str)); - m.add_str("has", has, Dfn::nl(vec![Object, Str], Bool)); - m.add_str("keys", keys, Dfn::nl(vec![Object], Type::Array(Box::new(Str)))); - m.add_str("chars", chars, Dfn::nl(vec![Str], Type::Array(Box::new(Str)))); - m.add_str("wait_next", wait_next, Dfn::nl(vec![Type::in_ty()], Any)); - m.add_str("next", next, Dfn::nl(vec![Type::in_ty()], Type::option())); - - m.no_ns(); - m - } - - /// Sets namespace for following added functions. - pub fn ns(&mut self, ns: &str) { - self.register_namespace = Arc::new(ns - .split("::") - .map(|s| Arc::new(s.into())) - .collect()); - } - - /// Sets no namespace. - pub fn no_ns(&mut self) { - self.register_namespace = Arc::new(vec![]); - } - - fn register(&mut self, function: ast::Function) { - self.functions.push(function); - } - - /// Find function relative another function index. - pub fn find_function(&self, name: &Arc, relative: usize) -> FnIndex { - for (i, f) in self.functions.iter().enumerate().rev() { - if &f.name == name { - return FnIndex::Loaded(i as isize - relative as isize); - } - } - for f in self.ext_prelude.iter().rev() { - if &f.name == name { - return if f.p.returns() { - if f.p.lazy == LAZY_NO { - FnIndex::ExternalReturn(FnExternalRef(f.f)) - } else { - FnIndex::ExternalLazy(FnExternalRef(f.f), f.p.lazy) - } - } else { - FnIndex::ExternalVoid(FnExternalRef(f.f)) - }; - } - } - FnIndex::None - } - - /// Generates an error message. - fn error(&self, range: Range, msg: &str, rt: &Runtime) -> String { - let fnindex = if let Some(x) = rt.call_stack.last() {x.index} - else {return msg.into()}; - self.error_fnindex(range, msg, fnindex) - } - - /// Generates an error with a function index. - fn error_fnindex(&self, range: Range, msg: &str, fnindex: usize) -> String { - let source = &self.functions[fnindex].source; - self.error_source(range, msg, source) - } - - /// Generates an error message with a source. - fn error_source(&self, range: Range, msg: &str, source: &Arc) -> String { - use piston_meta::ParseErrorHandler; - - let mut w: Vec = vec![]; - ParseErrorHandler::new(source) - .write_msg(&mut w, range, msg) - .unwrap(); - String::from_utf8(w).unwrap() - } - - /// Adds a new external prelude function. - pub fn add( - &mut self, - name: Arc, - f: fn(&mut Runtime) -> Result, String>, - prelude_function: Dfn - ) { - self.ext_prelude.push(FnExternal { - namespace: self.register_namespace.clone(), - name: name.clone(), - f, - p: prelude_function, - }); - } - - /// Adds a new external prelude function. - pub fn add_str( - &mut self, - name: &str, - f: fn(&mut Runtime) -> Result, String>, - prelude_function: Dfn - ) { - self.ext_prelude.push(FnExternal { - namespace: self.register_namespace.clone(), - name: Arc::new(name.into()), - f, - p: prelude_function, - }); - } -} - /// Runs a program using a source file. pub fn run(source: &str) -> Result<(), String> { let mut module = Module::new(); diff --git a/src/module.rs b/src/module.rs new file mode 100644 index 00000000..e20f1a30 --- /dev/null +++ b/src/module.rs @@ -0,0 +1,438 @@ +use super::*; + +/// Stores functions for a Dyon module. +#[derive(Clone)] +pub struct Module { + pub(crate) functions: Vec, + pub(crate) ext_prelude: Vec, + pub(crate) register_namespace: Arc>>, +} + +impl Default for Module { + fn default() -> Module {Module::new()} +} + +impl Module { + /// Creates a new empty module. + pub fn empty() -> Module { + Module { + functions: vec![], + ext_prelude: vec![], + register_namespace: Arc::new(vec![]), + } + } + + /// Creates a new module with standard library. + pub fn new() -> Module { + use Type::*; + use dyon_std::*; + + let mut m = Module::empty(); + m.ns("std"); + m.add_str("and_also", and_also, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Bool, Bool], + ret: Any, + ext: vec![ + (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), + (vec![], vec![Bool; 2], Bool) + ], + lazy: LAZY_AND + }); + m.add_str("or_else", or_else, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Bool, Bool], + ret: Any, + ext: vec![ + (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), + (vec![], vec![Bool; 2], Bool) + ], + lazy: LAZY_OR + }); + m.add_str("add", add, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + Type::all_ext(vec![F64, F64], F64), + Type::all_ext(vec![Vec4, Vec4], Vec4), + Type::all_ext(vec![Vec4, F64], Vec4), + Type::all_ext(vec![F64, Vec4], Vec4), + Type::all_ext(vec![Mat4, Mat4], Mat4), + Type::all_ext(vec![F64, Mat4], Mat4), + Type::all_ext(vec![Mat4, F64], Mat4), + Type::all_ext(vec![Bool, Bool], Bool), + Type::all_ext(vec![Str, Str], Str), + Type::all_ext(vec![Link, Link], Link), + ], + lazy: LAZY_NO + }); + m.add_str("sub", sub, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + Type::all_ext(vec![F64, F64], F64), + Type::all_ext(vec![Vec4, Vec4], Vec4), + Type::all_ext(vec![Vec4, F64], Vec4), + Type::all_ext(vec![F64, Vec4], Vec4), + Type::all_ext(vec![Mat4, Mat4], Mat4), + Type::all_ext(vec![F64, Mat4], Mat4), + Type::all_ext(vec![Mat4, F64], Mat4), + Type::all_ext(vec![Bool, Bool], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("mul", mul, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + (vec![], vec![Mat4, Mat4], Mat4), + (vec![], vec![F64, Mat4], Mat4), + (vec![], vec![Mat4, F64], Mat4), + (vec![], vec![Mat4, Vec4], Vec4), + Type::all_ext(vec![Bool, Bool], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("div", div, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + ], + lazy: LAZY_NO + }); + m.add_str("rem", rem, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + ], + lazy: LAZY_NO + }); + m.add_str("pow", pow, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![ + (vec![], vec![F64, F64], F64), + (vec![], vec![Vec4, Vec4], Vec4), + (vec![], vec![Vec4, F64], Vec4), + (vec![], vec![F64, Vec4], Vec4), + Type::all_ext(vec![Bool, Bool], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); + m.add_str("neg", neg, Dfn{ + lts: vec![Lt::Default], tys: vec![Any], ret: Any, + ext: vec![ + (vec![], vec![F64], F64), + (vec![], vec![Vec4], Vec4), + (vec![], vec![Mat4], Mat4), + ], + lazy: LAZY_NO + }); + m.add_str("dot", dot, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: F64, + ext: vec![ + (vec![], vec![Vec4, Vec4], F64), + (vec![], vec![Vec4, F64], F64), + (vec![], vec![F64, Vec4], F64), + ], + lazy: LAZY_NO + }); + m.add_str("cross", cross, Dfn::nl(vec![Vec4, Vec4], Vec4)); + m.add_str("x", x, Dfn::nl(vec![Vec4], F64)); + m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); + m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); + m.add_str("w", w, Dfn::nl(vec![Vec4], F64)); + m.add_str("norm", norm, Dfn::nl(vec![Vec4], F64)); + m.add_str("det", det, Dfn::nl(vec![Mat4], F64)); + m.add_str("inv", inv, Dfn::nl(vec![Mat4], Mat4)); + m.add_str("mov", mov, Dfn::nl(vec![Vec4], Mat4)); + m.add_str("rot__axis_angle", rot__axis_angle, Dfn::nl(vec![Vec4, F64], Mat4)); + m.add_str("ortho__pos_right_up_forward", ortho__pos_right_up_forward, + Dfn::nl(vec![Vec4; 4], Mat4)); + m.add_str("proj__fov_near_far_ar", proj__fov_near_far_ar, Dfn::nl(vec![F64; 4], Mat4)); + m.add_str("mvp__model_view_projection", mvp__model_view_projection, + Dfn::nl(vec![Mat4; 3], Mat4)); + m.add_str("scale", scale, Dfn::nl(vec![Vec4], Mat4)); + m.add_str("rx", rx, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("ry", ry, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("rz", rz, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("rw", rw, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("cx", cx, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("cy", cy, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("cz", cz, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("cw", cw, Dfn::nl(vec![Mat4], Vec4)); + m.add_str("cv", cv, Dfn::nl(vec![Mat4, F64], Vec4)); + m.add_str("clone", clone, Dfn::nl(vec![Any], Any)); + m.add_str("rv", rv, Dfn::nl(vec![Mat4, Type::F64], Vec4)); + m.add_str("s", s, Dfn::nl(vec![Vec4, F64], F64)); + m.add_str("println", println, Dfn::nl(vec![Any], Void)); + m.add_str("print", print, Dfn::nl(vec![Any], Void)); + m.add_str("sqrt", sqrt, Dfn::nl(vec![F64], F64)); + m.add_str("sin", sin, Dfn::nl(vec![F64], F64)); + m.add_str("asin", asin, Dfn::nl(vec![F64], F64)); + m.add_str("cos", cos, Dfn::nl(vec![F64], F64)); + m.add_str("acos", acos, Dfn::nl(vec![F64], F64)); + m.add_str("tan", tan, Dfn::nl(vec![F64], F64)); + m.add_str("atan", atan, Dfn::nl(vec![F64], F64)); + m.add_str("atan2", atan2, Dfn::nl(vec![F64; 2], F64)); + m.add_str("exp", exp, Dfn::nl(vec![F64], F64)); + m.add_str("ln", ln, Dfn::nl(vec![F64], F64)); + m.add_str("log2", log2, Dfn::nl(vec![F64], F64)); + m.add_str("log10", log10, Dfn::nl(vec![F64], F64)); + m.add_str("round", round, Dfn::nl(vec![F64], F64)); + m.add_str("abs", abs, Dfn::nl(vec![F64], F64)); + m.add_str("floor", floor, Dfn::nl(vec![F64], F64)); + m.add_str("ceil", ceil, Dfn::nl(vec![F64], F64)); + m.add_str("sleep", sleep, Dfn::nl(vec![F64], Void)); + m.add_str("random", random, Dfn::nl(vec![], F64)); + m.add_str("tau", tau, Dfn::nl(vec![], F64)); + m.add_str("read_line", read_line, Dfn::nl(vec![], Str)); + m.add_str("read_number", read_number, Dfn::nl(vec![Str], F64)); + m.add_str("parse_number", parse_number, Dfn::nl(vec![Str], Option(Box::new(Type::F64)))); + m.add_str("trim", trim, Dfn::nl(vec![Str], Str)); + m.add_str("trim_left", trim_left, Dfn::nl(vec![Str], Str)); + m.add_str("trim_right", trim_right, Dfn::nl(vec![Str], Str)); + m.add_str("str", _str, Dfn::nl(vec![Any], Str)); + m.add_str("json_string", json_string, Dfn::nl(vec![Str], Str)); + m.add_str("str__color", str__color, Dfn::nl(vec![Vec4], Str)); + m.add_str("srgb_to_linear__color", srgb_to_linear__color, Dfn::nl(vec![Vec4], Vec4)); + m.add_str("linear_to_srgb__color", linear_to_srgb__color, Dfn::nl(vec![Vec4], Vec4)); + m.add_str("typeof", _typeof, Dfn::nl(vec![Any], Str)); + m.add_str("debug", debug, Dfn::nl(vec![], Void)); + m.add_str("backtrace", backtrace, Dfn::nl(vec![], Void)); + m.add_str("none", none, Dfn::nl(vec![], Type::option())); + m.add_str("some", some, Dfn::nl(vec![Any], Type::option())); + m.add_str("ok", ok, Dfn::nl(vec![Any], Type::result())); + m.add_str("err", err, Dfn::nl(vec![Any], Type::result())); + m.add_str("dir__angle", dir__angle, Dfn::nl(vec![F64], Vec4)); + m.add_str("load__meta_file", load__meta_file, Dfn::nl(vec![Str; 2], + Type::Result(Box::new(Type::Array(Box::new(Type::array())))) + )); + m.add_str("load__meta_url", load__meta_url, Dfn::nl(vec![Str; 2], + Type::Result(Box::new(Type::Array(Box::new(Type::array())))) + )); + m.add_str("syntax__in_string", syntax__in_string, + Dfn::nl(vec![Type::Str; 2], Type::Result(Box::new(Any)))); + m.add_str("download__url_file", download__url_file, + Dfn::nl(vec![Type::Str; 2], Type::Result(Box::new(Str)))); + m.add_str("save__string_file", save__string_file, + Dfn::nl(vec![Type::Str; 2], Type::Result(Box::new(Str)))); + m.add_str("load_string__file", load_string__file, + Dfn::nl(vec![Str], Type::Result(Box::new(Str)))); + m.add_str("load_string__url", load_string__url, + Dfn::nl(vec![Str], Type::Result(Box::new(Str)))); + m.add_str("join__thread", join__thread, + Dfn::nl(vec![Type::thread()], Type::Result(Box::new(Any)))); + m.add_str("load_data__file", load_data__file, + Dfn::nl(vec![Str], Type::Result(Box::new(Any)))); + m.add_str("load_data__string", load_data__string, + Dfn::nl(vec![Str], Type::Result(Box::new(Any)))); + m.add_str("args_os", args_os, Dfn::nl(vec![], Type::Array(Box::new(Str)))); + m.add_str("now", now, Dfn::nl(vec![], F64)); + m.add_str("is_nan", is_nan, Dfn::nl(vec![F64], Bool)); + m.add_str("load", load, Dfn::nl(vec![Str], Type::result())); + m.add_str("load__source_imports", load__source_imports, + Dfn::nl(vec![Str, Type::array()], Type::result())); + m.add_str("module__in_string_imports", module__in_string_imports, + Dfn::nl(vec![Str, Str, Type::array()], Type::result())); + m.add_str("call", _call, Dfn::nl(vec![Any, Str, Type::array()], Void)); + m.add_str("call_ret", call_ret, Dfn::nl(vec![Any, Str, Type::array()], Any)); + m.add_str("functions", functions, Dfn::nl(vec![], Any)); + m.add_str("functions__module", functions__module, Dfn::nl(vec![Any], Any)); + m.add_str("is_err", is_err, Dfn::nl(vec![Type::result()], Bool)); + m.add_str("is_ok", is_ok, Dfn::nl(vec![Type::result()], Bool)); + m.add_str("min", min, Dfn::nl(vec![Type::Array(Box::new(F64))], F64)); + m.add_str("max", max, Dfn::nl(vec![Type::Array(Box::new(F64))], F64)); + m.add_str("unwrap", unwrap, Dfn::nl(vec![Any], Any)); + m.add_str("why", why, Dfn::nl(vec![Type::Secret(Box::new(Bool))], Type::array())); + m.add_str("where", _where, Dfn::nl(vec![Type::Secret(Box::new(F64))], Type::array())); + m.add_str("explain_why", explain_why, Dfn::nl(vec![Bool, Any], + Type::Secret(Box::new(Bool)))); + m.add_str("explain_where", explain_where, Dfn::nl(vec![F64, Any], + Type::Secret(Box::new(F64)))); + m.add_str("head", head, Dfn::nl(vec![Link], Any)); + m.add_str("tip", tip, Dfn::nl(vec![Link], Type::Option(Box::new(Any)))); + m.add_str("tail", tail, Dfn::nl(vec![Link], Link)); + m.add_str("neck", neck, Dfn::nl(vec![Link], Link)); + m.add_str("is_empty", is_empty, Dfn::nl(vec![Link], Bool)); + m.add_str("len", len, Dfn::nl(vec![Type::array()], F64)); + m.add_str("push_ref(mut,_)", push_ref, Dfn { + lts: vec![Lt::Default, Lt::Arg(0)], + tys: vec![Type::array(), Any], + ret: Void, + ext: vec![], + lazy: LAZY_NO + }); + m.add_str("insert_ref(mut,_,_)", insert_ref, Dfn { + lts: vec![Lt::Default, Lt::Default, Lt::Arg(0)], + tys: vec![Type::array(), F64, Any], + ret: Void, + ext: vec![], + lazy: LAZY_NO + }); + m.add_str("push(mut,_)", push, Dfn::nl(vec![Type::array(), Any], Void)); + m.add_str("insert(mut,_,_)", insert, Dfn { + lts: vec![Lt::Default; 3], + tys: vec![Type::array(), F64, Any], + ret: Void, + ext: vec![], + lazy: LAZY_NO + }); + m.add_str("pop(mut)", pop, Dfn { + lts: vec![Lt::Return], + tys: vec![Type::array()], + ret: Any, + ext: vec![], + lazy: LAZY_NO + }); + m.add_str("remove(mut,_)", remove, Dfn { + lts: vec![Lt::Return, Lt::Default], + tys: vec![Type::array(), F64], + ret: Any, + ext: vec![], + lazy: LAZY_NO + }); + m.add_str("reverse(mut)", reverse, Dfn::nl(vec![Type::array()], Void)); + m.add_str("clear(mut)", clear, Dfn::nl(vec![Type::array()], Void)); + m.add_str("swap(mut,_,_)", swap, Dfn::nl(vec![Type::array(), F64, F64], Void)); + m.add_str("unwrap_or", unwrap_or, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any; 2], + ret: Any, + ext: vec![], + lazy: LAZY_UNWRAP_OR + }); + m.add_str("unwrap_err", unwrap_err, Dfn::nl(vec![Any], Any)); + m.add_str("meta__syntax_in_string", + meta__syntax_in_string, Dfn::nl(vec![Any, Str, Str], + Type::Result(Box::new(Type::Array(Box::new(Type::array())))))); + m.add_str("save__data_file", save__data_file, Dfn::nl(vec![Any, Str], Str)); + m.add_str("json_from_meta_data", json_from_meta_data, + Dfn::nl(vec![Type::Array(Box::new(Type::array()))], Str)); + m.add_str("errstr__string_start_len_msg", + errstr__string_start_len_msg, Dfn::nl(vec![Str, F64, F64, Str], Str)); + m.add_str("has", has, Dfn::nl(vec![Object, Str], Bool)); + m.add_str("keys", keys, Dfn::nl(vec![Object], Type::Array(Box::new(Str)))); + m.add_str("chars", chars, Dfn::nl(vec![Str], Type::Array(Box::new(Str)))); + m.add_str("wait_next", wait_next, Dfn::nl(vec![Type::in_ty()], Any)); + m.add_str("next", next, Dfn::nl(vec![Type::in_ty()], Type::option())); + + m.no_ns(); + m + } + + /// Sets namespace for following added functions. + pub fn ns(&mut self, ns: &str) { + self.register_namespace = Arc::new(ns + .split("::") + .map(|s| Arc::new(s.into())) + .collect()); + } + + /// Sets no namespace. + pub fn no_ns(&mut self) { + self.register_namespace = Arc::new(vec![]); + } + + pub(crate) fn register(&mut self, function: ast::Function) { + self.functions.push(function); + } + + /// Find function relative another function index. + pub fn find_function(&self, name: &Arc, relative: usize) -> FnIndex { + for (i, f) in self.functions.iter().enumerate().rev() { + if &f.name == name { + return FnIndex::Loaded(i as isize - relative as isize); + } + } + for f in self.ext_prelude.iter().rev() { + if &f.name == name { + return if f.p.returns() { + if f.p.lazy == LAZY_NO { + FnIndex::ExternalReturn(FnExternalRef(f.f)) + } else { + FnIndex::ExternalLazy(FnExternalRef(f.f), f.p.lazy) + } + } else { + FnIndex::ExternalVoid(FnExternalRef(f.f)) + }; + } + } + FnIndex::None + } + + /// Generates an error message. + pub(crate) fn error(&self, range: Range, msg: &str, rt: &Runtime) -> String { + let fnindex = if let Some(x) = rt.call_stack.last() {x.index} + else {return msg.into()}; + self.error_fnindex(range, msg, fnindex) + } + + /// Generates an error with a function index. + pub(crate) fn error_fnindex(&self, range: Range, msg: &str, fnindex: usize) -> String { + let source = &self.functions[fnindex].source; + self.error_source(range, msg, source) + } + + /// Generates an error message with a source. + pub(crate) fn error_source(&self, range: Range, msg: &str, source: &Arc) -> String { + use piston_meta::ParseErrorHandler; + + let mut w: Vec = vec![]; + ParseErrorHandler::new(source) + .write_msg(&mut w, range, msg) + .unwrap(); + String::from_utf8(w).unwrap() + } + + /// Adds a new external prelude function. + pub fn add( + &mut self, + name: Arc, + f: fn(&mut Runtime) -> Result, String>, + prelude_function: Dfn + ) { + self.ext_prelude.push(FnExternal { + namespace: self.register_namespace.clone(), + name: name.clone(), + f, + p: prelude_function, + }); + } + + /// Adds a new external prelude function. + pub fn add_str( + &mut self, + name: &str, + f: fn(&mut Runtime) -> Result, String>, + prelude_function: Dfn + ) { + self.ext_prelude.push(FnExternal { + namespace: self.register_namespace.clone(), + name: Arc::new(name.into()), + f, + p: prelude_function, + }); + } +} From 05f9ba3595c66000cf3c63096fc4ce3c3c07ff65 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 12:02:24 +0200 Subject: [PATCH 59/83] Update "advancedresearch-tree_mem_sort" --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c42eac06..b8071e42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ read_color = "1.0.0" read_token = "1.0.0" lazy_static = "1.0.0" vecmath = "1.0.0" -advancedresearch-tree_mem_sort = "0.1.1" +advancedresearch-tree_mem_sort = "0.2.0" [dependencies.reqwest] version = "0.4.0" From f69bccc940e78ff3649fcf8b4e022379601b7415 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 14:17:58 +0200 Subject: [PATCH 60/83] Split `FnExternalRef` into `FnExternalReturnRef` and `FnExternalVoidRef` --- src/ast/mod.rs | 22 +-- src/dyon_std/mod.rs | 324 +++++++++++++++++++++++--------------------- src/lib.rs | 59 ++++++-- src/macros.rs | 16 +-- src/module.rs | 38 ++++-- src/runtime/mod.rs | 25 ++-- 6 files changed, 276 insertions(+), 208 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c7418ada..c67addcf 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2607,7 +2607,9 @@ impl Call { module: &Module, use_lookup: &UseLookup, ) { - use FnExternalRef; + use FnExternalReturnRef; + use FnExternalVoidRef; + use FnExt; let st = stack.len(); let f_index = if let Some(ref alias) = self.alias { @@ -2616,10 +2618,9 @@ impl Call { FnAlias::Loaded(i) => FnIndex::Loaded(i as isize - relative as isize), FnAlias::External(i) => { let f = &module.ext_prelude[i]; - if let Type::Void = f.p.ret { - FnIndex::ExternalVoid(FnExternalRef(f.f)) - } else { - FnIndex::ExternalReturn(FnExternalRef(f.f)) + match f.f { + FnExt::Void(ff) => FnIndex::ExternalVoid(FnExternalVoidRef(ff)), + FnExt::Return(ff) => FnIndex::ExternalReturn(FnExternalReturnRef(ff)), } } } @@ -4365,7 +4366,9 @@ impl In { module: &Module, use_lookup: &UseLookup ) { - use FnExternalRef; + use FnExternalReturnRef; + use FnExternalVoidRef; + use FnExt; let f_index = if let Some(ref alias) = self.alias { if let Some(&i) = use_lookup.aliases.get(alias).and_then(|map| map.get(&self.name)) { @@ -4373,10 +4376,9 @@ impl In { FnAlias::Loaded(i) => FnIndex::Loaded(i as isize - relative as isize), FnAlias::External(i) => { let f = &module.ext_prelude[i]; - if let Type::Void = f.p.ret { - FnIndex::ExternalVoid(FnExternalRef(f.f)) - } else { - FnIndex::ExternalReturn(FnExternalRef(f.f)) + match f.f { + FnExt::Void(ff) => FnIndex::ExternalVoid(FnExternalVoidRef(ff)), + FnExt::Return(ff) => FnIndex::ExternalReturn(FnExternalReturnRef(ff)), } } } diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 6e4fc77d..2404a23d 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -14,7 +14,7 @@ const HTTP_SUPPORT_DISABLED: &'static str = "Http support is disabled"; #[cfg(not(feature = "file"))] const FILE_SUPPORT_DISABLED: &'static str = "File support is disabled"; -pub(crate) fn and_also(rt: &mut Runtime) -> Result, String> { +pub(crate) fn and_also(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -23,10 +23,10 @@ pub(crate) fn and_also(rt: &mut Runtime) -> Result, String> { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && b, sec.clone()), _ => return Err("Expected `bool`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn or_else(rt: &mut Runtime) -> Result, String> { +pub(crate) fn or_else(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -35,10 +35,10 @@ pub(crate) fn or_else(rt: &mut Runtime) -> Result, String> { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a || b, sec.clone()), _ => return Err("Expected `bool`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn add(rt: &mut Runtime) -> Result, String> { +pub(crate) fn add(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -70,10 +70,10 @@ pub(crate) fn add(rt: &mut Runtime) -> Result, String> { (&Link(ref a), &Link(ref b)) => Link(Box::new(a.add(b))), _ => return Err("Expected `f64`, `vec4`, `mat4`, `bool`, `str` or `link`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn sub(rt: &mut Runtime) -> Result, String> { +pub(crate) fn sub(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -111,10 +111,10 @@ pub(crate) fn sub(rt: &mut Runtime) -> Result, String> { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && !b, sec.clone()), _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn mul(rt: &mut Runtime) -> Result, String> { +pub(crate) fn mul(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -140,10 +140,10 @@ pub(crate) fn mul(rt: &mut Runtime) -> Result, String> { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && b, sec.clone()), _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn div(rt: &mut Runtime) -> Result, String> { +pub(crate) fn div(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -161,10 +161,10 @@ pub(crate) fn div(rt: &mut Runtime) -> Result, String> { } _ => return Err("Expected `f64` or `vec4`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn rem(rt: &mut Runtime) -> Result, String> { +pub(crate) fn rem(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -182,10 +182,10 @@ pub(crate) fn rem(rt: &mut Runtime) -> Result, String> { } _ => return Err("Expected `f64` or `vec4`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn pow(rt: &mut Runtime) -> Result, String> { +pub(crate) fn pow(rt: &mut Runtime) -> Result { use Variable::*; let b = rt.stack.pop().expect(TINVOTS); @@ -205,19 +205,19 @@ pub(crate) fn pow(rt: &mut Runtime) -> Result, String> { (&Bool(a, ref sec), &Bool(ref b, _)) => Bool(a ^ b, sec.clone()), _ => return Err("Expected `f64`, `vec4` or `bool`".into()) }; - Ok(Some(r)) + Ok(r) } -pub(crate) fn not(rt: &mut Runtime) -> Result, String> { +pub(crate) fn not(rt: &mut Runtime) -> Result { let b = rt.stack.pop().expect(TINVOTS); let b = match *rt.resolve(&b) { Variable::Bool(ref b, ref sec) => Variable::Bool(!b, sec.clone()), ref x => return Err(rt.expected_arg(0, x, "bool")) }; - Ok(Some(b)) + Ok(b) } -pub(crate) fn neg(rt: &mut Runtime) -> Result, String> { +pub(crate) fn neg(rt: &mut Runtime) -> Result { let n = rt.stack.pop().expect(TINVOTS); let n = match *rt.resolve(&n) { Variable::F64(v, ref sec) => Variable::F64(-v, sec.clone()), @@ -230,10 +230,10 @@ pub(crate) fn neg(rt: &mut Runtime) -> Result, String> { ])), ref x => return Err(rt.expected_arg(0, x, "f64, vec4 or mat4")) }; - Ok(Some(n)) + Ok(n) } -pub(crate) fn dot(rt: &mut Runtime) -> Result, String> { +pub(crate) fn dot(rt: &mut Runtime) -> Result { let b = rt.stack.pop().expect(TINVOTS); let a = rt.stack.pop().expect(TINVOTS); let r = match (rt.resolve(&a), rt.resolve(&b)) { @@ -245,7 +245,7 @@ pub(crate) fn dot(rt: &mut Runtime) -> Result, String> { } _ => return Err("Expected (vec4, vec4), (vec4, f64) or (f64, vec4)".into()) }; - Ok(Some(Variable::f64(r))) + Ok(Variable::f64(r)) } dyon_fn!{fn cross(a: Vec4, b: Vec4) -> Vec4 { @@ -260,12 +260,12 @@ dyon_fn!{fn z(v: Vec4) -> f64 {f64::from(v.0[2])}} dyon_fn!{fn w(v: Vec4) -> f64 {f64::from(v.0[3])}} dyon_fn!{fn norm(v: Vec4) -> f32 {vecmath::vec4_len(v.0)}} -pub(crate) fn s(rt: &mut Runtime) -> Result, String> { +pub(crate) fn s(rt: &mut Runtime) -> Result { let ind: f64 = rt.pop().expect(TINVOTS); let ind = ind as usize; if ind >= 4 {return Err(format!("Index out of bounds `{}`", ind))}; let v: [f32; 4] = rt.pop_vec4().expect(TINVOTS); - Ok(Some(Variable::f64(f64::from(v[ind])))) + Ok(Variable::f64(f64::from(v[ind]))) } dyon_fn!{fn det(m: Mat4) -> f64 {f64::from(vecmath::mat4_det(m.0))}} @@ -337,12 +337,12 @@ dyon_fn!{fn ry(m: Mat4) -> Vec4 {Vec4([m.0[0][1], m.0[1][1], m.0[2][1], m.0[3][1 dyon_fn!{fn rz(m: Mat4) -> Vec4 {Vec4([m.0[0][2], m.0[1][2], m.0[2][2], m.0[3][2]])}} dyon_fn!{fn rw(m: Mat4) -> Vec4 {Vec4([m.0[0][3], m.0[1][3], m.0[2][3], m.0[3][3]])}} -pub(crate) fn rv(rt: &mut Runtime) -> Result, String> { +pub(crate) fn rv(rt: &mut Runtime) -> Result { let ind: f64 = rt.pop().expect(TINVOTS); let ind = ind as usize; if ind >= 4 {return Err(format!("Index out of bounds `{}`", ind))}; let m: [[f32; 4]; 4] = rt.pop_mat4().expect(TINVOTS); - Ok(Some(Variable::Vec4([m[0][ind], m[1][ind], m[2][ind], m[3][ind]]))) + Ok(Variable::Vec4([m[0][ind], m[1][ind], m[2][ind], m[3][ind]])) } dyon_fn!{fn cx(m: Mat4) -> Vec4 {Vec4(m.0[0])}} @@ -350,21 +350,21 @@ dyon_fn!{fn cy(m: Mat4) -> Vec4 {Vec4(m.0[1])}} dyon_fn!{fn cz(m: Mat4) -> Vec4 {Vec4(m.0[2])}} dyon_fn!{fn cw(m: Mat4) -> Vec4 {Vec4(m.0[3])}} -pub(crate) fn cv(rt: &mut Runtime) -> Result, String> { +pub(crate) fn cv(rt: &mut Runtime) -> Result { let ind: f64 = rt.pop().expect(TINVOTS); let ind = ind as usize; if ind >= 4 {return Err(format!("Index out of bounds `{}`", ind))}; let m: [[f32; 4]; 4] = rt.pop_mat4().expect(TINVOTS); - Ok(Some(Variable::Mat4(Box::new(m)))) + Ok(Variable::Mat4(Box::new(m))) } -pub(crate) fn clone(rt: &mut Runtime) -> Result, String> { +pub(crate) fn clone(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = rt.resolve(&v).deep_clone(&rt.stack); - Ok(Some(v)) + Ok(v) } -pub(crate) fn why(rt: &mut Runtime) -> Result, String> { +pub(crate) fn why(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Array(Arc::new(match rt.resolve(&v) { &Variable::Bool(true, Some(ref sec)) => { @@ -386,10 +386,10 @@ pub(crate) fn why(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "bool")) })); - Ok(Some(v)) + Ok(v) } -pub(crate) fn _where(rt: &mut Runtime) -> Result, String> { +pub(crate) fn _where(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Array(Arc::new(match rt.resolve(&v) { &Variable::F64(val, Some(ref sec)) => { @@ -412,10 +412,10 @@ pub(crate) fn _where(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "f64")) })); - Ok(Some(v)) + Ok(v) } -pub(crate) fn explain_why(rt: &mut Runtime) -> Result, String> { +pub(crate) fn explain_why(rt: &mut Runtime) -> Result { let why = rt.stack.pop().expect(TINVOTS); let val = rt.stack.pop().expect(TINVOTS); let (val, why) = match rt.resolve(&val) { @@ -431,10 +431,10 @@ pub(crate) fn explain_why(rt: &mut Runtime) -> Result, String> ), x => return Err(rt.expected_arg(0, x, "bool")) }; - Ok(Some(Variable::Bool(val, Some(why)))) + Ok(Variable::Bool(val, Some(why))) } -pub(crate) fn explain_where(rt: &mut Runtime) -> Result, String> { +pub(crate) fn explain_where(rt: &mut Runtime) -> Result { let wh = rt.stack.pop().expect(TINVOTS); let val = rt.stack.pop().expect(TINVOTS); let (val, wh) = match rt.resolve(&val) { @@ -450,24 +450,24 @@ pub(crate) fn explain_where(rt: &mut Runtime) -> Result, String ), x => return Err(rt.expected_arg(0, x, "bool")) }; - Ok(Some(Variable::F64(val, Some(wh)))) + Ok(Variable::F64(val, Some(wh))) } -pub(crate) fn println(rt: &mut Runtime) -> Result, String> { +pub(crate) fn println(rt: &mut Runtime) -> Result<(), String> { use write::{print_variable, EscapeString}; let x = rt.stack.pop().expect(TINVOTS); print_variable(rt, &x, EscapeString::None); println!(); - Ok(None) + Ok(()) } -pub(crate) fn print(rt: &mut Runtime) -> Result, String> { +pub(crate) fn print(rt: &mut Runtime) -> Result<(), String> { use write::{print_variable, EscapeString}; let x = rt.stack.pop().expect(TINVOTS); print_variable(rt, &x, EscapeString::None); - Ok(None) + Ok(()) } dyon_fn!{fn sqrt(a: f64) -> f64 {a.sqrt()}} @@ -495,61 +495,61 @@ dyon_fn!{fn sleep(v: f64) { sleep(Duration::new(secs, nanos)); }} -pub(crate) fn head(rt: &mut Runtime) -> Result, String> { +pub(crate) fn head(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Option(match rt.resolve(&v) { &Variable::Link(ref link) => link.head(), x => return Err(rt.expected_arg(0, x, "link")) }); - Ok(Some(v)) + Ok(v) } -pub(crate) fn tip(rt: &mut Runtime) -> Result, String> { +pub(crate) fn tip(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Option(match rt.resolve(&v) { &Variable::Link(ref link) => link.tip(), x => return Err(rt.expected_arg(0, x, "link")) }); - Ok(Some(v)) + Ok(v) } -pub(crate) fn tail(rt: &mut Runtime) -> Result, String> { +pub(crate) fn tail(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Link(Box::new(match rt.resolve(&v) { &Variable::Link(ref link) => link.tail(), x => return Err(rt.expected_arg(0, x, "link")) })); - Ok(Some(v)) + Ok(v) } -pub(crate) fn neck(rt: &mut Runtime) -> Result, String> { +pub(crate) fn neck(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Link(Box::new(match rt.resolve(&v) { &Variable::Link(ref link) => link.neck(), x => return Err(rt.expected_arg(0, x, "link")) })); - Ok(Some(v)) + Ok(v) } -pub(crate) fn is_empty(rt: &mut Runtime) -> Result, String> { +pub(crate) fn is_empty(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::bool(match rt.resolve(&v) { &Variable::Link(ref link) => link.is_empty(), x => return Err(rt.expected_arg(0, x, "link")) }); - Ok(Some(v)) + Ok(v) } -pub(crate) fn random(rt: &mut Runtime) -> Result, String> { +pub(crate) fn random(rt: &mut Runtime) -> Result { use rand::Rng; let v: f64 = rt.rng.gen(); - Ok(Some(Variable::f64(v))) + Ok(Variable::f64(v)) } dyon_fn!{fn tau() -> f64 {6.283_185_307_179_586}} -pub(crate) fn len(rt: &mut Runtime) -> Result, String> { +pub(crate) fn len(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = { let arr = match rt.resolve(&v) { @@ -558,10 +558,10 @@ pub(crate) fn len(rt: &mut Runtime) -> Result, String> { }; Variable::f64(arr.len() as f64) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn push_ref(rt: &mut Runtime) -> Result, String> { +pub(crate) fn push_ref(rt: &mut Runtime) -> Result<(), String> { let item = rt.stack.pop().expect(TINVOTS); let v = rt.stack.pop().expect(TINVOTS); @@ -584,10 +584,10 @@ pub(crate) fn push_ref(rt: &mut Runtime) -> Result, String> { "Expected reference to array".into() }) } - Ok(None) + Ok(()) } -pub(crate) fn insert_ref(rt: &mut Runtime) -> Result, String> { +pub(crate) fn insert_ref(rt: &mut Runtime) -> Result<(), String> { let item = rt.stack.pop().expect(TINVOTS); let index = rt.stack.pop().expect(TINVOTS); let index = match rt.resolve(&index) { @@ -621,10 +621,10 @@ pub(crate) fn insert_ref(rt: &mut Runtime) -> Result, String> { "Expected reference to array".into() }) } - Ok(None) + Ok(()) } -pub(crate) fn push(rt: &mut Runtime) -> Result, String> { +pub(crate) fn push(rt: &mut Runtime) -> Result<(), String> { let item = rt.stack.pop().expect(TINVOTS); let item = rt.resolve(&item).deep_clone(&rt.stack); let v = rt.stack.pop().expect(TINVOTS); @@ -648,10 +648,10 @@ pub(crate) fn push(rt: &mut Runtime) -> Result, String> { "Expected reference to array".into() }) } - Ok(None) + Ok(()) } -pub(crate) fn insert(rt: &mut Runtime) -> Result, String> { +pub(crate) fn insert(rt: &mut Runtime) -> Result<(), String> { let item = rt.stack.pop().expect(TINVOTS); let item = rt.resolve(&item).deep_clone(&rt.stack); let index = rt.stack.pop().expect(TINVOTS); @@ -686,10 +686,10 @@ pub(crate) fn insert(rt: &mut Runtime) -> Result, String> { "Expected reference to array".into() }) } - Ok(None) + Ok(()) } -pub(crate) fn pop(rt: &mut Runtime) -> Result, String> { +pub(crate) fn pop(rt: &mut Runtime) -> Result { let arr = rt.stack.pop().expect(TINVOTS); let mut v: Option = None; if let Variable::Ref(ind) = arr { @@ -718,10 +718,10 @@ pub(crate) fn pop(rt: &mut Runtime) -> Result, String> { }), Some(val) => val }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn remove(rt: &mut Runtime) -> Result, String> { +pub(crate) fn remove(rt: &mut Runtime) -> Result { let index = rt.stack.pop().expect(TINVOTS); let index = match rt.resolve(&index) { &Variable::F64(index, _) => index, @@ -737,7 +737,7 @@ pub(crate) fn remove(rt: &mut Runtime) -> Result, String> { } if let Variable::Array(ref mut arr) = rt.stack[ind] { let v = Arc::make_mut(arr).remove(index as usize); - return Ok(Some(v)); + return Ok(v); }; return Err({ rt.arg_err_index.set(Some(0)); @@ -751,7 +751,7 @@ pub(crate) fn remove(rt: &mut Runtime) -> Result, String> { } } -pub(crate) fn reverse(rt: &mut Runtime) -> Result, String> { +pub(crate) fn reverse(rt: &mut Runtime) -> Result<(), String> { let v = rt.stack.pop().expect(TINVOTS); if let Variable::Ref(ind) = v { let ok = if let Variable::Array(ref mut arr) = rt.stack[ind] { @@ -772,10 +772,10 @@ pub(crate) fn reverse(rt: &mut Runtime) -> Result, String> { "Expected reference to array".into() }) } - Ok(None) + Ok(()) } -pub(crate) fn clear(rt: &mut Runtime) -> Result, String> { +pub(crate) fn clear(rt: &mut Runtime) -> Result<(), String> { let v = rt.stack.pop().expect(TINVOTS); if let Variable::Ref(ind) = v { let ok = if let Variable::Array(ref mut arr) = rt.stack[ind] { @@ -796,10 +796,10 @@ pub(crate) fn clear(rt: &mut Runtime) -> Result, String> { "Expected reference to array".into() }) } - Ok(None) + Ok(()) } -pub(crate) fn swap(rt: &mut Runtime) -> Result, String> { +pub(crate) fn swap(rt: &mut Runtime) -> Result<(), String> { let j = rt.stack.pop().expect(TINVOTS); let i = rt.stack.pop().expect(TINVOTS); let j = match rt.resolve(&j) { @@ -830,10 +830,10 @@ pub(crate) fn swap(rt: &mut Runtime) -> Result, String> { "Expected reference to array".into() }) } - Ok(None) + Ok(()) } -pub(crate) fn read_line(_rt: &mut Runtime) -> Result, String> { +pub(crate) fn read_line(_rt: &mut Runtime) -> Result { use std::io::{self, Write}; use std::error::Error; @@ -843,14 +843,14 @@ pub(crate) fn read_line(_rt: &mut Runtime) -> Result, String> { Ok(_) => None, Err(error) => Some(error) }; - Ok(Some(if let Some(error) = error { + Ok(if let Some(error) = error { return Err(error.description().into()) } else { Variable::Str(Arc::new(input)) - })) + }) } -pub(crate) fn read_number(rt: &mut Runtime) -> Result, String> { +pub(crate) fn read_number(rt: &mut Runtime) -> Result { use std::io::{self, Write}; use std::error::Error; @@ -870,7 +870,7 @@ pub(crate) fn read_number(rt: &mut Runtime) -> Result, String> Err(_) => println!("{}", err), } }; - Ok(Some(Variable::f64(rv))) + Ok(Variable::f64(rv)) } dyon_fn!{fn parse_number(text: Arc) -> Option {text.trim().parse::().ok()}} @@ -878,22 +878,22 @@ dyon_fn!{fn trim(v: Arc) -> Arc {Arc::new(v.trim().into())}} dyon_fn!{fn trim_left(v: Arc) -> Arc {Arc::new(v.trim_start().into())}} dyon_fn!{fn trim_right(v: Arc) -> Arc {Arc::new(v.trim_end().into())}} -pub(crate) fn _str(rt: &mut Runtime) -> Result, String> { +pub(crate) fn _str(rt: &mut Runtime) -> Result { use write::{write_variable, EscapeString}; let v = rt.stack.pop().expect(TINVOTS); let mut buf: Vec = vec![]; write_variable(&mut buf, rt, rt.resolve(&v), EscapeString::None, 0).unwrap(); - Ok(Some(Variable::Str(Arc::new(String::from_utf8(buf).unwrap())))) + Ok(Variable::Str(Arc::new(String::from_utf8(buf).unwrap()))) } -pub(crate) fn json_string(rt: &mut Runtime) -> Result, String> { +pub(crate) fn json_string(rt: &mut Runtime) -> Result { use write::{write_variable, EscapeString}; let v = rt.stack.pop().expect(TINVOTS); let mut buf: Vec = vec![]; write_variable(&mut buf, rt, rt.resolve(&v), EscapeString::Json, 0).unwrap(); - Ok(Some(Variable::Str(Arc::new(String::from_utf8(buf).unwrap())))) + Ok(Variable::Str(Arc::new(String::from_utf8(buf).unwrap()))) } dyon_fn!{fn str__color(v: Vec4) -> Arc { @@ -946,7 +946,7 @@ dyon_fn!{fn linear_to_srgb__color(v: Vec4) -> Vec4 { Vec4([to_srgb(v[0]), to_srgb(v[1]), to_srgb(v[2]), v[3]]) }} -pub(crate) fn _typeof(rt: &mut Runtime) -> Result, String> { +pub(crate) fn _typeof(rt: &mut Runtime) -> Result { use crate::runtime::*; use crate::Variable::*; @@ -970,22 +970,22 @@ pub(crate) fn _typeof(rt: &mut Runtime) -> Result, String> { Closure(_, _) => CLOSURE_TYPE.clone(), In(_) => IN_TYPE.clone(), }); - Ok(Some(t)) + Ok(t) } -pub(crate) fn debug(rt: &mut Runtime) -> Result, String> { +pub(crate) fn debug(rt: &mut Runtime) -> Result<(), String> { println!("Stack {:#?}", rt.stack); println!("Locals {:#?}", rt.local_stack); println!("Currents {:#?}", rt.current_stack); - Ok(None) + Ok(()) } -pub(crate) fn backtrace(rt: &mut Runtime) -> Result, String> { +pub(crate) fn backtrace(rt: &mut Runtime) -> Result<(), String> { println!("{:#?}", rt.call_stack); - Ok(None) + Ok(()) } -pub(crate) fn load(rt: &mut Runtime) -> Result, String> { +pub(crate) fn load(rt: &mut Runtime) -> Result { use load; let v = rt.stack.pop().expect(TINVOTS); @@ -993,7 +993,10 @@ pub(crate) fn load(rt: &mut Runtime) -> Result, String> { &Variable::Str(ref text) => { let mut m = Module::empty(); for f in &rt.module.ext_prelude { - m.add(f.name.clone(), f.f, f.p.clone()); + match f.f { + FnExt::Void(ff) => m.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => m.add(f.name.clone(), ff, f.p.clone()), + } } if let Err(err) = load(text, &mut m) { Variable::Result(Err(Box::new(Error { @@ -1009,17 +1012,20 @@ pub(crate) fn load(rt: &mut Runtime) -> Result, String> { return Err(rt.expected_arg(0, x, "string")); } }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result, String> { +pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result { use load; let modules = rt.stack.pop().expect(TINVOTS); let source = rt.stack.pop().expect(TINVOTS); let mut new_module = Module::empty(); for f in &rt.module.ext_prelude { - new_module.add(f.name.clone(), f.f, f.p.clone()); + match f.f { + FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + } } let x = rt.resolve(&modules); match x { @@ -1034,7 +1040,10 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result, let has_external = new_module.ext_prelude.iter() .any(|a| a.name == f.name); if !has_external { - new_module.add(f.name.clone(), f.f, f.p.clone()); + match f.f { + FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + } } } // Register loaded functions from imports. @@ -1067,10 +1076,10 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result, } x => return Err(rt.expected_arg(0, x, "str")) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result, String> { +pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result { let modules = rt.stack.pop().expect(TINVOTS); let source = rt.stack.pop().expect(TINVOTS); let source = match rt.resolve(&source) { @@ -1084,7 +1093,10 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + } } let x = rt.resolve(&modules); match x { @@ -1099,7 +1111,10 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + } } } // Register loaded functions from imports. @@ -1127,10 +1142,10 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result Result, String> { +pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { // Use the source from calling function. let source = rt.module.functions[rt.call_stack.last().unwrap().index].source.clone(); let args = rt.stack.pop().expect(TINVOTS); @@ -1196,11 +1211,10 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result, String> { } None => return Err(rt.expected_arg(0, x, "Module")) } - - Ok(None) + Ok(()) } -pub(crate) fn call_ret(rt: &mut Runtime) -> Result, String> { +pub(crate) fn call_ret(rt: &mut Runtime) -> Result { // Use the source from calling function. let source = rt.module.functions[rt.call_stack.last().unwrap().index].source.clone(); let args = rt.stack.pop().expect(TINVOTS); @@ -1269,16 +1283,16 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result, String> { None => return Err(rt.expected_arg(0, x, "Module")) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn functions(rt: &mut Runtime) -> Result, String> { +pub(crate) fn functions(rt: &mut Runtime) -> Result { // List available functions in scope. let v = Variable::Array(Arc::new(functions::list_functions(&rt.module))); - Ok(Some(v)) + Ok(v) } -pub(crate) fn functions__module(rt: &mut Runtime) -> Result, String> { +pub(crate) fn functions__module(rt: &mut Runtime) -> Result { // List available functions in scope. let m = rt.stack.pop().expect(TINVOTS); let x = rt.resolve(&m); @@ -1294,58 +1308,58 @@ pub(crate) fn functions__module(rt: &mut Runtime) -> Result, St }; let v = Variable::Array(Arc::new(functions)); - Ok(Some(v)) + Ok(v) } -dyon_fn!{fn none() -> Option {None}} +dyon_fn!{fn none() -> Variable {Variable::Option(None)}} -pub(crate) fn some(rt: &mut Runtime) -> Result, String> { +pub(crate) fn some(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Option(Some(Box::new( rt.resolve(&v).deep_clone(&rt.stack) ))); - Ok(Some(v)) + Ok(v) } -pub(crate) fn ok(rt: &mut Runtime) -> Result, String> { +pub(crate) fn ok(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Result(Ok(Box::new( rt.resolve(&v).deep_clone(&rt.stack) ))); - Ok(Some(v)) + Ok(v) } -pub(crate) fn err(rt: &mut Runtime) -> Result, String> { +pub(crate) fn err(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = Variable::Result(Err(Box::new( Error { message: rt.resolve(&v).deep_clone(&rt.stack), trace: vec![] }))); - Ok(Some(v)) + Ok(v) } -pub(crate) fn is_err(rt: &mut Runtime) -> Result, String> { +pub(crate) fn is_err(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Result(Err(_)) => Variable::bool(true), &Variable::Result(Ok(_)) => Variable::bool(false), x => return Err(rt.expected_arg(0, x, "result")) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn is_ok(rt: &mut Runtime) -> Result, String> { +pub(crate) fn is_ok(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Result(Err(_)) => Variable::bool(false), &Variable::Result(Ok(_)) => Variable::bool(true), x => return Err(rt.expected_arg(0, x, "result")) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn min(rt: &mut Runtime) -> Result, String> { +pub(crate) fn min(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Array(ref arr) => { @@ -1359,10 +1373,10 @@ pub(crate) fn min(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "array")) }; - Ok(Some(Variable::f64(v))) + Ok(Variable::f64(v)) } -pub(crate) fn max(rt: &mut Runtime) -> Result, String> { +pub(crate) fn max(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Array(ref arr) => { @@ -1376,10 +1390,10 @@ pub(crate) fn max(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "array")) }; - Ok(Some(Variable::f64(v))) + Ok(Variable::f64(v)) } -pub(crate) fn unwrap(rt: &mut Runtime) -> Result, String> { +pub(crate) fn unwrap(rt: &mut Runtime) -> Result { use write::{write_variable, EscapeString}; // Return value does not depend on lifetime of argument since @@ -1414,10 +1428,10 @@ pub(crate) fn unwrap(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "some(_) or ok(_)")) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result, String> { +pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result { // Return value does not depend on lifetime of argument since // `ok(x)` and `some(x)` perform a deep clone. let def = rt.stack.pop().expect(TINVOTS); @@ -1429,16 +1443,16 @@ pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result, String> { &Variable::Result(Err(_)) => rt.resolve(&def).clone(), x => return Err(rt.expected_arg(0, x, "some(_) or ok(_)")) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn unwrap_err(rt: &mut Runtime) -> Result, String> { +pub(crate) fn unwrap_err(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); let v = match rt.resolve(&v) { &Variable::Result(Err(ref err)) => err.message.clone(), x => return Err(rt.expected_arg(0, x, "err(_)")) }; - Ok(Some(v)) + Ok(v) } dyon_fn!{fn dir__angle(val: f64) -> Vec4 {Vec4([val.cos() as f32, val.sin() as f32, 0.0, 0.0])}} @@ -1479,7 +1493,7 @@ dyon_fn!{fn syntax__in_string(name: Arc, text: Arc) -> Variable }) }} -pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result, String> { +pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result { use piston_meta::Syntax; let text = rt.stack.pop().expect(TINVOTS); @@ -1510,7 +1524,7 @@ pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result, file: Arc) -> Variable { @@ -1548,7 +1562,7 @@ dyon_fn!{fn save__string_file(text: Arc, file: Arc) -> Variable }} #[cfg(not(feature = "file"))] -pub(crate) fn save__string_file(_: &mut Runtime) -> Result, String> { +pub(crate) fn save__string_file(_: &mut Runtime) -> Result { Err(FILE_SUPPORT_DISABLED.into()) } @@ -1581,7 +1595,7 @@ dyon_fn!{fn load_string__file(file: Arc) -> Variable { }} #[cfg(not(feature = "file"))] -pub(crate) fn load_string__file(_: &mut Runtime) -> Result, String> { +pub(crate) fn load_string__file(_: &mut Runtime) -> Result { Err(FILE_SUPPORT_DISABLED.into()) } @@ -1599,7 +1613,7 @@ dyon_fn!{fn load_string__url(url: Arc) -> Variable { }) }} -pub(crate) fn join__thread(rt: &mut Runtime) -> Result, String> { +pub(crate) fn join__thread(rt: &mut Runtime) -> Result { let thread = rt.stack.pop().expect(TINVOTS); let handle_res = Thread::invalidate_handle(rt, thread); let v = Variable::Result({ @@ -1628,7 +1642,7 @@ pub(crate) fn join__thread(rt: &mut Runtime) -> Result, String> } } }); - Ok(Some(v)) + Ok(v) } dyon_fn!{fn load_data__file(file: Arc) -> Variable { @@ -1661,7 +1675,7 @@ dyon_fn!{fn load_data__string(text: Arc) -> Variable { Variable::Result(res) }} -pub(crate) fn args_os(_rt: &mut Runtime) -> Result, String> { +pub(crate) fn args_os(_rt: &mut Runtime) -> Result { let mut arr: Vec = vec![]; for arg in ::std::env::args_os() { if let Ok(t) = arg.into_string() { @@ -1670,11 +1684,11 @@ pub(crate) fn args_os(_rt: &mut Runtime) -> Result, String> { return Err("Invalid unicode in os argument".into()); } } - Ok(Some(Variable::Array(Arc::new(arr)))) + Ok(Variable::Array(Arc::new(arr))) } #[cfg(feature = "file")] -pub(crate) fn save__data_file(rt: &mut Runtime) -> Result, String> { +pub(crate) fn save__data_file(rt: &mut Runtime) -> Result { use std::error::Error; use std::fs::File; use std::io::BufWriter; @@ -1708,15 +1722,15 @@ pub(crate) fn save__data_file(rt: &mut Runtime) -> Result, Stri })) } }; - Ok(Some(Variable::Result(res))) + Ok(Variable::Result(res)) } #[cfg(not(feature = "file"))] -pub(crate) fn save__data_file(_: &mut Runtime) -> Result, String> { +pub(crate) fn save__data_file(_: &mut Runtime) -> Result { Err(FILE_SUPPORT_DISABLED.into()) } -pub(crate) fn json_from_meta_data(rt: &mut Runtime) -> Result, String> { +pub(crate) fn json_from_meta_data(rt: &mut Runtime) -> Result { use std::error::Error; let meta_data = rt.stack.pop().expect(TINVOTS); @@ -1730,10 +1744,10 @@ pub(crate) fn json_from_meta_data(rt: &mut Runtime) -> Result, } x => return Err(rt.expected_arg(0, x, "array")) }; - Ok(Some(Variable::Str(Arc::new(json)))) + Ok(Variable::Str(Arc::new(json))) } -pub(crate) fn errstr__string_start_len_msg(rt: &mut Runtime) -> Result, String> { +pub(crate) fn errstr__string_start_len_msg(rt: &mut Runtime) -> Result { use piston_meta::ParseErrorHandler; let msg = rt.stack.pop().expect(TINVOTS); @@ -1761,10 +1775,10 @@ pub(crate) fn errstr__string_start_len_msg(rt: &mut Runtime) -> Result Result, String> { +pub(crate) fn has(rt: &mut Runtime) -> Result { let key = rt.stack.pop().expect(TINVOTS); let key = match rt.resolve(&key) { &Variable::Str(ref t) => t.clone(), @@ -1775,10 +1789,10 @@ pub(crate) fn has(rt: &mut Runtime) -> Result, String> { &Variable::Object(ref obj) => obj.contains_key(&key), x => return Err(rt.expected_arg(0, x, "object")) }; - Ok(Some(Variable::bool(res))) + Ok(Variable::bool(res)) } -pub(crate) fn keys(rt: &mut Runtime) -> Result, String> { +pub(crate) fn keys(rt: &mut Runtime) -> Result { let obj = rt.stack.pop().expect(TINVOTS); let res = Variable::Array(Arc::new(match rt.resolve(&obj) { &Variable::Object(ref obj) => { @@ -1786,10 +1800,10 @@ pub(crate) fn keys(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "object")) })); - Ok(Some(res)) + Ok(res) } -pub(crate) fn chars(rt: &mut Runtime) -> Result, String> { +pub(crate) fn chars(rt: &mut Runtime) -> Result { let t = rt.stack.pop().expect(TINVOTS); let t = match rt.resolve(&t) { &Variable::Str(ref t) => t.clone(), @@ -1802,7 +1816,7 @@ pub(crate) fn chars(rt: &mut Runtime) -> Result, String> { Variable::Str(Arc::new(s)) }) .collect::>(); - Ok(Some(Variable::Array(Arc::new(res)))) + Ok(Variable::Array(Arc::new(res))) } dyon_fn!{fn now() -> f64 { @@ -1821,7 +1835,7 @@ dyon_fn!{fn now() -> f64 { dyon_fn!{fn is_nan(v: f64) -> bool {v.is_nan()}} -pub(crate) fn wait_next(rt: &mut Runtime) -> Result, String> { +pub(crate) fn wait_next(rt: &mut Runtime) -> Result { use std::error::Error; let v = rt.stack.pop().expect(TINVOTS); @@ -1838,10 +1852,10 @@ pub(crate) fn wait_next(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "in")) }; - Ok(Some(v)) + Ok(v) } -pub(crate) fn next(rt: &mut Runtime) -> Result, String> { +pub(crate) fn next(rt: &mut Runtime) -> Result { use std::error::Error; let v = rt.stack.pop().expect(TINVOTS); @@ -1858,5 +1872,5 @@ pub(crate) fn next(rt: &mut Runtime) -> Result, String> { } x => return Err(rt.expected_arg(0, x, "in")) }; - Ok(Some(v)) + Ok(v) } diff --git a/src/lib.rs b/src/lib.rs index 725b5937..bc2481ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,24 +327,67 @@ pub enum FnIndex { /// Relative to function you call from. Loaded(isize), /// External function with no return value. - ExternalVoid(FnExternalRef), + ExternalVoid(FnExternalVoidRef), /// Extern function with return value. - ExternalReturn(FnExternalRef), + ExternalReturn(FnExternalReturnRef), /// Extern function with return value and lazy invariant. - ExternalLazy(FnExternalRef, LazyInvariant), + ExternalLazy(FnExternalReturnRef, LazyInvariant), +} + +/// Refers to an external function. +#[derive(Clone, Copy)] +pub enum FnExt { + /// External function with no return value. + Void(fn(&mut Runtime) -> Result<(), String>), + /// External function with return value. + Return(fn(&mut Runtime) -> Result), +} + +impl From Result<(), String>> for FnExt { + fn from(val: fn(&mut Runtime) -> Result<(), String>) -> Self { + FnExt::Void(val) + } +} + +impl From Result> for FnExt { + fn from(val: fn(&mut Runtime) -> Result) -> Self { + FnExt::Return(val) + } +} + +impl fmt::Debug for FnExt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "FnExt") + } } /// Used to store direct reference to external function. #[derive(Copy)] -pub struct FnExternalRef(pub fn(&mut Runtime) -> Result, String>); +pub struct FnExternalReturnRef(pub fn(&mut Runtime) -> Result); + +impl Clone for FnExternalReturnRef { + fn clone(&self) -> FnExternalReturnRef { + *self + } +} + +impl fmt::Debug for FnExternalReturnRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "FnExternalRef") + } +} + +/// Used to store direct reference to external function that does not return anything. +#[derive(Copy)] +pub struct FnExternalVoidRef(pub fn(&mut Runtime) -> Result<(), String>); -impl Clone for FnExternalRef { - fn clone(&self) -> FnExternalRef { +impl Clone for FnExternalVoidRef { + fn clone(&self) -> FnExternalVoidRef { *self } } -impl fmt::Debug for FnExternalRef { +impl fmt::Debug for FnExternalVoidRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "FnExternalRef") } @@ -353,7 +396,7 @@ impl fmt::Debug for FnExternalRef { struct FnExternal { namespace: Arc>>, name: Arc, - f: fn(&mut Runtime) -> Result, String>, + f: FnExt, p: Dfn, } diff --git a/src/macros.rs b/src/macros.rs index 86dbfaef..bf54b726 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -24,49 +24,49 @@ macro_rules! dyon_fn_pop { macro_rules! dyon_fn { (fn $name:ident () -> $rt:ty $b:block) => { #[allow(non_snake_case)] - pub fn $name(_rt: &mut $crate::Runtime) -> Result, String> { + pub fn $name(_rt: &mut $crate::Runtime) -> Result<$crate::Variable, String> { fn inner() -> $rt { $b } - Ok(Some($crate::embed::PushVariable::push_var(&inner()))) + Ok($crate::embed::PushVariable::push_var(&inner())) } }; (fn $name:ident ($($arg:tt : $t:ty),+) -> $rt:ty $b:block) => { dyon_macro_items!{ #[allow(non_snake_case)] - pub fn $name(rt: &mut $crate::Runtime) -> Result, String> { + pub fn $name(rt: &mut $crate::Runtime) -> Result<$crate::Variable, String> { fn inner($($arg: $t),+) -> $rt { $b } dyon_fn_pop!(rt $($arg: $t),+); - Ok(Some($crate::embed::PushVariable::push_var(&inner($($arg),+)))) + Ok($crate::embed::PushVariable::push_var(&inner($($arg),+))) } } }; (fn $name:ident () $b:block) => { #[allow(non_snake_case)] - pub fn $name(_: &mut $crate::Runtime) -> Result, String> { + pub fn $name(_: &mut $crate::Runtime) -> Result<(), String> { fn inner() { $b } inner(); - Ok(None) + Ok(()) } }; (fn $name:ident ($($arg:tt : $t:ty),+) $b:block) => { dyon_macro_items!{ #[allow(non_snake_case)] - pub fn $name(rt: &mut $crate::Runtime) -> Result, String> { + pub fn $name(rt: &mut $crate::Runtime) -> Result<(), String> { fn inner($($arg: $t),+) { $b } dyon_fn_pop!(rt $($arg: $t),+); inner($($arg),+); - Ok(None) + Ok(()) } } }; diff --git a/src/module.rs b/src/module.rs index e20f1a30..4a5a3ab0 100644 --- a/src/module.rs +++ b/src/module.rs @@ -370,12 +370,24 @@ impl Module { if &f.name == name { return if f.p.returns() { if f.p.lazy == LAZY_NO { - FnIndex::ExternalReturn(FnExternalRef(f.f)) + if let FnExt::Return(ff) = f.f { + FnIndex::ExternalReturn(FnExternalReturnRef(ff)) + } else { + FnIndex::None + } } else { - FnIndex::ExternalLazy(FnExternalRef(f.f), f.p.lazy) + if let FnExt::Return(ff) = f.f { + FnIndex::ExternalLazy(FnExternalReturnRef(ff), f.p.lazy) + } else { + FnIndex::None + } } } else { - FnIndex::ExternalVoid(FnExternalRef(f.f)) + if let FnExt::Void(ff) = f.f { + FnIndex::ExternalVoid(FnExternalVoidRef(ff)) + } else { + FnIndex::None + } }; } } @@ -407,31 +419,35 @@ impl Module { } /// Adds a new external prelude function. - pub fn add( + pub fn add( &mut self, name: Arc, - f: fn(&mut Runtime) -> Result, String>, + f: fn(&mut Runtime) -> T, prelude_function: Dfn - ) { + ) + where fn(&mut Runtime) -> T: Into + { self.ext_prelude.push(FnExternal { namespace: self.register_namespace.clone(), name: name.clone(), - f, + f: f.into(), p: prelude_function, }); } /// Adds a new external prelude function. - pub fn add_str( + pub fn add_str( &mut self, name: &str, - f: fn(&mut Runtime) -> Result, String>, + f: fn(&mut Runtime) -> T, prelude_function: Dfn - ) { + ) + where fn(&mut Runtime) -> T: Into + { self.ext_prelude.push(FnExternal { namespace: self.register_namespace.clone(), name: Arc::new(name.into()), - f, + f: f.into(), p: prelude_function, }); } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 41e43d13..5aeaa47a 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1020,10 +1020,11 @@ impl Runtime { /// /// The `loader` flag is set to `true` when called from the outside. fn call_internal(&mut self, call: &ast::Call, loader: bool) -> FlowResult { - use FnExternalRef; + use FnExternalReturnRef; + use FnExternalVoidRef; match call.f_index.get() { - FnIndex::ExternalVoid(FnExternalRef(f)) => { + FnIndex::ExternalVoid(FnExternalVoidRef(f)) => { for arg in &call.args { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => self.stack.push(x), @@ -1044,7 +1045,7 @@ impl Runtime { })?; Ok((None, Flow::Continue)) } - FnIndex::ExternalReturn(FnExternalRef(f)) => { + FnIndex::ExternalReturn(FnExternalReturnRef(f)) => { for arg in &call.args { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => self.stack.push(x), @@ -1054,7 +1055,7 @@ impl Runtime { Expression did not return a value.") }; } - if let Some(x) = (f)(self).map_err(|err| { + Ok((Some((f)(self).map_err(|err| { let range = if let Some(ind) = self.arg_err_index.get() { self.arg_err_index.set(None); call.args[ind].source_range() @@ -1062,13 +1063,9 @@ impl Runtime { call.source_range }; self.module.error(range, &err, self) - })? { - Ok((Some(x), Flow::Continue)) - } else { - Ok((Some(self.stack.pop().expect(TINVOTS)), Flow::Continue)) - } + })?), Flow::Continue)) } - FnIndex::ExternalLazy(FnExternalRef(f), lazy) => { + FnIndex::ExternalLazy(FnExternalReturnRef(f), lazy) => { for (i, arg) in call.args.iter().enumerate() { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => { @@ -1107,7 +1104,7 @@ impl Runtime { Expression did not return a value.") }; } - if let Some(x) = (f)(self).map_err(|err| { + Ok((Some((f)(self).map_err(|err| { let range = if let Some(ind) = self.arg_err_index.get() { self.arg_err_index.set(None); call.args[ind].source_range() @@ -1115,11 +1112,7 @@ impl Runtime { call.source_range }; self.module.error(range, &err, self) - })? { - Ok((Some(x), Flow::Continue)) - } else { - Ok((Some(self.stack.pop().expect(TINVOTS)), Flow::Continue)) - } + })?), Flow::Continue)) } FnIndex::Loaded(f_index) => { use std::sync::atomic::Ordering; From 332a0a03cf910a9045d176413ae00d7c18507ae0 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 14:25:11 +0200 Subject: [PATCH 61/83] Renamed `FnExternalReturnRef` to `FnReturnRef` --- src/ast/mod.rs | 8 ++++---- src/lib.rs | 12 ++++++------ src/module.rs | 4 ++-- src/runtime/mod.rs | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c67addcf..81204ead 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2607,7 +2607,7 @@ impl Call { module: &Module, use_lookup: &UseLookup, ) { - use FnExternalReturnRef; + use FnReturnRef; use FnExternalVoidRef; use FnExt; @@ -2620,7 +2620,7 @@ impl Call { let f = &module.ext_prelude[i]; match f.f { FnExt::Void(ff) => FnIndex::ExternalVoid(FnExternalVoidRef(ff)), - FnExt::Return(ff) => FnIndex::ExternalReturn(FnExternalReturnRef(ff)), + FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), } } } @@ -4366,7 +4366,7 @@ impl In { module: &Module, use_lookup: &UseLookup ) { - use FnExternalReturnRef; + use FnReturnRef; use FnExternalVoidRef; use FnExt; @@ -4378,7 +4378,7 @@ impl In { let f = &module.ext_prelude[i]; match f.f { FnExt::Void(ff) => FnIndex::ExternalVoid(FnExternalVoidRef(ff)), - FnExt::Return(ff) => FnIndex::ExternalReturn(FnExternalReturnRef(ff)), + FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), } } } diff --git a/src/lib.rs b/src/lib.rs index bc2481ae..4a5c7901 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,9 +329,9 @@ pub enum FnIndex { /// External function with no return value. ExternalVoid(FnExternalVoidRef), /// Extern function with return value. - ExternalReturn(FnExternalReturnRef), + ExternalReturn(FnReturnRef), /// Extern function with return value and lazy invariant. - ExternalLazy(FnExternalReturnRef, LazyInvariant), + ExternalLazy(FnReturnRef, LazyInvariant), } /// Refers to an external function. @@ -363,15 +363,15 @@ impl fmt::Debug for FnExt { /// Used to store direct reference to external function. #[derive(Copy)] -pub struct FnExternalReturnRef(pub fn(&mut Runtime) -> Result); +pub struct FnReturnRef(pub fn(&mut Runtime) -> Result); -impl Clone for FnExternalReturnRef { - fn clone(&self) -> FnExternalReturnRef { +impl Clone for FnReturnRef { + fn clone(&self) -> FnReturnRef { *self } } -impl fmt::Debug for FnExternalReturnRef { +impl fmt::Debug for FnReturnRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "FnExternalRef") } diff --git a/src/module.rs b/src/module.rs index 4a5a3ab0..bf1d90b6 100644 --- a/src/module.rs +++ b/src/module.rs @@ -371,13 +371,13 @@ impl Module { return if f.p.returns() { if f.p.lazy == LAZY_NO { if let FnExt::Return(ff) = f.f { - FnIndex::ExternalReturn(FnExternalReturnRef(ff)) + FnIndex::ExternalReturn(FnReturnRef(ff)) } else { FnIndex::None } } else { if let FnExt::Return(ff) = f.f { - FnIndex::ExternalLazy(FnExternalReturnRef(ff), f.p.lazy) + FnIndex::ExternalLazy(FnReturnRef(ff), f.p.lazy) } else { FnIndex::None } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 5aeaa47a..1add1880 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1020,7 +1020,7 @@ impl Runtime { /// /// The `loader` flag is set to `true` when called from the outside. fn call_internal(&mut self, call: &ast::Call, loader: bool) -> FlowResult { - use FnExternalReturnRef; + use FnReturnRef; use FnExternalVoidRef; match call.f_index.get() { @@ -1045,7 +1045,7 @@ impl Runtime { })?; Ok((None, Flow::Continue)) } - FnIndex::ExternalReturn(FnExternalReturnRef(f)) => { + FnIndex::ExternalReturn(FnReturnRef(f)) => { for arg in &call.args { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => self.stack.push(x), @@ -1065,7 +1065,7 @@ impl Runtime { self.module.error(range, &err, self) })?), Flow::Continue)) } - FnIndex::ExternalLazy(FnExternalReturnRef(f), lazy) => { + FnIndex::ExternalLazy(FnReturnRef(f), lazy) => { for (i, arg) in call.args.iter().enumerate() { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => { From cfa11bc0f860d698aff099d28c6303d639f85a5b Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 14:27:57 +0200 Subject: [PATCH 62/83] Renamed `FnExternalVoidRef` to `FnVoidRef` --- src/ast/mod.rs | 8 ++++---- src/lib.rs | 10 +++++----- src/module.rs | 2 +- src/runtime/mod.rs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 81204ead..90316b26 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2608,7 +2608,7 @@ impl Call { use_lookup: &UseLookup, ) { use FnReturnRef; - use FnExternalVoidRef; + use FnVoidRef; use FnExt; let st = stack.len(); @@ -2619,7 +2619,7 @@ impl Call { FnAlias::External(i) => { let f = &module.ext_prelude[i]; match f.f { - FnExt::Void(ff) => FnIndex::ExternalVoid(FnExternalVoidRef(ff)), + FnExt::Void(ff) => FnIndex::ExternalVoid(FnVoidRef(ff)), FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), } } @@ -4367,7 +4367,7 @@ impl In { use_lookup: &UseLookup ) { use FnReturnRef; - use FnExternalVoidRef; + use FnVoidRef; use FnExt; let f_index = if let Some(ref alias) = self.alias { @@ -4377,7 +4377,7 @@ impl In { FnAlias::External(i) => { let f = &module.ext_prelude[i]; match f.f { - FnExt::Void(ff) => FnIndex::ExternalVoid(FnExternalVoidRef(ff)), + FnExt::Void(ff) => FnIndex::ExternalVoid(FnVoidRef(ff)), FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), } } diff --git a/src/lib.rs b/src/lib.rs index 4a5c7901..e3dca1db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,7 +327,7 @@ pub enum FnIndex { /// Relative to function you call from. Loaded(isize), /// External function with no return value. - ExternalVoid(FnExternalVoidRef), + ExternalVoid(FnVoidRef), /// Extern function with return value. ExternalReturn(FnReturnRef), /// Extern function with return value and lazy invariant. @@ -379,15 +379,15 @@ impl fmt::Debug for FnReturnRef { /// Used to store direct reference to external function that does not return anything. #[derive(Copy)] -pub struct FnExternalVoidRef(pub fn(&mut Runtime) -> Result<(), String>); +pub struct FnVoidRef(pub fn(&mut Runtime) -> Result<(), String>); -impl Clone for FnExternalVoidRef { - fn clone(&self) -> FnExternalVoidRef { +impl Clone for FnVoidRef { + fn clone(&self) -> FnVoidRef { *self } } -impl fmt::Debug for FnExternalVoidRef { +impl fmt::Debug for FnVoidRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "FnExternalRef") } diff --git a/src/module.rs b/src/module.rs index bf1d90b6..dccfa1f1 100644 --- a/src/module.rs +++ b/src/module.rs @@ -384,7 +384,7 @@ impl Module { } } else { if let FnExt::Void(ff) = f.f { - FnIndex::ExternalVoid(FnExternalVoidRef(ff)) + FnIndex::ExternalVoid(FnVoidRef(ff)) } else { FnIndex::None } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 1add1880..6464fff5 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1021,10 +1021,10 @@ impl Runtime { /// The `loader` flag is set to `true` when called from the outside. fn call_internal(&mut self, call: &ast::Call, loader: bool) -> FlowResult { use FnReturnRef; - use FnExternalVoidRef; + use FnVoidRef; match call.f_index.get() { - FnIndex::ExternalVoid(FnExternalVoidRef(f)) => { + FnIndex::ExternalVoid(FnVoidRef(f)) => { for arg in &call.args { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => self.stack.push(x), From 7b42297a416d2e99eb2adfabc626742b8e066597 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 14:31:59 +0200 Subject: [PATCH 63/83] Renamed `FnIndex::ExternalVoid` to FnIndex::Void` --- src/ast/mod.rs | 6 +++--- src/dyon_std/mod.rs | 4 ++-- src/lib.rs | 2 +- src/module.rs | 2 +- src/runtime/mod.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 90316b26..92eaf9f7 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2619,7 +2619,7 @@ impl Call { FnAlias::External(i) => { let f = &module.ext_prelude[i]; match f.f { - FnExt::Void(ff) => FnIndex::ExternalVoid(FnVoidRef(ff)), + FnExt::Void(ff) => FnIndex::Void(FnVoidRef(ff)), FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), } } @@ -2638,7 +2638,7 @@ impl Call { stack.push(None); } } - FnIndex::ExternalVoid(_) | + FnIndex::Void(_) | FnIndex::ExternalReturn(_) | FnIndex::ExternalLazy(_, _) => { // Don't push return since last value in block @@ -4377,7 +4377,7 @@ impl In { FnAlias::External(i) => { let f = &module.ext_prelude[i]; match f.f { - FnExt::Void(ff) => FnIndex::ExternalVoid(FnVoidRef(ff)), + FnExt::Void(ff) => FnIndex::Void(FnVoidRef(ff)), FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), } } diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 2404a23d..d73446c0 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -1189,7 +1189,7 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { })?; } FnIndex::None | - FnIndex::ExternalVoid(_) | + FnIndex::Void(_) | FnIndex::ExternalReturn(_) | FnIndex::ExternalLazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) @@ -1258,7 +1258,7 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result { })?; } FnIndex::None | - FnIndex::ExternalVoid(_) | + FnIndex::Void(_) | FnIndex::ExternalReturn(_) | FnIndex::ExternalLazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) diff --git a/src/lib.rs b/src/lib.rs index e3dca1db..eab5683c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,7 +327,7 @@ pub enum FnIndex { /// Relative to function you call from. Loaded(isize), /// External function with no return value. - ExternalVoid(FnVoidRef), + Void(FnVoidRef), /// Extern function with return value. ExternalReturn(FnReturnRef), /// Extern function with return value and lazy invariant. diff --git a/src/module.rs b/src/module.rs index dccfa1f1..f8970ebe 100644 --- a/src/module.rs +++ b/src/module.rs @@ -384,7 +384,7 @@ impl Module { } } else { if let FnExt::Void(ff) = f.f { - FnIndex::ExternalVoid(FnVoidRef(ff)) + FnIndex::Void(FnVoidRef(ff)) } else { FnIndex::None } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 6464fff5..98aff5f9 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1024,7 +1024,7 @@ impl Runtime { use FnVoidRef; match call.f_index.get() { - FnIndex::ExternalVoid(FnVoidRef(f)) => { + FnIndex::Void(FnVoidRef(f)) => { for arg in &call.args { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => self.stack.push(x), From 453aecdd5bbfadac4b06fbb08b5d43f531e96397 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 14:34:14 +0200 Subject: [PATCH 64/83] Renamed `FnIndex::ExternalReturn` to `FnIndex::Return` --- src/ast/mod.rs | 6 +++--- src/dyon_std/mod.rs | 4 ++-- src/lib.rs | 2 +- src/module.rs | 2 +- src/runtime/mod.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 92eaf9f7..57f92cce 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2620,7 +2620,7 @@ impl Call { let f = &module.ext_prelude[i]; match f.f { FnExt::Void(ff) => FnIndex::Void(FnVoidRef(ff)), - FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), + FnExt::Return(ff) => FnIndex::Return(FnReturnRef(ff)), } } } @@ -2639,7 +2639,7 @@ impl Call { } } FnIndex::Void(_) | - FnIndex::ExternalReturn(_) | + FnIndex::Return(_) | FnIndex::ExternalLazy(_, _) => { // Don't push return since last value in block // is used as return value. @@ -4378,7 +4378,7 @@ impl In { let f = &module.ext_prelude[i]; match f.f { FnExt::Void(ff) => FnIndex::Void(FnVoidRef(ff)), - FnExt::Return(ff) => FnIndex::ExternalReturn(FnReturnRef(ff)), + FnExt::Return(ff) => FnIndex::Return(FnReturnRef(ff)), } } } diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index d73446c0..d32d5914 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -1190,7 +1190,7 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { } FnIndex::None | FnIndex::Void(_) | - FnIndex::ExternalReturn(_) | + FnIndex::Return(_) | FnIndex::ExternalLazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) } @@ -1259,7 +1259,7 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result { } FnIndex::None | FnIndex::Void(_) | - FnIndex::ExternalReturn(_) | + FnIndex::Return(_) | FnIndex::ExternalLazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) } diff --git a/src/lib.rs b/src/lib.rs index eab5683c..261bd8be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,7 +329,7 @@ pub enum FnIndex { /// External function with no return value. Void(FnVoidRef), /// Extern function with return value. - ExternalReturn(FnReturnRef), + Return(FnReturnRef), /// Extern function with return value and lazy invariant. ExternalLazy(FnReturnRef, LazyInvariant), } diff --git a/src/module.rs b/src/module.rs index f8970ebe..7d579750 100644 --- a/src/module.rs +++ b/src/module.rs @@ -371,7 +371,7 @@ impl Module { return if f.p.returns() { if f.p.lazy == LAZY_NO { if let FnExt::Return(ff) = f.f { - FnIndex::ExternalReturn(FnReturnRef(ff)) + FnIndex::Return(FnReturnRef(ff)) } else { FnIndex::None } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 98aff5f9..02ebbc7f 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1045,7 +1045,7 @@ impl Runtime { })?; Ok((None, Flow::Continue)) } - FnIndex::ExternalReturn(FnReturnRef(f)) => { + FnIndex::Return(FnReturnRef(f)) => { for arg in &call.args { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => self.stack.push(x), From 2aa081327d019dc2b6192fc3ed09f3f9a2135b49 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 14:38:17 +0200 Subject: [PATCH 65/83] Renamed `FnIndex::ExternalLazy` to `FnIndex::Lazy` --- src/ast/mod.rs | 2 +- src/dyon_std/mod.rs | 4 ++-- src/lib.rs | 2 +- src/module.rs | 2 +- src/runtime/mod.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 57f92cce..64dbfd37 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2640,7 +2640,7 @@ impl Call { } FnIndex::Void(_) | FnIndex::Return(_) | - FnIndex::ExternalLazy(_, _) => { + FnIndex::Lazy(_, _) => { // Don't push return since last value in block // is used as return value. } diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index d32d5914..1dc3dd7f 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -1191,7 +1191,7 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { FnIndex::None | FnIndex::Void(_) | FnIndex::Return(_) | - FnIndex::ExternalLazy(_, _) => + FnIndex::Lazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) } // Use empty range instead of `call.source_range` (from when it was intrinsic). @@ -1260,7 +1260,7 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result { FnIndex::None | FnIndex::Void(_) | FnIndex::Return(_) | - FnIndex::ExternalLazy(_, _) => + FnIndex::Lazy(_, _) => return Err(format!("Could not find function `{}`", fn_name)) } // Use empty range instead of `call.source_range` (from when it was intrinsic). diff --git a/src/lib.rs b/src/lib.rs index 261bd8be..743ccf2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -331,7 +331,7 @@ pub enum FnIndex { /// Extern function with return value. Return(FnReturnRef), /// Extern function with return value and lazy invariant. - ExternalLazy(FnReturnRef, LazyInvariant), + Lazy(FnReturnRef, LazyInvariant), } /// Refers to an external function. diff --git a/src/module.rs b/src/module.rs index 7d579750..60069c82 100644 --- a/src/module.rs +++ b/src/module.rs @@ -377,7 +377,7 @@ impl Module { } } else { if let FnExt::Return(ff) = f.f { - FnIndex::ExternalLazy(FnReturnRef(ff), f.p.lazy) + FnIndex::Lazy(FnReturnRef(ff), f.p.lazy) } else { FnIndex::None } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 02ebbc7f..1e440452 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1065,7 +1065,7 @@ impl Runtime { self.module.error(range, &err, self) })?), Flow::Continue)) } - FnIndex::ExternalLazy(FnReturnRef(f), lazy) => { + FnIndex::Lazy(FnReturnRef(f), lazy) => { for (i, arg) in call.args.iter().enumerate() { match self.expression(arg, Side::Right)? { (Some(x), Flow::Continue) => { From dbb088872162b00a5d5918f97713baf011727c8e Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 16:19:55 +0200 Subject: [PATCH 66/83] Use `todo` list for type check --- src/lifetime/typecheck.rs | 122 +++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 33ed590f..8207a919 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -19,10 +19,7 @@ mod refine; /// Step 1 runs as long any type information is propagated in the graph. /// It stops when no further type information can be inferred. /// -/// This step is necessary to infer types of expressions, and can be quite complicated. -/// Instead of picking the next place to check, it simply loops over all nodes -/// looking for those that have no type information yet. -/// This is not the fastest algorithm, but easy to reason about. +/// This step is necessary to infer types of expressions, and can be quite complex. /// /// When a node gets type information, it will no longer be checked. /// Therefore, some nodes might delay setting a type to itself even it is known @@ -51,7 +48,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> use std::collections::HashMap; // Keep an extra todo-list for nodes that are affected by type refinement. - let mut todo: Vec = vec![]; + let mut todo: Vec = (0..nodes.len()).collect(); // Keep an extra delay-errors map for nodes that should not report an error after all, // if the type refined turned out to match. let mut delay_errs: HashMap> = HashMap::new(); @@ -59,17 +56,11 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> let mut changed; loop { changed = false; - // Prepare todo-list for binary search. - todo.sort(); - todo.dedup(); // Keep track of the old length of the todo-list, // in order to separate tasks already done from new tasks. let todo_len = todo.len(); - 'node: for i in 0..nodes.len() { - if nodes[i].ty.is_some() { - // Check if this node has been affected by type refinement. - if todo[..todo_len].binary_search(&i).is_err() {continue} - } + 'node: for i in 0..todo.len() { + let i = todo[i]; let kind = nodes[i].kind; let mut this_ty = None; match kind { @@ -87,6 +78,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> // If the block is unreachable at the end, // this does not tell anything about the type of the function. if nodes[ch].ty == Some(Type::Unreachable) { + todo.push(i); continue 'node; } // Infer return type from body of function. @@ -95,6 +87,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::CallArg => { if nodes[i].children.is_empty() || nodes[i].item_ids() { + todo.push(i); continue 'node; } let ch = nodes[i].children[0]; @@ -108,8 +101,8 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> // Take into account swizzling for the declared argument position. let j = { let mut sum = 0; - for &p_ch in &nodes[parent].children { - if p_ch == i { break; } + 'inner: for &p_ch in &nodes[parent].children { + if p_ch == i { break 'inner; } if let Some(sw) = nodes[p_ch] .find_child_by_kind(nodes, Kind::Swizzle) { for &sw_ch in &nodes[sw].children { @@ -148,10 +141,10 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> vec![j] }; - for &j in &js { + 'inner2: for &j in &js { if nodes[parent].kind == Kind::CallClosure { // TODO: Check argument type against closure. - continue; + continue 'inner2; } if let Some(decl) = nodes[parent].declaration { let arg = nodes[decl].children[j]; @@ -164,6 +157,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> Expected `{}`, found `{}`", arg_ty.description(), ch_ty.description()))); } + todo.push(i); continue 'node; } } @@ -185,6 +179,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> f.tys[j].description(), ty.description()) )); } + todo.push(i); continue 'node; } } @@ -201,6 +196,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> f.tys[j].description(), ty.description()) )); } + todo.push(i); continue 'node; } } @@ -253,7 +249,10 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::CallClosure => { if let Some(item) = nodes[i].find_child_by_kind(nodes, Kind::Item) { - if nodes[item].item_ids() { continue 'node; } + if nodes[item].item_ids() { + todo.push(i); + continue 'node; + } if let Some(decl) = nodes[item].declaration { if let Some(ref ty) = nodes[decl].ty { if let Some(ty) = ty.closure_ret_ty() { @@ -270,14 +269,23 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::Assign => { let left = match nodes[i].find_child_by_kind(nodes, Kind::Left) { - None => continue, + None => { + todo.push(i); + continue 'node; + } Some(x) => x }; let right = match nodes[i].find_child_by_kind(nodes, Kind::Right) { - None => continue, + None => { + todo.push(i); + continue 'node; + } Some(x) => x }; - if nodes[right].item_ids() { continue 'node; } + if nodes[right].item_ids() { + todo.push(i); + continue 'node; + } nodes[left].ty = match (&nodes[left].ty, &nodes[right].ty) { (&None, &Some(ref right_ty)) => { // Make assign return void since there is no more need for checking. @@ -303,15 +311,22 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } else { // TODO: Type conflict between left and refined right. // Might be caught by later rules. - continue + todo.push(i); + continue 'node; } } - _ => { continue } + _ => { + todo.push(i); + continue 'node; + } }; changed = true; } Kind::Item => { - if nodes[i].item_ids() { continue 'node; } + if nodes[i].item_ids() { + todo.push(i); + continue 'node; + } if let Some(decl) = nodes[i].declaration { match nodes[decl].kind { Kind::Sum | Kind::Min | Kind::Max | @@ -359,11 +374,19 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> Kind::ElseIfCond | Kind::Grab | Kind::Add | Kind::Mul | Kind::Pow => { // TODO: Report error for expected unary operator. - if nodes[i].children.is_empty() { continue 'node; } + if nodes[i].children.is_empty() { + todo.push(i); + continue 'node; + } let ch = nodes[i].children[0]; - if nodes[ch].item_ids() { continue 'node; } + if nodes[ch].item_ids() { + todo.push(i); + continue 'node;} let ty = match nodes[ch].ty { - None => continue 'node, + None => { + todo.push(i); + continue 'node + } Some(ref ty) => ty.clone() }; if nodes[i].kind == Kind::Grab && ty == Type::Void { @@ -404,10 +427,14 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::Compare => { let left = match nodes[i].find_child_by_kind(nodes, Kind::Left) { - None => continue 'node, + None => { + todo.push(i); + continue 'node; + } Some(x) => x }; if nodes[left].item_ids() { + todo.push(i); continue 'node; } match nodes[left].ty { @@ -428,7 +455,10 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> this_ty = Some(Type::Void); } if let Some(&ch) = nodes[i].children.last() { - if nodes[ch].item_ids() { continue 'node; } + if nodes[ch].item_ids() { + todo.push(i); + continue 'node; + } if let Some(ref ty) = nodes[ch].ty { this_ty = Some(nodes[i].inner_type(ty)); } @@ -439,6 +469,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> let ch = if let Some(ch) = nodes[i].find_child_by_kind(nodes, Kind::Block) { ch } else { + todo.push(i); continue 'node; }; if let Some(ref ty) = nodes[ch].ty { @@ -446,9 +477,15 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } } Kind::X | Kind::Y | Kind::Z | Kind::W => { - if nodes[i].children.is_empty() { continue 'node; } + if nodes[i].children.is_empty() { + todo.push(i); + continue 'node; + } let ch = nodes[i].children[0]; - if nodes[ch].item_ids() { continue 'node; } + if nodes[ch].item_ids() { + todo.push(i); + continue 'node; + } let expr_type = nodes[ch].ty.as_ref().map(|ty| nodes[i].inner_type(&ty)); if let Some(ref ty) = expr_type { @@ -462,19 +499,30 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } Kind::If => { let tb = match nodes[i].find_child_by_kind(nodes, Kind::TrueBlock) { - None => continue 'node, + None => { + todo.push(i); + continue 'node; + } Some(tb) => tb }; let true_type = match nodes[tb].ty.as_ref() .map(|ty| nodes[i].inner_type(&ty)) { - None => continue 'node, + None => { + todo.push(i); + continue 'node; + } Some(true_type) => true_type }; this_ty = Some(true_type); } Kind::Arg => { - this_ty = Some(Type::Any); + if nodes[i].ty.is_none() { + this_ty = Some(Type::Any); + } else { + // No further work needed for this node. + continue 'node; + } } Kind::Closure => { let mut lts = vec![]; @@ -528,6 +576,8 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } nodes[i].ty = this_ty; changed = true; + } else { + todo.push(i); } } @@ -537,6 +587,10 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> // even with type refinement, because the todo-list // waits for other changes to happen. if !changed { break; } + + // Prepare todo-list for binary search. + todo.sort(); + todo.dedup(); } // Report one delayed error, if any. From ba19536d6424858db3270cb9d693f31b41b31e31 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 16:46:10 +0200 Subject: [PATCH 67/83] Reduce work in type checker --- src/lifetime/typecheck.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 8207a919..f0a4466b 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -64,6 +64,10 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> let kind = nodes[i].kind; let mut this_ty = None; match kind { + // No further work required for these statements. + Kind::Uses | + Kind::Start | + Kind::End => continue 'node, Kind::Go => { // Infer thread type from function. if !nodes[i].children.is_empty() { @@ -74,6 +78,10 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> } } Kind::Fn => { + if nodes[i].ty.is_some() { + // No further work is required for function. + continue 'node; + } if let Some(ch) = nodes[i].find_child_by_kind(nodes, Kind::Expr) { // If the block is unreachable at the end, // this does not tell anything about the type of the function. @@ -373,9 +381,8 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> Kind::Exp | Kind::Base | Kind::Left | Kind::Right | Kind::ElseIfCond | Kind::Grab | Kind::Add | Kind::Mul | Kind::Pow => { - // TODO: Report error for expected unary operator. if nodes[i].children.is_empty() { - todo.push(i); + // No further work is required. continue 'node; } let ch = nodes[i].children[0]; From da655f592b603913a197d190824b95415e1b63d0 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Mon, 23 Sep 2019 23:54:34 +0200 Subject: [PATCH 68/83] Check `Kind::N` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added “typechk/arr_fail_2.dyon” --- source/typechk/arr_fail_2.dyon | 3 +++ src/lifetime/typecheck.rs | 2 +- tests/lib.rs | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 source/typechk/arr_fail_2.dyon diff --git a/source/typechk/arr_fail_2.dyon b/source/typechk/arr_fail_2.dyon new file mode 100644 index 00000000..01f76164 --- /dev/null +++ b/source/typechk/arr_fail_2.dyon @@ -0,0 +1,3 @@ +fn main() { + println([0; false]) +} diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index f0a4466b..9200ac60 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -483,7 +483,7 @@ pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> this_ty = Some(Type::Array(Box::new(ty.clone()))); } } - Kind::X | Kind::Y | Kind::Z | Kind::W => { + Kind::X | Kind::Y | Kind::Z | Kind::W | Kind::N => { if nodes[i].children.is_empty() { todo.push(i); continue 'node; diff --git a/tests/lib.rs b/tests/lib.rs index 2390bdea..2068315f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -180,6 +180,7 @@ fn test_typechk() { test_src("source/typechk/obj_pass_1.dyon"); test_src("source/typechk/arr_pass_1.dyon"); test_fail_src("source/typechk/arr_fail_1.dyon"); + test_fail_src("source/typechk/arr_fail_2.dyon"); test_fail_src("source/typechk/go.dyon"); test_fail_src("source/typechk/unused_result.dyon"); test_fail_src("source/typechk/unused_result_2.dyon"); From 9886f893bb29d4fcf9ae144a45e051dc9245bab5 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 24 Sep 2019 21:40:50 +0200 Subject: [PATCH 69/83] Removed unnecessary check of arguments This case is covered by the type checker. --- src/runtime/mod.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 1e440452..a7be413e 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1124,13 +1124,6 @@ impl Runtime { // Copy the module to avoid problems with borrow checker. let mod_copy = self.module.clone(); let ref f = mod_copy.functions[new_index]; - if call.arg_len() != f.args.len() { - return Err(self.module.error(call.source_range, - &format!("{}\nExpected {} arguments but found {}", - self.stack_trace(), - f.args.len(), - call.arg_len()), self)); - } // Arguments must be computed. if f.returns() { // Add return value before arguments on the stack. From aec0bacc4efbfc709d24f981a771fa265157ce92 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Tue, 24 Sep 2019 23:20:18 +0200 Subject: [PATCH 70/83] Simplify `Kind::Add` and `Kind::Mul` --- src/lifetime/mod.rs | 9 ++++++++- src/lifetime/node.rs | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 308be1c6..bbf65707 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -102,7 +102,7 @@ pub fn check( } } else if nodes[i].binops.len() == 1 && nodes[i].children.len() == 2 { use ast::BinOp::*; - + Node::rewrite_binop(i, match nodes[i].binops[0] { Add => crate::ADD.clone(), Sub => crate::SUB.clone(), @@ -118,6 +118,13 @@ pub fn check( } else if nodes[i].kind == Kind::Pow && nodes[i].children.len() == 2 { Node::rewrite_binop(i, crate::POW.clone(), &mut nodes); } + + if nodes[i].children.len() == 1 { + match nodes[i].kind { + Kind::Add | Kind::Mul => Node::simplify(i, &mut nodes), + _ => {} + } + } } // After graph rewrite, the graph might be unnormalized. diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 1f594f1e..f948e670 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -117,6 +117,26 @@ impl Node { nodes[i].children[1] = right; } + /// Simplifies a node by linking child with grand-parent. + /// + /// Removes the node that gets simplified. + pub fn simplify(i: usize, nodes: &mut Vec) { + if let Some(parent) = nodes[i].parent { + // Link child to grand-parent. + let ch = nodes[i].children[0]; + nodes[ch].parent = Some(parent); + for p_ch in &mut nodes[parent].children { + if *p_ch == i { + *p_ch = ch; + } + } + + // Disable this node. + nodes[i].parent = None; + nodes[i].children.clear(); + } + } + #[allow(dead_code)] pub fn print(&self, nodes: &[Node], indent: u32) { for _ in 0..indent { print!(" ") } From 8e8f9627cf138f8e2a01359542e91423acc42354 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 25 Sep 2019 01:28:33 +0200 Subject: [PATCH 71/83] Made compare operations standard external functions --- src/ast/infer_len.rs | 6 -- src/ast/mod.rs | 38 +++++----- src/ast/replace.rs | 9 --- src/dyon_std/mod.rs | 83 ++++++++++++++++++++++ src/grab.rs | 14 ---- src/lib.dyon | 42 ++++++++++++ src/lib.rs | 6 ++ src/module.rs | 78 +++++++++++++++++++++ src/runtime/mod.rs | 160 ------------------------------------------- src/write.rs | 36 +++++++--- 10 files changed, 256 insertions(+), 216 deletions(-) diff --git a/src/ast/infer_len.rs b/src/ast/infer_len.rs index 932c7ad0..4b4aef67 100644 --- a/src/ast/infer_len.rs +++ b/src/ast/infer_len.rs @@ -202,12 +202,6 @@ fn infer_expr( if res.is_some() { return res; } } } - Compare(ref cmp_expr) => { - let left = infer_expr(&cmp_expr.left, name, decls); - if left.is_some() { return left; } - let right = infer_expr(&cmp_expr.right, name, decls); - if right.is_some() { return right; } - } Variable(_) => {} Try(ref expr) => { let res = infer_expr(expr, name, decls); diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 64dbfd37..f6d47ca8 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1050,8 +1050,6 @@ pub enum Expression { LinkIn(Box), /// If-expression. If(Box), - /// Compare expression. - Compare(Box), /// Variable. /// /// This means it contains no members that depends on other expressions. @@ -1305,7 +1303,7 @@ impl Expression { } else if let Ok((range, val)) = Compare::from_meta_data( file, source, convert, ignored) { convert.update(range); - result = Some(Expression::Compare(Box::new(val))); + result = Some(val.into_expression()); } else if let Ok((range, _)) = convert.meta_bool("try") { convert.update(range); result = Some(Expression::Try(Box::new(result.unwrap()))); @@ -1408,7 +1406,6 @@ impl Expression { LinkFor(ref for_n_expr) => for_n_expr.source_range, LinkIn(ref for_in_expr) => for_in_expr.source_range, If(ref if_expr) => if_expr.source_range, - Compare(ref comp) => comp.source_range, Variable(ref range_var) => range_var.0, Try(ref expr) => expr.source_range(), Swizzle(ref swizzle) => swizzle.source_range, @@ -1505,8 +1502,6 @@ impl Expression { for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), If(ref if_expr) => if_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Compare(ref comp) => - comp.resolve_locals(relative, stack, closure_stack, module, use_lookup), Variable(_) => {} Try(ref expr) => expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), @@ -4261,19 +4256,24 @@ impl Compare { })) } - fn resolve_locals( - &self, - relative: usize, - stack: &mut Vec>>, - closure_stack: &mut Vec, - module: &Module, - use_lookup: &UseLookup, - ) { - let st = stack.len(); - self.left.resolve_locals(relative, stack, closure_stack, module, use_lookup); - stack.truncate(st); - self.right.resolve_locals(relative, stack, closure_stack, module, use_lookup); - stack.truncate(st); + fn into_expression(self) -> Expression { + use self::CompareOp::*; + + Expression::Call(Box::new(Call { + alias: None, + name: match self.op { + Less => crate::LESS.clone(), + LessOrEqual => crate::LESS_OR_EQUAL.clone(), + Greater => crate::GREATER.clone(), + GreaterOrEqual => crate::GREATER_OR_EQUAL.clone(), + Equal => crate::EQUAL.clone(), + NotEqual => crate::NOT_EQUAL.clone(), + }, + args: vec![self.left, self.right], + custom_source: None, + f_index: Cell::new(FnIndex::None), + source_range: self.source_range, + })) } } diff --git a/src/ast/replace.rs b/src/ast/replace.rs index 024a4e1b..cbf822b1 100644 --- a/src/ast/replace.rs +++ b/src/ast/replace.rs @@ -8,7 +8,6 @@ use super::{ Block, Call, CallClosure, - Compare, Expression, For, ForN, @@ -309,14 +308,6 @@ pub fn number(expr: &Expression, name: &Arc, val: f64) -> Expression { source_range: if_expr.source_range, })) } - E::Compare(ref cmp_expr) => { - E::Compare(Box::new(Compare { - op: cmp_expr.op, - left: number(&cmp_expr.left, name, val), - right: number(&cmp_expr.right, name, val), - source_range: cmp_expr.source_range, - })) - } E::Variable(_) => expr.clone(), E::Try(ref expr) => E::Try(Box::new(number(expr, name, val))), E::Swizzle(ref swizzle_expr) => { diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 1dc3dd7f..23432252 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -38,6 +38,89 @@ pub(crate) fn or_else(rt: &mut Runtime) -> Result { Ok(r) } +pub(crate) fn less(rt: &mut Runtime) -> Result { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&F64(a, ref sec), &F64(b, _)) => Bool(a < b, sec.clone()), + (&Str(ref a), &Str(ref b)) => Variable::bool(a < b), + _ => return Err("Expected `f64` or `str`".into()) + }; + Ok(r) +} + +pub(crate) fn less_or_equal(rt: &mut Runtime) -> Result { + use Variable::*; + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + let r = match (rt.resolve(&a), rt.resolve(&b)) { + (&F64(a, ref sec), &F64(b, _)) => Bool(a <= b, sec.clone()), + (&Str(ref a), &Str(ref b)) => Variable::bool(a <= b), + _ => return Err("Expected `f64` or `str`".into()) + }; + Ok(r) +} + +pub(crate) fn greater(rt: &mut Runtime) -> Result { + less_or_equal(rt).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { + panic!("Expected equal to return `bool`") + }) +} + +pub(crate) fn greater_or_equal(rt: &mut Runtime) -> Result { + less(rt).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { + panic!("Expected equal to return `bool`") + }) +} + +pub(crate) fn equal(rt: &mut Runtime) -> Result { + fn sub_eq(a: &Variable, b: &Variable) -> Result { + use Variable::*; + + Ok(match (a, b) { + (&F64(a, ref sec), &F64(b, _)) => Bool(a == b, sec.clone()), + (&Str(ref a), &Str(ref b)) => Variable::bool(a == b), + (&Bool(a, ref sec), &Bool(b, _)) => Bool(a == b, sec.clone()), + (&Vec4(a), &Vec4(b)) => Variable::bool(a == b), + (&Object(ref a), &Object(ref b)) => { + Variable::bool(a.len() == b.len() && + a.iter().all(|a| { + if let Some(b_val) = b.get(a.0) { + if let Ok(Variable::Bool(true, _)) = + sub_eq(&a.1, b_val) {true} + else {false} + } else {false} + })) + } + (&Array(ref a), &Array(ref b)) => { + Variable::bool(a.len() == b.len() && + a.iter().zip(b.iter()).all(|(a, b)| { + if let Ok(Variable::Bool(true, _)) = + sub_eq(a, b) {true} else {false} + })) + } + (&Option(None), &Option(None)) => Variable::bool(true), + (&Option(None), &Option(_)) => Variable::bool(false), + (&Option(_), &Option(None)) => Variable::bool(false), + (&Option(Some(ref a)), &Option(Some(ref b))) => sub_eq(a, b)?, + _ => return Err("Expected `f64`, `str`, `bool`, `vec4`, `{}`, `[]` or `opt`".into()) + }) + } + + let b = rt.stack.pop().expect(TINVOTS); + let a = rt.stack.pop().expect(TINVOTS); + sub_eq(rt.resolve(&a), rt.resolve(&b)) +} + +pub(crate) fn not_equal(rt: &mut Runtime) -> Result { + equal(rt).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { + panic!("Expected equal to return `bool`") + }) +} + pub(crate) fn add(rt: &mut Runtime) -> Result { use Variable::*; diff --git a/src/grab.rs b/src/grab.rs index dacb436f..5c4a986d 100644 --- a/src/grab.rs +++ b/src/grab.rs @@ -101,20 +101,6 @@ pub fn grab_expr( source_range: assign.source_range, }))), Flow::Continue)) }, - E::Compare(ref compare) => { - Ok((Grabbed::Expression(E::Compare(Box::new(ast::Compare { - op: compare.op, - left: match grab_expr(level, rt, &compare.left, side) { - Ok((Grabbed::Expression(x), Flow::Continue)) => x, - x => return x, - }, - right: match grab_expr(level, rt, &compare.right, side) { - Ok((Grabbed::Expression(x), Flow::Continue)) => x, - x => return x, - }, - source_range: compare.source_range, - }))), Flow::Continue)) - } E::If(ref if_expr) => { Ok((Grabbed::Expression(E::If(Box::new(ast::If { cond: match grab_expr(level, rt, &if_expr.cond, side) { diff --git a/src/lib.dyon b/src/lib.dyon index 24e704ac..a3272b89 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -1,5 +1,47 @@ ns std +/// Less. +fn less(a: any, b: any) -> bool { ... } + (sec[f64], f64) -> sec[bool] + (f64, f64) -> bool + (str, str) -> bool + +/// Less or equal. +fn less_or_equal(a: any, b: any) -> bool { ... } + (sec[f64], f64) -> sec[bool] + (f64, f64) -> bool + (str, str) -> bool + +/// Greater. +fn greater(a: any, b: any) -> bool { ... } + (sec[f64], f64) -> sec[bool] + (f64, f64) -> bool + (str, str) -> bool + +/// Greater or equal. +fn greater_or_equal(a: any, b: any) -> bool { ... } + (sec[f64], f64) -> sec[bool] + (f64, f64) -> bool + (str, str) -> bool + +/// Equal. +fn equal(a: any, b: any) -> bool { ... } + (sec[f64], f64) -> sec[bool] + (f64, f64) -> bool + (str, str) -> bool + (sec[bool], bool) -> sec[bool] + (bool, bool) -> bool + (vec4, vec4) -> bool + ({}, {}) -> bool + ([], []) -> bool + (opt, opt) -> bool + +/// Not equal. +fn not_equal(a: any, b: any) -> bool { ... } + (sec[f64], f64) -> sec[bool] + (f64, f64) -> bool + (str, str) -> bool + /// Lazy AND (`&&`). fn and_also(a: bool => false, b: bool) -> any { ... } (sec[bool], bool) -> sec[bool] diff --git a/src/lib.rs b/src/lib.rs index 743ccf2e..c6aa82a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,12 @@ pub use module::Module; pub const TINVOTS: &str = "There is no value on the stack"; lazy_static!{ + pub(crate) static ref LESS: Arc = Arc::new("less".into()); + pub(crate) static ref LESS_OR_EQUAL: Arc = Arc::new("less_or_equal".into()); + pub(crate) static ref GREATER: Arc = Arc::new("greater".into()); + pub(crate) static ref GREATER_OR_EQUAL: Arc = Arc::new("greater_or_equal".into()); + pub(crate) static ref EQUAL: Arc = Arc::new("equal".into()); + pub(crate) static ref NOT_EQUAL: Arc = Arc::new("not_equal".into()); pub(crate) static ref AND_ALSO: Arc = Arc::new("and_also".into()); pub(crate) static ref OR_ELSE: Arc = Arc::new("or_else".into()); pub(crate) static ref ADD: Arc = Arc::new("add".into()); diff --git a/src/module.rs b/src/module.rs index 60069c82..02421b53 100644 --- a/src/module.rs +++ b/src/module.rs @@ -29,6 +29,84 @@ impl Module { let mut m = Module::empty(); m.ns("std"); + m.add_str("less", less, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any, Any], + ret: Bool, + ext: vec![ + (vec![], vec![Secret(Box::new(F64)), F64], Secret(Box::new(Bool))), + (vec![], vec![F64; 2], Bool), + (vec![], vec![Str; 2], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("less_or_equal", less_or_equal, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any, Any], + ret: Bool, + ext: vec![ + (vec![], vec![Secret(Box::new(F64)), F64], Secret(Box::new(Bool))), + (vec![], vec![F64; 2], Bool), + (vec![], vec![Str; 2], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("greater", greater, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any, Any], + ret: Bool, + ext: vec![ + (vec![], vec![Secret(Box::new(F64)), F64], Secret(Box::new(Bool))), + (vec![], vec![F64; 2], Bool), + (vec![], vec![Str; 2], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("greater_or_equal", greater_or_equal, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any, Any], + ret: Bool, + ext: vec![ + (vec![], vec![Secret(Box::new(F64)), F64], Secret(Box::new(Bool))), + (vec![], vec![F64; 2], Bool), + (vec![], vec![Str; 2], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("equal", equal, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any, Any], + ret: Bool, + ext: vec![ + (vec![], vec![Secret(Box::new(F64)), F64], Secret(Box::new(Bool))), + (vec![], vec![F64; 2], Bool), + (vec![], vec![Str; 2], Bool), + (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), + (vec![], vec![Bool; 2], Bool), + (vec![], vec![Vec4; 2], Bool), + (vec![], vec![Type::object(), Type::object()], Bool), + (vec![], vec![Type::array(), Type::array()], Bool), + (vec![], vec![Type::option(), Type::option()], Bool), + ], + lazy: LAZY_NO + }); + m.add_str("not_equal", not_equal, Dfn { + lts: vec![Lt::Default; 2], + tys: vec![Any, Any], + ret: Bool, + ext: vec![ + (vec![], vec![Secret(Box::new(F64)), F64], Secret(Box::new(Bool))), + (vec![], vec![F64; 2], Bool), + (vec![], vec![Str; 2], Bool), + (vec![], vec![Secret(Box::new(Bool)), Bool], Secret(Box::new(Bool))), + (vec![], vec![Bool; 2], Bool), + (vec![], vec![Vec4; 2], Bool), + (vec![], vec![Type::object(), Type::object()], Bool), + (vec![], vec![Type::array(), Type::array()], Bool), + (vec![], vec![Type::option(), Type::option()], Bool), + ], + lazy: LAZY_NO + }); m.add_str("and_also", and_also, Dfn { lts: vec![Lt::Default; 2], tys: vec![Bool, Bool], diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index a7be413e..bcfc08f4 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -538,7 +538,6 @@ impl Runtime { LinkFor(ref for_n_expr) => self.link_for_n_expr(for_n_expr), LinkIn(ref for_in_expr) => self.link_for_in_expr(for_in_expr), If(ref if_expr) => self.if_expr(if_expr), - Compare(ref compare) => self.compare(compare), Variable(ref range_var) => Ok((Some(range_var.1.clone()), Flow::Continue)), Try(ref expr) => self.try(expr, side), Swizzle(ref sw) => { @@ -2212,165 +2211,6 @@ impl Runtime { stack.truncate(start_stack_len); Ok((Some(v), Flow::Continue)) } - - fn compare(&mut self, compare: &ast::Compare) -> FlowResult { - fn sub_compare( - rt: &Runtime, - compare: &ast::Compare, - a: &Variable, - b: &Variable - ) -> Result { - use ast::CompareOp::*; - - match (rt.resolve(&b), rt.resolve(&a)) { - (&Variable::F64(b, _), &Variable::F64(a, ref sec)) => { - Ok(Variable::Bool(match compare.op { - Less => a < b, - LessOrEqual => a <= b, - Greater => a > b, - GreaterOrEqual => a >= b, - Equal => a == b, - NotEqual => a != b - }, sec.clone())) - } - (&Variable::Str(ref b), &Variable::Str(ref a)) => { - Ok(Variable::bool(match compare.op { - Less => a < b, - LessOrEqual => a <= b, - Greater => a > b, - GreaterOrEqual => a >= b, - Equal => a == b, - NotEqual => a != b - })) - } - (&Variable::Bool(b, _), &Variable::Bool(a, ref sec)) => { - Ok(Variable::Bool(match compare.op { - Equal => a == b, - NotEqual => a != b, - x => return Err(rt.module.error(compare.source_range, - &format!("{}\n`{}` can not be used with bools", - rt.stack_trace(), - x.symbol()), rt)) - }, sec.clone())) - } - (&Variable::Vec4(ref b), &Variable::Vec4(ref a)) => { - Ok(Variable::bool(match compare.op { - Equal => a == b, - NotEqual => a != b, - x => return Err(rt.module.error(compare.source_range, - &format!("{}\n`{}` can not be used with vec4s", - rt.stack_trace(), - x.symbol()), rt)) - })) - } - (&Variable::Object(ref b), &Variable::Object(ref a)) => { - Ok(Variable::bool(match compare.op { - Equal => { - a.len() == b.len() && - a.iter().all(|a| { - if let Some(b_val) = b.get(a.0) { - if let Ok(Variable::Bool(true, _)) = - sub_compare(rt, compare, &a.1, b_val) {true} - else {false} - } else {false} - }) - } - NotEqual => { - a.len() != b.len() || - a.iter().any(|a| { - if let Some(b_val) = b.get(a.0) { - if let Ok(Variable::Bool(false, _)) = - sub_compare(rt, compare, &a.1, b_val) {false} - else {true} - } else {true} - }) - } - x => return Err(rt.module.error(compare.source_range, - &format!("{}\n`{}` can not be used with objects", - rt.stack_trace(), - x.symbol()), rt)) - })) - } - (&Variable::Array(ref b), &Variable::Array(ref a)) => { - Ok(Variable::bool(match compare.op { - Equal => { - a.len() == b.len() && - a.iter().zip(b.iter()).all(|(a, b)| { - if let Ok(Variable::Bool(true, _)) = - sub_compare(rt, compare, a, b) {true} else {false} - }) - } - NotEqual => { - a.len() != b.len() || - a.iter().zip(b.iter()).any(|(a, b)| { - if let Ok(Variable::Bool(false, _)) = - sub_compare(rt, compare, a, b) {false} else {true} - }) - } - x => return Err(rt.module.error(compare.source_range, - &format!("{}\n`{}` can not be used with arrays", - rt.stack_trace(), - x.symbol()), rt)) - })) - } - (&Variable::Option(None), &Variable::Option(None)) => { - Ok(Variable::bool(match compare.op { - Equal => true, - NotEqual => false, - x => return Err(rt.module.error(compare.source_range, - &format!("{}\n`{}` can not be used with options", - rt.stack_trace(), - x.symbol()), rt)) - })) - } - (&Variable::Option(None), &Variable::Option(_)) => { - Ok(Variable::bool(match compare.op { - Equal => false, - NotEqual => true, - x => return Err(rt.module.error(compare.source_range, - &format!("{}\n`{}` can not be used with options", - rt.stack_trace(), - x.symbol()), rt)) - })) - } - (&Variable::Option(_), &Variable::Option(None)) => { - Ok(Variable::bool(match compare.op { - Equal => false, - NotEqual => true, - x => return Err(rt.module.error(compare.source_range, - &format!("{}\n`{}` can not be used with options", - rt.stack_trace(), - x.symbol()), rt)) - })) - } - (&Variable::Option(Some(ref b)), - &Variable::Option(Some(ref a))) => { - sub_compare(rt, compare, a, b) - } - (b, a) => Err(rt.module.error(compare.source_range, - &format!( - "{}\n`{}` can not be used with `{}` and `{}`", - rt.stack_trace(), - compare.op.symbol(), - a.typeof_var(), - b.typeof_var()), rt)) - } - } - - let left = match self.expression(&compare.left, Side::Right)? { - (Some(x), Flow::Continue) => x, - (x, Flow::Return) => { return Ok((x, Flow::Return)); } - _ => return self.err(compare.left.source_range(), - "Expected something from the left argument") - }; - let right = match self.expression(&compare.right, Side::Right)? { - (Some(x), Flow::Continue) => x, - (x, Flow::Return) => return Ok((x, Flow::Return)), - _ => return self.err(compare.right.source_range(), - "Expected something from the right argument") - }; - Ok((Some(sub_compare(self, compare, &left, &right)?), Flow::Continue)) - } fn if_expr( &mut self, if_expr: &ast::If diff --git a/src/write.rs b/src/write.rs index 59801577..32b2ec50 100644 --- a/src/write.rs +++ b/src/write.rs @@ -210,6 +210,8 @@ fn write_expr( write_neg(w, rt, &call.args[0], tabs)? } else if let Some(op) = standard_binop(call) { write_binop(w, rt, op, &call.args[0], &call.args[1], tabs)? + } else if let Some(op) = standard_compare(call) { + write_compare(w, rt, op, &call.args[0], &call.args[1], tabs)? } else { write_call(w, rt, call, tabs)? } @@ -242,7 +244,6 @@ fn write_expr( E::Vec4(ref vec4) => write_vec4(w, rt, vec4, tabs)?, E::Mat4(ref mat4) => write_mat4(w, rt, mat4, tabs)?, E::For(ref f) => write_for(w, rt, f, tabs)?, - E::Compare(ref comp) => write_compare(w, rt, comp, tabs)?, E::ForN(ref for_n) => { write!(w, "for ")?; write_for_n(w, rt, for_n, tabs)?; @@ -386,7 +387,6 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo use ast::Expression as E; match *expr { - E::Compare(_) => true, E::Call(ref call) => { if let Some(binop_op) = standard_binop(call) { match (op.precedence(), binop_op.precedence()) { @@ -396,6 +396,8 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo (1, 1) if right => true, _ => false } + } else if let Some(_) = standard_compare(call) { + true } else {false} } _ => false @@ -733,6 +735,22 @@ fn standard_binop(call: &ast::Call) -> Option { }) } +fn standard_compare(call: &ast::Call) -> Option { + use ast::CompareOp::*; + + if call.args.len() != 2 {return None}; + + Some(match &**call.name { + "less" => Less, + "less_or_equal" => LessOrEqual, + "greater" => Greater, + "greater_or_equal" => GreaterOrEqual, + "equal" => Equal, + "not_equal" => NotEqual, + _ => return None, + }) +} + fn compare_needs_parent(expr: &ast::Expression) -> bool { use ast::Expression as E; @@ -745,24 +763,26 @@ fn compare_needs_parent(expr: &ast::Expression) -> bool { fn write_compare( w: &mut W, rt: &Runtime, - comp: &ast::Compare, + op: ast::CompareOp, + left: &ast::Expression, + right: &ast::Expression, tabs: u32, ) -> Result<(), io::Error> { - let left_needs_parens = compare_needs_parent(&comp.left); - let right_needs_parens = compare_needs_parent(&comp.right); + let left_needs_parens = compare_needs_parent(left); + let right_needs_parens = compare_needs_parent(right); if left_needs_parens { write!(w, "(")?; } - write_expr(w, rt, &comp.left, tabs)?; + write_expr(w, rt, left, tabs)?; if left_needs_parens { write!(w, ")")?; } - write!(w, " {} ", comp.op.symbol())?; + write!(w, " {} ", op.symbol())?; if right_needs_parens { write!(w, "(")?; } - write_expr(w, rt, &comp.right, tabs)?; + write_expr(w, rt, right, tabs)?; if right_needs_parens { write!(w, ")")?; } From 4a8f706eb5a1f27493da15e89f6b0d0eaf039ed6 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 25 Sep 2019 16:40:50 +0200 Subject: [PATCH 72/83] Split calls --- src/ast/infer_len.rs | 16 +- src/ast/mod.rs | 394 +++++++++++++++-------- src/ast/replace.rs | 8 +- src/dyon_std/lifetimechk.rs | 3 +- src/dyon_std/mod.rs | 24 +- src/grab.rs | 74 ++++- src/runtime/mod.rs | 615 +++++++++++++++++++----------------- src/write.rs | 74 +++-- 8 files changed, 726 insertions(+), 482 deletions(-) diff --git a/src/ast/infer_len.rs b/src/ast/infer_len.rs index 4b4aef67..d67bec0f 100644 --- a/src/ast/infer_len.rs +++ b/src/ast/infer_len.rs @@ -1,4 +1,3 @@ -use std::cell::Cell; use std::sync::Arc; use FnIndex; @@ -6,6 +5,7 @@ use super::{ AssignOp, Block, Call, + CallInfo, CallClosure, Expression, ForN, @@ -19,14 +19,16 @@ pub fn infer(block: &Block, name: &str) -> Option { let res = list.map(|item| { let source_range = item.source_range; Expression::Call(Box::new(Call { - alias: None, - name: Arc::new("len".into()), - f_index: Cell::new(FnIndex::None), + f_index: FnIndex::None, args: vec![ Expression::Item(Box::new(item)) ], custom_source: None, - source_range, + info: Box::new(CallInfo { + alias: None, + name: Arc::new("len".into()), + source_range, + }) })) }); res @@ -93,6 +95,10 @@ fn infer_expr( let res = infer_call(call, name, decls); if res.is_some() { return res; } } + CallVoid(_) => unimplemented!("`CallVoid` is transformed from `Call` later"), + CallReturn(_) => unimplemented!("`CallReturn` is transformed from `Call` later"), + CallLazy(_) => unimplemented!("`CallLazy` is transformed from `Call` later"), + CallLoaded(_) => unimplemented!("`CallLoaded` is transformed from `Call` later"), Vec4(ref vec4_expr) => { for expr in &vec4_expr.args { let res = infer_expr(expr, name, decls); diff --git a/src/ast/mod.rs b/src/ast/mod.rs index f6d47ca8..5cb60142 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -51,9 +51,11 @@ pub fn convert( break; } } - for (i, f) in module.functions.iter().enumerate() { - f.resolve_locals(i, module, &use_lookup); + let mut new_functions = module.functions.clone(); + for i in 0..new_functions.len() { + new_functions[i].resolve_locals(i, module, &use_lookup); } + module.functions = new_functions; Ok(()) } @@ -480,7 +482,7 @@ impl Function { /// Returns `true` if the function returns something. pub fn returns(&self) -> bool { self.ret != Type::Void } - fn resolve_locals(&self, relative: usize, module: &Module, use_lookup: &UseLookup) { + fn resolve_locals(&mut self, relative: usize, module: &Module, use_lookup: &UseLookup) { use std::sync::atomic::Ordering; // Ensure sequential order just to be safe. @@ -586,7 +588,7 @@ impl Closure { pub fn returns(&self) -> bool { self.ret != Type::Void } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -671,7 +673,7 @@ impl Grab { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -744,7 +746,7 @@ impl TryExpr { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -958,7 +960,7 @@ impl Block { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -966,7 +968,7 @@ impl Block { use_lookup: &UseLookup, ) { let st = stack.len(); - for expr in &self.expressions { + for expr in &mut self.expressions { expr.resolve_locals(relative, stack, closure_stack, module, use_lookup); } stack.truncate(st); @@ -998,6 +1000,14 @@ pub enum Expression { Go(Box), /// Call expression. Call(Box), + /// Call external function. + CallVoid(Box), + /// Call external function that returns something. + CallReturn(Box), + /// Call external function with lazy invariant. + CallLazy(Box), + /// Call loaded function. + CallLoaded(Box), /// Item expression. Item(Box), /// Assignment expression. @@ -1379,7 +1389,11 @@ impl Expression { Continue(ref c) => c.source_range, Block(ref bl) => bl.source_range, Go(ref go) => go.source_range, - Call(ref call) => call.source_range, + Call(ref call) => call.info.source_range, + CallVoid(ref call) => call.info.source_range, + CallReturn(ref call) => call.info.source_range, + CallLazy(ref call) => call.info.source_range, + CallLoaded(ref call) => call.info.source_range, Item(ref it) => it.source_range, Assign(ref assign) => assign.source_range, Vec4(ref vec4) => vec4.source_range, @@ -1418,7 +1432,7 @@ impl Expression { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -1428,15 +1442,15 @@ impl Expression { use self::Expression::*; match *self { - Link(ref link) => + Link(ref mut link) => link.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Object(ref obj) => + Object(ref mut obj) => obj.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Array(ref arr) => + Array(ref mut arr) => arr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - ArrayFill(ref arr_fill) => + ArrayFill(ref mut arr_fill) => arr_fill.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Return(ref expr) => { + Return(ref mut expr) => { let st = stack.len(); expr.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); @@ -1444,78 +1458,116 @@ impl Expression { ReturnVoid(_) => {} Break(_) => {} Continue(_) => {} - Block(ref bl) => + Block(ref mut bl) => bl.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Go(ref go) => + Go(ref mut go) => go.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Call(ref call) => - call.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Item(ref it) => + Call(ref mut call) => { + call.resolve_locals(relative, stack, closure_stack, module, use_lookup); + match call.f_index { + FnIndex::Void(f) => { + *self = Expression::CallVoid(Box::new(self::CallVoid { + args: call.args.clone(), + fun: f, + info: call.info.clone(), + })) + } + FnIndex::Return(f) => { + *self = Expression::CallReturn(Box::new(self::CallReturn { + args: call.args.clone(), + fun: f, + info: call.info.clone(), + })) + } + FnIndex::Lazy(f, lazy_inv) => { + *self = Expression::CallLazy(Box::new(self::CallLazy { + args: call.args.clone(), + fun: f, + lazy_inv, + info: call.info.clone(), + })) + } + FnIndex::Loaded(f) => { + *self = Expression::CallLoaded(Box::new(self::CallLoaded { + args: call.args.clone(), + custom_source: call.custom_source.clone(), + fun: f, + info: call.info.clone(), + })) + } + _ => {} + } + } + CallVoid(_) => unimplemented!("`CallVoid` is transformed from `Call`"), + CallReturn(_) => unimplemented!("`CallReturn` is transformed from `Call`"), + CallLazy(_) => unimplemented!("`CallLazy` is transformed from `Call`"), + CallLoaded(_) => unimplemented!("`CallLoaded` is transform from `Call`"), + Item(ref mut it) => it.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Assign(ref assign) => + Assign(ref mut assign) => assign.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Vec4(ref vec4) => + Vec4(ref mut vec4) => vec4.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Mat4(ref mat4) => + Mat4(ref mut mat4) => mat4.resolve_locals(relative, stack, closure_stack, module, use_lookup), - For(ref for_expr) => + For(ref mut for_expr) => for_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - ForN(ref for_n_expr) => + ForN(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - ForIn(ref for_n_expr) => + ForIn(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Sum(ref for_n_expr) => + Sum(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - SumIn(ref for_in_expr) => + SumIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - SumVec4(ref for_n_expr) => + SumVec4(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Prod(ref for_n_expr) => + Prod(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - ProdIn(ref for_in_expr) => + ProdIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - ProdVec4(ref for_n_expr) => + ProdVec4(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Min(ref for_n_expr) => + Min(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - MinIn(ref for_in_expr) => + MinIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Max(ref for_n_expr) => + Max(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - MaxIn(ref for_in_expr) => + MaxIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Sift(ref for_n_expr) => + Sift(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - SiftIn(ref for_in_expr) => + SiftIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Any(ref for_n_expr) => + Any(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - AnyIn(ref for_in_expr) => + AnyIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - All(ref for_n_expr) => + All(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - AllIn(ref for_in_expr) => + AllIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - LinkFor(ref for_n_expr) => + LinkFor(ref mut for_n_expr) => for_n_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - LinkIn(ref for_in_expr) => + LinkIn(ref mut for_in_expr) => for_in_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - If(ref if_expr) => + If(ref mut if_expr) => if_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), Variable(_) => {} - Try(ref expr) => + Try(ref mut expr) => expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Swizzle(ref swizzle) => + Swizzle(ref mut swizzle) => swizzle.expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Closure(ref closure) => - closure.resolve_locals(relative, stack, closure_stack, module, use_lookup), - CallClosure(ref call) => + Closure(ref mut closure) => + Arc::make_mut(closure).resolve_locals(relative, stack, closure_stack, module, use_lookup), + CallClosure(ref mut call) => call.resolve_locals(relative, stack, closure_stack, module, use_lookup), - Grab(ref grab) => + Grab(ref mut grab) => grab.resolve_locals(relative, stack, closure_stack, module, use_lookup), - TryExpr(ref try_expr) => + TryExpr(ref mut try_expr) => try_expr.resolve_locals(relative, stack, closure_stack, module, use_lookup), - In(ref in_expr) => + In(ref mut in_expr) => in_expr.resolve_locals(relative, module, use_lookup), } } @@ -1578,7 +1630,7 @@ impl Link { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -1586,7 +1638,7 @@ impl Link { use_lookup: &UseLookup, ) { let st = stack.len(); - for expr in &self.items { + for expr in &mut self.items { expr.resolve_locals(relative, stack, closure_stack, module, use_lookup); } stack.truncate(st); @@ -1686,7 +1738,7 @@ impl Object { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -1694,7 +1746,7 @@ impl Object { use_lookup: &UseLookup, ) { let st = stack.len(); - for &(_, ref expr) in &self.key_values { + for &mut (_, ref mut expr) in &mut self.key_values { expr.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); } @@ -1758,7 +1810,7 @@ impl Array { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -1766,7 +1818,7 @@ impl Array { use_lookup: &UseLookup, ) { let st = stack.len(); - for item in &self.items { + for item in &mut self.items { item.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); } @@ -1839,7 +1891,7 @@ impl ArrayFill { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -2184,7 +2236,7 @@ impl Id { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -2194,7 +2246,7 @@ impl Id { match *self { Id::String(_, _) => false, Id::F64(_, _) => false, - Id::Expression(ref expr) => { + Id::Expression(ref mut expr) => { let st = stack.len(); expr.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); @@ -2338,7 +2390,7 @@ impl Item { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -2356,7 +2408,7 @@ impl Item { } } } - for id in &self.ids { + for id in &mut self.ids { if id.resolve_locals(relative, stack, closure_stack, module, use_lookup) { stack.push(None); } @@ -2415,7 +2467,7 @@ impl Go { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -2423,7 +2475,7 @@ impl Go { use_lookup: &UseLookup, ) { let st = stack.len(); - for arg in &self.call.args { + for arg in &mut self.call.args { let st = stack.len(); arg.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); @@ -2432,21 +2484,76 @@ impl Go { } } -/// Function call. +/// Call info. #[derive(Debug, Clone)] -pub struct Call { - /// Alias. - pub alias: Option>, +pub struct CallInfo { /// Name of function. pub name: Arc, + /// Alias. + pub alias: Option>, + /// The range in source. + pub source_range: Range, +} + +/// Loaded function call. +#[derive(Debug, Clone)] +pub struct CallLoaded { + /// Arguments. + pub args: Vec, + /// Relative to function you call from. + pub fun: isize, + /// Info about the call. + pub info: Box, + /// A custom source, such as when calling a function inside a loaded module. + pub custom_source: Option>, +} + +/// External function call. +#[derive(Debug, Clone)] +pub struct CallLazy { + /// Arguments. + pub args: Vec, + /// Function pointer. + pub fun: crate::FnReturnRef, + /// Lazy invariant. + pub lazy_inv: crate::LazyInvariant, + /// Info about the call. + pub info: Box, +} + +/// External function call. +#[derive(Debug, Clone)] +pub struct CallReturn { + /// Arguments. + pub args: Vec, + /// Function pointer. + pub fun: crate::FnReturnRef, + /// Info about the call. + pub info: Box, +} + +/// External function call. +#[derive(Debug, Clone)] +pub struct CallVoid { + /// Arguments. + pub args: Vec, + /// Function pointer. + pub fun: crate::FnVoidRef, + /// Info about the call. + pub info: Box, +} + +/// Function call. +#[derive(Debug, Clone)] +pub struct Call { /// Arguments. pub args: Vec, /// Function index. - pub f_index: Cell, + pub f_index: FnIndex, + /// Info about the call. + pub info: Box, /// A custom source, such as when calling a function inside a loaded module. pub custom_source: Option>, - /// The range in source. - pub source_range: Range, } impl Call { @@ -2512,12 +2619,14 @@ impl Call { } Ok((convert.subtract(start), Call { - alias, - name, args, - f_index: Cell::new(FnIndex::None), + f_index: FnIndex::None, custom_source: None, - source_range: convert.source(start).unwrap(), + info: Box::new(CallInfo { + alias, + name, + source_range: convert.source(start).unwrap(), + }) })) } @@ -2585,17 +2694,19 @@ impl Call { } Ok((convert.subtract(start), Call { - alias, - name: Arc::new(name), args, - f_index: Cell::new(FnIndex::None), + f_index: FnIndex::None, custom_source: None, - source_range: convert.source(start).unwrap(), + info: Box::new(CallInfo { + alias, + name: Arc::new(name), + source_range: convert.source(start).unwrap(), + }) })) } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -2607,8 +2718,9 @@ impl Call { use FnExt; let st = stack.len(); - let f_index = if let Some(ref alias) = self.alias { - if let Some(&i) = use_lookup.aliases.get(alias).and_then(|map| map.get(&self.name)) { + let f_index = if let Some(ref alias) = self.info.alias { + if let Some(&i) = use_lookup.aliases.get(alias) + .and_then(|map| map.get(&self.info.name)) { match i { FnAlias::Loaded(i) => FnIndex::Loaded(i as isize - relative as isize), FnAlias::External(i) => { @@ -2623,9 +2735,9 @@ impl Call { FnIndex::None } } else { - module.find_function(&self.name, relative) + module.find_function(&self.info.name, relative) }; - self.f_index.set(f_index); + self.f_index = f_index; match f_index { FnIndex::Loaded(f_index) => { let index = (f_index + relative as isize) as usize; @@ -2641,7 +2753,7 @@ impl Call { } FnIndex::None => {} } - for arg in &self.args { + for arg in &mut self.args { let arg_st = stack.len(); arg.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(arg_st); @@ -2812,7 +2924,7 @@ impl CallClosure { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -2824,7 +2936,7 @@ impl CallClosure { // All closures must return a value. // Use return type because it has the same name. stack.push(Some(crate::runtime::RETURN_TYPE.clone())); - for arg in &self.args { + for arg in &mut self.args { let arg_st = stack.len(); arg.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(arg_st); @@ -2904,12 +3016,14 @@ impl Norm { fn into_call_expr(self) -> Expression { Expression::Call(Box::new(Call { - alias: None, args: vec![self.expr], custom_source: None, - f_index: Cell::new(FnIndex::None), - name: crate::NORM.clone(), - source_range: self.source_range, + f_index: FnIndex::None, + info: Box::new(CallInfo { + alias: None, + name: crate::NORM.clone(), + source_range: self.source_range, + }) })) } } @@ -2932,23 +3046,25 @@ impl BinOpExpression { use self::BinOp::*; Expression::Call(Box::new(Call { - alias: None, - name: match self.op { - Add => crate::ADD.clone(), - Sub => crate::SUB.clone(), - Mul => crate::MUL.clone(), - Div => crate::DIV.clone(), - Rem => crate::REM.clone(), - Pow => crate::POW.clone(), - Dot => crate::DOT.clone(), - Cross => crate::CROSS.clone(), - AndAlso => crate::AND_ALSO.clone(), - OrElse => crate::OR_ELSE.clone(), - }, args: vec![self.left, self.right], custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, + f_index: FnIndex::None, + info: Box::new(CallInfo { + alias: None, + name: match self.op { + Add => crate::ADD.clone(), + Sub => crate::SUB.clone(), + Mul => crate::MUL.clone(), + Div => crate::DIV.clone(), + Rem => crate::REM.clone(), + Pow => crate::POW.clone(), + Dot => crate::DOT.clone(), + Cross => crate::CROSS.clone(), + AndAlso => crate::AND_ALSO.clone(), + OrElse => crate::OR_ELSE.clone(), + }, + source_range: self.source_range, + }) })) } } @@ -2993,12 +3109,14 @@ impl UnOpExpression { _ => return Err(()) }; Expression::Call(Box::new(Call { - alias: None, - name: name, args: vec![expr], custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: convert.source(start).unwrap() + f_index: FnIndex::None, + info: Box::new(CallInfo { + alias: None, + name: name, + source_range: convert.source(start).unwrap() + }) })) })) } @@ -3088,7 +3206,7 @@ impl Assign { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -3219,7 +3337,7 @@ impl Mat4 { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -3227,7 +3345,7 @@ impl Mat4 { use_lookup: &UseLookup, ) { let st = stack.len(); - for arg in &self.args { + for arg in &mut self.args { let arg_st = stack.len(); arg.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(arg_st); @@ -3320,7 +3438,7 @@ impl Vec4 { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -3328,7 +3446,7 @@ impl Vec4 { use_lookup: &UseLookup, ) { let st = stack.len(); - for arg in &self.args { + for arg in &mut self.args { let arg_st = stack.len(); arg.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(arg_st); @@ -3655,7 +3773,7 @@ impl For { } fn resolve_locals( - &self, relative: usize, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, module: &Module, @@ -3740,7 +3858,7 @@ impl ForIn { } fn resolve_locals( - &self, relative: usize, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, module: &Module, @@ -3896,7 +4014,7 @@ impl ForN { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -3904,7 +4022,7 @@ impl ForN { use_lookup: &UseLookup, ) { let st = stack.len(); - if let Some(ref start) = self.start { + if let Some(ref mut start) = self.start { start.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); } @@ -4150,7 +4268,7 @@ impl If { } fn resolve_locals( - &self, + &mut self, relative: usize, stack: &mut Vec>>, closure_stack: &mut Vec, @@ -4164,15 +4282,15 @@ impl If { stack.truncate(st); // Does not matter that conditions are resolved before blocks, // since the stack gets truncated anyway. - for else_if_cond in &self.else_if_conds { + for else_if_cond in &mut self.else_if_conds { else_if_cond.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); } - for else_if_block in &self.else_if_blocks { + for else_if_block in &mut self.else_if_blocks { else_if_block.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); } - if let Some(ref else_block) = self.else_block { + if let Some(ref mut else_block) = self.else_block { else_block.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(st); } @@ -4260,19 +4378,21 @@ impl Compare { use self::CompareOp::*; Expression::Call(Box::new(Call { - alias: None, - name: match self.op { - Less => crate::LESS.clone(), - LessOrEqual => crate::LESS_OR_EQUAL.clone(), - Greater => crate::GREATER.clone(), - GreaterOrEqual => crate::GREATER_OR_EQUAL.clone(), - Equal => crate::EQUAL.clone(), - NotEqual => crate::NOT_EQUAL.clone(), - }, args: vec![self.left, self.right], custom_source: None, - f_index: Cell::new(FnIndex::None), - source_range: self.source_range, + f_index: FnIndex::None, + info: Box::new(CallInfo { + alias: None, + name: match self.op { + Less => crate::LESS.clone(), + LessOrEqual => crate::LESS_OR_EQUAL.clone(), + Greater => crate::GREATER.clone(), + GreaterOrEqual => crate::GREATER_OR_EQUAL.clone(), + Equal => crate::EQUAL.clone(), + NotEqual => crate::NOT_EQUAL.clone(), + }, + source_range: self.source_range, + }) })) } } @@ -4361,7 +4481,7 @@ impl In { } fn resolve_locals( - &self, + &mut self, relative: usize, module: &Module, use_lookup: &UseLookup diff --git a/src/ast/replace.rs b/src/ast/replace.rs index cbf822b1..8c79f607 100644 --- a/src/ast/replace.rs +++ b/src/ast/replace.rs @@ -91,6 +91,10 @@ pub fn number(expr: &Expression, name: &Arc, val: f64) -> Expression { E::Call(ref call_expr) => { E::Call(Box::new(number_call(call_expr, name, val))) } + E::CallVoid(_) => unimplemented!("`CallVoid` is transformed from `Call` later"), + E::CallReturn(_) => unimplemented!("`CallVoid` is transformed from `Call` later"), + E::CallLazy(_) => unimplemented!("`CallLazy` is transformed from `Call` later"), + E::CallLoaded(_) => unimplemented!("`CallLoaded` is transform from `Call` later"), E::Array(ref array_expr) => { let mut new_items: Vec = vec![]; for item in &array_expr.items { @@ -339,12 +343,10 @@ fn number_call(call_expr: &Call, name: &Arc, val: f64) -> Call { new_args.push(number(arg, name, val)); } Call { - alias: call_expr.alias.clone(), - name: call_expr.name.clone(), args: new_args, f_index: call_expr.f_index.clone(), custom_source: None, - source_range: call_expr.source_range, + info: call_expr.info.clone(), } } diff --git a/src/dyon_std/lifetimechk.rs b/src/dyon_std/lifetimechk.rs index 31625d43..419d7e99 100644 --- a/src/dyon_std/lifetimechk.rs +++ b/src/dyon_std/lifetimechk.rs @@ -15,8 +15,7 @@ pub fn check(f: &ast::Function, args: &Array) -> Result<(), String> { if let Some(ref lt) = arg.lifetime { if let Variable::Ref(_) = args[i] { if &**lt == "return" { continue; } - } - else { + } else { return Err(format!("Expected reference in argument {}", i)); } match map.get(lt) { diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 23432252..3be32c9d 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -1251,8 +1251,6 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { match obj.lock().unwrap() .downcast_ref::>() { Some(m) => { - use std::cell::Cell; - let f_index = m.find_function(&fn_name, 0); match f_index { FnIndex::Loaded(f_index) => { @@ -1280,14 +1278,16 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { // Use empty range instead of `call.source_range` (from when it was intrinsic). let call_range = Range::empty(0); let call = ast::Call { - alias: None, - name: fn_name.clone(), - f_index: Cell::new(f_index), + f_index, args: args.iter().map(|arg| ast::Expression::Variable(Box::new(( call_range, arg.clone())))).collect(), custom_source: Some(source), - source_range: call_range, + info: Box::new(ast::CallInfo { + alias: None, + name: fn_name.clone(), + source_range: call_range, + }) }; rt.call(&call, &m)?; @@ -1320,8 +1320,6 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result { let v = match obj.lock().unwrap() .downcast_ref::>() { Some(m) => { - use std::cell::Cell; - let f_index = m.find_function(&fn_name, 0); match f_index { FnIndex::Loaded(f_index) => { @@ -1349,14 +1347,16 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result { // Use empty range instead of `call.source_range` (from when it was intrinsic). let call_range = Range::empty(0); let call = ast::Call { - alias: None, - name: fn_name.clone(), - f_index: Cell::new(f_index), + f_index, args: args.iter().map(|arg| ast::Expression::Variable(Box::new(( call_range, arg.clone())))).collect(), custom_source: Some(source), - source_range: call_range, + info: Box::new(ast::CallInfo { + source_range: call_range, + alias: None, + name: fn_name.clone(), + }) }; if let Some(v) = rt.call(&call, &m)?.0 {v} else { diff --git a/src/grab.rs b/src/grab.rs index 5c4a986d..876fefb2 100644 --- a/src/grab.rs +++ b/src/grab.rs @@ -149,8 +149,6 @@ pub fn grab_expr( let call = &go.call; Ok((Grabbed::Expression(E::Go(Box::new(ast::Go { call: ast::Call { - alias: call.alias.clone(), - name: call.name.clone(), args: { let mut new_args = vec![]; for arg in &call.args { @@ -161,7 +159,7 @@ pub fn grab_expr( } new_args }, - source_range: call.source_range, + info: call.info.clone(), f_index: call.f_index.clone(), custom_source: call.custom_source.clone(), }, @@ -170,8 +168,6 @@ pub fn grab_expr( } E::Call(ref call) => { Ok((Grabbed::Expression(E::Call(Box::new(ast::Call { - alias: call.alias.clone(), - name: call.name.clone(), args: { let mut new_args = vec![]; for arg in &call.args { @@ -182,11 +178,77 @@ pub fn grab_expr( } new_args }, - source_range: call.source_range, + info: call.info.clone(), f_index: call.f_index.clone(), custom_source: call.custom_source.clone(), }))), Flow::Continue)) } + E::CallVoid(ref call) => { + Ok((Grabbed::Expression(E::CallVoid(Box::new(ast::CallVoid { + args: { + let mut new_args = vec![]; + for arg in &call.args { + new_args.push(match grab_expr(level, rt, arg, side) { + Ok((Grabbed::Expression(x), Flow::Continue)) => x, + x => return x, + }); + } + new_args + }, + info: call.info.clone(), + fun: call.fun.clone(), + }))), Flow::Continue)) + } + E::CallReturn(ref call) => { + Ok((Grabbed::Expression(E::CallReturn(Box::new(ast::CallReturn { + args: { + let mut new_args = vec![]; + for arg in &call.args { + new_args.push(match grab_expr(level, rt, arg, side) { + Ok((Grabbed::Expression(x), Flow::Continue)) => x, + x => return x, + }); + } + new_args + }, + info: call.info.clone(), + fun: call.fun.clone(), + }))), Flow::Continue)) + } + E::CallLazy(ref call) => { + Ok((Grabbed::Expression(E::CallLazy(Box::new(ast::CallLazy { + args: { + let mut new_args = vec![]; + for arg in &call.args { + new_args.push(match grab_expr(level, rt, arg, side) { + Ok((Grabbed::Expression(x), Flow::Continue)) => x, + x => return x, + }); + } + new_args + }, + lazy_inv: call.lazy_inv, + info: call.info.clone(), + fun: call.fun.clone(), + }))), Flow::Continue)) + } + E::CallLoaded(ref call) => { + Ok((Grabbed::Expression(E::CallLoaded(Box::new(ast::CallLoaded { + args: { + let mut new_args = vec![]; + for arg in &call.args { + new_args.push(match grab_expr(level, rt, arg, side) { + Ok((Grabbed::Expression(x), Flow::Continue)) => x, + x => return x, + }); + } + new_args + }, + info: call.info.clone(), + custom_source: call.custom_source.clone(), + fun: call.fun.clone(), + }))), Flow::Continue)) + } E::CallClosure(ref call_closure) => { Ok((Grabbed::Expression(E::CallClosure(Box::new(ast::CallClosure { item: match grab_item(level, rt, &call_closure.item, side) { diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index bcfc08f4..40c64e98 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -512,6 +512,15 @@ impl Runtime { let loader = false; self.call_internal(call, loader) } + CallVoid(ref call) => self.call_void(&call.args, call.fun, &call.info), + CallReturn(ref call) => self.call_return(&call.args, call.fun, &call.info), + CallLazy(ref call) => + self.call_lazy(&call.args, call.fun, call.lazy_inv, &call.info), + CallLoaded(ref call) => { + let loader = false; + self.call_loaded(&call.args, call.fun, &call.info, + &call.custom_source, loader) + } Item(ref item) => self.item(item, side), Assign(ref assign) => self.assign(assign.op, &assign.left, &assign.right), Vec4(ref vec4) => self.vec4(vec4, side), @@ -749,14 +758,16 @@ impl Runtime { let old_module = replace(&mut self.module, module.clone()); let name: Arc = MAIN.clone(); let call = ast::Call { - alias: None, - name: name.clone(), - f_index: Cell::new(module.find_function(&name, 0)), + f_index: module.find_function(&name, 0), args: vec![], custom_source: None, - source_range: Range::empty(0), + info: Box::new(ast::CallInfo { + alias: None, + name: name.clone(), + source_range: Range::empty(0), + }) }; - match call.f_index.get() { + match call.f_index { FnIndex::Loaded(f_index) => { let f = &module.functions[f_index as usize]; if !f.args.is_empty() { @@ -778,7 +789,7 @@ impl Runtime { } _ => { self.module = old_module; - Err(module.error(call.source_range, + Err(module.error(call.info.source_range, "Could not find function `main`", self)) } } @@ -816,12 +827,10 @@ impl Runtime { let mut stack = vec![]; let relative = self.call_stack.last().map(|c| c.index).unwrap(); let mut fake_call = ast::Call { - alias: go.call.alias.clone(), - name: go.call.name.clone(), - f_index: Cell::new(self.module.find_function(&go.call.name, relative)), + f_index: self.module.find_function(&go.call.info.name, relative), args: Vec::with_capacity(n), custom_source: None, - source_range: go.call.source_range, + info: go.call.info.clone(), }; // Evaluate the arguments and put a deep clone on the new stack. // This prevents the arguments from containing any reference to other variables. @@ -1013,295 +1022,329 @@ impl Runtime { res } - /// Used internally because loaded functions are resolved - /// relative to the caller, which stores its index on the - /// call stack. - /// - /// The `loader` flag is set to `true` when called from the outside. - fn call_internal(&mut self, call: &ast::Call, loader: bool) -> FlowResult { - use FnReturnRef; - use FnVoidRef; - - match call.f_index.get() { - FnIndex::Void(FnVoidRef(f)) => { - for arg in &call.args { - match self.expression(arg, Side::Right)? { - (Some(x), Flow::Continue) => self.stack.push(x), - (x, Flow::Return) => { return Ok((x, Flow::Return)); } - _ => return self.err(arg.source_range(), - "Expected something. \ - Expression did not return a value.") - }; - } - (f)(self).map_err(|err| { - let range = if let Some(ind) = self.arg_err_index.get() { - self.arg_err_index.set(None); - call.args[ind].source_range() - } else { - call.source_range - }; - self.module.error(range, &err, self) - })?; - Ok((None, Flow::Continue)) - } - FnIndex::Return(FnReturnRef(f)) => { - for arg in &call.args { - match self.expression(arg, Side::Right)? { - (Some(x), Flow::Continue) => self.stack.push(x), - (x, Flow::Return) => { return Ok((x, Flow::Return)); } - _ => return self.err(arg.source_range(), - "Expected something. \ - Expression did not return a value.") - }; - } - Ok((Some((f)(self).map_err(|err| { - let range = if let Some(ind) = self.arg_err_index.get() { - self.arg_err_index.set(None); - call.args[ind].source_range() - } else { - call.source_range - }; - self.module.error(range, &err, self) - })?), Flow::Continue)) - } - FnIndex::Lazy(FnReturnRef(f), lazy) => { - for (i, arg) in call.args.iter().enumerate() { - match self.expression(arg, Side::Right)? { - (Some(x), Flow::Continue) => { - use ast::Lazy; - // Return immediately if equal to lazy invariant. - if let Some(&ls) = lazy.get(i) { - for lazy in ls { - match *lazy { - Lazy::Variable(ref val) => { - if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} - } - Lazy::UnwrapOk => { - if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { - return Ok((Some((**x).clone()), Flow::Continue)) - } - } - Lazy::UnwrapErr => { - if let &Variable::Result(Err(ref x)) = self.resolve(&x) { - return Ok((Some(x.message.clone()), Flow::Continue)) - } - } - Lazy::UnwrapSome => { - if let &Variable::Option(Some(ref x)) = self.resolve(&x) { - return Ok((Some((**x).clone()), Flow::Continue)) - } - } + fn call_void( + &mut self, + args: &[ast::Expression], + fun: crate::FnVoidRef, + info: &Box, + ) -> FlowResult { + for arg in args { + match self.expression(arg, Side::Right)? { + (Some(x), Flow::Continue) => self.stack.push(x), + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(arg.source_range(), + "Expected something. \ + Expression did not return a value.") + }; + } + (fun.0)(self).map_err(|err| { + let range = if let Some(ind) = self.arg_err_index.get() { + self.arg_err_index.set(None); + args[ind].source_range() + } else { + info.source_range + }; + self.module.error(range, &err, self) + })?; + Ok((None, Flow::Continue)) + } + + fn call_return( + &mut self, + args: &[ast::Expression], + fun: crate::FnReturnRef, + info: &Box, + ) -> FlowResult { + for arg in args { + match self.expression(arg, Side::Right)? { + (Some(x), Flow::Continue) => self.stack.push(x), + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(arg.source_range(), + "Expected something. \ + Expression did not return a value.") + }; + } + Ok((Some((fun.0)(self).map_err(|err| { + let range = if let Some(ind) = self.arg_err_index.get() { + self.arg_err_index.set(None); + args[ind].source_range() + } else { + info.source_range + }; + self.module.error(range, &err, self) + })?), Flow::Continue)) + } + + fn call_lazy( + &mut self, + args: &[ast::Expression], + fun: crate::FnReturnRef, + lazy_inv: crate::LazyInvariant, + info: &Box, + ) -> FlowResult { + for (i, arg) in args.iter().enumerate() { + match self.expression(arg, Side::Right)? { + (Some(x), Flow::Continue) => { + use ast::Lazy; + // Return immediately if equal to lazy invariant. + if let Some(&ls) = lazy_inv.get(i) { + for lazy in ls { + match *lazy { + Lazy::Variable(ref val) => { + if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} + } + Lazy::UnwrapOk => { + if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } + } + Lazy::UnwrapErr => { + if let &Variable::Result(Err(ref x)) = self.resolve(&x) { + return Ok((Some(x.message.clone()), Flow::Continue)) + } + } + Lazy::UnwrapSome => { + if let &Variable::Option(Some(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) } } } - - self.stack.push(x) } - (x, Flow::Return) => { return Ok((x, Flow::Return)); } - _ => return self.err(arg.source_range(), - "Expected something. \ - Expression did not return a value.") - }; - } - Ok((Some((f)(self).map_err(|err| { - let range = if let Some(ind) = self.arg_err_index.get() { - self.arg_err_index.set(None); - call.args[ind].source_range() - } else { - call.source_range - }; - self.module.error(range, &err, self) - })?), Flow::Continue)) - } - FnIndex::Loaded(f_index) => { - use std::sync::atomic::Ordering; + } - let relative = if loader {0} else { - self.call_stack.last().map(|c| c.index).unwrap_or(0) - }; - let new_index = (f_index + relative as isize) as usize; - // Copy the module to avoid problems with borrow checker. - let mod_copy = self.module.clone(); - let ref f = mod_copy.functions[new_index]; - // Arguments must be computed. - if f.returns() { - // Add return value before arguments on the stack. - // The stack value should remain, but the local should not. - self.stack.push(Variable::Return); + self.stack.push(x) } - let st = self.stack.len(); - let lc = self.local_stack.len(); - let cu = self.current_stack.len(); - - for (i, arg) in call.args.iter().enumerate() { - match self.expression(arg, Side::Right)? { - (Some(x), Flow::Continue) => { - use ast::Lazy; - // Return immediately if equal to lazy invariant. - if let Some(lz) = f.lazy_inv.get(i) { - for lazy in lz { - match *lazy { - Lazy::Variable(ref val) => { - if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} - } - Lazy::UnwrapOk => { - if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { - return Ok((Some((**x).clone()), Flow::Continue)) - } - } - Lazy::UnwrapErr => { - if let &Variable::Result(Err(ref x)) = self.resolve(&x) { - return Ok((Some(x.message.clone()), Flow::Continue)) - } - } - Lazy::UnwrapSome => { - if let &Variable::Option(Some(ref x)) = self.resolve(&x) { - return Ok((Some((**x).clone()), Flow::Continue)) - } - } + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(arg.source_range(), + "Expected something. \ + Expression did not return a value.") + }; + } + Ok((Some((fun.0)(self).map_err(|err| { + let range = if let Some(ind) = self.arg_err_index.get() { + self.arg_err_index.set(None); + args[ind].source_range() + } else { + info.source_range + }; + self.module.error(range, &err, self) + })?), Flow::Continue)) + } + + fn call_loaded( + &mut self, + args: &[ast::Expression], + f_index: isize, + info: &Box, + custom_source: &Option>, + loader: bool + ) -> FlowResult { + use std::sync::atomic::Ordering; + + let relative = if loader {0} else { + self.call_stack.last().map(|c| c.index).unwrap_or(0) + }; + let new_index = (f_index + relative as isize) as usize; + // Copy the module to avoid problems with borrow checker. + let mod_copy = self.module.clone(); + let ref f = mod_copy.functions[new_index]; + // Arguments must be computed. + if f.returns() { + // Add return value before arguments on the stack. + // The stack value should remain, but the local should not. + self.stack.push(Variable::Return); + } + let st = self.stack.len(); + let lc = self.local_stack.len(); + let cu = self.current_stack.len(); + + for (i, arg) in args.iter().enumerate() { + match self.expression(arg, Side::Right)? { + (Some(x), Flow::Continue) => { + use ast::Lazy; + // Return immediately if equal to lazy invariant. + if let Some(lz) = f.lazy_inv.get(i) { + for lazy in lz { + match *lazy { + Lazy::Variable(ref val) => { + if self.resolve(&x) == val {return Ok((Some(x), Flow::Continue))} + } + Lazy::UnwrapOk => { + if let &Variable::Result(Ok(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) + } + } + Lazy::UnwrapErr => { + if let &Variable::Result(Err(ref x)) = self.resolve(&x) { + return Ok((Some(x.message.clone()), Flow::Continue)) + } + } + Lazy::UnwrapSome => { + if let &Variable::Option(Some(ref x)) = self.resolve(&x) { + return Ok((Some((**x).clone()), Flow::Continue)) } } } - - self.stack.push(x) } - (None, Flow::Continue) => {} - (x, Flow::Return) => { return Ok((x, Flow::Return)); } - _ => return self.err(arg.source_range(), - "Expected something. \ - Check that expression returns a value.") - }; + } + + self.stack.push(x) } + (None, Flow::Continue) => {} + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(arg.source_range(), + "Expected something. \ + Check that expression returns a value.") + }; + } - // Look for variable in current stack. - if !f.currents.is_empty() { - for current in &f.currents { - let mut res = None; - for &(ref cname, ind) in self.current_stack.iter().rev() { - if cname == ¤t.name { - res = Some(ind); - break; - } - } - if let Some(ind) = res { - self.local_stack.push((current.name.clone(), self.stack.len())); - self.stack.push(Variable::Ref(ind)); - } else { - return Err(self.module.error(call.source_range, &format!( - "{}\nCould not find current variable `{}`", - self.stack_trace(), current.name), self)); - } + // Look for variable in current stack. + if !f.currents.is_empty() { + for current in &f.currents { + let mut res = None; + for &(ref cname, ind) in self.current_stack.iter().rev() { + if cname == ¤t.name { + res = Some(ind); + break; } } + if let Some(ind) = res { + self.local_stack.push((current.name.clone(), self.stack.len())); + self.stack.push(Variable::Ref(ind)); + } else { + return Err(self.module.error(info.source_range, &format!( + "{}\nCould not find current variable `{}`", + self.stack_trace(), current.name), self)); + } + } + } - // Send arguments to senders. - if f.senders.0.load(Ordering::Relaxed) { - let n = self.stack.len(); - let mut msg = Vec::with_capacity(n - st); - for i in st..n { - msg.push(self.stack[i].deep_clone(&self.stack)); - } - let msg = Arc::new(msg); - // Uses smart swapping of channels to put the closed ones at the end. - let mut channels = f.senders.1.lock().unwrap(); - let mut open = channels.len(); - for i in (0..channels.len()).rev() { - match channels[i].send(Variable::Array(msg.clone())) { - Ok(_) => {} - Err(_) => { - open -= 1; - channels.swap(i, open); - } - } - } - channels.truncate(open); - if channels.len() == 0 { - // Change of flag is guarded by the mutex. - f.senders.0.store(false, Ordering::Relaxed); + // Send arguments to senders. + if f.senders.0.load(Ordering::Relaxed) { + let n = self.stack.len(); + let mut msg = Vec::with_capacity(n - st); + for i in st..n { + msg.push(self.stack[i].deep_clone(&self.stack)); + } + let msg = Arc::new(msg); + // Uses smart swapping of channels to put the closed ones at the end. + let mut channels = f.senders.1.lock().unwrap(); + let mut open = channels.len(); + for i in (0..channels.len()).rev() { + match channels[i].send(Variable::Array(msg.clone())) { + Ok(_) => {} + Err(_) => { + open -= 1; + channels.swap(i, open); } - drop(channels); } + } + channels.truncate(open); + if channels.len() == 0 { + // Change of flag is guarded by the mutex. + f.senders.0.store(false, Ordering::Relaxed); + } + drop(channels); + } - self.push_fn(call.name.clone(), new_index, Some(f.file.clone()), st, lc, cu); - if f.returns() { - // Use return type because it has same name. - self.local_stack.push((RETURN_TYPE.clone(), st - 1)); - } - for (i, arg) in f.args.iter().enumerate() { - // Do not resolve locals to keep fixed length from end of stack. - self.local_stack.push((arg.name.clone(), st + i)); - } - let (x, flow) = self.block(&f.block)?; - match flow { - Flow::Break(None) => - return self.err(call.source_range, "Can not break from function"), - Flow::ContinueLoop(None) => - return self.err(call.source_range, "Can not continue from function"), - Flow::Break(Some(ref label)) => - return Err(self.module.error(call.source_range, - &format!("{}\nThere is no loop labeled `{}`", - self.stack_trace(), label), self)), - Flow::ContinueLoop(Some(ref label)) => - return Err(self.module.error(call.source_range, - &format!("{}\nThere is no loop labeled `{}`", - self.stack_trace(), label), self)), - _ => {} - } - self.pop_fn(call.name.clone()); - match (f.returns(), x) { - (true, None) => { - match self.stack.pop().expect(TINVOTS) { - Variable::Return => { - let source = call.custom_source.as_ref().unwrap_or( - &self.module.functions[ - self.call_stack.last().unwrap().index - ].source - ); - Err(self.module.error_source( - call.source_range, &format!( - "{}\nFunction `{}` did not return a value", - self.stack_trace(), - f.name), source)) - } - x => { - // This happens when return is only - // assigned to `return = x`. - Ok((Some(x), Flow::Continue)) - } - } - } - (false, Some(_)) => { - let source = call.custom_source.as_ref().unwrap_or( - &self.module.functions[self.call_stack.last().unwrap().index].source - ); - Err(self.module.error_source(call.source_range, - &format!( - "{}\nFunction `{}` should not return a value", - self.stack_trace(), - f.name), source)) - } - (true, Some(Variable::Return)) => { - // TODO: Could return the last value on the stack. - // Requires .pop_fn delayed after. - let source = call.custom_source.as_ref().unwrap_or( - &self.module.functions[self.call_stack.last().unwrap().index].source + self.push_fn(info.name.clone(), new_index, Some(f.file.clone()), st, lc, cu); + if f.returns() { + // Use return type because it has same name. + self.local_stack.push((RETURN_TYPE.clone(), st - 1)); + } + for (i, arg) in f.args.iter().enumerate() { + // Do not resolve locals to keep fixed length from end of stack. + self.local_stack.push((arg.name.clone(), st + i)); + } + let (x, flow) = self.block(&f.block)?; + match flow { + Flow::Break(None) => + return self.err(info.source_range, "Can not break from function"), + Flow::ContinueLoop(None) => + return self.err(info.source_range, "Can not continue from function"), + Flow::Break(Some(ref label)) => + return Err(self.module.error(info.source_range, + &format!("{}\nThere is no loop labeled `{}`", + self.stack_trace(), label), self)), + Flow::ContinueLoop(Some(ref label)) => + return Err(self.module.error(info.source_range, + &format!("{}\nThere is no loop labeled `{}`", + self.stack_trace(), label), self)), + _ => {} + } + self.pop_fn(info.name.clone()); + match (f.returns(), x) { + (true, None) => { + match self.stack.pop().expect(TINVOTS) { + Variable::Return => { + let source = custom_source.as_ref().unwrap_or( + &self.module.functions[ + self.call_stack.last().unwrap().index + ].source ); - Err(self.module.error_source(call.source_range, - &format!( - "{}\nFunction `{}` did not return a value. \ - Did you forget a `return`?", - self.stack_trace(), - f.name), source)) + Err(self.module.error_source( + info.source_range, &format!( + "{}\nFunction `{}` did not return a value", + self.stack_trace(), + f.name), source)) } - (returns, b) => { - if returns { self.stack.pop(); } - Ok((b, Flow::Continue)) + x => { + // This happens when return is only + // assigned to `return = x`. + Ok((Some(x), Flow::Continue)) } } } + (false, Some(_)) => { + let source = custom_source.as_ref().unwrap_or( + &self.module.functions[self.call_stack.last().unwrap().index].source + ); + Err(self.module.error_source(info.source_range, + &format!( + "{}\nFunction `{}` should not return a value", + self.stack_trace(), + f.name), source)) + } + (true, Some(Variable::Return)) => { + // TODO: Could return the last value on the stack. + // Requires .pop_fn delayed after. + let source = custom_source.as_ref().unwrap_or( + &self.module.functions[self.call_stack.last().unwrap().index].source + ); + Err(self.module.error_source(info.source_range, + &format!( + "{}\nFunction `{}` did not return a value. \ + Did you forget a `return`?", + self.stack_trace(), + f.name), source)) + } + (returns, b) => { + if returns { self.stack.pop(); } + Ok((b, Flow::Continue)) + } + } + } + + /// Used internally because loaded functions are resolved + /// relative to the caller, which stores its index on the + /// call stack. + /// + /// The `loader` flag is set to `true` when called from the outside. + fn call_internal(&mut self, call: &ast::Call, loader: bool) -> FlowResult { + match call.f_index { + FnIndex::Void(f) => + self.call_void(&call.args, f, &call.info), + FnIndex::Return(f) => + self.call_return(&call.args, f, &call.info), + FnIndex::Lazy(f, lazy_inv) => + self.call_lazy(&call.args, f, lazy_inv, &call.info), + FnIndex::Loaded(f_index) => { + self.call_loaded(&call.args, f_index, &call.info, + &call.custom_source, loader) + } FnIndex::None => { - Err(self.module.error(call.source_range, - &format!("{}\nUnknown function `{}`", self.stack_trace(), call.name), self)) + Err(self.module.error(call.info.source_range, + &format!("{}\nUnknown function `{}`", self.stack_trace(), call.info.name), self)) } } } @@ -1316,15 +1359,17 @@ impl Runtime { match module.find_function(&name, 0) { FnIndex::Loaded(f_index) => { let call = ast::Call { - alias: None, - name: name.clone(), - f_index: Cell::new(FnIndex::Loaded(f_index)), + f_index: FnIndex::Loaded(f_index), args: args.iter() .map(|arg| ast::Expression::Variable(Box::new(( Range::empty(0), arg.clone())))) .collect(), custom_source: None, - source_range: Range::empty(0), + info: Box::new(ast::CallInfo { + alias: None, + name: name.clone(), + source_range: Range::empty(0), + }) }; self.call(&call, &module)?; Ok(()) @@ -1347,21 +1392,23 @@ impl Runtime { } let call = ast::Call { - alias: None, - name: name.clone(), - f_index: Cell::new(fn_index), + f_index: fn_index, args: args .iter() .map(|arg| ast::Expression::Variable(Box::new((Range::empty(0), arg.clone())))) .collect(), custom_source: None, - source_range: Range::empty(0), + info: Box::new(ast::CallInfo { + alias: None, + name: name.clone(), + source_range: Range::empty(0), + }) }; match self.call(&call, &module) { Ok((Some(val), Flow::Continue)) => Ok(val), Err(err) => Err(err), _ => Err(module.error( - call.source_range, + call.info.source_range, &format!("{}\nExpected something", self.stack_trace()), self, )), diff --git a/src/write.rs b/src/write.rs index 32b2ec50..62769a07 100644 --- a/src/write.rs +++ b/src/write.rs @@ -4,6 +4,8 @@ use ast; use Runtime; use Variable; +use std::sync::Arc; + #[derive(Copy, Clone)] pub(crate) enum EscapeString { Json, @@ -201,21 +203,11 @@ fn write_expr( E::Object(ref obj) => write_obj(w, rt, obj, tabs)?, E::Array(ref arr) => write_arr(w, rt, arr, tabs)?, E::ArrayFill(ref arr_fill) => write_arr_fill(w, rt, arr_fill, tabs)?, - E::Call(ref call) => { - if &**call.name == "norm" && call.args.len() == 1 { - write_norm(w, rt, &call.args[0], tabs)? - } else if &**call.name == "not" && call.args.len() == 1 { - write_not(w, rt, &call.args[0], tabs)? - } else if &**call.name == "neg" && call.args.len() == 1 { - write_neg(w, rt, &call.args[0], tabs)? - } else if let Some(op) = standard_binop(call) { - write_binop(w, rt, op, &call.args[0], &call.args[1], tabs)? - } else if let Some(op) = standard_compare(call) { - write_compare(w, rt, op, &call.args[0], &call.args[1], tabs)? - } else { - write_call(w, rt, call, tabs)? - } - } + E::Call(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, + E::CallVoid(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, + E::CallReturn(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, + E::CallLazy(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, + E::CallLoaded(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, E::Return(ref expr) => { write!(w, "return ")?; write_expr(w, rt, expr, tabs)?; @@ -238,7 +230,7 @@ fn write_expr( E::Block(ref b) => write_block(w, rt, b, tabs)?, E::Go(ref go) => { write!(w, "go ")?; - write_call(w, rt, &go.call, tabs)?; + write_call(w, rt, &go.call.info.name, &go.call.args, tabs)?; } E::Assign(ref assign) => write_assign(w, rt, assign, tabs)?, E::Vec4(ref vec4) => write_vec4(w, rt, vec4, tabs)?, @@ -388,7 +380,7 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo match *expr { E::Call(ref call) => { - if let Some(binop_op) = standard_binop(call) { + if let Some(binop_op) = standard_binop(&call.info.name, &call.args) { match (op.precedence(), binop_op.precedence()) { (3, _) => true, (2, 1) => true, @@ -396,10 +388,11 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo (1, 1) if right => true, _ => false } - } else if let Some(_) = standard_compare(call) { + } else if let Some(_) = standard_compare(&call.info.name, &call.args) { true } else {false} } + // TODO: Handle `E::CallVoid`. _ => false } } @@ -523,18 +516,31 @@ fn write_obj( fn write_call( w: &mut W, rt: &Runtime, - call: &ast::Call, + name: &Arc, + args: &[ast::Expression], tabs: u32, ) -> Result<(), io::Error> { - write!(w, "{}(", call.name)?; - for (i, arg) in call.args.iter().enumerate() { - write_expr(w, rt, arg, tabs)?; - if i + 1 < call.args.len() { - write!(w, ", ")?; + if &**name == "norm" && args.len() == 1 { + write_norm(w, rt, &args[0], tabs) + } else if &**name == "not" && args.len() == 1 { + write_not(w, rt, &args[0], tabs) + } else if &**name == "neg" && args.len() == 1 { + write_neg(w, rt, &args[0], tabs) + } else if let Some(op) = standard_binop(name, args) { + write_binop(w, rt, op, &args[0], &args[1], tabs) + } else if let Some(op) = standard_compare(name, args) { + write_compare(w, rt, op, &args[0], &args[1], tabs) + } else { + write!(w, "{}(", name)?; + for (i, arg) in args.iter().enumerate() { + write_expr(w, rt, arg, tabs)?; + if i + 1 < args.len() { + write!(w, ", ")?; + } } + write!(w, ")")?; + Ok(()) } - write!(w, ")")?; - Ok(()) } fn write_call_closure( @@ -715,12 +721,13 @@ fn write_for( Ok(()) } -fn standard_binop(call: &ast::Call) -> Option { +fn standard_binop(name: &Arc, args: &[ast::Expression]) -> Option { use ast::BinOp::*; - if call.args.len() != 2 {return None}; + if args.len() != 2 {return None}; - Some(match &**call.name { + let name: &str = &**name; + Some(match name { "dot" => Dot, "cross" => Cross, "add" => Add, @@ -735,12 +742,13 @@ fn standard_binop(call: &ast::Call) -> Option { }) } -fn standard_compare(call: &ast::Call) -> Option { +fn standard_compare(name: &Arc, args: &[ast::Expression]) -> Option { use ast::CompareOp::*; - if call.args.len() != 2 {return None}; + if args.len() != 2 {return None}; - Some(match &**call.name { + let name: &str = &**name; + Some(match name { "less" => Less, "less_or_equal" => LessOrEqual, "greater" => Greater, @@ -755,7 +763,7 @@ fn compare_needs_parent(expr: &ast::Expression) -> bool { use ast::Expression as E; match *expr { - E::Call(ref call) => standard_binop(call).is_some(), + E::Call(ref call) => standard_binop(&call.info.name, &call.args).is_some(), _ => false } } From 61f7bd33ad75e5cbdfe14e3efc87734b48b7723a Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Fri, 27 Sep 2019 17:27:29 +0200 Subject: [PATCH 73/83] Added extern binary and unary operators --- src/ast/infer_len.rs | 2 + src/ast/mod.rs | 78 +++++++++++++++-- src/ast/replace.rs | 2 + src/dyon_std/mod.rs | 196 +++++++++++++++++++++---------------------- src/grab.rs | 24 ++++++ src/lib.rs | 52 ++++++++++++ src/module.rs | 95 ++++++++++++++------- src/runtime/mod.rs | 68 +++++++++++++++ src/write.rs | 3 + 9 files changed, 378 insertions(+), 142 deletions(-) diff --git a/src/ast/infer_len.rs b/src/ast/infer_len.rs index d67bec0f..68c7e7a5 100644 --- a/src/ast/infer_len.rs +++ b/src/ast/infer_len.rs @@ -99,6 +99,8 @@ fn infer_expr( CallReturn(_) => unimplemented!("`CallReturn` is transformed from `Call` later"), CallLazy(_) => unimplemented!("`CallLazy` is transformed from `Call` later"), CallLoaded(_) => unimplemented!("`CallLoaded` is transformed from `Call` later"), + CallBinOp(_) => unimplemented!("`CallBinOp` is transformed from `Call` later"), + CallUnOp(_) => unimplemented!("`CallUnOp` is transformed from `Call` later"), Vec4(ref vec4_expr) => { for expr in &vec4_expr.args { let res = infer_expr(expr, name, decls); diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 5cb60142..1b2374f0 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1008,6 +1008,10 @@ pub enum Expression { CallLazy(Box), /// Call loaded function. CallLoaded(Box), + /// Binary operator. + CallBinOp(Box), + /// Unary operator. + CallUnOp(Box), /// Item expression. Item(Box), /// Assignment expression. @@ -1392,6 +1396,8 @@ impl Expression { Call(ref call) => call.info.source_range, CallVoid(ref call) => call.info.source_range, CallReturn(ref call) => call.info.source_range, + CallBinOp(ref call) => call.info.source_range, + CallUnOp(ref call) => call.info.source_range, CallLazy(ref call) => call.info.source_range, CallLoaded(ref call) => call.info.source_range, Item(ref it) => it.source_range, @@ -1479,6 +1485,21 @@ impl Expression { info: call.info.clone(), })) } + FnIndex::BinOp(f) => { + *self = Expression::CallBinOp(Box::new(self::CallBinOp { + left: call.args[0].clone(), + right: call.args[1].clone(), + fun: f, + info: call.info.clone(), + })) + } + FnIndex::UnOp(f) => { + *self = Expression::CallUnOp(Box::new(self::CallUnOp { + arg: call.args[0].clone(), + fun: f, + info: call.info.clone(), + })) + } FnIndex::Lazy(f, lazy_inv) => { *self = Expression::CallLazy(Box::new(self::CallLazy { args: call.args.clone(), @@ -1495,13 +1516,15 @@ impl Expression { info: call.info.clone(), })) } - _ => {} + FnIndex::None => {} } } CallVoid(_) => unimplemented!("`CallVoid` is transformed from `Call`"), CallReturn(_) => unimplemented!("`CallReturn` is transformed from `Call`"), CallLazy(_) => unimplemented!("`CallLazy` is transformed from `Call`"), - CallLoaded(_) => unimplemented!("`CallLoaded` is transform from `Call`"), + CallLoaded(_) => unimplemented!("`CallLoaded` is transformed from `Call`"), + CallBinOp(_) => unimplemented!("`CallBinOp` is transformed from `Call`"), + CallUnOp(_) => unimplemented!("`CallUnOp` is transformed from `Call`"), Item(ref mut it) => it.resolve_locals(relative, stack, closure_stack, module, use_lookup), Assign(ref mut assign) => @@ -2521,6 +2544,30 @@ pub struct CallLazy { pub info: Box, } +/// External function call. +#[derive(Debug, Clone)] +pub struct CallBinOp { + /// Left argument. + pub left: Expression, + /// Right argument. + pub right: Expression, + /// Function pointer. + pub fun: crate::FnBinOpRef, + /// Info about the call. + pub info: Box, +} + +/// External function call. +#[derive(Debug, Clone)] +pub struct CallUnOp { + /// Argument. + pub arg: Expression, + /// Function pointer. + pub fun: crate::FnUnOpRef, + /// Info about the call. + pub info: Box, +} + /// External function call. #[derive(Debug, Clone)] pub struct CallReturn { @@ -2713,6 +2760,8 @@ impl Call { module: &Module, use_lookup: &UseLookup, ) { + use FnUnOpRef; + use FnBinOpRef; use FnReturnRef; use FnVoidRef; use FnExt; @@ -2728,6 +2777,8 @@ impl Call { match f.f { FnExt::Void(ff) => FnIndex::Void(FnVoidRef(ff)), FnExt::Return(ff) => FnIndex::Return(FnReturnRef(ff)), + FnExt::BinOp(ff) => FnIndex::BinOp(FnBinOpRef(ff)), + FnExt::UnOp(ff) => FnIndex::UnOp(FnUnOpRef(ff)), } } } @@ -2747,7 +2798,9 @@ impl Call { } FnIndex::Void(_) | FnIndex::Return(_) | - FnIndex::Lazy(_, _) => { + FnIndex::Lazy(_, _) | + FnIndex::BinOp(_) | + FnIndex::UnOp(_) => { // Don't push return since last value in block // is used as return value. } @@ -2757,15 +2810,18 @@ impl Call { let arg_st = stack.len(); arg.resolve_locals(relative, stack, closure_stack, module, use_lookup); stack.truncate(arg_st); - match *arg { - Expression::Swizzle(ref swizzle) => { - for _ in 0..swizzle.len() { + if let FnIndex::BinOp(_) = f_index {} + else { + match *arg { + Expression::Swizzle(ref swizzle) => { + for _ in 0..swizzle.len() { + stack.push(None); + } + } + _ => { stack.push(None); } } - _ => { - stack.push(None); - } } } stack.truncate(st); @@ -4486,6 +4542,8 @@ impl In { module: &Module, use_lookup: &UseLookup ) { + use FnUnOpRef; + use FnBinOpRef; use FnReturnRef; use FnVoidRef; use FnExt; @@ -4499,6 +4557,8 @@ impl In { match f.f { FnExt::Void(ff) => FnIndex::Void(FnVoidRef(ff)), FnExt::Return(ff) => FnIndex::Return(FnReturnRef(ff)), + FnExt::BinOp(ff) => FnIndex::BinOp(FnBinOpRef(ff)), + FnExt::UnOp(ff) => FnIndex::UnOp(FnUnOpRef(ff)), } } } diff --git a/src/ast/replace.rs b/src/ast/replace.rs index 8c79f607..872bbcfb 100644 --- a/src/ast/replace.rs +++ b/src/ast/replace.rs @@ -95,6 +95,8 @@ pub fn number(expr: &Expression, name: &Arc, val: f64) -> Expression { E::CallReturn(_) => unimplemented!("`CallVoid` is transformed from `Call` later"), E::CallLazy(_) => unimplemented!("`CallLazy` is transformed from `Call` later"), E::CallLoaded(_) => unimplemented!("`CallLoaded` is transform from `Call` later"), + E::CallBinOp(_) => unimplemented!("`CallBinOp` is transformed from `Call` later"), + E::CallUnOp(_) => unimplemented!("`CallUnOp` is transformed from `Call` later"), E::Array(ref array_expr) => { let mut new_items: Vec = vec![]; for item in &array_expr.items { diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 3be32c9d..a69e6603 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -38,12 +38,10 @@ pub(crate) fn or_else(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn less(rt: &mut Runtime) -> Result { +pub(crate) fn less(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => Bool(a < b, sec.clone()), (&Str(ref a), &Str(ref b)) => Variable::bool(a < b), _ => return Err("Expected `f64` or `str`".into()) @@ -51,12 +49,10 @@ pub(crate) fn less(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn less_or_equal(rt: &mut Runtime) -> Result { +pub(crate) fn less_or_equal(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => Bool(a <= b, sec.clone()), (&Str(ref a), &Str(ref b)) => Variable::bool(a <= b), _ => return Err("Expected `f64` or `str`".into()) @@ -64,69 +60,61 @@ pub(crate) fn less_or_equal(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn greater(rt: &mut Runtime) -> Result { - less_or_equal(rt).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { +pub(crate) fn greater(a: &Variable, b: &Variable) -> Result { + less_or_equal(a, b).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { panic!("Expected equal to return `bool`") }) } -pub(crate) fn greater_or_equal(rt: &mut Runtime) -> Result { - less(rt).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { +pub(crate) fn greater_or_equal(a: &Variable, b: &Variable) -> Result { + less(a, b).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { panic!("Expected equal to return `bool`") }) } -pub(crate) fn equal(rt: &mut Runtime) -> Result { - fn sub_eq(a: &Variable, b: &Variable) -> Result { - use Variable::*; - - Ok(match (a, b) { - (&F64(a, ref sec), &F64(b, _)) => Bool(a == b, sec.clone()), - (&Str(ref a), &Str(ref b)) => Variable::bool(a == b), - (&Bool(a, ref sec), &Bool(b, _)) => Bool(a == b, sec.clone()), - (&Vec4(a), &Vec4(b)) => Variable::bool(a == b), - (&Object(ref a), &Object(ref b)) => { - Variable::bool(a.len() == b.len() && - a.iter().all(|a| { - if let Some(b_val) = b.get(a.0) { - if let Ok(Variable::Bool(true, _)) = - sub_eq(&a.1, b_val) {true} - else {false} - } else {false} - })) - } - (&Array(ref a), &Array(ref b)) => { - Variable::bool(a.len() == b.len() && - a.iter().zip(b.iter()).all(|(a, b)| { - if let Ok(Variable::Bool(true, _)) = - sub_eq(a, b) {true} else {false} - })) - } - (&Option(None), &Option(None)) => Variable::bool(true), - (&Option(None), &Option(_)) => Variable::bool(false), - (&Option(_), &Option(None)) => Variable::bool(false), - (&Option(Some(ref a)), &Option(Some(ref b))) => sub_eq(a, b)?, - _ => return Err("Expected `f64`, `str`, `bool`, `vec4`, `{}`, `[]` or `opt`".into()) - }) - } +pub(crate) fn equal(a: &Variable, b: &Variable) -> Result { + use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - sub_eq(rt.resolve(&a), rt.resolve(&b)) + Ok(match (a, b) { + (&F64(a, ref sec), &F64(b, _)) => Bool(a == b, sec.clone()), + (&Str(ref a), &Str(ref b)) => Variable::bool(a == b), + (&Bool(a, ref sec), &Bool(b, _)) => Bool(a == b, sec.clone()), + (&Vec4(a), &Vec4(b)) => Variable::bool(a == b), + (&Object(ref a), &Object(ref b)) => { + Variable::bool(a.len() == b.len() && + a.iter().all(|a| { + if let Some(b_val) = b.get(a.0) { + if let Ok(Variable::Bool(true, _)) = + equal(&a.1, b_val) {true} + else {false} + } else {false} + })) + } + (&Array(ref a), &Array(ref b)) => { + Variable::bool(a.len() == b.len() && + a.iter().zip(b.iter()).all(|(a, b)| { + if let Ok(Variable::Bool(true, _)) = + equal(a, b) {true} else {false} + })) + } + (&Option(None), &Option(None)) => Variable::bool(true), + (&Option(None), &Option(_)) => Variable::bool(false), + (&Option(_), &Option(None)) => Variable::bool(false), + (&Option(Some(ref a)), &Option(Some(ref b))) => equal(a, b)?, + _ => return Err("Expected `f64`, `str`, `bool`, `vec4`, `{}`, `[]` or `opt`".into()) + }) } -pub(crate) fn not_equal(rt: &mut Runtime) -> Result { - equal(rt).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { +pub(crate) fn not_equal(a: &Variable, b: &Variable) -> Result { + equal(a, b).map(|v| if let Variable::Bool(a, sec) = v {Variable::Bool(!a, sec)} else { panic!("Expected equal to return `bool`") }) } -pub(crate) fn add(rt: &mut Runtime) -> Result { +pub(crate) fn add(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a + b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_add(a, b)), (&Vec4(a), &F64(b, _)) | (&F64(b, _), &Vec4(a)) => { @@ -156,12 +144,10 @@ pub(crate) fn add(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn sub(rt: &mut Runtime) -> Result { +pub(crate) fn sub(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a - b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_sub(a, b)), (&Vec4(a), &F64(b, _)) => { @@ -197,12 +183,10 @@ pub(crate) fn sub(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn mul(rt: &mut Runtime) -> Result { +pub(crate) fn mul(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a * b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_mul(a, b)), (&Vec4(a), &F64(b, _)) | (&F64(b, _), &Vec4(a)) => { @@ -226,12 +210,10 @@ pub(crate) fn mul(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn div(rt: &mut Runtime) -> Result { +pub(crate) fn div(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a * b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), (&Vec4(a), &F64(b, _)) => { @@ -247,12 +229,10 @@ pub(crate) fn div(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn rem(rt: &mut Runtime) -> Result { +pub(crate) fn rem(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a % b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), (&Vec4(a), &F64(b, _)) => { @@ -268,12 +248,10 @@ pub(crate) fn rem(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn pow(rt: &mut Runtime) -> Result { +pub(crate) fn pow(a: &Variable, b: &Variable) -> Result { use Variable::*; - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + let r = match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a.powf(b), sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4([a[0].powf(b[0]), a[1].powf(b[1]), a[2].powf(b[2]), a[3].powf(b[3])]), @@ -291,18 +269,16 @@ pub(crate) fn pow(rt: &mut Runtime) -> Result { Ok(r) } -pub(crate) fn not(rt: &mut Runtime) -> Result { - let b = rt.stack.pop().expect(TINVOTS); - let b = match *rt.resolve(&b) { +pub(crate) fn not(a: &Variable) -> Result { + let b = match *a { Variable::Bool(ref b, ref sec) => Variable::Bool(!b, sec.clone()), - ref x => return Err(rt.expected_arg(0, x, "bool")) + _ => return Err("Expected `bool`".into()) }; Ok(b) } -pub(crate) fn neg(rt: &mut Runtime) -> Result { - let n = rt.stack.pop().expect(TINVOTS); - let n = match *rt.resolve(&n) { +pub(crate) fn neg(a: &Variable) -> Result { + let n = match *a { Variable::F64(v, ref sec) => Variable::F64(-v, sec.clone()), Variable::Vec4(v) => Variable::Vec4([-v[0], -v[1], -v[2], -v[3]]), Variable::Mat4(ref m) => Variable::Mat4(Box::new([ @@ -311,15 +287,13 @@ pub(crate) fn neg(rt: &mut Runtime) -> Result { [-m[2][0], -m[2][1], -m[2][2], -m[2][3]], [-m[3][0], -m[3][1], -m[3][2], -m[3][3]], ])), - ref x => return Err(rt.expected_arg(0, x, "f64, vec4 or mat4")) + _ => return Err("Expected `f64`, `vec4` or `mat4`".into()) }; Ok(n) } -pub(crate) fn dot(rt: &mut Runtime) -> Result { - let b = rt.stack.pop().expect(TINVOTS); - let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { +pub(crate) fn dot(a: &Variable, b: &Variable) -> Result { + let r = match (a, b) { (&Variable::Vec4(a), &Variable::Vec4(b)) => vecmath::vec4_dot(a, b) as f64, (&Variable::Vec4(a), &Variable::F64(b, _)) | (&Variable::F64(b, _), &Variable::Vec4(a)) => { @@ -341,7 +315,14 @@ dyon_fn!{fn x(v: Vec4) -> f64 {f64::from(v.0[0])}} dyon_fn!{fn y(v: Vec4) -> f64 {f64::from(v.0[1])}} dyon_fn!{fn z(v: Vec4) -> f64 {f64::from(v.0[2])}} dyon_fn!{fn w(v: Vec4) -> f64 {f64::from(v.0[3])}} -dyon_fn!{fn norm(v: Vec4) -> f32 {vecmath::vec4_len(v.0)}} + +pub(crate) fn norm(v: &Variable) -> Result { + if let Variable::Vec4(v) = *v { + Ok(Variable::f64(vecmath::vec4_len(v) as f64)) + } else { + Err("Expected `vec4`".into()) + } +} pub(crate) fn s(rt: &mut Runtime) -> Result { let ind: f64 = rt.pop().expect(TINVOTS); @@ -632,16 +613,11 @@ pub(crate) fn random(rt: &mut Runtime) -> Result { dyon_fn!{fn tau() -> f64 {6.283_185_307_179_586}} -pub(crate) fn len(rt: &mut Runtime) -> Result { - let v = rt.stack.pop().expect(TINVOTS); - let v = { - let arr = match rt.resolve(&v) { - &Variable::Array(ref arr) => arr, - x => return Err(rt.expected_arg(0, x, "array")) - }; - Variable::f64(arr.len() as f64) - }; - Ok(v) +pub(crate) fn len(a: &Variable) -> Result { + match a { + &Variable::Array(ref arr) => Ok(Variable::f64(arr.len() as f64)), + _ => return Err("Expected array".into()) + } } pub(crate) fn push_ref(rt: &mut Runtime) -> Result<(), String> { @@ -1079,6 +1055,8 @@ pub(crate) fn load(rt: &mut Runtime) -> Result { match f.f { FnExt::Void(ff) => m.add(f.name.clone(), ff, f.p.clone()), FnExt::Return(ff) => m.add(f.name.clone(), ff, f.p.clone()), + FnExt::BinOp(ff) => m.add_binop(f.name.clone(), ff, f.p.clone()), + FnExt::UnOp(ff) => m.add_unop(f.name.clone(), ff, f.p.clone()), } } if let Err(err) = load(text, &mut m) { @@ -1108,6 +1086,8 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result match f.f { FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), + FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), } } let x = rt.resolve(&modules); @@ -1124,8 +1104,14 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result .any(|a| a.name == f.name); if !has_external { match f.f { - FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Void(ff) => + new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => + new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::BinOp(ff) => + new_module.add_binop(f.name.clone(), ff, f.p.clone()), + FnExt::UnOp(ff) => + new_module.add_unop(f.name.clone(), ff, f.p.clone()), } } } @@ -1179,6 +1165,8 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result new_module.add(f.name.clone(), ff, f.p.clone()), FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), + FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), } } let x = rt.resolve(&modules); @@ -1197,6 +1185,8 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result new_module.add(f.name.clone(), ff, f.p.clone()), FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), + FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), } } } @@ -1272,7 +1262,9 @@ pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { FnIndex::None | FnIndex::Void(_) | FnIndex::Return(_) | - FnIndex::Lazy(_, _) => + FnIndex::Lazy(_, _) | + FnIndex::BinOp(_) | + FnIndex::UnOp(_) => return Err(format!("Could not find function `{}`", fn_name)) } // Use empty range instead of `call.source_range` (from when it was intrinsic). @@ -1341,7 +1333,9 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result { FnIndex::None | FnIndex::Void(_) | FnIndex::Return(_) | - FnIndex::Lazy(_, _) => + FnIndex::Lazy(_, _) | + FnIndex::BinOp(_) | + FnIndex::UnOp(_) => return Err(format!("Could not find function `{}`", fn_name)) } // Use empty range instead of `call.source_range` (from when it was intrinsic). diff --git a/src/grab.rs b/src/grab.rs index 876fefb2..a4d4300c 100644 --- a/src/grab.rs +++ b/src/grab.rs @@ -215,6 +215,30 @@ pub fn grab_expr( fun: call.fun.clone(), }))), Flow::Continue)) } + E::CallBinOp(ref call) => { + Ok((Grabbed::Expression(E::CallBinOp(Box::new(ast::CallBinOp { + left: match grab_expr(level, rt, &call.left, side) { + Ok((Grabbed::Expression(x), Flow::Continue)) => x, + x => return x, + }, + right: match grab_expr(level, rt, &call.right, side) { + Ok((Grabbed::Expression(x), Flow::Continue)) => x, + x => return x, + }, + info: call.info.clone(), + fun: call.fun.clone(), + }))), Flow::Continue)) + } + E::CallUnOp(ref call) => { + Ok((Grabbed::Expression(E::CallUnOp(Box::new(ast::CallUnOp { + arg: match grab_expr(level, rt, &call.arg, side) { + Ok((Grabbed::Expression(x), Flow::Continue)) => x, + x => return x, + }, + info: call.info.clone(), + fun: call.fun.clone(), + }))), Flow::Continue)) + } E::CallLazy(ref call) => { Ok((Grabbed::Expression(E::CallLazy(Box::new(ast::CallLazy { args: { diff --git a/src/lib.rs b/src/lib.rs index c6aa82a4..47ebe1e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -338,6 +338,10 @@ pub enum FnIndex { Return(FnReturnRef), /// Extern function with return value and lazy invariant. Lazy(FnReturnRef, LazyInvariant), + /// Extern binary operator. + BinOp(FnBinOpRef), + /// Extern unary operator. + UnOp(FnUnOpRef), } /// Refers to an external function. @@ -347,6 +351,10 @@ pub enum FnExt { Void(fn(&mut Runtime) -> Result<(), String>), /// External function with return value. Return(fn(&mut Runtime) -> Result), + /// External binary operator. + BinOp(fn(&Variable, &Variable) -> Result), + /// External unary operator. + UnOp(fn(&Variable) -> Result), } impl From Result<(), String>> for FnExt { @@ -361,12 +369,56 @@ impl From Result> for FnExt { } } +impl From Result> for FnExt { + fn from(val: fn(&Variable, &Variable) -> Result) -> Self { + FnExt::BinOp(val) + } +} + +impl From Result> for FnExt { + fn from(val: fn(&Variable) -> Result) -> Self { + FnExt::UnOp(val) + } +} + impl fmt::Debug for FnExt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "FnExt") } } +/// Used to store direct reference to external function. +#[derive(Copy)] +pub struct FnUnOpRef(pub fn(&Variable) -> Result); + +impl Clone for FnUnOpRef { + fn clone(&self) -> FnUnOpRef { + *self + } +} + +impl fmt::Debug for FnUnOpRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "FnUnOpRef") + } +} + +/// Used to store direct reference to external function. +#[derive(Copy)] +pub struct FnBinOpRef(pub fn(&Variable, &Variable) -> Result); + +impl Clone for FnBinOpRef { + fn clone(&self) -> FnBinOpRef { + *self + } +} + +impl fmt::Debug for FnBinOpRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "FnBinOpRef") + } +} + /// Used to store direct reference to external function. #[derive(Copy)] pub struct FnReturnRef(pub fn(&mut Runtime) -> Result); diff --git a/src/module.rs b/src/module.rs index 02421b53..1e7c316d 100644 --- a/src/module.rs +++ b/src/module.rs @@ -29,7 +29,7 @@ impl Module { let mut m = Module::empty(); m.ns("std"); - m.add_str("less", less, Dfn { + m.add_binop(crate::LESS.clone(), less, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any, Any], ret: Bool, @@ -40,7 +40,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("less_or_equal", less_or_equal, Dfn { + m.add_binop(crate::LESS_OR_EQUAL.clone(), less_or_equal, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any, Any], ret: Bool, @@ -51,7 +51,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("greater", greater, Dfn { + m.add_binop(crate::GREATER.clone(), greater, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any, Any], ret: Bool, @@ -62,7 +62,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("greater_or_equal", greater_or_equal, Dfn { + m.add_binop(crate::GREATER_OR_EQUAL.clone(), greater_or_equal, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any, Any], ret: Bool, @@ -73,7 +73,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("equal", equal, Dfn { + m.add_binop(crate::EQUAL.clone(), equal, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any, Any], ret: Bool, @@ -90,7 +90,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("not_equal", not_equal, Dfn { + m.add_binop(crate::NOT_EQUAL.clone(), not_equal, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any, Any], ret: Bool, @@ -127,7 +127,7 @@ impl Module { ], lazy: LAZY_OR }); - m.add_str("add", add, Dfn { + m.add_binop(crate::ADD.clone(), add, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], ret: Any, @@ -145,7 +145,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("sub", sub, Dfn { + m.add_binop(crate::SUB.clone(), sub, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], ret: Any, @@ -161,7 +161,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("mul", mul, Dfn { + m.add_binop(crate::MUL.clone(), mul, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], ret: Any, @@ -178,7 +178,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("div", div, Dfn { + m.add_binop(crate::DIV.clone(), div, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], ret: Any, @@ -190,7 +190,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("rem", rem, Dfn { + m.add_binop(crate::REM.clone(), rem, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], ret: Any, @@ -202,7 +202,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("pow", pow, Dfn { + m.add_binop(crate::POW.clone(), pow, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], ret: Any, @@ -215,8 +215,8 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("not", not, Dfn::nl(vec![Bool], Bool)); - m.add_str("neg", neg, Dfn{ + m.add_unop(crate::NOT.clone(), not, Dfn::nl(vec![Bool], Bool)); + m.add_unop(crate::NEG.clone(), neg, Dfn{ lts: vec![Lt::Default], tys: vec![Any], ret: Any, ext: vec![ (vec![], vec![F64], F64), @@ -225,7 +225,7 @@ impl Module { ], lazy: LAZY_NO }); - m.add_str("dot", dot, Dfn { + m.add_binop(crate::DOT.clone(), dot, Dfn { lts: vec![Lt::Default; 2], tys: vec![Any; 2], ret: F64, @@ -241,7 +241,7 @@ impl Module { m.add_str("y", y, Dfn::nl(vec![Vec4], F64)); m.add_str("z", z, Dfn::nl(vec![Vec4], F64)); m.add_str("w", w, Dfn::nl(vec![Vec4], F64)); - m.add_str("norm", norm, Dfn::nl(vec![Vec4], F64)); + m.add_unop_str("norm", norm, Dfn::nl(vec![Vec4], F64)); m.add_str("det", det, Dfn::nl(vec![Mat4], F64)); m.add_str("inv", inv, Dfn::nl(vec![Mat4], Mat4)); m.add_str("mov", mov, Dfn::nl(vec![Vec4], Mat4)); @@ -354,7 +354,7 @@ impl Module { m.add_str("tail", tail, Dfn::nl(vec![Link], Link)); m.add_str("neck", neck, Dfn::nl(vec![Link], Link)); m.add_str("is_empty", is_empty, Dfn::nl(vec![Link], Bool)); - m.add_str("len", len, Dfn::nl(vec![Type::array()], F64)); + m.add_unop_str("len", len, Dfn::nl(vec![Type::array()], F64)); m.add_str("push_ref(mut,_)", push_ref, Dfn { lts: vec![Lt::Default, Lt::Arg(0)], tys: vec![Type::array(), Any], @@ -446,26 +446,17 @@ impl Module { } for f in self.ext_prelude.iter().rev() { if &f.name == name { - return if f.p.returns() { - if f.p.lazy == LAZY_NO { - if let FnExt::Return(ff) = f.f { + return match f.f { + FnExt::Return(ff) => { + if f.p.lazy == LAZY_NO { FnIndex::Return(FnReturnRef(ff)) } else { - FnIndex::None - } - } else { - if let FnExt::Return(ff) = f.f { FnIndex::Lazy(FnReturnRef(ff), f.p.lazy) - } else { - FnIndex::None } } - } else { - if let FnExt::Void(ff) = f.f { - FnIndex::Void(FnVoidRef(ff)) - } else { - FnIndex::None - } + FnExt::BinOp(ff) => FnIndex::BinOp(FnBinOpRef(ff)), + FnExt::UnOp(ff) => FnIndex::UnOp(FnUnOpRef(ff)), + FnExt::Void(ff) => FnIndex::Void(FnVoidRef(ff)), }; } } @@ -529,4 +520,44 @@ impl Module { p: prelude_function, }); } + + /// Adds a new external prelude binary operator. + pub fn add_binop( + &mut self, + name: Arc, + f: fn(&Variable, &Variable) -> Result, + prelude_function: Dfn + ) { + self.ext_prelude.push(FnExternal { + namespace: self.register_namespace.clone(), + name: name.clone(), + f: f.into(), + p: prelude_function, + }); + } + + /// Adds a new external prelude unary operator. + pub fn add_unop( + &mut self, + name: Arc, + f: fn(&Variable) -> Result, + prelude_function: Dfn + ) { + self.ext_prelude.push(FnExternal { + namespace: self.register_namespace.clone(), + name: name.clone(), + f: f.into(), + p: prelude_function, + }); + } + + /// Adds a new external prelude unary operator. + pub fn add_unop_str( + &mut self, + name: &str, + f: fn(&Variable) -> Result, + prelude_function: Dfn + ) { + self.add_unop(Arc::new(name.into()), f, prelude_function) + } } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 40c64e98..ab2b0496 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -514,6 +514,8 @@ impl Runtime { } CallVoid(ref call) => self.call_void(&call.args, call.fun, &call.info), CallReturn(ref call) => self.call_return(&call.args, call.fun, &call.info), + CallBinOp(ref call) => self.call_binop(&call.left, &call.right, call.fun, &call.info), + CallUnOp(ref call) => self.call_unop(&call.arg, call.fun, &call.info), CallLazy(ref call) => self.call_lazy(&call.args, call.fun, call.lazy_inv, &call.info), CallLoaded(ref call) => { @@ -1075,6 +1077,68 @@ impl Runtime { })?), Flow::Continue)) } + fn call_binop( + &mut self, + left_expr: &ast::Expression, + right_expr: &ast::Expression, + fun: crate::FnBinOpRef, + info: &Box, + ) -> FlowResult { + let left = match self.expression(left_expr, Side::Right)? { + (Some(x), Flow::Continue) => x, + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(left_expr.source_range(), + "Expected something. \ + Expression did not return a value.") + }; + let right = match self.expression(right_expr, Side::Right)? { + (Some(x), Flow::Continue) => x, + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(right_expr.source_range(), + "Expected something. \ + Expression did not return a value.") + }; + let left = self.resolve(&left); + let right = self.resolve(&right); + Ok((Some((fun.0)(left, right).map_err(|err| { + let range = if let Some(ind) = self.arg_err_index.get() { + self.arg_err_index.set(None); + if ind == 0 {left_expr.source_range()} + else if ind == 1 {right_expr.source_range()} + else {info.source_range} + } else { + info.source_range + }; + self.module.error(range, &err, self) + })?), Flow::Continue)) + } + + fn call_unop( + &mut self, + expr: &ast::Expression, + fun: crate::FnUnOpRef, + info: &Box, + ) -> FlowResult { + let r = match self.expression(expr, Side::Right)? { + (Some(x), Flow::Continue) => x, + (x, Flow::Return) => { return Ok((x, Flow::Return)); } + _ => return self.err(expr.source_range(), + "Expected something. \ + Expression did not return a value.") + }; + let r = self.resolve(&r); + Ok((Some((fun.0)(r).map_err(|err| { + let range = if let Some(ind) = self.arg_err_index.get() { + self.arg_err_index.set(None); + if ind == 0 {expr.source_range()} + else {info.source_range} + } else { + info.source_range + }; + self.module.error(range, &err, self) + })?), Flow::Continue)) + } + fn call_lazy( &mut self, args: &[ast::Expression], @@ -1338,6 +1402,10 @@ impl Runtime { self.call_return(&call.args, f, &call.info), FnIndex::Lazy(f, lazy_inv) => self.call_lazy(&call.args, f, lazy_inv, &call.info), + FnIndex::BinOp(f) => + self.call_binop(&call.args[0], &call.args[1], f, &call.info), + FnIndex::UnOp(f) => + self.call_unop(&call.args[0], f, &call.info), FnIndex::Loaded(f_index) => { self.call_loaded(&call.args, f_index, &call.info, &call.custom_source, loader) diff --git a/src/write.rs b/src/write.rs index 62769a07..461c33e2 100644 --- a/src/write.rs +++ b/src/write.rs @@ -206,6 +206,9 @@ fn write_expr( E::Call(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, E::CallVoid(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, E::CallReturn(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, + E::CallBinOp(ref call) => write_call(w, rt, &call.info.name, + &[call.left.clone(), call.right.clone()], tabs)?, + E::CallUnOp(ref call) => write_call(w, rt, &call.info.name, &[call.arg.clone()], tabs)?, E::CallLazy(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, E::CallLoaded(ref call) => write_call(w, rt, &call.info.name, &call.args, tabs)?, E::Return(ref expr) => { From b60b15ca66dc8a6a635e45357654962accc38721 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 28 Sep 2019 14:30:05 +0200 Subject: [PATCH 74/83] Added `check__in_string_imports` - Added `_` as printable character of objects --- src/dyon_std/mod.rs | 122 +++++++++++++++++++++++++++++++++++++- src/lib.dyon | 5 ++ src/lib.rs | 43 +++++++++++--- src/lifetime/lt.rs | 4 +- src/lifetime/mod.rs | 33 +++++++---- src/lifetime/node.rs | 4 +- src/lifetime/normalize.rs | 2 +- src/lifetime/typecheck.rs | 2 +- src/module.rs | 3 + src/write.rs | 2 +- 10 files changed, 191 insertions(+), 29 deletions(-) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index a69e6603..b8d9db04 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -1218,6 +1218,126 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result Result { + let modules = rt.stack.pop().expect(TINVOTS); + let source = rt.stack.pop().expect(TINVOTS); + let source = match rt.resolve(&source) { + &Variable::Str(ref t) => t.clone(), + x => return Err(rt.expected_arg(1, x, "str")) + }; + let name = rt.stack.pop().expect(TINVOTS); + let name = match rt.resolve(&name) { + &Variable::Str(ref t) => t.clone(), + x => return Err(rt.expected_arg(0, x, "str")) + }; + let mut new_module = Module::empty(); + for f in &rt.module.ext_prelude { + match f.f { + FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), + FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), + } + } + let x = rt.resolve(&modules); + match x { + &Variable::Array(ref array) => { + for it in &**array { + match rt.resolve(it) { + &Variable::RustObject(ref obj) => { + match obj.lock().unwrap().downcast_ref::>() { + Some(m) => { + // Add external functions from imports. + for f in &m.ext_prelude { + let has_external = new_module.ext_prelude.iter() + .any(|a| a.name == f.name); + if !has_external { + match f.f { + FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), + FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), + FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), + } + } + } + // Register loaded functions from imports. + for f in &m.functions { + new_module.register(f.clone()) + } + } + None => return Err(rt.expected_arg(2, x, "[Module]")) + } + } + x => return Err(rt.expected_arg(2, x, "[Module]")) + } + } + } + x => return Err(rt.expected_arg(2, x, "[Module]")) + } + let v = match check_str(&name, source, &new_module) { + Err(err) => Variable::Result(Err(Box::new(Error { + message: Variable::Str(Arc::new( + format!("When attempting to load module:\n{}", err))), + trace: vec![] + }))), + Ok(nodes) => { + Variable::Result(Ok(Box::new(Variable::Array({ + use embed::PushVariable; + + let mut res = vec![]; + lazy_static!{ + static ref KIND: Arc = Arc::new("kind".into()); + static ref CHILDREN: Arc = Arc::new("children".into()); + static ref NAMES: Arc = Arc::new("names".into()); + static ref PARENT: Arc = Arc::new("parent".into()); + static ref TY: Arc = Arc::new("ty".into()); + static ref ALIAS: Arc = Arc::new("alias".into()); + static ref MUTABLE: Arc = Arc::new("mutable".into()); + static ref TRY: Arc = Arc::new("try".into()); + static ref GRAB_LEVEL: Arc = Arc::new("grab_level".into()); + static ref SOURCE_OFFSET: Arc = Arc::new("source_offset".into()); + static ref SOURCE_LENGTH: Arc = Arc::new("source_length".into()); + static ref START: Arc = Arc::new("start".into()); + static ref END: Arc = Arc::new("end".into()); + static ref LIFETIME: Arc = Arc::new("lifetime".into()); + static ref DECLARATION: Arc = Arc::new("declaration".into()); + static ref OP: Arc = Arc::new("op".into()); + static ref LTS: Arc = Arc::new("lts".into()); + } + for n in &nodes { + let mut obj = HashMap::new(); + obj.insert(KIND.clone(), format!("{:?}", n.kind).push_var()); + obj.insert(CHILDREN.clone(), n.children.push_var()); + obj.insert(NAMES.clone(), n.names.push_var()); + obj.insert(PARENT.clone(), n.parent.push_var()); + obj.insert(TY.clone(), n.ty.as_ref().map(|ty| ty.description()).push_var()); + obj.insert(ALIAS.clone(), n.alias.push_var()); + obj.insert(MUTABLE.clone(), n.mutable.push_var()); + obj.insert(TRY.clone(), n.try.push_var()); + obj.insert(GRAB_LEVEL.clone(), (n.grab_level as u32).push_var()); + obj.insert(SOURCE_OFFSET.clone(), n.source.offset.push_var()); + obj.insert(SOURCE_LENGTH.clone(), n.source.length.push_var()); + obj.insert(START.clone(), n.start.push_var()); + obj.insert(END.clone(), n.end.push_var()); + obj.insert(LIFETIME.clone(), n.lifetime.push_var()); + obj.insert(DECLARATION.clone(), n.declaration.push_var()); + obj.insert(OP.clone(), + n.op.as_ref().map(|op| format!("{:?}", op)).push_var()); + obj.insert(LTS.clone(), + n.lts.iter() + .map(|lt| format!("{:?}", lt)).collect::>() + .push_var()); + res.push(Variable::Object(Arc::new(obj))); + } + Arc::new(res) + })))) + } + }; + Ok(v) +} + pub(crate) fn _call(rt: &mut Runtime) -> Result<(), String> { // Use the source from calling function. let source = rt.module.functions[rt.call_stack.last().unwrap().index].source.clone(); @@ -1571,8 +1691,6 @@ dyon_fn!{fn syntax__in_string(name: Arc, text: Arc) -> Variable }} pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result { - use piston_meta::Syntax; - let text = rt.stack.pop().expect(TINVOTS); let text = match rt.resolve(&text) { &Variable::Str(ref t) => t.clone(), diff --git a/src/lib.dyon b/src/lib.dyon index a3272b89..3b2ee988 100644 --- a/src/lib.dyon +++ b/src/lib.dyon @@ -236,6 +236,11 @@ fn load__source_imports(source: str, imports: [any]) -> res[any] { ... } /// Returns `ok(module)` if it succeeds. fn module__in_string_imports(name: str, code: str, imports: [any]) -> res[any] { ... } +/// Returns data from lifetime/type-checker from source. +/// Ignores any lifetime or type error. +/// Returns an error if there are any syntax errors. +fn check__in_string_imports(name: str, code: str, imports: [any]) -> res[[{}]] { ... } + /// Calls function in module with arguments. fn call(module: any, function: str, arguments: [any]) { ... } diff --git a/src/lib.rs b/src/lib.rs index 47ebe1e1..c17be1dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ use std::thread::JoinHandle; use std::sync::{Arc, Mutex}; use std::collections::HashMap; use range::Range; -use piston_meta::MetaData; +use piston_meta::{parse_errstr, syntax_errstr, MetaData, Syntax}; pub mod ast; pub mod runtime; @@ -553,6 +553,39 @@ pub fn load(source: &str, module: &mut Module) -> Result<(), String> { load_str(source, data, module) } +lazy_static! { + static ref SYNTAX_RULES: Result = { + let syntax = include_str!("../assets/syntax.txt"); + syntax_errstr(syntax) + }; +} + +/// Generates graph of nodes after lifetime and type check. +/// +/// Ignores errors generated during lifetime or type check. +/// Returns an error if the source does not satisfy the syntax. +/// +/// This data is what the lifetime/type-checker knows about the source. +pub(crate) fn check_str( + source: &str, + d: Arc, + module: &Module +) -> Result, String> { + let syntax_rules = SYNTAX_RULES.as_ref().map_err(|err| err.clone())?; + + let mut data = vec![]; + parse_errstr(syntax_rules, &d, &mut data).map_err( + |err| format!("In `{}:`\n{}", source, err) + )?; + + let check_data = data.clone(); + let prelude = Arc::new(Prelude::from_module(module)); + + let mut nodes = vec![]; + let _ = lifetime::check_core(&mut nodes, &check_data, &prelude); + Ok(nodes) +} + /// Loads a source from string. /// /// - source - The name of source file @@ -560,14 +593,6 @@ pub fn load(source: &str, module: &mut Module) -> Result<(), String> { /// - module - The module to load the source pub fn load_str(source: &str, d: Arc, module: &mut Module) -> Result<(), String> { use std::thread; - use piston_meta::{parse_errstr, syntax_errstr, Syntax}; - - lazy_static! { - static ref SYNTAX_RULES: Result = { - let syntax = include_str!("../assets/syntax.txt"); - syntax_errstr(syntax) - }; - } let syntax_rules = SYNTAX_RULES.as_ref().map_err(|err| err.clone())?; diff --git a/src/lifetime/lt.rs b/src/lifetime/lt.rs index 69812647..645a2284 100644 --- a/src/lifetime/lt.rs +++ b/src/lifetime/lt.rs @@ -72,7 +72,7 @@ fn compare_argument_outlives(a: &[usize], b: &[usize]) -> Option { } /// Gets the lifetime of a function argument. -pub fn arg_lifetime( +pub(crate) fn arg_lifetime( declaration: usize, arg: &Node, nodes: &[Node], @@ -108,7 +108,7 @@ pub fn arg_lifetime( }) } -pub fn compare_lifetimes( +pub(crate) fn compare_lifetimes( l: &Option, r: &Option, nodes: &[Node] diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index bbf65707..9e944462 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -6,7 +6,8 @@ use std::collections::{HashMap, HashSet}; use self::piston_meta::MetaData; use self::range::Range; use self::kind::Kind; -use self::node::{convert_meta_data, Node}; +use self::node::convert_meta_data; +pub(crate) use self::node::Node; use self::lt::{arg_lifetime, compare_lifetimes, Lifetime}; use prelude::{Lt, Prelude}; @@ -24,10 +25,20 @@ mod normalize; /// Returns refined return types of functions to put in AST. pub fn check( data: &[Range], - prelude: &Prelude + prelude: &Prelude, ) -> Result, Type>, Range> { let mut nodes: Vec = vec![]; - convert_meta_data(&mut nodes, data)?; + check_core(&mut nodes, data, prelude) +} + +// Core lifetime and type check. +pub(crate) fn check_core( + nodes: &mut Vec, + data: &[Range], + prelude: &Prelude +) -> Result, Type>, Range> { + + convert_meta_data(nodes, data)?; // Rewrite multiple binary operators into nested ones. for i in 0..nodes.len() { @@ -95,9 +106,9 @@ pub fn check( for i in 0..nodes.len() { if nodes[i].children.len() == 1 { match nodes[i].kind { - Kind::Norm => Node::rewrite_unop(i, crate::NORM.clone(), &mut nodes), - Kind::Not => Node::rewrite_unop(i, crate::NOT.clone(), &mut nodes), - Kind::Neg => Node::rewrite_unop(i, crate::NEG.clone(), &mut nodes), + Kind::Norm => Node::rewrite_unop(i, crate::NORM.clone(), nodes), + Kind::Not => Node::rewrite_unop(i, crate::NOT.clone(), nodes), + Kind::Neg => Node::rewrite_unop(i, crate::NEG.clone(), nodes), _ => {} } } else if nodes[i].binops.len() == 1 && nodes[i].children.len() == 2 { @@ -114,21 +125,21 @@ pub fn check( Cross => crate::CROSS.clone(), AndAlso => crate::AND_ALSO.clone(), OrElse => crate::OR_ELSE.clone(), - }, &mut nodes); + }, nodes); } else if nodes[i].kind == Kind::Pow && nodes[i].children.len() == 2 { - Node::rewrite_binop(i, crate::POW.clone(), &mut nodes); + Node::rewrite_binop(i, crate::POW.clone(), nodes); } if nodes[i].children.len() == 1 { match nodes[i].kind { - Kind::Add | Kind::Mul => Node::simplify(i, &mut nodes), + Kind::Add | Kind::Mul => Node::simplify(i, nodes), _ => {} } } } // After graph rewrite, the graph might be unnormalized. - normalize::fix(&mut nodes); + normalize::fix(nodes); // Add mutability information to function names. for i in 0..nodes.len() { @@ -985,7 +996,7 @@ pub fn check( } } - typecheck::run(&mut nodes, prelude, &use_lookup)?; + typecheck::run(nodes, prelude, &use_lookup)?; // Copy refined return types to use in AST. let mut refined_rets: HashMap, Type> = HashMap::new(); diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index f948e670..b2c04188 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -10,7 +10,7 @@ use Lt; use Type; #[derive(Debug)] -pub struct Node { +pub(crate) struct Node { /// The kind of node. pub kind: Kind, /// The namespace alias. @@ -364,7 +364,7 @@ impl Node { } } -pub fn convert_meta_data( +pub(crate) fn convert_meta_data( nodes: &mut Vec, data: &[Range] ) -> Result<(), Range> { diff --git a/src/lifetime/normalize.rs b/src/lifetime/normalize.rs index 18262590..6bc5cb75 100644 --- a/src/lifetime/normalize.rs +++ b/src/lifetime/normalize.rs @@ -2,6 +2,6 @@ use super::*; /// Normalize directed acyclic graph such that all children are sorted in memory, /// and no child is stored before its parent. -pub fn fix(nodes: &mut [Node]) { +pub(crate) fn fix(nodes: &mut [Node]) { tree_mem_sort::sort(nodes, |n| &mut n.parent, |n| &mut n.children) } diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index 9200ac60..aa07b802 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -44,7 +44,7 @@ mod refine; /// The type propagation step uses this assumption without checking the whole `if` expression. /// After type propagation, all blocks in the `if` expression should have some type information, /// but no further propagation is necessary, so it only need to check for consistency. -pub fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> Result<(), Range> { +pub(crate) fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLookup) -> Result<(), Range> { use std::collections::HashMap; // Keep an extra todo-list for nodes that are affected by type refinement. diff --git a/src/module.rs b/src/module.rs index 1e7c316d..4587c8de 100644 --- a/src/module.rs +++ b/src/module.rs @@ -334,6 +334,9 @@ impl Module { Dfn::nl(vec![Str, Type::array()], Type::result())); m.add_str("module__in_string_imports", module__in_string_imports, Dfn::nl(vec![Str, Str, Type::array()], Type::result())); + m.add_str("check__in_string_imports", check__in_string_imports, + Dfn::nl(vec![Str, Str, Type::array()], + Type::Result(Box::new(Type::Array(Box::new(Type::Object)))))); m.add_str("call", _call, Dfn::nl(vec![Any, Str, Type::array()], Void)); m.add_str("call_ret", call_ret, Dfn::nl(vec![Any, Str, Type::array()], Any)); m.add_str("functions", functions, Dfn::nl(vec![], Any)); diff --git a/src/write.rs b/src/write.rs index 461c33e2..7eb59062 100644 --- a/src/write.rs +++ b/src/write.rs @@ -90,7 +90,7 @@ pub(crate) fn write_variable( write!(w, "{{")?; let n = obj.len(); for (i, (k, v)) in obj.iter().enumerate() { - if k.chars().all(|c| c.is_alphanumeric()) { + if k.chars().all(|c| c.is_alphanumeric() || c == '_') { write!(w, "{}: ", k)?; } else { json::write_string(w, &k)?; From c91e29003dbd9bdaf0dae1ad6452fd000da85831 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 28 Sep 2019 16:46:32 +0200 Subject: [PATCH 75/83] Define constants for precedence levels --- src/ast/mod.rs | 13 +++++++++---- src/write.rs | 8 ++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1b2374f0..d7d2fcac 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2196,6 +2196,11 @@ pub enum BinOp { AndAlso, } +pub(crate) const BINOP_PREC_POW: u8 = 3; +pub(crate) const BINOP_PREC_MUL: u8 = 2; +pub(crate) const BINOP_PREC_ADD: u8 = 1; +pub(crate) const BINOP_PREC_OR: u8 = 0; + impl BinOp { /// Returns symbol of binary operator. pub fn symbol(self) -> &'static str { @@ -2226,11 +2231,11 @@ impl BinOp { /// Used to put parentheses in right places when printing out closures. pub fn precedence(self) -> u8 { match self { - BinOp::OrElse | BinOp::AndAlso => 0, - BinOp::Add | BinOp::Sub => 1, + BinOp::OrElse | BinOp::AndAlso => BINOP_PREC_OR, + BinOp::Add | BinOp::Sub => BINOP_PREC_ADD, BinOp::Mul | BinOp::Dot | BinOp::Cross - | BinOp::Div | BinOp::Rem => 2, - BinOp::Pow => 3, + | BinOp::Div | BinOp::Rem => BINOP_PREC_MUL, + BinOp::Pow => BINOP_PREC_POW, } } } diff --git a/src/write.rs b/src/write.rs index 7eb59062..96666630 100644 --- a/src/write.rs +++ b/src/write.rs @@ -385,10 +385,10 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo E::Call(ref call) => { if let Some(binop_op) = standard_binop(&call.info.name, &call.args) { match (op.precedence(), binop_op.precedence()) { - (3, _) => true, - (2, 1) => true, - (2, 2) if right => true, - (1, 1) if right => true, + (ast::BINOP_PREC_POW, _) => true, + (ast::BINOP_PREC_MUL, ast::BINOP_PREC_ADD) => true, + (ast::BINOP_PREC_MUL, ast::BINOP_PREC_MUL) if right => true, + (ast::BINOP_PREC_ADD, ast::BINOP_PREC_ADD) if right => true, _ => false } } else if let Some(_) = standard_compare(&call.info.name, &call.args) { From c27293075bcf295de665debe68d5ce297fd10dc4 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 28 Sep 2019 17:47:59 +0200 Subject: [PATCH 76/83] Move `CompareOp` into `BinOp` --- assets/syntax.txt | 4 +- src/ast/mod.rs | 190 ++++++++++++------------------------------- src/lifetime/mod.rs | 6 ++ src/lifetime/node.rs | 24 ++++++ src/write.rs | 58 +------------ 5 files changed, 87 insertions(+), 195 deletions(-) diff --git a/assets/syntax.txt b/assets/syntax.txt index 6a68620e..2d5cfdeb 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -146,7 +146,7 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" 49 assign_op = { ":=":":=" "=":"=" "+=":"+=" "-=":"-=" "*=":"*=" "/=":"/=" "%=":"%=" "^=":"^=" } -50 compare = [lexpr:"left" wn compare_op ?w expr:"right"] +50 compare = [lexpr:"expr" wn compare_op ?w expr:"expr"] 51 compare_op = {"==":"==" "!=":"!=" "¬=":"!=" "<=":"<=" "<":"<" ">=":">=" ">":">"} 52 grab = ["grab" ?[w "'" .$:"grab_level"] w expr:"expr"] 53 try_expr = ["try" w expr:"expr"] @@ -239,7 +239,7 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" 203 / = [wn "/":"/" !"/" ?w] 204 % = [wn "%":"%" ?w] 205 pow = [lexpr:"base" wn {"^" "⊻" ["xor" w]} ?w lexpr:"exp"] -206 mul = .s!({* / %} {unop_neg:"neg" pow:"pow" lexpr:"val"}) +206 mul = .s!({* / %} {unop_neg:"neg" pow:"pow" lexpr:"expr"}) 207 mul_expr = {mul:"mul"} 208 add = .s!({+ -} mul_expr:"expr") diff --git a/src/ast/mod.rs b/src/ast/mod.rs index d7d2fcac..43726345 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1153,7 +1153,11 @@ impl Expression { convert.update(range); result = Some(val); } else if let Ok((range, val)) = Mul::from_meta_data( - file, source, convert, ignored) { + file, source, "mul", convert, ignored) { + convert.update(range); + result = Some(val.into_expression()); + } else if let Ok((range, val)) = Mul::from_meta_data( + file, source, "compare", convert, ignored) { convert.update(range); result = Some(val.into_expression()); } else if let Ok((range, val)) = Item::from_meta_data( @@ -1314,10 +1318,6 @@ impl Expression { file, source, convert, ignored) { convert.update(range); result = Some(Expression::If(Box::new(val))); - } else if let Ok((range, val)) = Compare::from_meta_data( - file, source, convert, ignored) { - convert.update(range); - result = Some(val.into_expression()); } else if let Ok((range, _)) = convert.meta_bool("try") { convert.update(range); result = Some(Expression::Try(Box::new(result.unwrap()))); @@ -2025,11 +2025,11 @@ impl Mul { pub fn from_meta_data( file: &Arc, source: &Arc, + node: &str, mut convert: Convert, ignored: &mut Vec) -> Result<(Range, Mul), ()> { let start = convert; - let node = "mul"; let start_range = convert.start_node(node)?; convert.update(start_range); @@ -2048,7 +2048,7 @@ impl Mul { convert.update(range); items.push(val.into_expression()); } else if let Ok((range, val)) = Expression::from_meta_data( - file, source, "val", convert, ignored) { + file, source, "expr", convert, ignored) { convert.update(range); items.push(val); } else if let Ok((range, _)) = convert.meta_bool("*.") { @@ -2069,6 +2069,24 @@ impl Mul { } else if let Ok((range, _)) = convert.meta_bool("&&") { convert.update(range); ops.push(BinOp::AndAlso); + } else if let Ok((range, _)) = convert.meta_bool("<") { + convert.update(range); + ops.push(BinOp::Less); + } else if let Ok((range, _)) = convert.meta_bool("<=") { + convert.update(range); + ops.push(BinOp::LessOrEqual); + } else if let Ok((range, _)) = convert.meta_bool(">") { + convert.update(range); + ops.push(BinOp::Greater); + } else if let Ok((range, _)) = convert.meta_bool(">=") { + convert.update(range); + ops.push(BinOp::GreaterOrEqual); + } else if let Ok((range, _)) = convert.meta_bool("==") { + convert.update(range); + ops.push(BinOp::Equal); + } else if let Ok((range, _)) = convert.meta_bool("!=") { + convert.update(range); + ops.push(BinOp::NotEqual); } else { let range = convert.ignore(); convert.update(range); @@ -2194,6 +2212,18 @@ pub enum BinOp { OrElse, /// Lazy AND operator (`&&`). AndAlso, + /// Less. + Less, + /// Less or equal. + LessOrEqual, + /// Greater. + Greater, + /// Greater or equal. + GreaterOrEqual, + /// Equal. + Equal, + /// Not equal. + NotEqual, } pub(crate) const BINOP_PREC_POW: u8 = 3; @@ -2215,6 +2245,12 @@ impl BinOp { BinOp::Pow => "^", BinOp::OrElse => "||", BinOp::AndAlso => "&&", + BinOp::Less => "<", + BinOp::LessOrEqual => "<=", + BinOp::Greater => ">", + BinOp::GreaterOrEqual => ">=", + BinOp::Equal => "==", + BinOp::NotEqual => "!=", } } @@ -2231,6 +2267,9 @@ impl BinOp { /// Used to put parentheses in right places when printing out closures. pub fn precedence(self) -> u8 { match self { + BinOp::Less | BinOp::LessOrEqual | + BinOp::Greater | BinOp::GreaterOrEqual | + BinOp::Equal | BinOp::NotEqual => BINOP_PREC_OR, BinOp::OrElse | BinOp::AndAlso => BINOP_PREC_OR, BinOp::Add | BinOp::Sub => BINOP_PREC_ADD, BinOp::Mul | BinOp::Dot | BinOp::Cross @@ -3123,6 +3162,12 @@ impl BinOpExpression { Cross => crate::CROSS.clone(), AndAlso => crate::AND_ALSO.clone(), OrElse => crate::OR_ELSE.clone(), + Less => crate::LESS.clone(), + LessOrEqual => crate::LESS_OR_EQUAL.clone(), + Greater => crate::GREATER.clone(), + GreaterOrEqual => crate::GREATER_OR_EQUAL.clone(), + Equal => crate::EQUAL.clone(), + NotEqual => crate::NOT_EQUAL.clone(), }, source_range: self.source_range, }) @@ -4358,137 +4403,6 @@ impl If { } } -/// Compare expression. -#[derive(Debug, Clone)] -pub struct Compare { - /// Comparison operator. - pub op: CompareOp, - /// Left side of expression. - pub left: Expression, - /// Right side of expression. - pub right: Expression, - /// The range in source. - pub source_range: Range, -} - -impl Compare { - /// Creates compare expression from meta data. - pub fn from_meta_data( - file: &Arc, - source: &Arc, - mut convert: Convert, - ignored: &mut Vec) - -> Result<(Range, Compare), ()> { - let start = convert; - let node = "compare"; - let start_range = convert.start_node(node)?; - convert.update(start_range); - - let mut op: Option = None; - let mut left: Option = None; - let mut right: Option = None; - loop { - if let Ok(range) = convert.end_node(node) { - convert.update(range); - break; - } else if let Ok((range, _)) = convert.meta_bool("<") { - convert.update(range); - op = Some(CompareOp::Less); - } else if let Ok((range, _)) = convert.meta_bool("<=") { - convert.update(range); - op = Some(CompareOp::LessOrEqual); - } else if let Ok((range, _)) = convert.meta_bool(">") { - convert.update(range); - op = Some(CompareOp::Greater); - } else if let Ok((range, _)) = convert.meta_bool(">=") { - convert.update(range); - op = Some(CompareOp::GreaterOrEqual); - } else if let Ok((range, _)) = convert.meta_bool("==") { - convert.update(range); - op = Some(CompareOp::Equal); - } else if let Ok((range, _)) = convert.meta_bool("!=") { - convert.update(range); - op = Some(CompareOp::NotEqual); - } else if let Ok((range, val)) = Expression::from_meta_data( - file, source, "left", convert, ignored) { - convert.update(range); - left = Some(val); - } else if let Ok((range, val)) = Expression::from_meta_data( - file, source, "right", convert, ignored) { - convert.update(range); - right = Some(val); - } else { - let range = convert.ignore(); - convert.update(range); - ignored.push(range); - } - } - - let op = op.ok_or(())?; - let left = left.ok_or(())?; - let right = right.ok_or(())?; - Ok((convert.subtract(start), Compare { - op, - left, - right, - source_range: convert.source(start).unwrap(), - })) - } - - fn into_expression(self) -> Expression { - use self::CompareOp::*; - - Expression::Call(Box::new(Call { - args: vec![self.left, self.right], - custom_source: None, - f_index: FnIndex::None, - info: Box::new(CallInfo { - alias: None, - name: match self.op { - Less => crate::LESS.clone(), - LessOrEqual => crate::LESS_OR_EQUAL.clone(), - Greater => crate::GREATER.clone(), - GreaterOrEqual => crate::GREATER_OR_EQUAL.clone(), - Equal => crate::EQUAL.clone(), - NotEqual => crate::NOT_EQUAL.clone(), - }, - source_range: self.source_range, - }) - })) - } -} - -/// Comparison operator. -#[derive(Debug, Clone, Copy)] -pub enum CompareOp { - /// Less. - Less, - /// Less or equal. - LessOrEqual, - /// Greater. - Greater, - /// Greater or equal. - GreaterOrEqual, - /// Equal. - Equal, - /// Not equal. - NotEqual, -} - -impl CompareOp { - /// Returns symbol for the comparison operator. - pub fn symbol(self) -> &'static str { - match self { - CompareOp::Less => "<", - CompareOp::LessOrEqual => "<=", - CompareOp::Greater => ">", - CompareOp::GreaterOrEqual => ">=", - CompareOp::Equal => "==", - CompareOp::NotEqual => "!=", - } - } -} - /// Stores `in ` expression. #[derive(Debug, Clone)] pub struct In { diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index 9e944462..d78e6a4b 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -125,6 +125,12 @@ pub(crate) fn check_core( Cross => crate::CROSS.clone(), AndAlso => crate::AND_ALSO.clone(), OrElse => crate::OR_ELSE.clone(), + Less => crate::LESS.clone(), + LessOrEqual => crate::LESS_OR_EQUAL.clone(), + Greater => crate::GREATER.clone(), + GreaterOrEqual => crate::GREATER_OR_EQUAL.clone(), + Equal => crate::EQUAL.clone(), + NotEqual => crate::NOT_EQUAL.clone(), }, nodes); } else if nodes[i].kind == Kind::Pow && nodes[i].children.len() == 2 { Node::rewrite_binop(i, crate::POW.clone(), nodes); diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index b2c04188..26705274 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -599,6 +599,30 @@ pub(crate) fn convert_meta_data( let i = *parents.last().unwrap(); nodes[i].binops.push(BinOp::OrElse); } + "<" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::Less); + } + "<=" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::LessOrEqual); + } + ">" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::Greater); + } + ">=" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::GreaterOrEqual); + } + "==" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::Equal); + } + "!=" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::NotEqual); + } _ => {} } } diff --git a/src/write.rs b/src/write.rs index 96666630..d9841d49 100644 --- a/src/write.rs +++ b/src/write.rs @@ -390,12 +390,11 @@ fn binop_needs_parens(op: ast::BinOp, expr: &ast::Expression, right: bool) -> bo (ast::BINOP_PREC_MUL, ast::BINOP_PREC_MUL) if right => true, (ast::BINOP_PREC_ADD, ast::BINOP_PREC_ADD) if right => true, _ => false + // TODO: Handle precedence for comparison operators. } - } else if let Some(_) = standard_compare(&call.info.name, &call.args) { - true } else {false} } - // TODO: Handle `E::CallVoid`. + // TODO: Handle `E::CallVoid` etc. _ => false } } @@ -531,8 +530,6 @@ fn write_call( write_neg(w, rt, &args[0], tabs) } else if let Some(op) = standard_binop(name, args) { write_binop(w, rt, op, &args[0], &args[1], tabs) - } else if let Some(op) = standard_compare(name, args) { - write_compare(w, rt, op, &args[0], &args[1], tabs) } else { write!(w, "{}(", name)?; for (i, arg) in args.iter().enumerate() { @@ -741,65 +738,16 @@ fn standard_binop(name: &Arc, args: &[ast::Expression]) -> Option Pow, "and_also" => AndAlso, "or_else" => OrElse, - _ => return None - }) -} - -fn standard_compare(name: &Arc, args: &[ast::Expression]) -> Option { - use ast::CompareOp::*; - - if args.len() != 2 {return None}; - - let name: &str = &**name; - Some(match name { "less" => Less, "less_or_equal" => LessOrEqual, "greater" => Greater, "greater_or_equal" => GreaterOrEqual, "equal" => Equal, "not_equal" => NotEqual, - _ => return None, + _ => return None }) } -fn compare_needs_parent(expr: &ast::Expression) -> bool { - use ast::Expression as E; - - match *expr { - E::Call(ref call) => standard_binop(&call.info.name, &call.args).is_some(), - _ => false - } -} - -fn write_compare( - w: &mut W, - rt: &Runtime, - op: ast::CompareOp, - left: &ast::Expression, - right: &ast::Expression, - tabs: u32, -) -> Result<(), io::Error> { - let left_needs_parens = compare_needs_parent(left); - let right_needs_parens = compare_needs_parent(right); - - if left_needs_parens { - write!(w, "(")?; - } - write_expr(w, rt, left, tabs)?; - if left_needs_parens { - write!(w, ")")?; - } - write!(w, " {} ", op.symbol())?; - if right_needs_parens { - write!(w, "(")?; - } - write_expr(w, rt, right, tabs)?; - if right_needs_parens { - write!(w, ")")?; - } - Ok(()) -} - fn write_for_n( w: &mut W, rt: &Runtime, From d9edb8e93dc381b43bfd8824b0ad0c326a0c8912 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 28 Sep 2019 17:52:45 +0200 Subject: [PATCH 77/83] Removed `ast::Add` --- src/ast/mod.rs | 96 ++++++++------------------------------------------ 1 file changed, 14 insertions(+), 82 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 43726345..a08a3659 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1144,8 +1144,8 @@ impl Expression { file, source, "block", convert, ignored) { convert.update(range); result = Some(Expression::Block(Box::new(val))); - } else if let Ok((range, val)) = Add::from_meta_data( - file, source, convert, ignored) { + } else if let Ok((range, val)) = Mul::from_meta_data( + file, source, "add", convert, ignored) { convert.update(range); result = Some(val.into_expression()); } else if let Ok((range, val)) = UnOpExpression::from_meta_data( @@ -1929,86 +1929,6 @@ impl ArrayFill { } } -/// Addition expression. -#[derive(Debug, Clone)] -pub struct Add { - /// Item expressions. - pub items: Vec, - /// Binary operators. - pub ops: Vec, - /// The range in source. - pub source_range: Range, -} - -impl Add { - /// Creates addition expression from meta data. - pub fn from_meta_data( - file: &Arc, - source: &Arc, - mut convert: Convert, - ignored: &mut Vec) - -> Result<(Range, Add), ()> { - let start = convert; - let node = "add"; - let start_range = convert.start_node(node)?; - convert.update(start_range); - - let mut items = vec![]; - let mut ops = vec![]; - loop { - if let Ok(range) = convert.end_node(node) { - convert.update(range); - break; - } else if let Ok((range, val)) = Expression::from_meta_data( - file, source, "expr", convert, ignored) { - convert.update(range); - items.push(val); - } else if let Ok((range, _)) = convert.meta_bool("+") { - convert.update(range); - ops.push(BinOp::Add); - } else if let Ok((range, _)) = convert.meta_bool("-") { - convert.update(range); - ops.push(BinOp::Sub); - } else if let Ok((range, _)) = convert.meta_bool("||") { - convert.update(range); - ops.push(BinOp::OrElse); - } else if let Ok((range, _)) = convert.meta_bool("⊻") { - convert.update(range); - ops.push(BinOp::Pow); - } else { - let range = convert.ignore(); - convert.update(range); - ignored.push(range); - } - } - - if items.is_empty() { - return Err(()) - } - Ok((convert.subtract(start), Add { - items, - ops, - source_range: convert.source(start).unwrap() - })) - } - - fn into_expression(mut self) -> Expression { - if self.items.len() == 1 { - self.items[0].clone() - } else { - let op = self.ops.pop().unwrap(); - let last = self.items.pop().unwrap(); - let source_range = self.source_range; - BinOpExpression { - op, - left: self.into_expression(), - right: last, - source_range - }.into_expression() - } - } -} - /// Multiply expression. #[derive(Debug, Clone)] pub struct Mul { @@ -2066,6 +1986,18 @@ impl Mul { } else if let Ok((range, _)) = convert.meta_bool("%") { convert.update(range); ops.push(BinOp::Rem); + } else if let Ok((range, _)) = convert.meta_bool("+") { + convert.update(range); + ops.push(BinOp::Add); + } else if let Ok((range, _)) = convert.meta_bool("-") { + convert.update(range); + ops.push(BinOp::Sub); + } else if let Ok((range, _)) = convert.meta_bool("||") { + convert.update(range); + ops.push(BinOp::OrElse); + } else if let Ok((range, _)) = convert.meta_bool("⊻") { + convert.update(range); + ops.push(BinOp::Pow); } else if let Ok((range, _)) = convert.meta_bool("&&") { convert.update(range); ops.push(BinOp::AndAlso); From 90a7a46b1bf7b0b35f4bb68a353b2d591717d0a3 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 28 Sep 2019 18:14:01 +0200 Subject: [PATCH 78/83] Removed `ast::Pow` --- assets/syntax.txt | 2 +- src/ast/mod.rs | 74 ++------------------------------------- src/lifetime/kind.rs | 4 --- src/lifetime/mod.rs | 2 -- src/lifetime/node.rs | 6 ++-- src/lifetime/typecheck.rs | 2 +- 6 files changed, 9 insertions(+), 81 deletions(-) diff --git a/assets/syntax.txt b/assets/syntax.txt index 2d5cfdeb..aef695fe 100644 --- a/assets/syntax.txt +++ b/assets/syntax.txt @@ -238,7 +238,7 @@ _seps: "(){}[],.:;=<>*·+-/%^?~|&∧∨!¬∑∃∀\n\"\\" } ?w] 203 / = [wn "/":"/" !"/" ?w] 204 % = [wn "%":"%" ?w] -205 pow = [lexpr:"base" wn {"^" "⊻" ["xor" w]} ?w lexpr:"exp"] +205 pow = [lexpr:"expr" wn {"^":"^" "⊻":"^" ["xor":"^" w]} ?w lexpr:"expr"] 206 mul = .s!({* / %} {unop_neg:"neg" pow:"pow" lexpr:"expr"}) 207 mul_expr = {mul:"mul"} 208 add = .s!({+ -} mul_expr:"expr") diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a08a3659..3dd00a63 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1963,8 +1963,8 @@ impl Mul { "neg", file, source, convert, ignored) { convert.update(range); items.push(val); - } else if let Ok((range, val)) = Pow::from_meta_data( - file, source, convert, ignored) { + } else if let Ok((range, val)) = Mul::from_meta_data( + file, source, "pow", convert, ignored) { convert.update(range); items.push(val.into_expression()); } else if let Ok((range, val)) = Expression::from_meta_data( @@ -1995,7 +1995,7 @@ impl Mul { } else if let Ok((range, _)) = convert.meta_bool("||") { convert.update(range); ops.push(BinOp::OrElse); - } else if let Ok((range, _)) = convert.meta_bool("⊻") { + } else if let Ok((range, _)) = convert.meta_bool("^") { convert.update(range); ops.push(BinOp::Pow); } else if let Ok((range, _)) = convert.meta_bool("&&") { @@ -2053,74 +2053,6 @@ impl Mul { } } -/// Power expression. -#[derive(Debug, Clone)] -pub struct Pow { - /// Base expression. - /// - /// This is the `x` in `x^a`. - pub base: Expression, - /// Exponent expression. - /// - /// This is the `x` in `a^x`. - pub exp: Expression, - /// The range in source. - pub source_range: Range, -} - -impl Pow { - /// Creates power expression from meta data. - pub fn from_meta_data( - file: &Arc, - source: &Arc, - mut convert: Convert, - ignored: &mut Vec) - -> Result<(Range, Pow), ()> { - let start = convert; - let node = "pow"; - let start_range = convert.start_node(node)?; - convert.update(start_range); - - let mut base: Option = None; - let mut exp: Option = None; - loop { - if let Ok(range) = convert.end_node(node) { - convert.update(range); - break; - } else if let Ok((range, val)) = Expression::from_meta_data( - file, source, "base", convert, ignored) { - convert.update(range); - base = Some(val); - } else if let Ok((range, val)) = Expression::from_meta_data( - file, source, "exp", convert, ignored) { - convert.update(range); - exp = Some(val); - } else { - let range = convert.ignore(); - convert.update(range); - ignored.push(range); - } - } - - let base = base.ok_or(())?; - let exp = exp.ok_or(())?; - Ok((convert.subtract(start), Pow { - base, - exp, - source_range: convert.source(start).unwrap() - })) - } - - fn into_expression(self) -> Expression { - BinOpExpression { - op: BinOp::Pow, - left: self.base, - right: self.exp, - source_range: self.source_range, - }.into_expression() - } -} - /// Binary operator. #[derive(Debug, Copy, Clone)] pub enum BinOp { diff --git a/src/lifetime/kind.rs b/src/lifetime/kind.rs index 7a6aba84..bf68b973 100644 --- a/src/lifetime/kind.rs +++ b/src/lifetime/kind.rs @@ -12,8 +12,6 @@ pub enum Kind { Add, Mul, Pow, - Base, - Exp, Val, Call, CallArg, @@ -122,8 +120,6 @@ impl Kind { "add" => Kind::Add, "mul" => Kind::Mul, "pow" => Kind::Pow, - "base" => Kind::Base, - "exp" => Kind::Exp, "val" => Kind::Val, "call" => Kind::Call, "call_arg" => Kind::CallArg, diff --git a/src/lifetime/mod.rs b/src/lifetime/mod.rs index d78e6a4b..6dc45018 100644 --- a/src/lifetime/mod.rs +++ b/src/lifetime/mod.rs @@ -132,8 +132,6 @@ pub(crate) fn check_core( Equal => crate::EQUAL.clone(), NotEqual => crate::NOT_EQUAL.clone(), }, nodes); - } else if nodes[i].kind == Kind::Pow && nodes[i].children.len() == 2 { - Node::rewrite_binop(i, crate::POW.clone(), nodes); } if nodes[i].children.len() == 1 { diff --git a/src/lifetime/node.rs b/src/lifetime/node.rs index 26705274..e7ac38d6 100644 --- a/src/lifetime/node.rs +++ b/src/lifetime/node.rs @@ -306,8 +306,6 @@ impl Node { (_, Kind::ArrayItem) => {} (_, Kind::ArrayFill) => {} (_, Kind::Pow) => {} - (_, Kind::Base) => {} - (_, Kind::Exp) => {} (_, Kind::Block) => {} (_, Kind::If) => {} (_, Kind::TrueBlock) => {} @@ -583,6 +581,10 @@ pub(crate) fn convert_meta_data( let i = *parents.last().unwrap(); nodes[i].binops.push(BinOp::Rem); } + "^" => { + let i = *parents.last().unwrap(); + nodes[i].binops.push(BinOp::Pow); + } "&&" => { let i = *parents.last().unwrap(); nodes[i].binops.push(BinOp::AndAlso); diff --git a/src/lifetime/typecheck.rs b/src/lifetime/typecheck.rs index aa07b802..dacba976 100644 --- a/src/lifetime/typecheck.rs +++ b/src/lifetime/typecheck.rs @@ -378,7 +378,7 @@ pub(crate) fn run(nodes: &mut Vec, prelude: &Prelude, use_lookup: &UseLook } } Kind::Return | Kind::Val | Kind::Expr | Kind::Cond | - Kind::Exp | Kind::Base | Kind::Left | Kind::Right | + Kind::Left | Kind::Right | Kind::ElseIfCond | Kind::Grab | Kind::Add | Kind::Mul | Kind::Pow => { if nodes[i].children.is_empty() { From 6517726f798ad39c91d6e24ecf535ff206c686b3 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 28 Sep 2019 18:20:17 +0200 Subject: [PATCH 79/83] Renamed `ast::Mul` to `ast::BinOpSeq` --- src/ast/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 3dd00a63..71d150f8 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1144,7 +1144,7 @@ impl Expression { file, source, "block", convert, ignored) { convert.update(range); result = Some(Expression::Block(Box::new(val))); - } else if let Ok((range, val)) = Mul::from_meta_data( + } else if let Ok((range, val)) = BinOpSeq::from_meta_data( file, source, "add", convert, ignored) { convert.update(range); result = Some(val.into_expression()); @@ -1152,11 +1152,11 @@ impl Expression { "not", file, source, convert, ignored) { convert.update(range); result = Some(val); - } else if let Ok((range, val)) = Mul::from_meta_data( + } else if let Ok((range, val)) = BinOpSeq::from_meta_data( file, source, "mul", convert, ignored) { convert.update(range); result = Some(val.into_expression()); - } else if let Ok((range, val)) = Mul::from_meta_data( + } else if let Ok((range, val)) = BinOpSeq::from_meta_data( file, source, "compare", convert, ignored) { convert.update(range); result = Some(val.into_expression()); @@ -1929,9 +1929,9 @@ impl ArrayFill { } } -/// Multiply expression. +/// Parse sequence of binary operators. #[derive(Debug, Clone)] -pub struct Mul { +pub struct BinOpSeq { /// Item expressions. pub items: Vec, /// Binary operators. @@ -1940,7 +1940,7 @@ pub struct Mul { pub source_range: Range, } -impl Mul { +impl BinOpSeq { /// Creates multiply expression from meta data. pub fn from_meta_data( file: &Arc, @@ -1948,7 +1948,7 @@ impl Mul { node: &str, mut convert: Convert, ignored: &mut Vec) - -> Result<(Range, Mul), ()> { + -> Result<(Range, BinOpSeq), ()> { let start = convert; let start_range = convert.start_node(node)?; convert.update(start_range); @@ -1963,7 +1963,7 @@ impl Mul { "neg", file, source, convert, ignored) { convert.update(range); items.push(val); - } else if let Ok((range, val)) = Mul::from_meta_data( + } else if let Ok((range, val)) = BinOpSeq::from_meta_data( file, source, "pow", convert, ignored) { convert.update(range); items.push(val.into_expression()); @@ -2029,7 +2029,7 @@ impl Mul { if items.is_empty() { return Err(()) } - Ok((convert.subtract(start), Mul { + Ok((convert.subtract(start), BinOpSeq { items, ops, source_range: convert.source(start).unwrap(), From fe7f6e495937d66fc12007395aafaf04fdc9c838 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Sat, 28 Sep 2019 19:19:26 +0200 Subject: [PATCH 80/83] Removed unneeded `Send` impl --- src/runtime/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index ab2b0496..41ee2d9c 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -98,8 +98,6 @@ pub struct Runtime { pub arg_err_index: Cell>, } -unsafe impl Send for Runtime {} - impl Default for Runtime { fn default() -> Runtime {Runtime::new()} } From 1094d73146a1494c3e60f5e1c298d14e01c9c34e Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 2 Oct 2019 10:30:18 +0200 Subject: [PATCH 81/83] Use `Ok(...)` directly --- src/dyon_std/mod.rs | 217 ++++++++++++++++++-------------------------- 1 file changed, 86 insertions(+), 131 deletions(-) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index b8d9db04..3ba0df23 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -19,11 +19,10 @@ pub(crate) fn and_also(rt: &mut Runtime) -> Result { let b = rt.stack.pop().expect(TINVOTS); let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + Ok(match (rt.resolve(&a), rt.resolve(&b)) { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && b, sec.clone()), _ => return Err("Expected `bool`".into()) - }; - Ok(r) + }) } pub(crate) fn or_else(rt: &mut Runtime) -> Result { @@ -31,33 +30,30 @@ pub(crate) fn or_else(rt: &mut Runtime) -> Result { let b = rt.stack.pop().expect(TINVOTS); let a = rt.stack.pop().expect(TINVOTS); - let r = match (rt.resolve(&a), rt.resolve(&b)) { + Ok(match (rt.resolve(&a), rt.resolve(&b)) { (&Bool(a, ref sec), &Bool(b, _)) => Bool(a || b, sec.clone()), _ => return Err("Expected `bool`".into()) - }; - Ok(r) + }) } pub(crate) fn less(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => Bool(a < b, sec.clone()), (&Str(ref a), &Str(ref b)) => Variable::bool(a < b), _ => return Err("Expected `f64` or `str`".into()) - }; - Ok(r) + }) } pub(crate) fn less_or_equal(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => Bool(a <= b, sec.clone()), (&Str(ref a), &Str(ref b)) => Variable::bool(a <= b), _ => return Err("Expected `f64` or `str`".into()) - }; - Ok(r) + }) } pub(crate) fn greater(a: &Variable, b: &Variable) -> Result { @@ -114,7 +110,7 @@ pub(crate) fn not_equal(a: &Variable, b: &Variable) -> Result pub(crate) fn add(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a + b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_add(a, b)), (&Vec4(a), &F64(b, _)) | (&F64(b, _), &Vec4(a)) => { @@ -140,14 +136,13 @@ pub(crate) fn add(a: &Variable, b: &Variable) -> Result { } (&Link(ref a), &Link(ref b)) => Link(Box::new(a.add(b))), _ => return Err("Expected `f64`, `vec4`, `mat4`, `bool`, `str` or `link`".into()) - }; - Ok(r) + }) } pub(crate) fn sub(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a - b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_sub(a, b)), (&Vec4(a), &F64(b, _)) => { @@ -179,14 +174,13 @@ pub(crate) fn sub(a: &Variable, b: &Variable) -> Result { } (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && !b, sec.clone()), _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) - }; - Ok(r) + }) } pub(crate) fn mul(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a * b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4(vecmath::vec4_mul(a, b)), (&Vec4(a), &F64(b, _)) | (&F64(b, _), &Vec4(a)) => { @@ -206,14 +200,13 @@ pub(crate) fn mul(a: &Variable, b: &Variable) -> Result { (&Mat4(ref a), &Vec4(b)) => Vec4(vecmath::col_mat4_transform(**a, b)), (&Bool(a, ref sec), &Bool(b, _)) => Bool(a && b, sec.clone()), _ => return Err("Expected `f64`, `vec4`, `mat4` or `bool`".into()) - }; - Ok(r) + }) } pub(crate) fn div(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a * b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), (&Vec4(a), &F64(b, _)) => { @@ -225,14 +218,13 @@ pub(crate) fn div(a: &Variable, b: &Variable) -> Result { Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]) } _ => return Err("Expected `f64` or `vec4`".into()) - }; - Ok(r) + }) } pub(crate) fn rem(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a % b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4([a[0] % b[0], a[1] % b[1], a[2] % b[2], a[3] % b[3]]), (&Vec4(a), &F64(b, _)) => { @@ -244,14 +236,13 @@ pub(crate) fn rem(a: &Variable, b: &Variable) -> Result { Vec4([a % b[0], a % b[1], a % b[2], a % b[3]]) } _ => return Err("Expected `f64` or `vec4`".into()) - }; - Ok(r) + }) } pub(crate) fn pow(a: &Variable, b: &Variable) -> Result { use Variable::*; - let r = match (a, b) { + Ok(match (a, b) { (&F64(a, ref sec), &F64(b, _)) => F64(a.powf(b), sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4([a[0].powf(b[0]), a[1].powf(b[1]), a[2].powf(b[2]), a[3].powf(b[3])]), @@ -265,20 +256,18 @@ pub(crate) fn pow(a: &Variable, b: &Variable) -> Result { } (&Bool(a, ref sec), &Bool(ref b, _)) => Bool(a ^ b, sec.clone()), _ => return Err("Expected `f64`, `vec4` or `bool`".into()) - }; - Ok(r) + }) } pub(crate) fn not(a: &Variable) -> Result { - let b = match *a { + Ok(match *a { Variable::Bool(ref b, ref sec) => Variable::Bool(!b, sec.clone()), _ => return Err("Expected `bool`".into()) - }; - Ok(b) + }) } pub(crate) fn neg(a: &Variable) -> Result { - let n = match *a { + Ok(match *a { Variable::F64(v, ref sec) => Variable::F64(-v, sec.clone()), Variable::Vec4(v) => Variable::Vec4([-v[0], -v[1], -v[2], -v[3]]), Variable::Mat4(ref m) => Variable::Mat4(Box::new([ @@ -288,12 +277,11 @@ pub(crate) fn neg(a: &Variable) -> Result { [-m[3][0], -m[3][1], -m[3][2], -m[3][3]], ])), _ => return Err("Expected `f64`, `vec4` or `mat4`".into()) - }; - Ok(n) + }) } pub(crate) fn dot(a: &Variable, b: &Variable) -> Result { - let r = match (a, b) { + Ok(Variable::f64(match (a, b) { (&Variable::Vec4(a), &Variable::Vec4(b)) => vecmath::vec4_dot(a, b) as f64, (&Variable::Vec4(a), &Variable::F64(b, _)) | (&Variable::F64(b, _), &Variable::Vec4(a)) => { @@ -301,8 +289,7 @@ pub(crate) fn dot(a: &Variable, b: &Variable) -> Result { (a[0] * b + a[1] * b + a[2] * b + a[3] * b) as f64 } _ => return Err("Expected (vec4, vec4), (vec4, f64) or (f64, vec4)".into()) - }; - Ok(Variable::f64(r)) + })) } dyon_fn!{fn cross(a: Vec4, b: Vec4) -> Vec4 { @@ -424,13 +411,12 @@ pub(crate) fn cv(rt: &mut Runtime) -> Result { pub(crate) fn clone(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = rt.resolve(&v).deep_clone(&rt.stack); - Ok(v) + Ok(rt.resolve(&v).deep_clone(&rt.stack)) } pub(crate) fn why(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Array(Arc::new(match rt.resolve(&v) { + Ok(Variable::Array(Arc::new(match rt.resolve(&v) { &Variable::Bool(true, Some(ref sec)) => { let mut sec = (**sec).clone(); sec.reverse(); @@ -449,13 +435,12 @@ pub(crate) fn why(rt: &mut Runtime) -> Result { }) } x => return Err(rt.expected_arg(0, x, "bool")) - })); - Ok(v) + }))) } pub(crate) fn _where(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Array(Arc::new(match rt.resolve(&v) { + Ok(Variable::Array(Arc::new(match rt.resolve(&v) { &Variable::F64(val, Some(ref sec)) => { if val.is_nan() { return Err({ @@ -475,8 +460,7 @@ pub(crate) fn _where(rt: &mut Runtime) -> Result { }) } x => return Err(rt.expected_arg(0, x, "f64")) - })); - Ok(v) + }))) } pub(crate) fn explain_why(rt: &mut Runtime) -> Result { @@ -561,54 +545,48 @@ dyon_fn!{fn sleep(v: f64) { pub(crate) fn head(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Option(match rt.resolve(&v) { + Ok(Variable::Option(match rt.resolve(&v) { &Variable::Link(ref link) => link.head(), x => return Err(rt.expected_arg(0, x, "link")) - }); - Ok(v) + })) } pub(crate) fn tip(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Option(match rt.resolve(&v) { + Ok(Variable::Option(match rt.resolve(&v) { &Variable::Link(ref link) => link.tip(), x => return Err(rt.expected_arg(0, x, "link")) - }); - Ok(v) + })) } pub(crate) fn tail(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Link(Box::new(match rt.resolve(&v) { + Ok(Variable::Link(Box::new(match rt.resolve(&v) { &Variable::Link(ref link) => link.tail(), x => return Err(rt.expected_arg(0, x, "link")) - })); - Ok(v) + }))) } pub(crate) fn neck(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Link(Box::new(match rt.resolve(&v) { + Ok(Variable::Link(Box::new(match rt.resolve(&v) { &Variable::Link(ref link) => link.neck(), x => return Err(rt.expected_arg(0, x, "link")) - })); - Ok(v) + }))) } pub(crate) fn is_empty(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::bool(match rt.resolve(&v) { + Ok(Variable::bool(match rt.resolve(&v) { &Variable::Link(ref link) => link.is_empty(), x => return Err(rt.expected_arg(0, x, "link")) - }); - Ok(v) + })) } pub(crate) fn random(rt: &mut Runtime) -> Result { use rand::Rng; - let v: f64 = rt.rng.gen(); - Ok(Variable::f64(v)) + Ok(Variable::f64(rt.rng.gen())) } dyon_fn!{fn tau() -> f64 {6.283_185_307_179_586}} @@ -1010,7 +988,7 @@ pub(crate) fn _typeof(rt: &mut Runtime) -> Result { use crate::Variable::*; let v = rt.stack.pop().expect(TINVOTS); - let t = Variable::Str(match *rt.resolve(&v) { + Ok(Variable::Str(match *rt.resolve(&v) { Str(_) => TEXT_TYPE.clone(), F64(_, _) => F64_TYPE.clone(), Vec4(_) => VEC4_TYPE.clone(), @@ -1028,8 +1006,7 @@ pub(crate) fn _typeof(rt: &mut Runtime) -> Result { Thread(_) => THREAD_TYPE.clone(), Closure(_, _) => CLOSURE_TYPE.clone(), In(_) => IN_TYPE.clone(), - }); - Ok(t) + })) } pub(crate) fn debug(rt: &mut Runtime) -> Result<(), String> { @@ -1048,7 +1025,7 @@ pub(crate) fn load(rt: &mut Runtime) -> Result { use load; let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::Str(ref text) => { let mut m = Module::empty(); for f in &rt.module.ext_prelude { @@ -1072,8 +1049,7 @@ pub(crate) fn load(rt: &mut Runtime) -> Result { x => { return Err(rt.expected_arg(0, x, "string")); } - }; - Ok(v) + }) } pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result { @@ -1129,7 +1105,7 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result } x => return Err(rt.expected_arg(1, x, "[Module]")) } - let v = match rt.resolve(&source) { + Ok(match rt.resolve(&source) { &Variable::Str(ref text) => { if let Err(err) = load(text, &mut new_module) { Variable::Result(Err(Box::new(Error { @@ -1144,8 +1120,7 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result } } x => return Err(rt.expected_arg(0, x, "str")) - }; - Ok(v) + }) } pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result { @@ -1204,7 +1179,7 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result return Err(rt.expected_arg(2, x, "[Module]")) } - let v = if let Err(err) = load_str(&name, source, &mut new_module) { + Ok(if let Err(err) = load_str(&name, source, &mut new_module) { Variable::Result(Err(Box::new(Error { message: Variable::Str(Arc::new( format!("When attempting to load module:\n{}", err))), @@ -1214,8 +1189,7 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result Result return Err(rt.expected_arg(2, x, "[Module]")) } - let v = match check_str(&name, source, &new_module) { + Ok(match check_str(&name, source, &new_module) { Err(err) => Variable::Result(Err(Box::new(Error { message: Variable::Str(Arc::new( format!("When attempting to load module:\n{}", err))), @@ -1334,8 +1308,7 @@ pub(crate) fn check__in_string_imports(rt: &mut Runtime) -> Result Result<(), String> { @@ -1485,8 +1458,7 @@ pub(crate) fn call_ret(rt: &mut Runtime) -> Result { pub(crate) fn functions(rt: &mut Runtime) -> Result { // List available functions in scope. - let v = Variable::Array(Arc::new(functions::list_functions(&rt.module))); - Ok(v) + Ok(Variable::Array(Arc::new(functions::list_functions(&rt.module)))) } pub(crate) fn functions__module(rt: &mut Runtime) -> Result { @@ -1504,18 +1476,16 @@ pub(crate) fn functions__module(rt: &mut Runtime) -> Result { None => return Err(rt.expected_arg(0, x, "Module")) }; - let v = Variable::Array(Arc::new(functions)); - Ok(v) + Ok(Variable::Array(Arc::new(functions))) } dyon_fn!{fn none() -> Variable {Variable::Option(None)}} pub(crate) fn some(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Option(Some(Box::new( + Ok(Variable::Option(Some(Box::new( rt.resolve(&v).deep_clone(&rt.stack) - ))); - Ok(v) + )))) } pub(crate) fn ok(rt: &mut Runtime) -> Result { @@ -1528,37 +1498,34 @@ pub(crate) fn ok(rt: &mut Runtime) -> Result { pub(crate) fn err(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = Variable::Result(Err(Box::new( + Ok(Variable::Result(Err(Box::new( Error { message: rt.resolve(&v).deep_clone(&rt.stack), trace: vec![] - }))); - Ok(v) + })))) } pub(crate) fn is_err(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::Result(Err(_)) => Variable::bool(true), &Variable::Result(Ok(_)) => Variable::bool(false), x => return Err(rt.expected_arg(0, x, "result")) - }; - Ok(v) + }) } pub(crate) fn is_ok(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::Result(Err(_)) => Variable::bool(false), &Variable::Result(Ok(_)) => Variable::bool(true), x => return Err(rt.expected_arg(0, x, "result")) - }; - Ok(v) + }) } pub(crate) fn min(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(Variable::f64(match rt.resolve(&v) { &Variable::Array(ref arr) => { let mut min: f64 = ::std::f64::NAN; for v in &**arr { @@ -1569,13 +1536,12 @@ pub(crate) fn min(rt: &mut Runtime) -> Result { min } x => return Err(rt.expected_arg(0, x, "array")) - }; - Ok(Variable::f64(v)) + })) } pub(crate) fn max(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(Variable::f64(match rt.resolve(&v) { &Variable::Array(ref arr) => { let mut max: f64 = ::std::f64::NAN; for v in &**arr { @@ -1586,8 +1552,7 @@ pub(crate) fn max(rt: &mut Runtime) -> Result { max } x => return Err(rt.expected_arg(0, x, "array")) - }; - Ok(Variable::f64(v)) + })) } pub(crate) fn unwrap(rt: &mut Runtime) -> Result { @@ -1596,7 +1561,7 @@ pub(crate) fn unwrap(rt: &mut Runtime) -> Result { // Return value does not depend on lifetime of argument since // `ok(x)` and `some(x)` perform a deep clone. let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::Option(Some(ref v)) => (**v).clone(), &Variable::Option(None) => { return Err({ @@ -1624,8 +1589,7 @@ pub(crate) fn unwrap(rt: &mut Runtime) -> Result { }) } x => return Err(rt.expected_arg(0, x, "some(_) or ok(_)")) - }; - Ok(v) + }) } pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result { @@ -1633,23 +1597,21 @@ pub(crate) fn unwrap_or(rt: &mut Runtime) -> Result { // `ok(x)` and `some(x)` perform a deep clone. let def = rt.stack.pop().expect(TINVOTS); let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::Option(Some(ref v)) => (**v).clone(), &Variable::Result(Ok(ref ok)) => (**ok).clone(), &Variable::Option(None) | &Variable::Result(Err(_)) => rt.resolve(&def).clone(), x => return Err(rt.expected_arg(0, x, "some(_) or ok(_)")) - }; - Ok(v) + }) } pub(crate) fn unwrap_err(rt: &mut Runtime) -> Result { let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::Result(Err(ref err)) => err.message.clone(), x => return Err(rt.expected_arg(0, x, "err(_)")) - }; - Ok(v) + }) } dyon_fn!{fn dir__angle(val: f64) -> Vec4 {Vec4([val.cos() as f32, val.sin() as f32, 0.0, 0.0])}} @@ -1712,14 +1674,13 @@ pub(crate) fn meta__syntax_in_string(rt: &mut Runtime) -> Result s, None => return Err(rt.expected_arg(0, syntax_var, "Syntax")) }, &name, &text); - let v = Variable::Result(match res { + Ok(Variable::Result(match res { Ok(res) => Ok(Box::new(Variable::Array(Arc::new(res)))), Err(err) => Err(Box::new(Error { message: Variable::Str(Arc::new(err)), trace: vec![] })) - }); - Ok(v) + })) } dyon_fn!{fn download__url_file(url: Arc, file: Arc) -> Variable { @@ -1811,7 +1772,7 @@ dyon_fn!{fn load_string__url(url: Arc) -> Variable { pub(crate) fn join__thread(rt: &mut Runtime) -> Result { let thread = rt.stack.pop().expect(TINVOTS); let handle_res = Thread::invalidate_handle(rt, thread); - let v = Variable::Result({ + Ok(Variable::Result({ match handle_res { Ok(handle) => { match handle.join() { @@ -1836,8 +1797,7 @@ pub(crate) fn join__thread(rt: &mut Runtime) -> Result { })) } } - }); - Ok(v) + })) } dyon_fn!{fn load_data__file(file: Arc) -> Variable { @@ -1980,22 +1940,20 @@ pub(crate) fn has(rt: &mut Runtime) -> Result { x => return Err(rt.expected_arg(1, x, "str")) }; let obj = rt.stack.pop().expect(TINVOTS); - let res = match rt.resolve(&obj) { + Ok(Variable::bool(match rt.resolve(&obj) { &Variable::Object(ref obj) => obj.contains_key(&key), x => return Err(rt.expected_arg(0, x, "object")) - }; - Ok(Variable::bool(res)) + })) } pub(crate) fn keys(rt: &mut Runtime) -> Result { let obj = rt.stack.pop().expect(TINVOTS); - let res = Variable::Array(Arc::new(match rt.resolve(&obj) { + Ok(Variable::Array(Arc::new(match rt.resolve(&obj) { &Variable::Object(ref obj) => { obj.keys().map(|k| Variable::Str(k.clone())).collect() } x => return Err(rt.expected_arg(0, x, "object")) - })); - Ok(res) + }))) } pub(crate) fn chars(rt: &mut Runtime) -> Result { @@ -2004,14 +1962,13 @@ pub(crate) fn chars(rt: &mut Runtime) -> Result { &Variable::Str(ref t) => t.clone(), x => return Err(rt.expected_arg(0, x, "str")) }; - let res = t.chars() + Ok(Variable::Array(Arc::new(t.chars() .map(|ch| { let mut s = String::new(); s.push(ch); Variable::Str(Arc::new(s)) }) - .collect::>(); - Ok(Variable::Array(Arc::new(res))) + .collect::>()))) } dyon_fn!{fn now() -> f64 { @@ -2034,7 +1991,7 @@ pub(crate) fn wait_next(rt: &mut Runtime) -> Result { use std::error::Error; let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::In(ref mutex) => { match mutex.lock() { Ok(x) => match x.recv() { @@ -2046,15 +2003,14 @@ pub(crate) fn wait_next(rt: &mut Runtime) -> Result { } } x => return Err(rt.expected_arg(0, x, "in")) - }; - Ok(v) + }) } pub(crate) fn next(rt: &mut Runtime) -> Result { use std::error::Error; let v = rt.stack.pop().expect(TINVOTS); - let v = match rt.resolve(&v) { + Ok(match rt.resolve(&v) { &Variable::In(ref mutex) => { match mutex.lock() { Ok(x) => match x.try_recv() { @@ -2066,6 +2022,5 @@ pub(crate) fn next(rt: &mut Runtime) -> Result { } } x => return Err(rt.expected_arg(0, x, "in")) - }; - Ok(v) + }) } From 39868f71608ec94c06584fe6b43830652ce361ef Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Wed, 2 Oct 2019 12:11:46 +0200 Subject: [PATCH 82/83] Fixed bug in `div` --- src/dyon_std/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 3ba0df23..8424fb8e 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -207,7 +207,7 @@ pub(crate) fn div(a: &Variable, b: &Variable) -> Result { use Variable::*; Ok(match (a, b) { - (&F64(a, ref sec), &F64(b, _)) => F64(a * b, sec.clone()), + (&F64(a, ref sec), &F64(b, _)) => F64(a / b, sec.clone()), (&Vec4(a), &Vec4(b)) => Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]), (&Vec4(a), &F64(b, _)) => { let b = b as f32; From cc88f069c1b3feeed4689f62e02ce16fc1d69062 Mon Sep 17 00:00:00 2001 From: Sven Nilsen Date: Thu, 3 Oct 2019 05:45:25 +0200 Subject: [PATCH 83/83] Cleaned up import code --- src/dyon_std/mod.rs | 100 ++++---------------------------------------- src/module.rs | 23 ++++++++++ 2 files changed, 30 insertions(+), 93 deletions(-) diff --git a/src/dyon_std/mod.rs b/src/dyon_std/mod.rs index 8424fb8e..263ac48f 100644 --- a/src/dyon_std/mod.rs +++ b/src/dyon_std/mod.rs @@ -1028,14 +1028,7 @@ pub(crate) fn load(rt: &mut Runtime) -> Result { Ok(match rt.resolve(&v) { &Variable::Str(ref text) => { let mut m = Module::empty(); - for f in &rt.module.ext_prelude { - match f.f { - FnExt::Void(ff) => m.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => m.add(f.name.clone(), ff, f.p.clone()), - FnExt::BinOp(ff) => m.add_binop(f.name.clone(), ff, f.p.clone()), - FnExt::UnOp(ff) => m.add_unop(f.name.clone(), ff, f.p.clone()), - } - } + m.import_ext_prelude(&rt.module); if let Err(err) = load(text, &mut m) { Variable::Result(Err(Box::new(Error { message: Variable::Str(Arc::new(format!("When attempting to load module:\n{}", err))), @@ -1058,14 +1051,7 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result let modules = rt.stack.pop().expect(TINVOTS); let source = rt.stack.pop().expect(TINVOTS); let mut new_module = Module::empty(); - for f in &rt.module.ext_prelude { - match f.f { - FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), - FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), - } - } + new_module.import_ext_prelude(&rt.module); let x = rt.resolve(&modules); match x { &Variable::Array(ref array) => { @@ -1073,29 +1059,7 @@ pub(crate) fn load__source_imports(rt: &mut Runtime) -> Result match rt.resolve(it) { &Variable::RustObject(ref obj) => { match obj.lock().unwrap().downcast_ref::>() { - Some(m) => { - // Add external functions from imports. - for f in &m.ext_prelude { - let has_external = new_module.ext_prelude.iter() - .any(|a| a.name == f.name); - if !has_external { - match f.f { - FnExt::Void(ff) => - new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => - new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::BinOp(ff) => - new_module.add_binop(f.name.clone(), ff, f.p.clone()), - FnExt::UnOp(ff) => - new_module.add_unop(f.name.clone(), ff, f.p.clone()), - } - } - } - // Register loaded functions from imports. - for f in &m.functions { - new_module.register(f.clone()) - } - } + Some(m) => new_module.import(m), None => return Err(rt.expected_arg(1, x, "[Module]")) } } @@ -1136,14 +1100,7 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result return Err(rt.expected_arg(0, x, "str")) }; let mut new_module = Module::empty(); - for f in &rt.module.ext_prelude { - match f.f { - FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), - FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), - } - } + new_module.import_ext_prelude(&rt.module); let x = rt.resolve(&modules); match x { &Variable::Array(ref array) => { @@ -1151,25 +1108,7 @@ pub(crate) fn module__in_string_imports(rt: &mut Runtime) -> Result { match obj.lock().unwrap().downcast_ref::>() { - Some(m) => { - // Add external functions from imports. - for f in &m.ext_prelude { - let has_external = new_module.ext_prelude.iter() - .any(|a| a.name == f.name); - if !has_external { - match f.f { - FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), - FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), - } - } - } - // Register loaded functions from imports. - for f in &m.functions { - new_module.register(f.clone()) - } - } + Some(m) => new_module.import(m), None => return Err(rt.expected_arg(2, x, "[Module]")) } } @@ -1207,14 +1146,7 @@ pub(crate) fn check__in_string_imports(rt: &mut Runtime) -> Result return Err(rt.expected_arg(0, x, "str")) }; let mut new_module = Module::empty(); - for f in &rt.module.ext_prelude { - match f.f { - FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), - FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), - } - } + new_module.import_ext_prelude(&rt.module); let x = rt.resolve(&modules); match x { &Variable::Array(ref array) => { @@ -1222,25 +1154,7 @@ pub(crate) fn check__in_string_imports(rt: &mut Runtime) -> Result { match obj.lock().unwrap().downcast_ref::>() { - Some(m) => { - // Add external functions from imports. - for f in &m.ext_prelude { - let has_external = new_module.ext_prelude.iter() - .any(|a| a.name == f.name); - if !has_external { - match f.f { - FnExt::Void(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::Return(ff) => new_module.add(f.name.clone(), ff, f.p.clone()), - FnExt::BinOp(ff) => new_module.add_binop(f.name.clone(), ff, f.p.clone()), - FnExt::UnOp(ff) => new_module.add_unop(f.name.clone(), ff, f.p.clone()), - } - } - } - // Register loaded functions from imports. - for f in &m.functions { - new_module.register(f.clone()) - } - } + Some(m) => new_module.import(m), None => return Err(rt.expected_arg(2, x, "[Module]")) } } diff --git a/src/module.rs b/src/module.rs index 4587c8de..6588a9bc 100644 --- a/src/module.rs +++ b/src/module.rs @@ -22,6 +22,29 @@ impl Module { } } + /// Import external prelude from other module. + pub fn import_ext_prelude(&mut self, other: &Module) { + for f in &other.ext_prelude { + self.ext_prelude.push(f.clone()); + } + } + + /// Import external prelude and loaded functions from module. + pub fn import(&mut self, other: &Module) { + // Add external functions from imports. + for f in &other.ext_prelude { + let has_external = self.ext_prelude.iter() + .any(|a| a.name == f.name && a.namespace == f.namespace); + if !has_external { + self.ext_prelude.push(f.clone()); + } + } + // Register loaded functions from imports. + for f in &other.functions { + self.functions.push(f.clone()) + } + } + /// Creates a new module with standard library. pub fn new() -> Module { use Type::*;