use std::fmt::Display;
use super::Expression;
use super::eval::eval;
use crate::error::{ParseErrorKind, Result};
use crate::primitives::Primitive;
use crate::strings::{StringId, Strings};
use crate::token::{Kind, Operator, Tokens, Value};
pub(crate) mod prec {
pub const INITIAL: u8 = 0;
pub const CONDITIONAL: u8 = 2;
pub const EQUALITY: u8 = 3;
pub const LOGICAL: u8 = 4;
pub const SUM: u8 = 5;
pub const PRODUCT: u8 = 6;
pub const PREFIX: u8 = 8;
pub const CALL: u8 = 10;
pub const SUBCRIPT: u8 = 11;
}
fn get_precedence(op: Operator) -> u8 {
match op {
Operator::Dot | Operator::LBracket => prec::SUBCRIPT,
Operator::LParen => prec::CALL,
Operator::Mul | Operator::Div | Operator::Mod => prec::PRODUCT,
Operator::Plus | Operator::Minus => prec::SUM,
Operator::GreaterThan | Operator::GreaterThanOrEqual | Operator::LessThan | Operator::LessThanOrEqual => {
prec::LOGICAL
}
Operator::EqualEqual | Operator::NotEqual => prec::EQUALITY,
Operator::Or | Operator::And | Operator::Either => prec::CONDITIONAL,
_ => prec::INITIAL,
}
}
#[derive(Debug, Clone)]
pub(crate) enum Expr {
Unary {
op: Operator,
expr: Box<Expr>,
},
Binary {
lhs: Box<Expr>,
rhs: Box<Expr>,
op: Operator,
},
Primitive(Primitive),
Ident(StringId),
Str(StringId),
Call {
fun: Box<Expr>,
args: Vec<Expr>,
},
Array {
lhs: Box<Expr>,
index: Box<Expr>,
},
List(Vec<Expr>),
Map(Vec<(Expr, Expr)>),
}
impl Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expr::Unary { op, expr } => write!(f, "({op}{expr})"),
Expr::Binary { op, lhs, rhs } => write!(f, "({op} {lhs} {rhs})"),
Expr::Primitive(p) => write!(f, "{p}"),
Expr::Ident(sid) => write!(f, "{sid}"),
Expr::Str(sid) => write!(f, "\"{sid}\""),
Expr::Array { lhs, index } => write!(f, "{lhs}[{index}]"),
Expr::List(list) => {
let s = list.iter().map(|e| e.to_string()).collect::<Vec<_>>().join(", ");
write!(f, "[{s}]")
}
Expr::Map(map) => {
let s = map
.iter()
.map(|(k, v)| format!("{k}: {v}"))
.collect::<Vec<_>>()
.join(", ");
write!(f, "{{{s}}}")
}
Expr::Call { fun, args } => {
let s = args.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ");
write!(f, "{fun}({s})")
}
}
}
}
pub(crate) fn parse_expr(tokens: &mut Tokens, strings: &Strings) -> Result<Expression, ParseErrorKind> {
let expr = expr_bp(tokens, 0)?;
eval(expr, strings)
}
fn expr_bp(tokens: &mut Tokens, precedence: u8) -> Result<Expr, ParseErrorKind> {
let mut left = match tokens.next_no_indent() {
Kind::Op(Operator::LBracket) => parse_collection(tokens)?,
Kind::Op(Operator::LCurly) => parse_map(tokens)?,
Kind::Op(Operator::LParen) => {
let left = expr_bp(tokens, prec::INITIAL)?;
assert!(matches!(tokens.next_no_indent(), Kind::Op(Operator::RParen)));
left
}
Kind::Op(op) => Expr::Unary {
op,
expr: Box::new(expr_bp(tokens, prec::PREFIX)?),
},
Kind::Value(value) => match value {
Value::Primitive(val) => Expr::Primitive(val),
Value::Ident(ident) => Expr::Ident(ident),
Value::String(sid) => Expr::Str(sid),
},
Kind::Eof => panic!("unexpected eof"),
_kind => {
return Err(ParseErrorKind::InvalidToken {
expected: "valid token, found statement",
});
}
};
loop {
let Kind::Op(op) = tokens.peek_skip_indent() else {
return Ok(left);
};
let token_prec = get_precedence(op);
if precedence >= token_prec {
break;
}
tokens.consume();
match op {
Operator::LParen => {
left = parse_function(tokens, left)?;
continue;
}
Operator::LBracket => {
left = Expr::Array {
lhs: Box::new(left),
index: Box::new(expr_bp(tokens, prec::INITIAL)?),
};
let next_token = tokens.next_no_indent();
let Kind::Op(Operator::RBracket) = next_token else {
panic!("invalid token");
};
continue;
}
_ => {}
}
let right = expr_bp(tokens, token_prec)?;
left = Expr::Binary {
lhs: Box::new(left),
op,
rhs: Box::new(right),
};
continue;
}
Ok(left)
}
fn parse_function(tokens: &mut Tokens, left: Expr) -> Result<Expr, ParseErrorKind> {
let mut args = vec![];
loop {
match tokens.peek_skip_indent() {
Kind::Op(Operator::Comma) => {
tokens.consume();
continue;
}
Kind::Op(Operator::RParen) => {
tokens.consume();
break;
}
_ => (),
}
args.push(expr_bp(tokens, prec::INITIAL)?);
}
let expr = Expr::Call {
fun: Box::new(left),
args,
};
Ok(expr)
}
fn parse_collection(tokens: &mut Tokens) -> Result<Expr, ParseErrorKind> {
let mut elements = vec![];
loop {
match tokens.peek_skip_indent() {
Kind::Newline => {
tokens.consume();
continue;
}
Kind::Op(Operator::Comma) => {
tokens.consume();
continue;
}
Kind::Op(Operator::RBracket) => {
tokens.consume();
break;
}
_ => (),
}
elements.push(expr_bp(tokens, prec::INITIAL)?);
}
Ok(Expr::List(elements))
}
fn parse_map(tokens: &mut Tokens) -> Result<Expr, ParseErrorKind> {
let mut elements = vec![];
loop {
match tokens.peek_skip_indent() {
Kind::Newline => {
tokens.consume();
continue;
}
Kind::Op(Operator::Comma) => {
tokens.consume();
continue;
}
Kind::Op(Operator::RCurly) => {
tokens.consume();
break;
}
_ => (),
}
let key = expr_bp(tokens, prec::INITIAL)?;
match tokens.peek_skip_indent() {
Kind::Op(Operator::Colon) => tokens.consume(),
_ => break,
}
let value = expr_bp(tokens, prec::INITIAL)?;
elements.push((key, value));
}
Ok(Expr::Map(elements))
}
#[cfg(test)]
mod test {
use super::*;
use crate::error::Result;
use crate::lexer::Lexer;
use crate::strings::Strings;
fn parse_src(input: &str) -> Expr {
let mut strings = Strings::new();
let lexer = Lexer::new(input, &mut strings);
let tokens = lexer.collect::<Result<_>>().unwrap();
let mut tokens = Tokens::new(tokens, input.len());
expr_bp(&mut tokens, prec::INITIAL).unwrap()
}
fn parse(input: &str) -> String {
let expression = parse_src(input);
expression.to_string()
}
#[test]
fn add_sub() {
let input = "1 + 2";
assert_eq!(parse(input), "(+ 1 2)");
let input = "1 - 2";
assert_eq!(parse(input), "(- 1 2)");
}
#[test]
fn mul_div() {
let input = "5 + 1 * 2";
assert_eq!(parse(input), "(+ 5 (* 1 2))");
let input = "5 - 1 / 2";
assert_eq!(parse(input), "(- 5 (/ 1 2))");
}
#[test]
fn brackets() {
let input = "(5 + 1) * 2";
assert_eq!(parse(input), "(* (+ 5 1) 2)");
}
#[test]
fn function() {
let input = "fun(1, a + 2 * 3, 3)";
assert_eq!(parse(input), "<sid 1>(1, (+ <sid 2> (* 2 3)), 3)");
}
#[test]
fn function_no_args() {
let input = "f()";
assert_eq!(parse(input), "<sid 1>()");
}
#[test]
fn array_index() {
let input = "array[0][1]";
assert_eq!(parse(input), "<sid 1>[0][1]");
}
#[test]
fn map_lookup() {
let input = "map['key']";
assert_eq!(parse(input), "<sid 1>[\"<sid 2>\"]");
}
#[test]
fn dot_lookup() {
let input = "a.b.c";
assert_eq!(parse(input), "(. (. <sid 1> <sid 2>) <sid 3>)");
}
#[test]
fn modulo() {
let input = "5 + 1 % 2";
assert_eq!(parse(input), "(+ 5 (% 1 2))");
}
#[test]
fn list() {
let input = "[1, 2, a, 4]";
assert_eq!(parse(input), "[1, 2, <sid 1>, 4]");
}
#[test]
fn nested_list() {
let input = "[1, [2, 3, [4, 5]]]";
assert_eq!(parse(input), "[1, [2, 3, [4, 5]]]");
}
#[test]
fn equality() {
let input = "1 == 2";
assert_eq!(parse(input), "(== 1 2)");
}
#[test]
fn map() {
let input = "{a: 1, b: c}";
assert_eq!(parse(input), "{<sid 1>: 1, <sid 2>: <sid 3>}");
}
#[test]
fn and() {
let input = "1 == 2 && 3 == 4";
assert_eq!(parse(input), "(&& (== 1 2) (== 3 4))");
}
#[test]
fn not() {
let input = "1 != 2 && 3 != 4";
assert_eq!(parse(input), "(&& (!= 1 2) (!= 3 4))");
}
#[test]
fn either() {
let input = "a ? b ? c";
let actual = parse(input);
assert_eq!(actual, "(? (? <sid 1> <sid 2>) <sid 3>)");
}
}