diff --git a/spec/fail/badcalls.ulg b/spec/fail/badcalls.ulg index b7c9b765..256ae473 100644 --- a/spec/fail/badcalls.ulg +++ b/spec/fail/badcalls.ulg @@ -9,10 +9,10 @@ foo(100, '', 100) # !> Too many arguments to call foo(false, '', 100) # !> Too many arguments to call foo(100, '', '', false) # !> Too many arguments to call -foo(100, 100) # !> 12:7:error: Invalid argument. Expected 'String' but found 'Number' +foo(100, 100) # !> 12:9:error: Invalid argument. Expected 'String' but found 'Number' # !> 16:4:error: Invalid argument. Expected 'Number' but found 'String' -# !> 16:11:error: Invalid argument. Expected 'String' but found 'Bool' +# !> 16:13:error: Invalid argument. Expected 'String' but found 'Bool' foo('hello', false) let bar = 100 diff --git a/spec/malformed/invalid_calls.ulg b/spec/malformed/invalid_calls.ulg index ee1683f9..ff9912a8 100644 --- a/spec/malformed/invalid_calls.ulg +++ b/spec/malformed/invalid_calls.ulg @@ -5,5 +5,9 @@ end # !> 6:4:error: unexpected token: expected expression but found ',' foo(,) -# !> 9:15:error: unexpected token: expected expression but found ')' -foo(100, false,) \ No newline at end of file +# it's OK to have a trailing `,` +foo(100, false,) + +# don't go nuts though... +# !> 13:15:error: unexpected token: expected expression but found ',' +foo(969, true, ,) \ No newline at end of file diff --git a/src/sem/binder.rs b/src/sem/binder.rs index 2269e295..74042eb1 100644 --- a/src/sem/binder.rs +++ b/src/sem/binder.rs @@ -256,7 +256,6 @@ impl Binder { .iter() .map(|param| { param - .as_inner() .typ .as_ref() .map(|t| self.bind_type(&t.type_ref)) @@ -501,7 +500,7 @@ impl Binder { .iter() .zip(param_tys) .map(|(arg, param)| { - let bound_arg = self.bind_expression(arg.as_inner(), source); + let bound_arg = self.bind_expression(arg, source); if bound_arg.typ != param { self.diagnostics.push(Diagnostic::new( format!( @@ -616,7 +615,6 @@ impl Binder { .params .iter() .map(|p| { - let p = p.as_inner(); let ty = match p.typ.as_ref() { Some(anno) => self.bind_type(&anno.type_ref), None => { diff --git a/src/syntax/parse.rs b/src/syntax/parse.rs index 86ce5b10..1f762d90 100644 --- a/src/syntax/parse.rs +++ b/src/syntax/parse.rs @@ -20,10 +20,8 @@ mod tokeniser; mod checkparse_tests; use super::text::{Ident, SourceText, DUMMY_SPAN}; -use super::tree::{Literal, SyntaxTree, Token, TokenKind}; -use super::{ - BlockBody, DelimItem, Expression, InfixOp, PrefixOp, TypeAnno, TypeRef, TypedId, VarStyle, -}; +use super::tree::{Literal, SepList, SyntaxTree, Token, TokenKind}; +use super::{BlockBody, Expression, InfixOp, PrefixOp, TypeAnno, TypeRef, TypedId, VarStyle}; use crate::diag::Diagnostic; use std::iter::Iterator; use tokeniser::{TokenStream, Tokeniser}; @@ -401,27 +399,28 @@ impl<'a> Parser<'a> { /// Returns a list of zero or more elemnets delimited by the given /// tokens. Used to parse the parameter list for a function and /// the argument list for a call site. - fn delimited(&mut self, p: P, delimiter: TokenKind, close: TokenKind) -> Vec> + fn delimited(&mut self, p: P, delimiter: TokenKind, close: TokenKind) -> SepList where P: Fn(&mut Parser) -> T, { - let mut res = Vec::new(); - if !self.current_is(&close) { - res.push(DelimItem::First(p(self))); - } + let mut builder = SepList::builder(); let mut last_span = self.current.as_ref().map(|t| t.span()); while !self.current_is(&close) { - let delim = self.expect(&delimiter); - res.push(DelimItem::Follow(delim, p(self))); + let with_item = builder.push_item(p(self)); + if !self.current_is(&close) { + builder = with_item.push_sep(self.expect(&delimiter)); + } else { + return with_item.build(); + } - let cur_span = self.current.as_ref().map(|t| t.span()); + let cur_span = self.current.as_ref().map(|t| t.span()); if cur_span.is_some() && last_span == cur_span { break; } else { last_span = cur_span; } } - res + builder.build() } /// Parse Null Denotation diff --git a/src/syntax/parse/checkparse_tests.rs b/src/syntax/parse/checkparse_tests.rs index 42349830..c3c4d420 100644 --- a/src/syntax/parse/checkparse_tests.rs +++ b/src/syntax/parse/checkparse_tests.rs @@ -215,7 +215,7 @@ fn parse_simple_call() { check_parse!("foo()", |s| Expression::call( mk_ident(&s, "foo"), Token::new(TokenKind::OpenBracket), - Vec::new(), + SepList::empty(), Token::new(TokenKind::CloseBracket) )); } @@ -225,32 +225,25 @@ fn parse_complex_call() { check_parse!("hello(1, 1 + 23, -world)", |s| Expression::call( mk_ident(&s, "hello"), Token::new(TokenKind::OpenBracket), - vec![ - DelimItem::First(Expression::constant_num( + SepList::builder() + .push_item(Expression::constant_num( Token::new(TokenKind::Literal(Literal::Number(1))), 1 - )), - DelimItem::Follow( - Token::new(TokenKind::Comma), - Expression::infix( - Expression::constant_num(Token::new(TokenKind::Literal(Literal::Number(1))), 1), - Token::new(TokenKind::Plus), - InfixOp::Add, - Expression::constant_num( - Token::new(TokenKind::Literal(Literal::Number(23))), - 23 - ), - ) - ), - DelimItem::Follow( - Token::new(TokenKind::Comma), - Expression::prefix( - Token::new(TokenKind::Minus), - PrefixOp::Negate, - mk_ident(&s, "world"), - ) - ), - ], + )) + .push_sep(Token::new(TokenKind::Comma)) + .push_item(Expression::infix( + Expression::constant_num(Token::new(TokenKind::Literal(Literal::Number(1))), 1), + Token::new(TokenKind::Plus), + InfixOp::Add, + Expression::constant_num(Token::new(TokenKind::Literal(Literal::Number(23))), 23), + )) + .push_sep(Token::new(TokenKind::Comma)) + .push_item(Expression::prefix( + Token::new(TokenKind::Minus), + PrefixOp::Negate, + mk_ident(&s, "world"), + )) + .build(), Token::new(TokenKind::CloseBracket), )); } @@ -287,21 +280,19 @@ fn parse_indexing() { Token::new(TokenKind::CloseSqBracket) ), Token::new(TokenKind::OpenBracket), - vec![ - DelimItem::First(Expression::constant_num( + SepList::builder() + .push_item(Expression::constant_num( Token::new(TokenKind::Literal(Literal::Number(1))), 1 - )), - DelimItem::Follow( - Token::new(TokenKind::Comma), - Expression::index( - Expression::constant_num(Token::new(TokenKind::Literal(Literal::Number(2))), 2), - Token::new(TokenKind::OpenSqBracket), - Expression::constant_num(Token::new(TokenKind::Literal(Literal::Number(3))), 3), - Token::new(TokenKind::CloseSqBracket) - ) - ), - ], + )) + .push_sep(Token::new(TokenKind::Comma)) + .push_item(Expression::index( + Expression::constant_num(Token::new(TokenKind::Literal(Literal::Number(2))), 2), + Token::new(TokenKind::OpenSqBracket), + Expression::constant_num(Token::new(TokenKind::Literal(Literal::Number(3))), 3), + Token::new(TokenKind::CloseSqBracket) + )) + .build(), Token::new(TokenKind::CloseBracket), )); } @@ -327,10 +318,12 @@ fn parse_ternary_if() { Expression::call( mk_ident(&s, "hello"), Token::new(TokenKind::OpenBracket), - vec![DelimItem::First(Expression::constant_num( - Token::new(TokenKind::Literal(Literal::Number(1))), - 1, - ))], + SepList::builder() + .push_item(Expression::constant_num( + Token::new(TokenKind::Literal(Literal::Number(1))), + 1, + )) + .build(), Token::new(TokenKind::CloseBracket), ), Token::new(TokenKind::Word(s.intern("else"))), @@ -376,7 +369,7 @@ fn parse_function_def() { Token::new(TokenKind::Word(s.intern("fn"))), s.intern("test"), Token::new(TokenKind::OpenBracket), - Vec::new(), + SepList::empty(), Token::new(TokenKind::CloseBracket), mk_simple_ty_anno(&s, "Num"), blockify(vec![Expression::constant_num( @@ -392,7 +385,7 @@ fn parse_function_def() { Token::new(TokenKind::Word(s.intern("fn"))), s.intern("ünécød3"), Token::new(TokenKind::OpenBracket), - Vec::new(), + SepList::empty(), Token::new(TokenKind::CloseBracket), mk_simple_ty_anno(&s, "Num"), blockify(vec![Expression::if_then_else( @@ -429,10 +422,12 @@ fn parse_function_with_args() { Token::new(TokenKind::Word(s.intern("fn"))), s.intern("neg"), Token::new(TokenKind::OpenBracket), - vec![DelimItem::First(TypedId::new( - Token::new(TokenKind::Word(s.intern("i"))), - mk_simple_ty_anno(&s, "Num") - ))], + SepList::builder() + .push_item(TypedId::new( + Token::new(TokenKind::Word(s.intern("i"))), + mk_simple_ty_anno(&s, "Num") + )) + .build(), Token::new(TokenKind::CloseBracket), mk_simple_ty_anno(&s, "Num"), blockify(vec![Expression::prefix( @@ -447,23 +442,21 @@ fn parse_function_with_args() { Token::new(TokenKind::Word(s.intern("fn"))), s.intern("test"), Token::new(TokenKind::OpenBracket), - vec![ - DelimItem::First(TypedId::new( + SepList::builder() + .push_item(TypedId::new( Token::new(TokenKind::Word(s.intern("i"))), mk_simple_ty_anno(&s, "Num"), - )), - DelimItem::Follow( - Token::new(TokenKind::Comma), - TypedId::new_without_type(Token::new(TokenKind::Word(s.intern("j")))), - ), - DelimItem::Follow( - Token::new(TokenKind::Comma), - TypedId::new( - Token::new(TokenKind::Word(s.intern("k"))), - mk_simple_ty_anno(&s, "String"), - ), - ), - ], + )) + .push_sep(Token::new(TokenKind::Comma)) + .push_item(TypedId::new_without_type(Token::new(TokenKind::Word( + s.intern("j"), + )))) + .push_sep(Token::new(TokenKind::Comma)) + .push_item(TypedId::new( + Token::new(TokenKind::Word(s.intern("k"))), + mk_simple_ty_anno(&s, "String"), + )) + .build(), Token::new(TokenKind::CloseBracket), mk_simple_ty_anno(&s, "String"), blockify(vec![Expression::infix( @@ -523,7 +516,9 @@ fn parse_simple_tuple() { Token::new(TokenKind::Colon), TypeRef::tuple( Token::new(TokenKind::OpenBracket), - vec![DelimItem::First(mk_simple_ty(&s, "Num"))], + SepList::builder() + .push_item(mk_simple_ty(&s, "Num")) + .build(), Token::new(TokenKind::CloseBracket) ) )), @@ -540,17 +535,15 @@ fn parse_simple_tuple() { Token::new(TokenKind::Colon), TypeRef::tuple( Token::new(TokenKind::OpenBracket), - vec![ - DelimItem::First(mk_simple_ty(&s, "Num")), - DelimItem::Follow( - Token::new(TokenKind::Comma), - TypeRef::array( - Token::new(TokenKind::OpenSqBracket), - mk_simple_ty(&s, "String"), - Token::new(TokenKind::CloseSqBracket) - ) - ), - ], + SepList::builder() + .push_item(mk_simple_ty(&s, "Num")) + .push_sep(Token::new(TokenKind::Comma)) + .push_item(TypeRef::array( + Token::new(TokenKind::OpenSqBracket), + mk_simple_ty(&s, "String"), + Token::new(TokenKind::CloseSqBracket) + )) + .build(), Token::new(TokenKind::CloseBracket) ) )), diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs index 573cc4ef..e482a491 100644 --- a/src/syntax/tree.rs +++ b/src/syntax/tree.rs @@ -6,6 +6,7 @@ pub mod expression; pub mod operators; +mod seplist; mod token; mod trivia; pub mod types; @@ -16,11 +17,12 @@ use crate::diag::Diagnostic; use crate::parse::Parser; use crate::text::SourceText; +pub use self::seplist::{SepList, SepListBuilder}; pub use self::token::{Literal, Token, TokenKind}; pub use self::trivia::{TriviaToken, TriviaTokenKind}; +use self::expression::Expression; use super::SyntaxNode; -use expression::Expression; /// Syntax tree /// @@ -46,12 +48,12 @@ impl<'a> SyntaxTree<'a> { /// # Parameters /// /// * `root`: The body of the file. This could be an empty - /// sequence if the file is empty + /// sequence if the file is empty /// * `diagnostics`: Diagnostics raised in the parsing of the - /// source. + /// source. /// * `end`: The closing EOF token. This may have some leading - /// trivia attached and is therefore required for a full-fidelity - /// tree. + /// trivia attached and is therefore required for a + /// full-fidelity tree. pub fn new( source: &'a SourceText, root: Expression, @@ -144,7 +146,7 @@ where Expression::Prefix(p) => vec![&p.inner], Expression::Infix(i) => vec![&i.left, &i.right], Expression::Call(c) => std::iter::once(&*c.callee) - .chain(c.arguments.iter().map(|a| a.as_inner())) + .chain(c.arguments.iter()) .collect(), Expression::Index(i) => vec![&i.index, &i.indexee], Expression::IfThenElse(i) => vec![&i.cond, &i.if_true, &i.if_false], diff --git a/src/syntax/tree/expression.rs b/src/syntax/tree/expression.rs index 720b92bb..91bfdc31 100644 --- a/src/syntax/tree/expression.rs +++ b/src/syntax/tree/expression.rs @@ -8,7 +8,7 @@ use super::super::SyntaxNode; use super::operators::{InfixOp, PrefixOp}; use super::token::{Token, TokenKind}; use super::types::TypeAnno; -use std::borrow::Cow; +use super::SepList; /// An identifier, with an optional type attached #[derive(Debug, PartialEq)] @@ -57,47 +57,6 @@ impl TypedId { } } -/// Delimited Item -/// -/// A single element in a list of token-delimited values. -#[derive(Debug, PartialEq)] -pub enum DelimItem { - /// The first item in a list. Doesn't have a corresponding - /// dlimiter token. - First(T), - /// The follwing items in the list. Carries the token which - /// separated it from the previous element. - Follow(Token, T), -} - -impl DelimItem { - /// Borrow in inner item - pub fn as_inner(&self) -> &T { - match *self { - DelimItem::First(ref t) => t, - DelimItem::Follow(_, ref t) => t, - } - } -} - -impl SyntaxNode for DelimItem { - fn description(&self, source: &SourceText) -> Cow { - match *self { - DelimItem::First(ref t) => format!("Delim::First({})", t.description(source)).into(), - DelimItem::Follow(ref sep, ref t) => { - format!("Delim::Follow({}, {})", sep.kind, t.description(source)).into() - } - } - } - - fn span(&self) -> Span { - match *self { - DelimItem::First(ref t) => t.span(), - DelimItem::Follow(ref sep, ref t) => Span::enclosing(sep.span(), t.span()), - } - } -} - /// Literal / Constant Value #[derive(Debug, PartialEq, Clone)] pub enum Constant { @@ -170,7 +129,7 @@ pub struct CallExpression { /// The opening `(` of this call pub open_paren: Box, /// The list of arguments to the call. This could be empty. - pub arguments: Vec>, + pub arguments: SepList, /// THe closing `)` of this call pub close_paren: Box, } @@ -222,7 +181,7 @@ pub struct FunctionExpression { /// The open `(` before the parameter list pub params_open: Box, /// Function parameters - pub params: Vec>, + pub params: SepList, /// The closing `)` after the parameter list pub params_close: Box, /// Function return type @@ -427,7 +386,7 @@ impl Expression { pub fn call( callee: Expression, open_paren: Token, - args: Vec>, + args: SepList, close_paren: Token, ) -> Self { Expression::Call(CallExpression { @@ -479,7 +438,7 @@ impl Expression { fn_kw: Token, identifier: Ident, params_open: Token, - params: Vec>, + params: SepList, params_close: Token, return_type: TypeAnno, body: BlockBody, diff --git a/src/syntax/tree/seplist.rs b/src/syntax/tree/seplist.rs new file mode 100644 index 00000000..a06fd1b3 --- /dev/null +++ b/src/syntax/tree/seplist.rs @@ -0,0 +1,147 @@ +//! Separeted Syntax List +//! +//! This module holds the definition of the `SepList` +//! type. Separed lists are used in the syntax tree to hold delimited +//! items such as parameter or argument lists. +//! +//! A `SepList` is made up of two lists of items, the main tokens and +//! the separators. + +use super::Token; +use std::marker::PhantomData; + +/// The separated list type holds a list of syntax items and the +/// separators between then. +#[derive(Debug, PartialEq)] +pub struct SepList { + /// The items in the list + items: Vec, + /// The separators between the items + separators: Vec, +} + +impl SepList { + /// Create a new seplist from the given items and separators + /// + /// The separator length should be equal or 1 shorter than the + /// items length. + pub fn new(items: Vec, separators: Vec) -> Self { + SepList { items, separators } + } + + /// Create an empty separated list + /// + /// The new list will contain no items and no separators. This is + /// mainly useful for tests or when fabricating trees by hand. The + /// parser will usually genrate an empty list by calling + /// `SepList::builder().build()` + pub fn empty() -> Self { + SepList::new(Vec::new(), Vec::new()) + } + + /// Create a list builder. This provides a structured way of + /// incrementally building a separated list. + pub fn builder() -> SepListBuilder { + SepListBuilder { + items: Vec::new(), + separators: Vec::new(), + state: Default::default(), + } + } + + /// Borrow the separators as a slice + /// + /// Standard iteration of this collection just accesses the main + /// items. This allows access to the separators too. + pub fn separators(&self) -> &[S] { + &self.separators + } +} + +impl std::ops::Deref for SepList { + type Target = [T]; + + fn deref(&self) -> &[T] { + &self.items + } +} + +/// Fluent typestate API for builing a separated list +pub struct SepListBuilder { + /// The buffered items for this list + items: Vec, + /// The buffered separators for this list + separators: Vec, + /// Phantom state data + state: PhantomData, +} + +/// Initial state for the separated list builder +pub struct Item {} + +/// Separated list builder state when item has been seen +pub struct Separator {} + +impl SepListBuilder { + /// Finish building the list + pub fn build(self) -> SepList { + SepList::new(self.items, self.separators) + } +} + +impl SepListBuilder { + /// Push an item into the separated list and wait for a separator + pub fn push_item(mut self, item: T) -> SepListBuilder { + self.items.push(item); + SepListBuilder { + items: self.items, + separators: self.separators, + state: Default::default(), + } + } +} + +impl SepListBuilder { + /// Push a separator onto the list and wait for another item + pub fn push_sep(mut self, sep: S) -> SepListBuilder { + self.separators.push(sep); + SepListBuilder { + items: self.items, + separators: self.separators, + state: Default::default(), + } + } +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn create_new_seplist() { + let empty = SepList::<(), u32>::new(Vec::new(), Vec::new()); + let with_items = SepList::new(vec![1, 2, 4, 8], vec![',', '!', '*']); + + assert_eq!(0, empty.len()); + assert_eq!(4, with_items.len()); + } + + #[test] + fn seplist_builder() { + let list = SepList::builder() + .push_item(123) + .push_sep(',') + .push_item(456) + .push_sep('.') + .build(); + + assert_eq!(2, list.len()); + assert_eq!(Some(&123), list.get(0)); + assert_eq!(Some(&456), list.get(1)); + assert_eq!(None, list.get(2)); + assert_eq!(Some(&','), list.separators().get(0)); + assert_eq!(Some(&'.'), list.separators().get(1)); + assert_eq!(None, list.separators().get(2)); + } +} diff --git a/src/syntax/tree/types.rs b/src/syntax/tree/types.rs index 455a4a16..a1f7f848 100644 --- a/src/syntax/tree/types.rs +++ b/src/syntax/tree/types.rs @@ -4,8 +4,7 @@ //! reference types. use super::super::text::{SourceText, Span, DUMMY_SPAN}; -use super::super::SyntaxNode; -use super::expression::DelimItem; +use super::super::{SepList, SyntaxNode}; use super::Token; use std::borrow::Cow; @@ -20,7 +19,7 @@ pub enum TypeRef { /// The Unit Type Unit(Box, Box), /// A non-empty Tuple - Tuple(Box, Vec>, Box), + Tuple(Box, SepList, Box), /// An Array Type Array(Box, Box, Box), /// Missing type. Used to represent type information being missing @@ -63,7 +62,7 @@ impl TypeRef { /// /// A tuple type is an ordered collection of values. Each value /// can be of a different type. - pub fn tuple(open: Token, inner: Vec>, close: Token) -> Self { + pub fn tuple(open: Token, inner: SepList, close: Token) -> Self { if inner.is_empty() { Self::unit(open, close) } else {