diff options
| -rw-r--r-- | progs/tests/tuples.sy | 24 | ||||
| -rw-r--r-- | progs/tests/tuples_fail_invalid_singleton.sy | 7 | ||||
| -rw-r--r-- | progs/tests/tuples_fail_out_of_bounds.sy | 6 | ||||
| -rw-r--r-- | src/compiler.rs | 34 | ||||
| -rw-r--r-- | src/lib.rs | 21 | ||||
| -rw-r--r-- | src/vm.rs | 10 |
6 files changed, 91 insertions, 11 deletions
diff --git a/progs/tests/tuples.sy b/progs/tests/tuples.sy new file mode 100644 index 0000000..642e381 --- /dev/null +++ b/progs/tests/tuples.sy @@ -0,0 +1,24 @@ +start :: fn { + a := (1,2) + a[0] <=> 1 + a[1] <=> 2 + + b := (1,) + b[0] <=> 1 + + empty := () + c := (empty,) + c[0] <=> () + + d := (1,2) + d[0] <=> d[0] + + { + q := d[2-2] + d[d[(5 + 3) * 0]] <=> d[d[(5 + 3) * 0]] + w := d[0] + q + w + q - w + q * w + } +} diff --git a/progs/tests/tuples_fail_invalid_singleton.sy b/progs/tests/tuples_fail_invalid_singleton.sy new file mode 100644 index 0000000..a8fafaa --- /dev/null +++ b/progs/tests/tuples_fail_invalid_singleton.sy @@ -0,0 +1,7 @@ +start :: fn { + a := (,) + a +} + +//TODO(gu) See #100 +// errors: [ErrorKind::SyntaxError(_, _), ErrorKind::SyntaxError(_, _), ErrorKind::SyntaxError(_, _)] diff --git a/progs/tests/tuples_fail_out_of_bounds.sy b/progs/tests/tuples_fail_out_of_bounds.sy new file mode 100644 index 0000000..cb0764a --- /dev/null +++ b/progs/tests/tuples_fail_out_of_bounds.sy @@ -0,0 +1,6 @@ +start :: fn { + a := (1, 2) + a[2] +} + +// errors: [ErrorKind::IndexOutOfBounds(_, 2, 2)] diff --git a/src/compiler.rs b/src/compiler.rs index 2aad62b..b45d47e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -51,7 +51,7 @@ macro_rules! parse_branch { let num_errors = $compiler.errors.len(); let mut stored_errors = Vec::new(); - // Closures for early return on success. + // Loop for early return on success. let success = loop { // We risk getting a lot of errors if we are in an invalid state // when we start the parse. @@ -187,6 +187,7 @@ nextable_enum!(Prec { Comp, Term, Factor, + Index, }); #[derive(Clone, Debug)] @@ -604,6 +605,8 @@ impl Compiler { fn precedence(&self, token: Token) -> Prec { match token { + Token::LeftBracket => Prec::Index, + Token::Star | Token::Slash => Prec::Factor, Token::Minus | Token::Plus => Prec::Term, @@ -685,33 +688,46 @@ impl Compiler { } fn tuple(&mut self, block: &mut Block) { - expect!(self, Token::LeftParen, "Expected '(' at start of tuple"); + expect!(self, Token::LeftParen, "Expected '(' at start of tuple."); let mut num_args = 0; - loop { + let trailing_comma = loop { match self.peek() { Token::RightParen | Token::EOF => { - break; + break false; } Token::Newline => { self.eat(); } + Token::Comma => { + //TODO(gu): This creates a lot of syntax errors since the compiler panic is + // ignored and the statement is tried as a grouping instead, even though we + // _know_ that this can't be parsed as a grouping either. + // Tracked in #100. + error!(self, "Tuples must begin with an element or ')'."); + return; + } _ => { self.expression(block); num_args += 1; match self.peek() { - Token::Comma => { self.eat(); }, + Token::Comma => { + self.eat(); + if matches!(self.peek(), Token::RightParen) { + break true; + } + }, Token::RightParen => {}, _ => { - error!(self, "Expected ',' or ')' in tuple"); + error!(self, "Expected ',' or ')' after tuple element."); return; }, } } } - } - if num_args == 1 { - error!(self, "A tuple must contain more than 1 element."); + }; + if num_args == 1 && !trailing_comma { + error!(self, "A tuple with 1 element must end with a trailing comma."); return; } @@ -178,7 +178,10 @@ pub enum Value { String(Rc<String>), Function(Vec<Rc<RefCell<UpValue>>>, Rc<RefCell<Block>>), ExternFunction(usize), + /// This value should not be present when running, only when type checking. + /// Most operations are valid but produce funky results. Unknown, + /// Should not be present when running. Nil, } @@ -561,7 +564,7 @@ pub enum Op { /// /// Broken out because they need to be recursive. mod op { - use super::Value; + use super::{Type, Value}; use std::rc::Rc; fn tuple_bin_op(a: &Rc<Vec<Value>>, b: &Rc<Vec<Value>>, f: fn (&Value, &Value) -> Value) -> Value { @@ -577,6 +580,7 @@ mod op { Value::Float(a) => Value::Float(-*a), Value::Int(a) => Value::Int(-*a), Value::Tuple(a) => tuple_un_op(a, neg), + Value::Unknown => Value::Unknown, _ => Value::Nil, } } @@ -585,6 +589,7 @@ mod op { match value { Value::Bool(a) => Value::Bool(!*a), Value::Tuple(a) => tuple_un_op(a, not), + Value::Unknown => Value::from(Type::Bool), _ => Value::Nil, } } @@ -596,6 +601,8 @@ mod op { (Value::Int(a), Value::Int(b)) => Value::Int(a + b), (Value::String(a), Value::String(b)) => Value::String(Rc::from(format!("{}{}", a, b))), (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::Nil, } } @@ -609,6 +616,8 @@ mod op { (Value::Float(a), Value::Float(b)) => Value::Float(a * b), (Value::Int(a), Value::Int(b)) => Value::Int(a * b), (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::Nil, } } @@ -618,6 +627,8 @@ mod op { (Value::Float(a), Value::Float(b)) => Value::Float(a / b), (Value::Int(a), Value::Int(b)) => Value::Int(a / b), (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::Nil, } } @@ -629,6 +640,8 @@ mod op { (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::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => eq(a, a), + (Value::Unknown, Value::Unknown) => Value::Unknown, _ => Value::Nil, } } @@ -640,6 +653,8 @@ mod op { (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::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => less(a, a), + (Value::Unknown, Value::Unknown) => Value::Unknown, _ => Value::Nil, } } @@ -652,6 +667,8 @@ mod op { match (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, and), + (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => and(a, a), + (Value::Unknown, Value::Unknown) => Value::Unknown, _ => Value::Nil, } } @@ -660,6 +677,8 @@ mod op { match (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, or), + (Value::Unknown, a) | (a, Value::Unknown) if !matches!(a, Value::Unknown) => or(a, a), + (Value::Unknown, Value::Unknown) => Value::Unknown, _ => Value::Nil, } } @@ -294,7 +294,7 @@ impl VM { match (val, slot) { (Value::Tuple(v), Value::Int(slot)) => { let slot = slot as usize; - if v.len() < slot { + if v.len() <= slot { self.stack.push(Value::Nil); let len = v.len(); error!(self, ErrorKind::IndexOutOfBounds(Value::Tuple(v), len, slot)); @@ -715,6 +715,14 @@ impl VM { }; } + Op::Index => { + // We don't have any information about the slot and the indexable might contain + // mixed types. + self.stack.pop().unwrap(); + self.stack.pop().unwrap(); + self.stack.push(Value::Unknown); + } + Op::Call(num_args) => { let new_base = self.stack.len() - 1 - num_args; match self.stack[new_base].clone() { |
