use sylt_common::error::Error;
use crate::statement::block_statement;
use super::*;
#[derive(Debug, Clone)]
pub enum ExpressionKind {
Get(Assignable),
TypeConstant(Type),
Add(Box<Expression>, Box<Expression>),
Sub(Box<Expression>, Box<Expression>),
Mul(Box<Expression>, Box<Expression>),
Div(Box<Expression>, Box<Expression>),
Neg(Box<Expression>),
Is(Box<Expression>, Box<Expression>),
Eq(Box<Expression>, Box<Expression>),
Neq(Box<Expression>, Box<Expression>),
Gt(Box<Expression>, Box<Expression>),
Gteq(Box<Expression>, Box<Expression>),
Lt(Box<Expression>, Box<Expression>),
Lteq(Box<Expression>, Box<Expression>),
AssertEq(Box<Expression>, Box<Expression>),
In(Box<Expression>, Box<Expression>),
And(Box<Expression>, Box<Expression>),
Or(Box<Expression>, Box<Expression>),
Not(Box<Expression>),
IfExpression {
condition: Box<Expression>,
pass: Box<Expression>,
fail: Box<Expression>,
},
Duplicate(Box<Expression>),
IfShort {
condition: Box<Expression>,
fail: Box<Expression>,
},
Function {
name: String,
params: Vec<(Identifier, Type)>,
ret: Type,
body: Box<Statement>,
},
Instance {
blob: Assignable,
fields: Vec<(String, Expression)>, },
Tuple(Vec<Expression>),
List(Vec<Expression>),
Set(Vec<Expression>),
Dict(Vec<Expression>),
Float(f64),
Int(i64),
Str(String),
Bool(bool),
Nil,
}
#[derive(Debug, Clone)]
pub struct Expression {
pub span: Span,
pub kind: ExpressionKind,
}
fn function<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
use RuntimeType::Void;
use TypeKind::Resolved;
let span = ctx.span();
let mut ctx = expect!(ctx, T::Fn, "Expected 'fn' for function expression");
let mut params = Vec::new();
let ret = loop {
match ctx.token() {
T::Identifier(name) => {
let ident = Identifier {
span: ctx.span(),
name: name.clone(),
};
ctx = expect!(ctx.skip(1), T::Colon, "Expected ':' after parameter name");
let (_ctx, param) = parse_type(ctx)?;
ctx = _ctx;
params.push((ident, param));
ctx = if matches!(ctx.token(), T::Comma | T::Arrow | T::LeftBrace) {
ctx.skip_if(T::Comma)
} else {
raise_syntax_error!(ctx, "Expected ',' '{{' or '->' after type parameter")
};
}
T::Arrow => {
ctx = ctx.skip(1);
break if let Ok((_ctx, ret)) = parse_type(ctx) {
ctx = _ctx; ret
} else {
Type {
span: ctx.span(),
kind: Resolved(Void),
}
};
}
T::LeftBrace => {
break Type {
span: ctx.span(),
kind: Resolved(Void),
};
}
t => {
raise_syntax_error!(ctx, "Didn't expect '{:?}' in function", t);
}
}
};
let (ctx, mut statement) = block_statement(ctx)?;
let statements = if let StatementKind::Block { statements } = &mut statement.kind {
statements
} else {
unreachable!("Function blocks should only be blocks");
};
if !matches!(ret.kind, Resolved(Void)) {
let last_statement = statements.pop();
if let Some(Statement {
span,
kind: StatementKind::StatementExpression { value },
}) = last_statement
{
statements.push(Statement {
span,
kind: StatementKind::Ret { value },
});
} else if let Some(statement) = last_statement {
statements.push(statement);
}
}
use ExpressionKind::Function;
let function = Function {
name: "lambda".into(),
params,
ret,
body: Box::new(statement),
};
Ok((
ctx,
Expression {
span,
kind: function,
},
))
}
fn parse_precedence<'t>(ctx: Context<'t>, prec: Prec) -> ParseResult<'t, Expression> {
let (mut ctx, mut expr) = prefix(ctx)?;
while prec <= precedence(ctx.token()) {
if let Ok((_ctx, _expr)) = infix(ctx, &expr) {
ctx = _ctx;
expr = _expr;
} else {
break;
}
}
Ok((ctx, expr))
}
#[rustfmt::skip]
fn precedence(token: &T) -> Prec {
use Prec;
match token {
T::LeftBracket => Prec::Index,
T::Star | T::Slash => Prec::Factor,
T::Minus | T::Plus => Prec::Term,
T::EqualEqual
| T::Greater
| T::GreaterEqual
| T::Less
| T::LessEqual
| T::NotEqual => Prec::Comp,
T::And => Prec::BoolAnd,
T::Or => Prec::BoolOr,
T::Is => Prec::Index,
T::In => Prec::Index,
T::AssertEqual => Prec::Assert,
T::Arrow => Prec::Arrow,
_ => Prec::No,
}
}
fn value<'t>(ctx: Context<'t>) -> Result<(Context<'t>, Expression), (Context<'t>, Vec<Error>)> {
use ExpressionKind::*;
let (token, span, ctx) = ctx.eat();
let kind = match token.clone() {
T::Float(f) => Float(f),
T::Int(i) => Int(i),
T::Bool(b) => Bool(b),
T::Nil => Nil,
T::String(s) => Str(s),
t => {
raise_syntax_error!(ctx, "Cannot parse value, '{:?}' is not a valid value", t);
}
};
Ok((ctx, Expression { span, kind }))
}
fn prefix<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
use ExpressionKind::{Get, TypeConstant};
match ctx.token() {
T::LeftParen => grouping_or_tuple(ctx),
T::LeftBracket => list(ctx),
T::LeftBrace => set_or_dict(ctx),
T::Colon => {
let span = ctx.span();
let (ctx, ty) = parse_type(ctx.skip(1))?;
Ok((
ctx,
Expression {
span,
kind: TypeConstant(ty),
},
))
}
T::Float(_) | T::Int(_) | T::Bool(_) | T::String(_) | T::Nil => value(ctx),
T::Minus | T::Bang => unary(ctx),
T::Identifier(_) => {
let span = ctx.span();
match (blob(ctx), assignable(ctx)) {
(Ok(result), _) => Ok(result),
(_, Ok((ctx, assign))) => Ok((
ctx,
Expression {
span,
kind: Get(assign),
},
)),
(Err((ctx, _)), Err(_)) => {
raise_syntax_error!(ctx, "Neither a blob instantiation or an identifier");
}
}
}
t => {
raise_syntax_error!(ctx, "No valid expression starts with '{:?}'", t);
}
}
}
fn unary<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
use ExpressionKind::{Neg, Not};
let (op, span, ctx) = ctx.eat();
let (ctx, expr) = parse_precedence(ctx, Prec::Factor)?;
let expr = Box::new(expr);
let kind = match op {
T::Minus => Neg(expr),
T::Bang => Not(expr),
_ => {
raise_syntax_error!(ctx, "Invalid unary operator");
}
};
Ok((ctx, Expression { span, kind }))
}
fn if_short<'t>(ctx: Context<'t>, lhs: &Expression) -> ParseResult<'t, Expression> {
use ExpressionKind::*;
let span = ctx.span();
let ctx = expect!(ctx, T::If, "Expected 'if' at start of if-expression");
let lhs = Expression {
span: lhs.span,
kind: Duplicate(Box::new(lhs.clone())),
};
let (ctx, condition) = infix(ctx, &lhs)?;
let ctx = expect!(
ctx,
T::Else,
"Expected 'else' after short if-expression condition"
);
let (ctx, rhs) = parse_precedence(ctx, Prec::No)?;
let condition = Box::new(condition.clone());
let fail = Box::new(rhs);
Ok((
ctx,
Expression {
span,
kind: IfShort {
condition,
fail,
},
},
))
}
fn if_expression<'t>(ctx: Context<'t>, lhs: &Expression) -> ParseResult<'t, Expression> {
let span = ctx.span();
let ctx = expect!(ctx, T::If, "Expected 'if' at start of if-expression");
use ExpressionKind::*;
let (ctx, condition) = parse_precedence(ctx, Prec::No)?;
let ctx = expect!(
ctx,
T::Else,
"Expected 'else' after if-expression condition"
);
let (ctx, rhs) = parse_precedence(ctx, Prec::No)?;
let condition = Box::new(condition.clone());
let pass = Box::new(lhs.clone());
let fail = Box::new(rhs);
Ok((
ctx,
Expression {
span,
kind: IfExpression {
condition,
pass,
fail,
},
},
))
}
fn arrow_call<'t>(ctx: Context<'t>, lhs: &Expression) -> ParseResult<'t, Expression> {
let ctx = expect!(ctx, T::Arrow, "Expected '->' in arrow function call");
let (ctx, rhs) = expression(ctx)?;
use ExpressionKind::*;
use AssignableKind::{Call, ArrowCall};
fn prepend_expresion<'t>(ctx: Context<'t>, lhs: Expression, rhs: Expression) -> ParseResult<'t, Expression> {
let span = ctx.span();
let kind = match rhs.kind {
Get(Assignable {
kind: Call(callee, args),
..
}) => Get(Assignable {
kind: ArrowCall(Box::new(lhs), callee, args),
span: rhs.span,
}),
Get(Assignable {
kind: ArrowCall(pre, callee, args),
..
}) => {
let (_, pre) = prepend_expresion(ctx, lhs, *pre)?;
Get(Assignable {
kind: ArrowCall(Box::new(pre), callee, args),
span: rhs.span,
})
}
_ => { raise_syntax_error!(ctx, "Expected a call-expression after '->'"); }
};
Ok((ctx, Expression { span, kind }))
}
prepend_expresion(ctx, lhs.clone(), rhs)
}
fn infix<'t>(ctx: Context<'t>, lhs: &Expression) -> ParseResult<'t, Expression> {
use ExpressionKind::*;
match (ctx.token(), precedence(ctx.skip(1).token())) {
(T::If, Prec::No) => {
return if_expression(ctx, lhs);
}
(T::If, _) => {
return if_short(ctx, lhs);
}
(T::Arrow, _) => {
return arrow_call(ctx, lhs);
}
_ => {}
}
let (op, span, ctx) = ctx.eat();
match op {
T::Plus
| T::Minus
| T::Star
| T::Slash
| T::EqualEqual
| T::NotEqual
| T::Greater
| T::GreaterEqual
| T::Less
| T::LessEqual
| T::Is
| T::And
| T::Or
| T::AssertEqual
| T::In
=> {}
_ => {
return Err((ctx, Vec::new()));
}
};
let (ctx, rhs) = parse_precedence(ctx, precedence(op).next())?;
let lhs = Box::new(lhs.clone());
let rhs = Box::new(rhs);
let kind = match op {
T::Plus => Add(lhs, rhs),
T::Minus => Sub(lhs, rhs),
T::Star => Mul(lhs, rhs),
T::Slash => Div(lhs, rhs),
T::EqualEqual => Eq(lhs, rhs),
T::NotEqual => Neq(lhs, rhs),
T::Greater => Gt(lhs, rhs),
T::GreaterEqual => Gteq(lhs, rhs),
T::Less => Lt(lhs, rhs),
T::LessEqual => Lteq(lhs, rhs),
T::Is => Is(lhs, rhs),
T::And => And(lhs, rhs),
T::Or => Or(lhs, rhs),
T::AssertEqual => AssertEq(lhs, rhs),
T::In => In(lhs, rhs),
_ => {
unreachable!();
}
};
Ok((ctx, Expression { span, kind }))
}
fn grouping_or_tuple<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
let span = ctx.span();
let ctx = expect!(ctx, T::LeftParen, "Expected '('");
let (mut ctx, skip_newlines) = ctx.push_skip_newlines(true);
let mut exprs = Vec::new();
let mut is_tuple = matches!(ctx.token(), T::Comma | T::RightParen);
loop {
ctx = ctx.skip_if(T::Comma);
match ctx.token() {
T::EOF | T::RightParen => {
break;
}
_ => {
let (_ctx, expr) = expression(ctx)?;
exprs.push(expr);
ctx = _ctx;
is_tuple |= matches!(ctx.token(), T::Comma);
}
}
}
let ctx = ctx.pop_skip_newlines(skip_newlines);
let ctx = expect!(ctx, T::RightParen, "Expected ')'");
use ExpressionKind::Tuple;
let result = if is_tuple {
Expression {
span,
kind: Tuple(exprs),
}
} else {
exprs.remove(0)
};
Ok((ctx, result))
}
fn blob<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
let span = ctx.span();
let (ctx, blob) = assignable(ctx)?;
let ctx = expect!(ctx, T::LeftBrace, "Expected '{{' after blob name");
let (mut ctx, skip_newlines) = ctx.push_skip_newlines(true);
let mut fields = Vec::new();
loop {
match ctx.token() {
T::RightBrace | T::EOF => {
break;
}
T::Identifier(name) => {
let name = name.clone();
ctx = expect!(ctx.skip(1), T::Colon, "Expected ':' after field name");
let (_ctx, expr) = expression(ctx)?;
ctx = _ctx;
if !matches!(ctx.token(), T::Comma | T::RightBrace) {
raise_syntax_error!(
ctx,
"Expected a field delimiter ',' - but got {:?}",
ctx.token()
);
}
ctx = ctx.skip_if(T::Comma);
fields.push((name, expr));
}
t => {
raise_syntax_error!(ctx, "Unexpected token ('{:?}') in blob initalizer", t);
}
}
}
let ctx = ctx.pop_skip_newlines(skip_newlines);
let ctx = expect!(ctx, T::RightBrace, "Expected '}}' after blob initalizer");
if matches!(ctx.token(), T::Else) {
raise_syntax_error!(ctx, "Parsed a blob instance not an if-statement");
}
use ExpressionKind::Instance;
Ok((
ctx,
Expression {
span,
kind: Instance { blob, fields },
},
))
}
fn list<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
let span = ctx.span();
let ctx = expect!(ctx, T::LeftBracket, "Expected '['");
let (mut ctx, skip_newlines) = ctx.push_skip_newlines(true);
let mut exprs = Vec::new();
loop {
match ctx.token() {
T::EOF | T::RightBracket => {
break;
}
_ => {
let (_ctx, expr) = expression(ctx)?;
exprs.push(expr);
ctx = _ctx; ctx = ctx.skip_if(T::Comma);
}
}
}
let ctx = ctx.pop_skip_newlines(skip_newlines);
let ctx = expect!(ctx, T::RightBracket, "Expected ']'");
use ExpressionKind::List;
Ok((
ctx,
Expression {
span,
kind: List(exprs),
},
))
}
fn set_or_dict<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
let span = ctx.span();
let ctx = expect!(ctx, T::LeftBrace, "Expected '{{'");
let (mut ctx, skip_newlines) = ctx.push_skip_newlines(true);
let mut exprs = Vec::new();
let mut is_dict = None;
loop {
match ctx.token() {
T::EOF | T::RightBrace => {
break;
}
T::Colon => {
if let Some(is_dict) = is_dict {
raise_syntax_error!(
ctx,
"Empty dict pair is invalid in a {}",
if is_dict { "dict" } else { "set" }
);
}
is_dict = Some(true);
ctx = ctx.skip(1);
}
_ => {
let (_ctx, expr) = expression(ctx)?;
ctx = _ctx; exprs.push(expr);
if *is_dict.get_or_insert_with(|| matches!(ctx.token(), T::Colon)) {
ctx = expect!(ctx, T::Colon, "Expected ':' for dict pair");
let (_ctx, expr) = expression(ctx)?;
ctx = _ctx; exprs.push(expr);
}
ctx = ctx.skip_if(T::Comma);
}
}
}
let ctx = ctx.pop_skip_newlines(skip_newlines);
let ctx = expect!(ctx, T::RightBrace, "Expected '}}'");
use ExpressionKind::{Dict, Set};
let kind = if is_dict.unwrap_or(false) {
Dict(exprs)
} else {
Set(exprs)
};
Ok((ctx, Expression { span, kind }))
}
pub fn expression<'t>(ctx: Context<'t>) -> ParseResult<'t, Expression> {
match ctx.token() {
T::Fn => function(ctx),
_ => parse_precedence(ctx, Prec::No),
}
}
#[cfg(test)]
mod test {
use super::ExpressionKind::*;
use crate::expression;
use crate::test;
use crate::Assignable;
use crate::AssignableKind::*;
test!(expression, value: "0" => Int(0));
test!(expression, add: "0 + 1.0" => Add(_, _));
test!(expression, mul: "\"abc\" * \"abc\"" => Mul(_, _));
test!(expression, ident: "a" => Get(Assignable { kind: Read(_), .. }));
test!(expression, access: "a.b" => Get(Assignable { kind: Access(_, _), .. }));
test!(expression, index_ident: "a[a]" => Get(Assignable { kind: Index(_, _), .. }));
test!(expression, index_expr: "a[1 + 2 + 3]" => Get(Assignable { kind: Index(_, _), .. }));
test!(expression, grouping: "(0 * 0) + 1" => Add(_, _));
test!(expression, grouping_one: "(0)" => Int(0));
test!(expression, tuple: "(0, 0)" => Tuple(_));
test!(expression, tuple_one: "(0,)" => Tuple(_));
test!(expression, tuple_empty: "()" => Tuple(_));
test!(expression, list: "[0, 0]" => List(_));
test!(expression, set: "{1, 1}" => Set(_));
test!(expression, dict: "{1: 1}" => Dict(_));
test!(expression, zero_set: "{}" => Set(_));
test!(expression, zero_dict: "{:}" => Dict(_));
test!(expression, in_list: "a in [1, 2, 3]" => In(_, _));
test!(expression, in_set: "2 in {1, 1, 2}" => In(_, _));
test!(expression, in_grouping: "1 + 2 in b" => Add(_, _));
test!(expression, in_grouping_paren: "(1 + 2) in b" => In(_, _));
test!(expression, call_simple_paren: "a()" => Get(_));
test!(expression, call_call: "a()()" => Get(_));
test!(expression, call_simple_bang: "a'" => Get(_));
test!(expression, call_chaining_paren: "a().b" => Get(_));
test!(expression, call_chaining_bang: "a'.b" => Get(_));
test!(expression, call_args_paren: "a(1, 2, 3)" => Get(_));
test!(expression, call_args_bang: "a' 1, 2, 3" => Get(_));
test!(expression, call_args_chaining_paren: "a(1, 2, 3).b" => Get(_));
test!(expression, call_args_chaining_paren_trailing: "a(1, 2, 3,).b" => Get(_));
test!(expression, assignable_index: "a[0]" => Get(_));
test!(expression, assignable_index_twice: "a[0][1]" => Get(_));
test!(expression, assignable_mixed: "a[0]()" => Get(_));
test!(expression, assignable_mixed_many: "a()[0]()[1]()()()[2][3]" => Get(_));
test!(expression, call_args_chaining_bang: "a' 1, 2, 3 .b" => Get(_));
test!(expression, call_args_chaining_bang_trailing: "a' 1, 2, 3, .b" => Get(_));
test!(expression, call_arrow: "1 + 0 -> a' 2, 3" => Add(_, _));
test!(expression, call_arrow_grouping: "(1 + 0) -> a' 2, 3" => Get(_));
test!(expression, instance: "A { a: 1 + 1, b: nil }" => Instance { .. });
test!(expression, instance_more: "A { a: 2, \n c: 2 }" => Instance { .. });
test!(expression, instance_empty: "A {}" => Instance { .. });
test!(expression, simple: "fn -> {}" => _);
test!(expression, argument: "fn a: int -> int { ret a + 1 }" => _);
test!(expression, booleans: "true && false || !false" => _);
test!(expression, bool_and: "true && a" => _);
test!(expression, bool_or: "a || false" => _);
test!(expression, bool_neg: "!a" => _);
test!(expression, bool_neg_multiple: "!a && b" => _);
test!(expression, bool_neg_multiple_rev: "a && !b" => _);
test!(expression, cmp_is: "a is B" => _);
test!(expression, cmp_eq: "a == b" => _);
test!(expression, cmp_neq: "a != b" => _);
test!(expression, cmp_leq: "a <= b" => _);
test!(expression, cmp_geq: "a >= b" => _);
test!(expression, cmp_gt: "a > b" => _);
test!(expression, cmp_lt: "a < b" => _);
test!(expression, neg: "-a" => _);
test!(expression, expr: "-a + b < 3 * true && false / 2" => _);
test!(expression, type_expr_int: ":int" => _);
test!(expression, type_expr_void: ":void" => _);
test!(expression, type_expr_float: ":float" => _);
test!(expression, type_expr_str: ":str" => _);
test!(expression, type_expr_custom: ":A" => _);
test!(expression, type_expr_custom_chaining: ":A.b.C" => _);
test!(expression, void_simple: "fn {}" => _);
test!(expression, void_argument: "fn a: int { ret a + 1 }" => _);
test!(expression, if_expr: "a if b else c" => IfExpression { .. });
test!(expression, if_expr_more: "1 + 1 + 1 if b else 2 + 2 + 2" => IfExpression { .. });
}