ayin/src/parser/parser.rs
2025-12-20 12:52:58 +02:00

478 lines
14 KiB
Rust

use super::types::*;
use crate::ast;
pub fn parse<T>(tokens: &mut Tokens, parser: Parser<T>) -> Result<T, Error> {
if let Some(result) = parser(tokens)?
&& tokens.peek().is_none()
{
Ok(result)
} else {
Err(Error::UnexpectedTokenForParser(
"parser".into(),
tokens.next(),
))
}
}
pub fn parse_program(tokens: &mut Tokens) -> ParseResult<ast::Program> {
Ok(parse_definitions(tokens)?.map(ast::Program))
}
fn parse_definitions(tokens: &mut Tokens) -> ParseResult<Vec<ast::Definition>> {
tokens.many(parse_definition)
}
fn parse_op(tokens: &mut Tokens) -> ParseResult<(ast::Expr, Op)> {
if let Some(LocatedToken {
token: Token::Op(op),
..
}) = tokens.next_if(&Token::Op(Op::Add))
{
if let Some(expr) = parse_expr(tokens)? {
Ok(Some((expr, op)))
} else {
Err(Error::UnexpectedTokenForParser(
"parse_op".into(),
tokens.next(),
))
}
} else {
Ok(None)
}
}
fn parse_set(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
if let Some(_eq) = tokens.next_if(&Token::Equals) {
if let Some(expr) = parse_expr(tokens)? {
Ok(Some(expr))
} else {
Err(Error::UnexpectedTokenForParser(
"expr".into(),
tokens.next(),
))
}
} else {
Ok(None)
}
}
fn parse_dot(tokens: &mut Tokens, expr: ast::Expr) -> Result<ast::Expr, Error> {
if let Some(labels) = tokens.many(parse_label)? {
Ok(labels
.into_iter()
.fold(expr, |acc, label| ast::Expr::Access {
expr: Box::new(acc),
field: label,
}))
} else {
Ok(expr)
}
}
fn parse_definition(tokens: &mut Tokens) -> ParseResult<ast::Definition> {
if let Some(_) = tokens.next_if(&Token::Let) {
let mutable = match tokens.next_if(&Token::Mut) {
Some(_) => true,
_ => false,
};
if let Some(name) = parse_identifier_name(tokens)? {
if let Some(expr) = parse_set(tokens)? {
if let Some(_) = tokens.next_if(&Token::Semicolon) {
Ok(Some(ast::Definition {
mutable,
name,
expr,
}))
} else {
Err(Error::UnexpectedToken {
expected: Token::Semicolon,
got: tokens.next(),
})
}
} else {
Err(Error::UnexpectedToken {
expected: Token::Equals,
got: tokens.next(),
})
}
} else {
Err(Error::UnexpectedToken {
expected: Token::Identifier("".into()),
got: tokens.next(),
})
}
} else {
Ok(None)
}
}
fn parse_expr(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
if let Some(expr1) = parse_simple_expr(tokens)? {
let expr1 = parse_dot(tokens, expr1)?;
if let Some(args) = tokens.between(&Token::OpenParen, &Token::CloseParen, |ts| {
ts.many_sep_by(&Token::Comma, parse_expr)
})? {
Ok(Some(ast::Expr::FunCall {
func: Box::new(expr1),
args,
}))
} else if let Some((expr2, op)) = parse_op(tokens)? {
let op = match op {
Op::Eq => ast::Op::Compare(ast::Cmp::Eq),
Op::NotEq => ast::Op::Compare(ast::Cmp::NotEq),
Op::Gt => ast::Op::Compare(ast::Cmp::Gt),
Op::Gte => ast::Op::Compare(ast::Cmp::Gte),
Op::Lt => ast::Op::Compare(ast::Cmp::Lt),
Op::Lte => ast::Op::Compare(ast::Cmp::Lte),
Op::Add => ast::Op::Calc(ast::Calc::Add),
Op::Sub => ast::Op::Calc(ast::Calc::Sub),
Op::Mul => ast::Op::Calc(ast::Calc::Mul),
Op::Div => ast::Op::Calc(ast::Calc::Div),
Op::Mod => ast::Op::Calc(ast::Calc::Mod),
Op::BinAnd => ast::Op::Calc(ast::Calc::BinAnd),
Op::BinOr => ast::Op::Calc(ast::Calc::BinOr),
Op::And => ast::Op::Bool(ast::BoolOp::And),
Op::Or => ast::Op::Bool(ast::BoolOp::Or),
};
Ok(Some(ast::Expr::Op {
op,
lhs: Box::new(expr1.clone()),
rhs: Box::new(expr2),
}))
} else {
Ok(parse_set(tokens)?
.map(|expr2| ast::Expr::Op {
op: ast::Op::Assign,
lhs: Box::new(expr1.clone()),
rhs: Box::new(expr2),
})
.or(Some(expr1)))
}
} else {
Ok(None)
}
}
fn parse_simple_expr(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
tokens.one_of(vec![
|t| {
if let Some(_) = t.next_if(&Token::Not) {
Ok(parse_expr(t)?.map(|expr| ast::Expr::Not(Box::new(expr))))
} else {
Ok(None)
}
},
parse_number,
parse_identifier,
parse_string,
parse_bool,
parse_fn,
parse_record,
parse_vector,
|t| t.between(&Token::OpenParen, &Token::CloseParen, parse_expr),
])
}
fn parse_fn(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
if let Some(_) = tokens.next_if(&Token::Fn) {
if let Some(args) = parse_args(tokens)? {
if let Some(body) =
tokens.between(&Token::OpenCurly, &Token::CloseCurly, parse_block)?
{
Ok(Some(ast::Expr::Func(Box::new(ast::Fn {
args,
body: ast::Expr::Block(body),
}))))
} else {
Err(Error::UnexpectedTokenForParser(
"fn_body".into(),
tokens.next(),
))
}
} else {
Err(Error::UnexpectedTokenForParser(
"fn_args".into(),
tokens.next(),
))
}
} else {
Ok(None)
}
}
fn parse_block(tokens: &mut Tokens) -> ParseResult<Vec<ast::Statement>> {
tokens.many(parse_statement)
}
fn parse_statement(tokens: &mut Tokens) -> ParseResult<ast::Statement> {
tokens.one_of(vec![
|tokens| {
if let Some(expr) = parse_expr(tokens)?.map(ast::Statement::Expr) {
if let Some(_) = tokens.next_if(&Token::Semicolon) {
Ok(Some(expr))
} else {
println!("parse statement expr");
Err(Error::UnexpectedToken {
expected: Token::Semicolon,
got: tokens.next(),
})
}
} else {
Ok(None)
}
},
|tokens| Ok(parse_definition(tokens)?.map(ast::Statement::Let)),
|tokens| {
if let Some(_) = tokens.next_if(&Token::Return) {
let expr = parse_expr(tokens)?;
if let Some(_) = tokens.next_if(&Token::Semicolon) {
Ok(Some(ast::Statement::Return(expr)))
} else {
Err(Error::UnexpectedToken {
expected: (Token::Return),
got: tokens.next(),
})
}
} else {
Ok(None)
}
},
])
}
fn parse_args(tokens: &mut Tokens) -> ParseResult<Vec<ast::Arg>> {
tokens.between(&Token::OpenParen, &Token::CloseParen, |ts| {
ts.many_sep_by(&Token::Comma, parse_identifier_arg)
})
}
fn parse_record(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
if let Some(vect) = tokens.between(&Token::OpenCurly, &Token::CloseCurly, |tokens| {
tokens.many_sep_by(&Token::Comma, parse_record_field_expr)
})? {
Ok(Some(ast::Expr::Record(vect.into_iter().collect())))
} else {
Ok(None)
}
}
fn parse_vector(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
if let Some(vect) = tokens.between(&Token::OpenBracket, &Token::CloseBracket, |tokens| {
tokens.many_sep_by(&Token::Comma, parse_expr)
})? {
Ok(Some(ast::Expr::Vector(vect)))
} else {
Ok(None)
}
}
fn parse_record_field_expr(tokens: &mut Tokens) -> ParseResult<(ast::Label, ast::Expr)> {
if let Some(label) = parse_label(tokens)? {
if let Some(_) = tokens.next_if(&Token::Colon) {
if let Some(expr) = parse_expr(tokens)? {
Ok(Some((label, expr)))
} else {
Err(Error::UnexpectedTokenForParser(
"parse_record_field_expr_expr".into(),
tokens.next(),
))
}
} else {
Err(Error::UnexpectedToken {
expected: Token::Colon,
got: tokens.next(),
})
}
} else {
Ok(None)
}
}
fn parse_string(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
if let Some(LocatedToken {
token: Token::String(string),
..
}) = tokens.next_if(&Token::String("".into()))
{
Ok(Some(ast::Expr::Value(ast::Value::String(string))))
} else {
Ok(None)
}
}
fn parse_number(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
if let Some(LocatedToken {
token: Token::Number(number),
..
}) = tokens.next_if(&Token::Number(0))
{
Ok(Some(ast::Expr::Value(ast::Value::Int(number.into()))))
} else {
Ok(None)
}
}
fn parse_identifier(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
Ok(parse_identifier_name(tokens)?.map(|name| ast::Expr::Var(name)))
}
fn parse_identifier_arg(tokens: &mut Tokens) -> ParseResult<ast::Arg> {
Ok(parse_identifier_name(tokens)?.map(|name| ast::Arg { name }))
}
fn parse_identifier_name(tokens: &mut Tokens) -> ParseResult<ast::Name> {
Ok(parse_identifier_string(tokens)?.map(|string| ast::Name(string)))
}
fn parse_label(tokens: &mut Tokens) -> ParseResult<ast::Label> {
if let Some(LocatedToken {
token: Token::Label(string),
..
}) = tokens.next_if(&Token::Label("".into()))
{
Ok(Some(ast::Label(string)))
} else {
Ok(None)
}
}
fn parse_identifier_string(tokens: &mut Tokens) -> ParseResult<String> {
if let Some(LocatedToken {
token: Token::Identifier(string),
..
}) = tokens.next_if(&Token::Identifier("".into()))
{
Ok(Some(string))
} else {
Ok(None)
}
}
fn parse_bool(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
tokens.one_of(vec![
|t| {
Ok({
if let Some(_) = t.next_if(&Token::True) {
Some(ast::Expr::Value(ast::Value::Boolean(true)))
} else {
None
}
})
},
|t| {
Ok({
if let Some(_) = t.next_if(&Token::False) {
Some(ast::Expr::Value(ast::Value::Boolean(false)))
} else {
None
}
})
},
])
}
#[cfg(test)]
mod tests {
use super::super::scanner::*;
use super::*;
#[test]
fn number() {
let program = "108".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn negative_number() {
let program = "-108".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn paren_identifier() {
let program = "(hello)".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn record() {
let program = "{ .hello: 17, .hi: \"cool\"}".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn vector_empty() {
let program = "[]".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn vector() {
let program = "[x, 1, 2 ,3]".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn assign() {
let program = "x = [2, { .hello: 7 }]".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn fn_def() {
let program = "fn(a1, boco, c_c) { return 108; }".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn fncall() {
let program = "call(arg1, arg2)".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn paren_fncall() {
let program = "(fn(a1, boco, c_c) { return 108; })()".to_string();
let result = parse(&mut scan(program), parse_expr);
insta::assert_debug_snapshot!(result);
}
#[test]
fn let_number() {
let program = "let x = 108;".to_string();
let result = parse(&mut scan(program), parse_definition);
insta::assert_debug_snapshot!(result);
}
#[test]
fn let_mut_number() {
let program = "let mut x = 108;".to_string();
let result = parse(&mut scan(program), parse_definition);
insta::assert_debug_snapshot!(result);
}
#[test]
fn full_program() {
let program = "
let setup = fn() {
return {
.player: { .position: { .x: 10, .y: 20 }, },
};
};
let update = fn(state, events) {
let new = 100;
state.player.position.x = new;
return state;
};
let draw = fn(frame, state) {
frame.clear(0,0,0);
};
let migrate = fn(state) {
return { .player: { .pos: state.player.position } };
};
"
.to_string();
let result = parse(&mut scan(program), parse_definitions);
insta::assert_debug_snapshot!(result);
}
}