478 lines
14 KiB
Rust
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);
|
|
}
|
|
}
|