From 68ea4a31e315922c345e90fa91d9ce7e4ec0af86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 19:02:20 +0100 Subject: add union type and union value --- src/lib.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a138c33..d0a45f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ pub enum Type { Bool, String, Tuple(Vec), + Union(Vec), Function(Vec, Box), Blob(Rc), Instance(Rc), @@ -104,6 +105,9 @@ impl PartialEq for Type { (Type::Tuple(a), Type::Tuple(b)) => { a.iter().zip(b.iter()).all(|(a, b)| a == b) } + (Type::Union(a), b) | (b, Type::Union(a)) => { + a.iter().any(|x| x == b) + } (Type::Function(a_args, a_ret), Type::Function(b_args, b_ret)) => a_args == b_args && a_ret == b_ret, _ => false, @@ -119,6 +123,9 @@ impl From<&Value> for Type { Value::Tuple(v) => { Type::Tuple(v.iter().map(|x| Type::from(x)).collect()) } + Value::Union(v) => { + Type::Union(v.iter().map(|x| Type::from(x)).collect()) + } Value::Int(_) => Type::Int, Value::Float(_) => Type::Float, Value::Bool(_) => Type::Bool, @@ -147,6 +154,9 @@ impl From<&Type> for Value { Type::Tuple(fields) => { Value::Tuple(Rc::new(fields.iter().map(Value::from).collect())) } + Type::Union(v) => { + Value::Union(v.iter().map(Value::from).collect()) + } Type::Unknown => Value::Unknown, Type::Int => Value::Int(1), Type::Float => Value::Float(1.0), @@ -172,6 +182,7 @@ pub enum Value { Blob(Rc), Instance(Rc, Rc>>), Tuple(Rc>), + Union(Vec), Float(f64), Int(i64), Bool(bool), @@ -200,6 +211,7 @@ impl Debug for Value { Value::Unknown => write!(fmt, "(unknown)"), Value::Nil => write!(fmt, "(nil)"), Value::Tuple(v) => write!(fmt, "({:?})", v), + Value::Union(v) => write!(fmt, "(U {:?})", v), } } } @@ -214,6 +226,9 @@ impl PartialEq for Value { (Value::Tuple(a), Value::Tuple(b)) => { a.len() == b.len() && a.iter().zip(b.iter()).all(|(a, b)| a == b) } + (Value::Union(a), b) | (b, Value::Union(a)) => { + a.iter().any(|x| x == b) + } (Value::Nil, Value::Nil) => true, _ => false, } @@ -575,11 +590,34 @@ mod op { Value::Tuple(Rc::new(a.iter().map(f).collect())) } + fn union_un_op(a: &Vec, f: fn (&Value) -> Value) -> Value { + a.iter().find_map(|x| { + let x = f(x); + if x.is_nil() { + None + } else { + Some(x) + } + }).unwrap_or(Value::Nil) + } + + fn union_bin_op(a: &Vec, b: &Value, f: fn (&Value, &Value) -> Value) -> Value { + a.iter().find_map(|x| { + let x = f(x, b); + if x.is_nil() { + None + } else { + Some(x) + } + }).unwrap_or(Value::Nil) + } + pub fn neg(value: &Value) -> Value { match value { Value::Float(a) => Value::Float(-*a), Value::Int(a) => Value::Int(-*a), Value::Tuple(a) => tuple_un_op(a, neg), + Value::Union(v) => union_un_op(&v, neg), Value::Unknown => Value::Unknown, _ => Value::Nil, } @@ -589,6 +627,7 @@ mod op { match value { Value::Bool(a) => Value::Bool(!*a), Value::Tuple(a) => tuple_un_op(a, not), + Value::Union(v) => union_un_op(&v, not), Value::Unknown => Value::from(Type::Bool), _ => Value::Nil, } @@ -603,6 +642,7 @@ mod op { (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, add), (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => add(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, + (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, add), _ => Value::Nil, } } @@ -618,6 +658,7 @@ mod op { (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, mul), (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => mul(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, + (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, mul), _ => Value::Nil, } } @@ -629,6 +670,7 @@ mod op { (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, div), (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => div(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, + (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, div), _ => Value::Nil, } } @@ -642,6 +684,7 @@ mod op { (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, eq), (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => eq(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, + (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, eq), _ => Value::Nil, } } @@ -655,6 +698,7 @@ mod op { (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, less), (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => less(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, + (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, less), _ => Value::Nil, } } @@ -669,6 +713,7 @@ mod op { (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, and), (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => and(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, + (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, and), _ => Value::Nil, } } @@ -679,6 +724,7 @@ mod op { (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, or), (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => or(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, + (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, or), _ => Value::Nil, } } -- cgit v1.2.1 From e0cde67ed427a46be3adfd0938a4020d494d692f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 19:02:29 +0100 Subject: add questionmark token --- src/tokenizer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 3b61e5f..6e142e3 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -117,6 +117,8 @@ pub enum Token { Or, #[token("!")] Bang, + #[token("?")] + Questionmark, #[token(",")] Comma, -- cgit v1.2.1 From ea6adb4ca98d5b272db010ca4de87210abf4b49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 19:15:23 +0100 Subject: parse nullable types --- src/compiler.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/compiler.rs b/src/compiler.rs index b45d47e..47b8b84 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1401,8 +1401,17 @@ impl Compiler { } fn parse_type(&mut self) -> Result { - match self.peek() { + let ty = self.parse_simple_type(); + if self.peek() == Token::Questionmark { + self.eat(); + ty.map(|x| Type::Union(vec![x, Type::Void])) + } else { + ty + } + } + fn parse_simple_type(&mut self) -> Result { + match self.peek() { Token::Fn => { self.eat(); let mut params = Vec::new(); -- cgit v1.2.1 From 3bd382ae68eea0b40d60a0491aad760ae757ef2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 19:22:46 +0100 Subject: fix return type error to be correct --- src/vm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm.rs b/src/vm.rs index e001ebc..2859d83 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -656,7 +656,7 @@ impl VM { let ret = inner.ret(); if Type::from(&a) != *ret { - error!(self, ErrorKind::TypeMismatch(a.into(), ret.clone()), + error!(self, ErrorKind::TypeMismatch(ret.clone(), a.into()), "Value does not match return type."); } } -- cgit v1.2.1 From 858d5c1756b64f4973588424b8ba375136740510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 20:53:01 +0100 Subject: order the return type correctly --- progs/tests/wrong_params.sy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/progs/tests/wrong_params.sy b/progs/tests/wrong_params.sy index e49e89b..0082a3a 100644 --- a/progs/tests/wrong_params.sy +++ b/progs/tests/wrong_params.sy @@ -4,4 +4,4 @@ start :: fn { f } -// errors: [ErrorKind::TypeMismatch(_, _), ErrorKind::TypeMismatch(Type::Void, Type::Int)] +// errors: [ErrorKind::TypeMismatch(_, _), ErrorKind::TypeMismatch(Type::Int, Type::Void)] -- cgit v1.2.1 From 4e6ef21576d9ec6a8861246464b1905819b68efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 21:57:52 +0100 Subject: fix some nice tests for the nullable_types --- progs/tests/nullable_types.sy | 33 +++++++++++++++++++++++++++++++++ src/compiler.rs | 10 ++++++---- src/lib.rs | 1 + src/tokenizer.rs | 3 +++ src/vm.rs | 4 ++++ 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 progs/tests/nullable_types.sy diff --git a/progs/tests/nullable_types.sy b/progs/tests/nullable_types.sy new file mode 100644 index 0000000..2088c53 --- /dev/null +++ b/progs/tests/nullable_types.sy @@ -0,0 +1,33 @@ +test001 :: fn -> int { + a : int? = nil + a = 2 + ret a +} + +test002 :: fn b:bool -> int? { + if b { + ret nil + } else { + ret 0 + } +} + +// TODO(ed): Introduce type type! +test003 :: fn { + a := test002! false + a += 1 + a <=> 1 +} + + +start :: fn { + test001! + nil <=> test002! true + 0 <=> test002! false + q : bool? = true + q <=> true + q = nil + q <=> nil + test003! +} + diff --git a/src/compiler.rs b/src/compiler.rs index 47b8b84..4fdd13b 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -634,10 +634,11 @@ impl Compiler { Token::LeftParen => self.grouping_or_tuple(block), Token::Minus => self.unary(block), - Token::Float(_) => self.value(block), - Token::Int(_) => self.value(block), - Token::Bool(_) => self.value(block), - Token::String(_) => self.value(block), + Token::Float(_) + | Token::Int(_) + | Token::Bool(_) + | Token::String(_) + | Token::Nil => self.value(block), Token::Bang => self.unary(block), @@ -676,6 +677,7 @@ impl Compiler { Token::Float(f) => { Value::Float(f) }, Token::Int(i) => { Value::Int(i) } Token::Bool(b) => { Value::Bool(b) } + Token::Nil => { Value::Nil } Token::String(s) => { Value::String(Rc::from(s)) } _ => { error!(self, "Cannot parse value."); Value::Bool(false) } }; diff --git a/src/lib.rs b/src/lib.rs index d0a45f6..e7c4a35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -685,6 +685,7 @@ mod op { (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => eq(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, eq), + (Value::Nil, Value::Nil) => Value::Bool(true), _ => Value::Nil, } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6e142e3..664532a 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -15,6 +15,9 @@ pub enum Token { #[regex(r"[\d]+", |lex| lex.slice().parse())] Int(i64), + #[regex(r"nil")] + Nil, + #[regex(r"true|false", |lex| lex.slice().parse(), priority=2)] Bool(bool), diff --git a/src/vm.rs b/src/vm.rs index 2859d83..0595639 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -669,6 +669,10 @@ impl VM { let ty = self.ty(ty); let top_type = self.stack.last().unwrap().into(); match (ty, top_type) { + (a, b) if matches!(a, Type::Union(_)) && a == &b => { + let last = self.stack.len() - 1; + self.stack[last] = Value::from(a); + } (Type::Unknown, top_type) if top_type != Type::Unknown => {} (a, b) if a != &b => { -- cgit v1.2.1 From 8a5202b02d909265fc16ddaf4f74112f32dc0b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 23:05:00 +0100 Subject: syntax for union types --- src/compiler.rs | 27 ++++++++++++++++++++++----- src/tokenizer.rs | 2 ++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 4fdd13b..22256e4 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1403,12 +1403,29 @@ impl Compiler { } fn parse_type(&mut self) -> Result { - let ty = self.parse_simple_type(); - if self.peek() == Token::Questionmark { - self.eat(); - ty.map(|x| Type::Union(vec![x, Type::Void])) + let mut tys = vec![self.parse_simple_type()?]; + loop { + match self.peek() { + Token::Questionmark => { + self.eat(); + tys.push(Type::Void); + return Ok(Type::Union(tys)); + }, + + Token::Pipe => { + self.eat(); + tys.push(self.parse_simple_type()?); + }, + + _ => { + break; + }, + } + } + if tys.len() == 1 { + Ok(tys[0].clone()) } else { - ty + Ok(Type::Union(tys)) } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 664532a..4d1280d 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -122,6 +122,8 @@ pub enum Token { Bang, #[token("?")] Questionmark, + #[token("|")] + Pipe, #[token(",")] Comma, -- cgit v1.2.1 From 6ff6793c94fc715a5587c6515cf7ae989f0a1571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 23:10:09 +0100 Subject: add void type --- src/compiler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler.rs b/src/compiler.rs index 22256e4..4c59d86 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1484,6 +1484,7 @@ impl Compiler { Token::Identifier(x) => { self.eat(); match x.as_str() { + "void" => Ok(Type::Void), "int" => Ok(Type::Int), "float" => Ok(Type::Float), "bool" => Ok(Type::Bool), -- cgit v1.2.1 From d6b4e028462f24861b8386dbd919f9ef977d9f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 23:11:32 +0100 Subject: return is implicit void --- src/compiler.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/compiler.rs b/src/compiler.rs index 4c59d86..bdc5901 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1752,7 +1752,13 @@ impl Compiler { (Token::Ret, ..) => { self.eat(); - self.expression(block); + if self.peek() == Token::Newline { + self.eat(); + let nil = self.add_constant(Value::Nil); + add_op(self, block, Op::Constant(nil)); + } else { + self.expression(block); + } add_op(self, block, Op::Return); } -- cgit v1.2.1 From fe3909375bd0b200989b9d88158e8c3412b3d639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 23:39:59 +0100 Subject: change how tuple comparison functions --- progs/tests/sub.sy | 3 ++- src/lib.rs | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/progs/tests/sub.sy b/progs/tests/sub.sy index e5d60f2..cca4f2c 100644 --- a/progs/tests/sub.sy +++ b/progs/tests/sub.sy @@ -1,3 +1,4 @@ start :: fn { - (1, -2, 3, -4) - (4, 3, -2, -1) <=> (-3, 1, 1, -5) + print (1, -2, 3, -4) - (4, 3, -2, -1) + (1, -2, 3, -4) - (4, 3, -2, -1) <=> (-3, -5, 5, -3) } diff --git a/src/lib.rs b/src/lib.rs index e7c4a35..c085cca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -681,7 +681,13 @@ mod op { (Value::Int(a), Value::Int(b)) => Value::Bool(a == b), (Value::String(a), Value::String(b)) => Value::Bool(a == b), (Value::Bool(a), Value::Bool(b)) => Value::Bool(a == b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, eq), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => { + a.iter().zip(b.iter()).find_map( + |(a, b)| match eq(a, b) { + Value::Bool(true) => None, + a => Some(a), + }).unwrap_or(Value::Bool(true)) + } (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => eq(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, eq), @@ -696,7 +702,13 @@ mod op { (Value::Int(a), Value::Int(b)) => Value::Bool(a < b), (Value::String(a), Value::String(b)) => Value::Bool(a < b), (Value::Bool(a), Value::Bool(b)) => Value::Bool(a < b), - (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => tuple_bin_op(a, b, less), + (Value::Tuple(a), Value::Tuple(b)) if a.len() == b.len() => { + a.iter().zip(b.iter()).find_map( + |(a, b)| match less(a, b) { + Value::Bool(true) => None, + a => Some(a), + }).unwrap_or(Value::Bool(true)) + } (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => less(a, a), (Value::Unknown, Value::Unknown) => Value::Unknown, (Value::Union(a), b) | (b, Value::Union(a)) => union_bin_op(&a, b, less), -- cgit v1.2.1 From 50e3477ed34697be12890081b03d8412703ba8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Mon, 8 Mar 2021 23:40:16 +0100 Subject: add in fancy tests for union types Functions in union return-types aren't callable, because the call code is a bit wack. A nice starting point though. --- progs/tests/union_types_simple.sy | 23 +++++++++++++++++++++++ progs/tests/union_types_simple_faulty.sy | 25 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 progs/tests/union_types_simple.sy create mode 100644 progs/tests/union_types_simple_faulty.sy diff --git a/progs/tests/union_types_simple.sy b/progs/tests/union_types_simple.sy new file mode 100644 index 0000000..490717e --- /dev/null +++ b/progs/tests/union_types_simple.sy @@ -0,0 +1,23 @@ +f :: fn a:bool -> int | str | void { + if a { + ret 1 + } else { + ret + } +} + +g :: fn a:bool -> int | (bool, bool) { + if a { + ret 1 + } else { + ret (true, true) + } +} + +start :: fn { + 1 <=> f! true + nil <=> f! false + (true, true) <=> g! false + 1 <=> g! true + f(true) <=> g(true) +} diff --git a/progs/tests/union_types_simple_faulty.sy b/progs/tests/union_types_simple_faulty.sy new file mode 100644 index 0000000..b742054 --- /dev/null +++ b/progs/tests/union_types_simple_faulty.sy @@ -0,0 +1,25 @@ +f :: fn a:bool -> int | void { + if a { + ret 1 + } else { + ret "hello!" + } +} + +g :: fn a:bool -> int | (bool, bool) { + if a { + ret 1 + } else { + ret (true, 1.0) + } +} + +start :: fn { + 0 <=> f! true + 0.0 <=> f! false + ("hello!", "there") <=> g! false + 1 <=> g! true + f(true) <=> g(true) +} + +// errors: [ ErrorKind::TypeMismatch(_, _), ErrorKind::TypeMismatch(_, _), ErrorKind::TypeError(_, _), ErrorKind::TypeError(_, _) ] -- cgit v1.2.1 From 9ef8eaa4564b2e498c56cad50491f2fdcea9d643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 9 Mar 2021 17:01:53 +0100 Subject: change union to hashset from vector --- src/compiler.rs | 11 ++++++----- src/lib.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index bdc5901..588a9e9 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; use std::cell::RefCell; -use std::collections::{HashMap, hash_map::Entry}; +use std::collections::{HashMap, HashSet, hash_map::Entry}; use std::rc::Rc; use crate::{Blob, Block, Op, Prog, RustFunction, Type, Value}; @@ -1403,18 +1403,19 @@ impl Compiler { } fn parse_type(&mut self) -> Result { - let mut tys = vec![self.parse_simple_type()?]; + let mut tys = HashSet::new(); + tys.insert(self.parse_simple_type()?); loop { match self.peek() { Token::Questionmark => { self.eat(); - tys.push(Type::Void); + tys.insert(Type::Void); return Ok(Type::Union(tys)); }, Token::Pipe => { self.eat(); - tys.push(self.parse_simple_type()?); + tys.insert(self.parse_simple_type()?); }, _ => { @@ -1423,7 +1424,7 @@ impl Compiler { } } if tys.len() == 1 { - Ok(tys[0].clone()) + Ok(tys.iter().next().unwrap().clone()) } else { Ok(Type::Union(tys)) } diff --git a/src/lib.rs b/src/lib.rs index c085cca..6f63ad7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::collections::hash_map::Entry; use std::fmt::Debug; use std::path::{Path, PathBuf}; @@ -86,12 +86,56 @@ pub enum Type { Bool, String, Tuple(Vec), - Union(Vec), + Union(HashSet), Function(Vec, Box), Blob(Rc), Instance(Rc), } +impl Hash for Type { + fn hash(&self, h: &mut H) { + match self { + Type::Void => 0, + Type::Unknown => 1, + Type::Int => 2, + Type::Float => 3, + Type::Bool => 4, + Type::String => 5, + Type::Tuple(ts) => { + for t in ts.iter() { + t.hash(h); + } + 6 + } + Type::Union(ts) => { + for t in ts { + t.hash(h); + } + 7 + } + Type::Function(args, ret) => { + ret.hash(h); + for t in args.iter() { + t.hash(h); + } + 8 + } + Type::Blob(b) => { + for (_, t) in b.fields.values() { + t.hash(h); + } + 10 + } + Type::Instance(b) => { + for (_, t) in b.fields.values() { + t.hash(h); + } + 11 + } + }.hash(h); + } +} + impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { match (self, other) { @@ -115,6 +159,8 @@ impl PartialEq for Type { } } +impl Eq for Type {} + impl From<&Value> for Type { fn from(value: &Value) -> Type { match value { -- cgit v1.2.1 From 30461c655f08e37f0758e0de137b679b789024cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 9 Mar 2021 17:41:57 +0100 Subject: fix functions as optional types --- progs/tests/union_types_simple.sy | 13 +++++ src/vm.rs | 99 ++++++++++++++++++++++++--------------- 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/progs/tests/union_types_simple.sy b/progs/tests/union_types_simple.sy index 490717e..f6cf622 100644 --- a/progs/tests/union_types_simple.sy +++ b/progs/tests/union_types_simple.sy @@ -14,10 +14,23 @@ g :: fn a:bool -> int | (bool, bool) { } } +h :: fn a:bool -> int | fn -> int { + if a { + f :: fn -> int { ret 1 } + ret f + } else { + ret 1 + } +} + start :: fn { 1 <=> f! true nil <=> f! false (true, true) <=> g! false 1 <=> g! true f(true) <=> g(true) + + 1 <=> h! false + q :: h! true + 1 <=> q() } diff --git a/src/vm.rs b/src/vm.rs index 0595639..f99f611 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,6 +1,6 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::rc::Rc; @@ -729,55 +729,76 @@ impl VM { Op::Call(num_args) => { let new_base = self.stack.len() - 1 - num_args; - match self.stack[new_base].clone() { - Value::Blob(blob) => { - let mut values = Vec::with_capacity(blob.fields.len()); - for _ in 0..values.capacity() { - values.push(Value::Nil); - } + let callable = &self.stack[new_base]; + + let call_callable = |callable: &Value| { + let args = &self.stack[new_base+1..]; + let args: Vec<_> = args.iter().map(|x| x.into()).collect(); + match callable { + Value::Blob(blob) => { + let mut values = Vec::with_capacity(blob.fields.len()); + for _ in 0..values.capacity() { + values.push(Value::Nil); + } - for (slot, ty) in blob.fields.values() { - values[*slot] = ty.into(); - } + for (slot, ty) in blob.fields.values() { + values[*slot] = ty.into(); + } - self.pop(); - self.push(Value::Instance(blob, Rc::new(RefCell::new(values)))); - } - Value::Function(_, block) => { - let inner = block.borrow(); - let args = inner.args(); - if args.len() != num_args { - error!(self, ErrorKind::ArgumentCount(args.len(), num_args)); + Ok(Value::Instance(blob.clone(), Rc::new(RefCell::new(values)))) } - let stack_args = &self.stack[self.stack.len() - args.len()..]; - let stack_args: Vec<_> = stack_args.iter().map(|x| x.into()).collect(); - if args != &stack_args { - error!(self, ErrorKind::ArgumentType(args.clone(), stack_args)); + Value::Function(_, block) => { + let inner = block.borrow(); + let fargs = inner.args(); + if fargs != &args { + Err(ErrorKind::ArgumentType(args.clone(), args)) + } else { + Ok(inner.ret().into()) + } + } - self.stack[new_base] = block.borrow().ret().into(); + Value::ExternFunction(slot) => { + let extern_func = self.extern_functions[*slot]; + extern_func(&self.stack[new_base+1..], false) + } - self.stack.truncate(new_base + 1); + _ => { + Err(ErrorKind::InvalidProgram) + } } - Value::ExternFunction(slot) => { - let extern_func = self.extern_functions[slot]; - let res = match extern_func(&self.stack[new_base+1..], false) { - Ok(value) => value, - Err(ek) => { - self.stack.truncate(new_base); - self.push(Value::Nil); - error!(self, ek, "Error from external function.") + }; + + let mut err = None; + self.stack[new_base] = match callable { + Value::Union(alts) => { + let mut returns = Vec::new(); + for alt in alts.iter() { + if let Ok(res) = call_callable(&alt) { + returns.push(res); } - }; - self.stack.truncate(new_base); - self.push(res); - } + } + if returns.is_empty() { + err = Some(ErrorKind::InvalidProgram); + Value::Nil + } else { + Value::Union(returns) + } + }, _ => { - error!(self, - ErrorKind::InvalidProgram, - format!("Tried to call non-function {:?}", self.stack[new_base])); + match call_callable(callable) { + Err(e) => { + err = Some(e); + Value::Nil + }, + Ok(v) => v + } } + }; + self.stack.truncate(new_base + 1); + if err.is_some() { + error!(self, err.unwrap()); } } -- cgit v1.2.1 From 64faa5bad78b74fff1aca73987b010cb2961c027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 9 Mar 2021 17:46:37 +0100 Subject: more hash sets! --- src/lib.rs | 7 ++++--- src/vm.rs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6f63ad7..5ea0b63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ pub enum Value { Blob(Rc), Instance(Rc, Rc>>), Tuple(Rc>), - Union(Vec), + Union(HashSet), Float(f64), Int(i64), Bool(bool), @@ -627,6 +627,7 @@ pub enum Op { mod op { use super::{Type, Value}; use std::rc::Rc; + use std::collections::HashSet; fn tuple_bin_op(a: &Rc>, b: &Rc>, f: fn (&Value, &Value) -> Value) -> Value { Value::Tuple(Rc::new(a.iter().zip(b.iter()).map(|(a, b)| f(a, b)).collect())) @@ -636,7 +637,7 @@ mod op { Value::Tuple(Rc::new(a.iter().map(f).collect())) } - fn union_un_op(a: &Vec, f: fn (&Value) -> Value) -> Value { + fn union_un_op(a: &HashSet, f: fn (&Value) -> Value) -> Value { a.iter().find_map(|x| { let x = f(x); if x.is_nil() { @@ -647,7 +648,7 @@ mod op { }).unwrap_or(Value::Nil) } - fn union_bin_op(a: &Vec, b: &Value, f: fn (&Value, &Value) -> Value) -> Value { + fn union_bin_op(a: &HashSet, b: &Value, f: fn (&Value, &Value) -> Value) -> Value { a.iter().find_map(|x| { let x = f(x, b); if x.is_nil() { diff --git a/src/vm.rs b/src/vm.rs index f99f611..3652321 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -773,10 +773,10 @@ impl VM { let mut err = None; self.stack[new_base] = match callable { Value::Union(alts) => { - let mut returns = Vec::new(); + let mut returns = HashSet::new(); for alt in alts.iter() { if let Ok(res) = call_callable(&alt) { - returns.push(res); + returns.insert(res); } } if returns.is_empty() { -- cgit v1.2.1 From 8d4493d0ac314318498155556467d46b6036d863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Th=C3=B6rnros?= Date: Tue, 9 Mar 2021 17:48:17 +0100 Subject: Questionmark -> QuestionMark --- src/compiler.rs | 2 +- src/tokenizer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 588a9e9..9ab868f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1407,7 +1407,7 @@ impl Compiler { tys.insert(self.parse_simple_type()?); loop { match self.peek() { - Token::Questionmark => { + Token::QuestionMark => { self.eat(); tys.insert(Type::Void); return Ok(Type::Union(tys)); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 4d1280d..8b06324 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -121,7 +121,7 @@ pub enum Token { #[token("!")] Bang, #[token("?")] - Questionmark, + QuestionMark, #[token("|")] Pipe, -- cgit v1.2.1