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 {