use crate::compiler::parser::ast::AtomKind::Map;
use crate::compiler::parser::ast::ExprKind::{
Binary, FunCall, Literal, Tuple, ValueDiscard, Variable,
};
use crate::compiler::parser::ast::{
Atom, AtomKind, BinOpToken, Block, Expr, ExprKind, FunctionDefinition, LiteralToken, MapTarget,
NamedParameter, Stmt, TypeDefinition, TypeName, TypedVariableDefinition, UnaryOpToken,
};
use crate::compiler::parser::lexer::TokenType::{
AmpAmp, Bang, BangBang, BangEquals, Colon, Comma, Dot, Eof, Equals, EqualsEquals, False,
Function, GreaterThan, Identifier, LeftCurly, LeftParen, LessThan, Match, Minus, Number,
PipePipe, Plus, Return, RightArrow, RightCurly, RightParen, Semicolon, Slash, Star,
StringLiteral, True, Type,
};
use crate::compiler::parser::lexer::{Token, TokenStream, TokenType};
use crate::compiler::parser::parse_state::ParseState;
use crate::compiler::parser::parser::Precedence::{
And, Assignment, Call, Comparison, Factor, NoPrecedence, Or, Term, Unary,
};
use crate::compiler::parser::pointer::P;
use crate::compiler::parser::CompileError;
use crate::compiler::value::primitives::Primitive;
use crate::compiler::value::primitives::Primitive::Integer;
use crate::compiler::value::values::Value::{FunctionNameValue, PrimitiveValue};
use crate::string_to_value;
use std::mem;
use tracing::{error, trace};
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Copy, Clone)]
enum Precedence {
NoPrecedence,
Assignment,
Call,
Or,
And,
Comparison,
Term,
Factor,
Unary,
}
pub struct Parser<'input> {
scanner: TokenStream<'input>,
previous: Token<'input>,
current: Token<'input>,
finished: bool,
currently_in_error: bool,
}
impl<'input> Parser<'input> {
pub fn new(scanner: TokenStream<'input>) -> Self {
Parser {
scanner,
previous: Token {
line: 1,
offset_in_line: 1,
t_type: Eof,
value: "",
},
current: Token {
line: 1,
offset_in_line: 1,
t_type: Eof,
value: "",
},
finished: false,
currently_in_error: false,
}
}
fn advance(&mut self) {
let next_token = self.scanner.next().unwrap_or_else(|| {
self.finished = true;
Token {
line: 1,
offset_in_line: 1,
t_type: Eof,
value: "",
}
});
self.previous = mem::replace(&mut self.current, next_token);
}
fn check(&self, t_type: TokenType) -> bool {
self.current.t_type == t_type
}
fn consume(&mut self, t_type: TokenType, message: &str) -> Result<(), CompileError> {
if !self.check(t_type) {
self.advance();
return Err(self.error(message));
}
self.advance();
Ok(())
}
fn match_token(&mut self, t_type: TokenType) -> bool {
if self.current.t_type == t_type {
self.advance();
return true;
}
false
}
fn error(&mut self, error: &str) -> CompileError {
self.currently_in_error = true;
return CompileError::new(error.to_string(), self.previous.span());
}
fn synchronize_on_error(&mut self) {
self.currently_in_error = false;
loop {
match self.current.t_type {
tt if tt == RightCurly || tt == Semicolon || tt == Eof => {
self.advance();
break;
}
_ => {
self.advance();
}
}
}
}
pub fn parse(&mut self, state: &mut ParseState) {
self.advance();
while !self.finished {
self.top_level_statement(state);
}
}
fn top_level_statement(&mut self, state: &mut ParseState) {
let error = match self.current.t_type {
Function => function_definition(self, state),
Type => type_definition(self, state),
Match => match_statement(self, state),
_ => statement(self, state),
};
match error {
Ok(stmt) => {
state.statements.push(stmt);
}
Err(error) => {
state.errors.push(error);
}
}
if self.currently_in_error {
self.synchronize_on_error();
}
if self.current.t_type == Eof {
self.finished = true;
}
}
}
fn type_definition(parser: &mut Parser, state: &mut ParseState) -> Result<Stmt, CompileError> {
let mut result = TypeDefinition::default();
parser.advance(); parser.consume(
Identifier,
"Type definitions need a name after the type keyword",
)?;
result.name = parser.previous.value.to_string();
parser.consume(
LeftCurly,
"A type definition needs the type fields in '{', '}'",
)?;
loop {
if !parser.check(RightCurly) {
result.fields.push(type_field(parser)?);
}
if !parser.match_token(Comma) {
break;
}
}
parser.consume(
RightCurly,
"A type definition needs the type fields in '{', '}'",
)?;
parser.match_token(Semicolon);
state.type_definitions.push(result);
Ok(Stmt::Empty)
}
fn type_field(parser: &mut Parser) -> Result<TypedVariableDefinition, CompileError> {
parser.consume(
Identifier,
"Variable declarations are <identifier>:<type> pairs",
)?;
let name = parser.previous.value.to_string();
parser.consume(
Colon,
"Variable declarations are <identifier>:<type> pairs",
)?;
parser.consume(
Identifier,
"Variable declarations are <identifier>:<type> pairs",
)?;
let field_type = parser.previous.value.to_string();
Ok(TypedVariableDefinition {
name,
type_name: TypeName { name: field_type },
})
}
fn function_definition(parser: &mut Parser, state: &mut ParseState) -> Result<Stmt, CompileError> {
let mut result = FunctionDefinition::default();
parser.consume(
Function,
"Function definitions must start with the `function` keyword",
)?;
parser.consume(
Identifier,
"Function definitions require a name after the `function` keyword",
)?;
result.name = parser.previous.value.to_string();
state.start_function_definition(parser.previous.value.to_string());
parser.consume(
LeftParen,
"A function definition must have the parameters after the name",
)?;
if !parser.check(RightParen) {
loop {
if parser.match_token(Identifier) {
result.formal_arguments.push(primary(parser, state)?);
} else {
result
.formal_arguments
.push(expression(parser, state, Assignment)?);
}
if !parser.match_token(Comma) {
break;
}
}
}
parser.consume(RightParen, "A function parameter list must end with ')'")?;
parser.consume(
Equals,
"Function definition must be followed by a '=' and a function body in '{'... '}'",
)?;
parser.consume(
LeftCurly,
"Function body must be defined in a block starting with '{'",
)?;
result.body = block(parser, state)?;
state.end_function_definition(result);
Ok(Stmt::Empty)
}
fn block(parser: &mut Parser, state: &mut ParseState) -> Result<Vec<Stmt>, CompileError> {
let mut result = vec![];
while !parser.check(RightCurly) && !parser.check(Eof) {
let statement = statement(parser, state)?;
result.push(statement);
}
parser.consume(RightCurly, "Block must end with '}'")?;
Ok(result)
}
fn statement(parser: &mut Parser, state: &mut ParseState) -> Result<Stmt, CompileError> {
if parser.match_token(Return) {
let return_exprs = argument_list(parser, state, Assignment)?;
parser.match_token(Semicolon);
return Ok(Stmt::Return(return_exprs));
}
if parser.match_token(BangBang) {
return Ok(Stmt::BangBang);
}
let expr = expression(parser, state, Assignment)?;
if parser.check(Semicolon) {
parser.advance();
Ok(Stmt::Semi(expr))
} else {
Ok(Stmt::Expr(expr))
}
}
#[derive(Copy, Clone)]
pub enum Parselet {
NoCode,
Tuple,
Unary,
Binary,
Assignment, Primary,
Constant,
Dot,
}
#[derive(Copy, Clone)]
pub struct ParseRule {
prefix: Parselet,
infix: Parselet,
precedence: Precedence,
}
#[rustfmt::skip] static PRATT: [ParseRule; mem::variant_count::<TokenType>()] = {
let mut arr = [
ParseRule { prefix: Parselet::NoCode, infix: Parselet::NoCode, precedence: NoPrecedence };
mem::variant_count::<TokenType>()];
arr[LeftParen as usize] = ParseRule { prefix: Parselet::Tuple, infix: Parselet::NoCode, precedence: NoPrecedence,};
arr[Bang as usize] = ParseRule { prefix: Parselet::Unary, infix: Parselet::NoCode, precedence: Term,};
arr[Plus as usize] = ParseRule { prefix: Parselet::Unary, infix: Parselet::Binary, precedence: Term,};
arr[Minus as usize] = ParseRule { prefix: Parselet::Unary, infix: Parselet::Binary, precedence: Term,};
arr[Star as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: Factor,};
arr[Slash as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: Factor,};
arr[GreaterThan as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: Comparison,};
arr[LessThan as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: Comparison,};
arr[EqualsEquals as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: Comparison,};
arr[BangEquals as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: Comparison,};
arr[AmpAmp as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: And,};
arr[PipePipe as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Binary, precedence: Or,};
arr[True as usize] = ParseRule { prefix: Parselet::Constant, infix: Parselet::NoCode, precedence: NoPrecedence,};
arr[False as usize] = ParseRule { prefix: Parselet::Constant, infix: Parselet::NoCode, precedence: NoPrecedence,};
arr[Number as usize] = ParseRule { prefix: Parselet::Constant, infix: Parselet::NoCode, precedence: NoPrecedence,};
arr[StringLiteral as usize] = ParseRule { prefix: Parselet::Constant, infix: Parselet::NoCode, precedence: NoPrecedence,};
arr[Identifier as usize] = ParseRule { prefix: Parselet::Primary, infix: Parselet::NoCode, precedence: NoPrecedence,};
arr[Equals as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Assignment, precedence: Assignment,};
arr[Dot as usize] = ParseRule { prefix: Parselet::NoCode, infix: Parselet::Dot, precedence: Call,};
arr
};
fn expression(
parser: &mut Parser,
state: &mut ParseState,
precedence: Precedence,
) -> Result<P<Expr>, CompileError> {
let mut lhs = parse_prefix(parser, state)?;
while PRATT[parser.current.t_type as usize].precedence >= precedence {
parser.advance();
lhs = match PRATT[parser.previous.t_type as usize].infix {
Parselet::Binary => {
let op = <TokenType as TryInto<BinOpToken>>::try_into(parser.previous.t_type);
let rhs = expression(
parser,
state,
PRATT[parser.previous.t_type as usize].precedence,
)?;
P::P(Expr {
kind: Binary(op.unwrap(), lhs, rhs),
span: parser.previous.span(),
})
}
Parselet::Assignment => {
let rhs = expression(parser, state, Assignment)?;
P::P(Expr {
kind: ExprKind::Assignment(lhs, rhs),
span: parser.previous.span(),
})
}
Parselet::Dot => {
parser.consume(Identifier, "Expected field name after the '.'")?;
let field_name =
state.add_value_constant(string_to_value!(parser.previous.value.to_string()));
P::P(Expr {
kind: ExprKind::FieldAccess(lhs, field_name),
span: parser.previous.span(),
})
}
_ => {
return Err(
parser.error(&format!("Unable to parse symbol {}", parser.previous.value,))
);
}
}
}
Ok(lhs)
}
fn parse_prefix(parser: &mut Parser, state: &mut ParseState) -> Result<P<Expr>, CompileError> {
parser.advance();
let result = match PRATT[parser.previous.t_type as usize].prefix {
Parselet::Tuple => tuple(parser, state)?,
Parselet::Unary => match parser.previous.t_type {
Minus => {
let span = parser.previous.span();
let actual = expression(parser, state, Unary)?;
P::P(Expr {
kind: ExprKind::Unary(UnaryOpToken::Minus, actual),
span,
})
}
Bang => {
let actual = expression(parser, state, Unary)?;
P::P(Expr {
kind: ExprKind::Unary(UnaryOpToken::Bang, actual),
span: parser.previous.span(),
})
}
_ => {
return Err(parser.error(&format!(
"Cannot parse {} as unary operator",
parser.previous.value,
)));
}
},
Parselet::Primary => primary(parser, state)?,
Parselet::Constant => match parser.previous.t_type {
Number => {
let index = state.add_value_constant(PrimitiveValue(Integer(
parser.previous.value.parse::<i32>().unwrap(),
)));
P::P(Expr {
kind: Literal(LiteralToken::Integer(index)),
span: parser.previous.span(),
})
}
StringLiteral => {
let index = state.add_value_constant(PrimitiveValue(Primitive::MyString(
parser.previous.value.parse::<String>().unwrap(),
)));
P::P(Expr {
kind: Literal(LiteralToken::StringLiteral(index)),
span: parser.previous.span(),
})
}
True => P::P(Expr {
kind: Literal(LiteralToken::True),
span: parser.previous.span(),
}),
False => P::P(Expr {
kind: Literal(LiteralToken::False),
span: parser.previous.span(),
}),
_ => {
panic!(
"Cannot handle {:?} as constant, this is a big problem",
parser.previous.t_type
);
}
},
_ => {
return Err(parser.error(&format!("Unexpected token '{}'", &parser.previous.value)));
}
};
Ok(result)
}
fn tuple(parser: &mut Parser, state: &mut ParseState) -> Result<P<Expr>, CompileError> {
let mut arguments = argument_list(parser, state, Assignment)?;
parser.consume(RightParen, "Expected ')'")?;
if arguments.len() == 1 {
Ok(arguments.pop().unwrap())
} else {
Ok(P::P(Expr {
kind: Tuple(arguments),
span: parser.previous.span(),
}))
}
}
fn atom(parser: &mut Parser, state: &mut ParseState) -> Result<P<Atom>, CompileError> {
match parser.current.t_type {
Identifier => {
let lhs = expression(parser, state, Assignment)?;
return if let FunCall(name, params) = &lhs.kind {
if parser.check(RightArrow) {
parser.advance();
let rhs = expression(parser, state, Assignment)?;
let maps_to = match &rhs.kind {
Tuple(pattern) => MapTarget::Tuple(pattern.clone()),
Variable(id) => MapTarget::Tuple(vec![P::P(Expr {
kind: Variable(*id),
span: rhs.span,
})]),
ValueDiscard => MapTarget::Tuple(vec![P::P(Expr {
kind: ValueDiscard,
span: rhs.span,
})]),
Literal(token) => MapTarget::Tuple(vec![P::P(Expr {
kind: Literal(*token),
span: rhs.span,
})]),
FunCall(name, args) => MapTarget::Type(*name, args.clone()),
_ => {
return Err(parser.error(
&format!("When mapping function result, receiver can currently be only a tuple value, not {}", rhs.kind
)));
}
};
Ok(P::P(Atom {
kind: Map(*name, params.clone(), maps_to),
span: lhs.span,
}))
} else {
Ok(P::P(Atom {
kind: AtomKind::FunCall(*name, params.clone()),
span: lhs.span,
}))
}
} else {
Err(parser.error(&format!(
"Query atom must be a function call, return statement or block, not {}",
&lhs.kind
)))
};
}
Return => {
let span = parser.current.span();
parser.advance();
let value = argument_list(parser, state, Assignment)?;
parser.match_token(Semicolon);
return Ok(P::P(Atom {
kind: AtomKind::Return(value),
span,
}));
}
LeftCurly => {
let span = parser.current.span();
parser.advance();
let block = block(parser, state)?;
let mut expression_found = false;
let mut bangbang_found = false;
if !block.is_empty() {
for stmt in &block {
if expression_found {
return Err(
parser.error("An expression can only appear at the end of a block")
);
}
if bangbang_found {
return Err(
parser.error("A BangBang can only appear at the end of a block")
);
}
match stmt {
Stmt::Expr(_) => {
expression_found = true;
}
Stmt::BangBang => bangbang_found = true,
_ => {}
}
}
if !expression_found && !bangbang_found {
return Err(
parser.error("A query block atom must end in either an expression or a !!")
);
}
}
return Ok(P::P(Atom {
kind: AtomKind::Block(P::P(Block { stmts: block, span })),
span,
}));
}
_ => Err(parser.error(&format!(
"Query atom cannot start with {}",
parser.current.value
))),
}
}
fn match_statement(parser: &mut Parser, state: &mut ParseState) -> Result<Stmt, CompileError> {
let mut result = vec![];
state.start_query_block();
parser.advance();
result.push(atom(parser, state)?);
while parser.check(Comma) {
parser.advance();
result.push(atom(parser, state)?);
}
state.end_query_block();
Ok(Stmt::Match(result))
}
fn argument_list(
parser: &mut Parser,
state: &mut ParseState,
precedence: Precedence,
) -> Result<Vec<P<Expr>>, CompileError> {
let mut result = vec![];
if !parser.check(RightParen) {
loop {
result.push(expression(parser, state, precedence)?);
if !parser.match_token(Comma) {
break;
}
}
}
Ok(result)
}
fn named_parameter_list(
parser: &mut Parser,
state: &mut ParseState,
precedence: Precedence,
) -> Result<Vec<NamedParameter>, CompileError> {
let mut result = vec![];
if !parser.check(RightParen) {
loop {
let possibly_before_colon = expression(parser, state, precedence)?;
if parser.match_token(Colon) {
if let Variable(name) = possibly_before_colon.kind {
let value = expression(parser, state, precedence)?;
result.push(NamedParameter {
name: Some(name),
value,
});
} else {
return Err(parser.error("Parameter names have to be valid identifiers"));
}
} else {
result.push(NamedParameter {
name: None,
value: possibly_before_colon,
});
}
if !parser.match_token(Comma) {
break;
}
}
}
Ok(result)
}
fn primary(parser: &mut Parser, state: &mut ParseState) -> Result<P<Expr>, CompileError> {
if parser.check(LeftParen) {
function_call(parser, state, Call)
} else {
variable(parser, state)
}
}
fn function_call(
parser: &mut Parser,
state: &mut ParseState,
precedence: Precedence,
) -> Result<P<Expr>, CompileError> {
let name = state.add_value_constant(FunctionNameValue(parser.previous.value.to_string()));
parser.consume(
LeftParen,
"Function calls must have parameters that start with '('",
)?;
let arguments = named_parameter_list(parser, state, precedence)?;
let result = P::P(Expr {
kind: FunCall(name, arguments),
span: parser.previous.span(),
});
parser.consume(
RightParen,
"Function arguments must be contained in '(', ')'",
)?;
Ok(result)
}
fn variable(parser: &mut Parser, state: &mut ParseState) -> Result<P<Expr>, CompileError> {
let key = parser.previous.value;
if key == "_" {
return Ok(P::P(Expr {
kind: ValueDiscard,
span: parser.previous.span(),
}));
}
let index = if let Some(variable) = state.get_variable(key) {
variable.index as u8
} else {
state.declare_variable(key.to_string())
};
Ok(P::P(Expr {
kind: Variable(index),
span: parser.previous.span(),
}))
}
#[cfg(test)]
pub(crate) mod tests {
use crate::compiler::parser::ast::BinOpToken::Plus;
use crate::compiler::parser::ast::ExprKind::{
Assignment, Binary, FunCall, Literal, Tuple, Unary, ValueDiscard, Variable,
};
use crate::compiler::parser::ast::LiteralToken::{Integer, StringLiteral};
use crate::compiler::parser::ast::Stmt::Match;
use crate::compiler::parser::ast::{
Atom, AtomKind, BinOpToken, Expr, LiteralToken, NamedParameter, Stmt, UnaryOpToken,
DUMMY_SPAN,
};
use crate::compiler::parser::ast::{MapTarget, TypeName};
use crate::compiler::parser::pointer::P;
use crate::compiler::parser::tests::base_test;
use crate::compiler::parser::{ast, CompileError};
impl From<Vec<CompileError>> for CompileError {
fn from(value: Vec<CompileError>) -> Self {
value.last().cloned().unwrap()
}
}
#[test]
fn unclosed_tuple_is_error() -> Result<(), CompileError> {
base_test("(a, 1, 2", false)?;
Ok(())
}
#[test]
fn unopened_tuple_is_error() -> Result<(), CompileError> {
base_test("a, 1, 2)", false)?;
Ok(())
}
#[test]
fn unopened_nested_tuple_is_error() -> Result<(), CompileError> {
base_test("(a, ), 1, 2)", false)?;
Ok(())
}
#[test]
fn unclosed_nested_tuple_is_error() -> Result<(), CompileError> {
base_test("(a, (, 1, 2)", false)?;
Ok(())
}
#[test]
fn call_with_extra_comma_is_error() -> Result<(), CompileError> {
base_test("a( ,1, 2)", false)?;
Ok(())
}
#[test]
fn single_element_tuple_parses_as_parenthesis() -> Result<(), CompileError> {
let program = "(a)";
let mut script = base_test(program, true)?;
assert_eq!(1, script.statements.len());
assert_eq!(
script.statements.pop().unwrap(),
Stmt::Expr(P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN
}))
);
Ok(())
}
#[test]
fn self_assignment_parses_ok() -> Result<(), CompileError> {
let program = "a = a\n";
let script = base_test(program, true)?;
assert_eq!(1, script.statements.len());
Ok(())
}
#[test]
fn parses_variable_expressions_in_tuple() -> Result<(), CompileError> {
let program = "tuple_result = (first_variable, first_variable + second_variable - 2)";
let script = base_test(program, true)?;
assert_eq!(1, script.statements.len());
if let Stmt::Expr(result) = script.statements.get(0).unwrap() {
assert_eq!(
Assignment(
P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Tuple(vec![
P::P(Expr {
kind: Variable(1),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Binary(
Plus,
P::P(Expr {
kind: Variable(1),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Binary(
BinOpToken::Minus,
P::P(Expr {
kind: Variable(2),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Literal(Integer(0)),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
})
]),
span: DUMMY_SPAN
}),
),
result.kind
);
} else {
}
Ok(())
}
#[test]
fn parses_string_literal() -> Result<(), CompileError> {
let program = "my_string = \"t4u93q0gurepR$@#QT%$#WIG(%T$W+R$#QT_$#Q\"";
base_test(program, true)?;
Ok(())
}
#[test]
fn define_function_without_value_is_valid() -> Result<(), CompileError> {
let program = "function a";
base_test(program, false)?;
Ok(())
}
#[test]
fn define_function_without_block_is_error() -> Result<(), CompileError> {
let program = "function a(1) = 2";
base_test(program, false)?;
Ok(())
}
#[test]
fn define_function_without_variable_but_with_block_is_weird_but_ok() -> Result<(), CompileError>
{
let program = "function a(1) = { 2 }";
base_test(program, true)?;
Ok(())
}
#[test]
fn multiply_is_higher_prec_than_add() -> Result<(), CompileError> {
let program = "1 + 2 * 3";
let result = base_test(program, true)?;
assert_eq!(1, result.statements.len());
let stmt = result.statements.get(0).unwrap();
if let Stmt::Expr(expr) = stmt {
assert_eq!(
Binary(
Plus,
P::P(Expr {
kind: Literal(Integer(0)),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Binary(
BinOpToken::Star,
P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Literal(Integer(2)),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
})
),
expr.kind
);
} else {
panic!();
}
Ok(())
}
#[test]
fn or_is_higher_prec_than_and() -> Result<(), CompileError> {
let program = "true || false && false";
let result = base_test(program, true)?;
assert_eq!(1, result.statements.len());
let stmt = result.statements.get(0).unwrap();
assert_eq!(
&Stmt::Expr(P::P(Expr {
kind: Binary(
BinOpToken::Or,
P::P(Expr {
kind: Literal(LiteralToken::True),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Binary(
BinOpToken::And,
P::P(Expr {
kind: Literal(LiteralToken::False),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Literal(LiteralToken::False),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
}),
),
span: DUMMY_SPAN
})),
stmt
);
Ok(())
}
#[test]
fn parentheses_beat_precedence() -> Result<(), CompileError> {
let program = "(true || false) && false";
let result = base_test(program, true)?;
assert_eq!(1, result.statements.len());
let stmt = result.statements.get(0).unwrap();
assert_eq!(
&Stmt::Expr(P::P(Expr {
kind: Binary(
BinOpToken::And,
P::P(Expr {
kind: Binary(
BinOpToken::Or,
P::P(Expr {
kind: Literal(LiteralToken::True),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Literal(LiteralToken::False),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Literal(LiteralToken::False),
span: DUMMY_SPAN
}),
),
span: DUMMY_SPAN
})),
stmt
);
Ok(())
}
#[test]
fn unary_is_higher_prec_than_binary() -> Result<(), CompileError> {
let program = "1 + -2 * 3";
let result = base_test(program, true)?;
assert_eq!(1, result.statements.len());
let stmt = result.statements.get(0).unwrap();
assert_eq!(
&Stmt::Expr(P::P(Expr {
kind: Binary(
Plus,
P::P(Expr {
kind: Literal(Integer(0)),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Binary(
BinOpToken::Star,
P::P(Expr {
kind: Unary(
UnaryOpToken::Minus,
P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
}),
P::P(Expr {
kind: Literal(Integer(2)),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
})
),
span: DUMMY_SPAN
})),
stmt
);
Ok(())
}
#[test]
fn can_call_function_without_arguments() -> Result<(), CompileError> {
let program = "a()";
let result = base_test(program, true)?;
assert_eq!(1, result.statements.len());
let stmt = result.statements.get(0).unwrap();
assert_eq!(
&Stmt::Expr(P::P(Expr {
kind: FunCall(0, vec![]),
span: DUMMY_SPAN
})),
stmt
);
Ok(())
}
#[test]
fn named_parameters() -> Result<(), CompileError> {
let program = "a(variable, param_name: variable, 2)";
let result = base_test(program, true)?;
assert_eq!(1, result.statements.len());
let stmt = result.statements.get(0).unwrap();
let first_param = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
};
let second_param = NamedParameter {
name: Some(1),
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
};
let third_param = NamedParameter {
name: None,
value: P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN,
}),
};
assert_eq!(
stmt,
&Stmt::Expr(P::P(Expr {
kind: FunCall(0, vec![first_param, second_param, third_param]),
span: DUMMY_SPAN
}))
);
Ok(())
}
#[test]
fn call_is_higher_prec_than_assignment() -> Result<(), CompileError> {
let program = "a(1) = b(2)";
let result = base_test(program, true)?;
assert_eq!(1, result.statements.len());
let stmt = result.statements.get(0).unwrap();
let lhs_call_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN,
}),
};
let lhs = P::P(Expr {
kind: FunCall(0, vec![lhs_call_parameter]),
span: DUMMY_SPAN,
});
let rhs_call_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Literal(Integer(3)),
span: DUMMY_SPAN,
}),
};
let rhs = P::P(Expr {
kind: FunCall(2, vec![rhs_call_parameter]),
span: DUMMY_SPAN,
});
assert_eq!(
&Stmt::Expr(P::P(Expr {
kind: Assignment(lhs, rhs),
span: DUMMY_SPAN
})),
stmt
);
Ok(())
}
#[test]
fn multiple_definitions_for_same_function() -> Result<(), CompileError> {
let program = "\
function f(1, a) = { a } \n\
function f(2, a) = { a + 12 } \n\
function f(3, a) = { a + 23 } \n\
a = f(3, 2) \n\
b = f(2, 2) \n\
c = f(1, 2) \n\
";
base_test(program, true)?;
Ok(())
}
#[test]
fn nested_definitions_are_disallowed() -> Result<(), CompileError> {
let program = "\
function f(a) = { \n\
function g(b) = {}\n\
}
";
base_test(program, false)?;
Ok(())
}
#[test]
fn can_parse_constant_query() -> Result<(), CompileError> {
let query = "\
match 2
";
base_test(query, false)?;
Ok(())
}
#[test]
fn can_parse_variable_query() -> Result<(), CompileError> {
let query = "\
match x
";
base_test(query, false)?;
Ok(())
}
#[test]
fn can_parse_constant_to_function_query() -> Result<(), CompileError> {
let query = "\
match f(2)
";
let script = base_test(query, true)?;
assert_eq!(1, script.statements.len());
let result = script.statements.get(0).unwrap();
let mut match_stmts = vec![];
let call_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN,
}),
};
match_stmts.push(P::P(Atom {
kind: AtomKind::FunCall(0, vec![call_parameter]),
span: DUMMY_SPAN,
}));
assert_eq!(&Match(match_stmts), result);
Ok(())
}
#[test]
fn cartesian() -> Result<(), CompileError> {
let query = "\
match f(x), g(y)
";
let script = base_test(query, true)?;
let mut match_stmts = vec![];
let f_call_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
};
let g_call_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(1),
span: DUMMY_SPAN,
}),
};
match_stmts.push(P::P(Atom {
kind: AtomKind::FunCall(0, vec![f_call_parameter]),
span: DUMMY_SPAN,
}));
match_stmts.push(P::P(Atom {
kind: AtomKind::FunCall(1, vec![g_call_parameter]),
span: DUMMY_SPAN,
}));
assert_eq!(1, script.statements.len());
assert_eq!(&Match(match_stmts), script.statements.get(0).unwrap());
Ok(())
}
#[test]
fn can_parse_variable_to_function_to_constant() -> Result<(), CompileError> {
let query = "\
match f(x) -> (\"two\")
";
let constant = P::P(Expr {
kind: Literal(StringLiteral(1)),
span: DUMMY_SPAN,
});
let f_call_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
};
let map = P::P(Atom {
kind: AtomKind::Map(0, vec![f_call_parameter], MapTarget::Tuple(vec![constant])),
span: DUMMY_SPAN,
});
let result = base_test(query, true)?;
assert_eq!(1, result.statements.len());
assert_eq!(&Match(vec![map]), result.statements.get(0).unwrap());
Ok(())
}
#[test]
fn parse_function_tuple_query() -> Result<(), CompileError> {
let query = "\
match f(_, x, 4)
";
let result = base_test(query, true)?;
let mut match_stmts = vec![];
let first_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: ValueDiscard,
span: DUMMY_SPAN,
}),
};
let second_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
};
let third_parameter = NamedParameter {
name: None,
value: P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN,
}),
};
match_stmts.push(P::P(Atom {
kind: AtomKind::FunCall(0, vec![first_parameter, second_parameter, third_parameter]),
span: DUMMY_SPAN,
}));
assert_eq!(1, result.statements.len());
assert_eq!(&Match(match_stmts), result.statements.get(0).unwrap());
Ok(())
}
#[test]
fn tree_join() -> Result<(), CompileError> {
let query = "\
match f(x) -> (y), g(v) -> (w), h(x) -> (w)
";
let script = base_test(query, true)?;
let mut match_stmts = vec![];
let f_param = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
};
let g_param = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(2),
span: DUMMY_SPAN,
}),
};
let h_param = NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
};
let f_bind = P::P(Expr {
kind: Variable(1),
span: DUMMY_SPAN,
});
let f_bind = MapTarget::Tuple(vec![f_bind]);
let g_bind = P::P(Expr {
kind: Variable(3),
span: DUMMY_SPAN,
});
let g_bind = MapTarget::Tuple(vec![g_bind]);
let h_bind = P::P(Expr {
kind: Variable(3),
span: DUMMY_SPAN,
});
let h_bind = MapTarget::Tuple(vec![h_bind]);
let f_map = P::P(Atom {
kind: AtomKind::Map(0, vec![f_param], f_bind),
span: DUMMY_SPAN,
});
let g_map = P::P(Atom {
kind: AtomKind::Map(1, vec![g_param], g_bind),
span: DUMMY_SPAN,
});
let h_map = P::P(Atom {
kind: AtomKind::Map(2, vec![h_param], h_bind),
span: DUMMY_SPAN,
});
match_stmts.push(f_map);
match_stmts.push(g_map);
match_stmts.push(h_map);
assert_eq!(1, script.statements.len());
assert_eq!(&Match(match_stmts), script.statements.get(0).unwrap());
Ok(())
}
#[test]
fn parse_function_tuple_query_to_tuple() -> Result<(), CompileError> {
let query = "\
match f(x, _, 4) -> (y, _, 3)
";
let script = base_test(query, true)?;
let f_params = vec![
NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
},
NamedParameter {
name: None,
value: P::P(Expr {
kind: ValueDiscard,
span: DUMMY_SPAN,
}),
},
NamedParameter {
name: None,
value: P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN,
}),
},
];
let f_bind = MapTarget::Tuple(vec![
P::P(Expr {
kind: Variable(1),
span: DUMMY_SPAN,
}),
P::P(Expr {
kind: ValueDiscard,
span: DUMMY_SPAN,
}),
P::P(Expr {
kind: Literal(Integer(2)),
span: DUMMY_SPAN,
}),
]);
let f_map = P::P(Atom {
kind: AtomKind::Map(0, f_params, f_bind),
span: DUMMY_SPAN,
});
assert_eq!(1, script.statements.len());
assert_eq!(&Match(vec![f_map]), script.statements.get(0).unwrap());
Ok(())
}
#[test]
fn parse_function_tuple_with_expression_in_argument() -> Result<(), CompileError> {
let query = "\
match f(x+2) -> (x)
";
let script = base_test(query, true)?;
let x_var = P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
});
let constant = P::P(Expr {
kind: Literal(Integer(1)),
span: DUMMY_SPAN,
});
let f_arg = P::P(Expr {
kind: Binary(Plus, x_var, constant),
span: DUMMY_SPAN,
});
let f_param = vec![NamedParameter {
name: None,
value: f_arg,
}];
let f_bind = P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
});
let f_bind = MapTarget::Tuple(vec![f_bind]);
let f_map = P::P(Atom {
kind: AtomKind::Map(0, f_param, f_bind),
span: DUMMY_SPAN,
});
assert_eq!(1, script.statements.len());
assert_eq!(&Match(vec![f_map]), script.statements.get(0).unwrap());
Ok(())
}
#[test]
fn parse_function_to_tuple_and_other_function_and_join_a_third() -> Result<(), CompileError> {
let query = "\
match f(x, 2, z) -> (v, w), g(x, x) -> (v), h(z, z)
";
base_test(query, true)?;
Ok(())
}
#[test]
fn parse_match_with_constant_in_production() -> Result<(), CompileError> {
let query = "\
match f(x, 2, z) -> (v, w)
";
base_test(query, true)?;
Ok(())
}
#[test]
fn match_with_code_block() -> Result<(), CompileError> {
let query = "\
match f(x) -> (\"two\"), { \n\
a = x + 1; \n\
print(a) \n\
}
";
let script = base_test(query, true)?;
let map_atom = P::P(Atom {
kind: AtomKind::Map(
0,
vec![NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
}],
MapTarget::Tuple(vec![P::P(Expr {
kind: Literal(StringLiteral(1)),
span: DUMMY_SPAN,
})]),
),
span: DUMMY_SPAN,
});
let assignment_in_block = Stmt::Semi(P::P(Expr {
kind: Assignment(
P::P(Expr {
kind: Variable(1),
span: DUMMY_SPAN,
}),
P::P(Expr {
kind: Binary(
Plus,
P::P(Expr {
kind: Variable(0),
span: DUMMY_SPAN,
}),
P::P(Expr {
kind: Literal(Integer(2)),
span: DUMMY_SPAN,
}),
),
span: DUMMY_SPAN,
}),
),
span: DUMMY_SPAN,
}));
let call_print_in_block = Stmt::Expr(P::P(Expr {
kind: FunCall(
3,
vec![NamedParameter {
name: None,
value: P::P(Expr {
kind: Variable(1),
span: DUMMY_SPAN,
}),
}],
),
span: DUMMY_SPAN,
}));
let block_atom = P::P(Atom {
kind: AtomKind::Block(P::P(ast::Block {
stmts: vec![assignment_in_block, call_print_in_block],
span: DUMMY_SPAN,
})),
span: DUMMY_SPAN,
});
let expected = Match(vec![map_atom, block_atom]);
assert_eq!(1, script.statements.len());
assert_eq!(&expected, script.statements.get(0).unwrap());
Ok(())
}
#[test]
fn assign_to_struct_field() -> Result<(), CompileError> {
let query = "\
alpha.beta = 1;
";
let _script = base_test(query, true)?;
Ok(())
}
#[test]
fn should_parse_expression_for_unbound_variable() -> Result<(), CompileError> {
let query = "\
match f(x-1)
";
let _script = base_test(query, true)?;
Ok(())
}
#[test]
fn should_parse_expression_for_bound_variable_1() -> Result<(), CompileError> {
let query = "\
match f(x-1, x)
";
let _script = base_test(query, true)?;
Ok(())
}
#[test]
fn should_parse_expression_for_bound_variable_2() -> Result<(), CompileError> {
let query = "\
match f(x, x-1)
";
let _script = base_test(query, true)?;
Ok(())
}
#[test]
fn parse_type_definition() -> Result<(), CompileError> {
let type_def = "\
type Activity { \n\
name: String, \n\
id: int \n\
} \n\
";
let mut script = base_test(type_def, true)?;
assert_eq!(1, script.type_definitions.len());
let mut type_def = script.type_definitions.pop().unwrap();
assert_eq!("Activity", type_def.name);
assert_eq!(2, type_def.fields.len());
let second_field = type_def.fields.pop().unwrap();
let first_field = type_def.fields.pop().unwrap();
assert_eq!("name", first_field.name);
assert_eq!(
TypeName {
name: "String".to_string()
},
first_field.type_name
);
assert_eq!("id", second_field.name);
assert_eq!(
TypeName {
name: "int".to_string()
},
second_field.type_name
);
Ok(())
}
#[test]
fn parse_type_definition_trailing_comma() -> Result<(), CompileError> {
let type_def = "\
type Activity { \n\
name: String, \n\
id: int, \n\
} \n\
";
let mut script = base_test(type_def, true)?;
assert_eq!(1, script.type_definitions.len());
let mut type_def = script.type_definitions.pop().unwrap();
assert_eq!("Activity", type_def.name);
assert_eq!(2, type_def.fields.len());
let second_field = type_def.fields.pop().unwrap();
let first_field = type_def.fields.pop().unwrap();
assert_eq!("name", first_field.name);
assert_eq!(
TypeName {
name: "String".to_string()
},
first_field.type_name
);
assert_eq!("id", second_field.name);
assert_eq!(
TypeName {
name: "int".to_string()
},
second_field.type_name
);
Ok(())
}
#[test]
fn parse_empty_type_definition() -> Result<(), CompileError> {
let type_def = "\
type Empty { \n\
} \n\
";
let mut script = base_test(type_def, true)?;
assert_eq!(1, script.type_definitions.len());
let type_def = script.type_definitions.pop().unwrap();
assert_eq!("Empty", type_def.name);
assert_eq!(0, type_def.fields.len());
Ok(())
}
}