use crate::ast::*;
use crate::diagnostics::FileId;
grammar(file_id: FileId);
Comma<T>: Vec<T> = {
<v:(<T> ",")*> <e:T?> => match e {
None=> v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
// TODO: Unify this with Comma, and keep track of weather we have a trailing semi (if we want that)
Semi<T>: Vec<T> = {
<v:(<T> ";")*> <e:T?> => match e {
None=> v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
// TODO: decide how many ns in spannnned
Spaned<T>: Spanned<T> = {
<start:@L> <node:T> <end:@R> => Spanned { node, span: Span { start, end, file_id } }
}
// Main Entry point
pub Program: Program<'input> = { <Item*> }
Item: Item<'input> = {
<Spaned<Function>> => Item::Function(<>)
}
Function: Function<'input> = {
"fn" <name:Name> <args:Spaned<Args>> <ret:("->" <Spaned<Type>> )?> <body:Body> => Function{<>}
}
Args = { "(" <Comma<Arg>> ")" }
// TODO: Maybe dont have names things, use a tuple, not a struct.
Arg: Arg<'input> = { <name:Name> ":" <ty:Spaned<Type>> => Arg{<>} }
Type: Type = {
"int" => Type::Int,
"bool" => Type::Bool,
"string" => Type::String,
}
Name = Spaned<RawName>;
RawName = { r"[a-zA-Z][a-zA-Z0-9_]*" }
Body = Spaned<BodyRaw>;
BodyRaw = { "{" <Semi<Stmt>> "}" }
// TODO: Should these be exprs? Should we have stmts?
Stmt: Stmt<'input> = {
<Expr> => Stmt::Expr(<>),
"let" <Name> "=" <Expr> => Stmt::Let(<>),
"print" <Expr> => Stmt::Print(<>),
"return" <Expr> => Stmt::Return(<>),
}
// Rename for convenience
Expr = { <OrExpr> }
BExpr: Box<Expr<'input>> = { <Expr> => Box::new(<>) }
// See http://www.craftinginterpreters.com/appendix-i.html
// https://cs.wmich.edu/~gupta/teaching/cs4850/sumII06/The%20syntax%20of%20C%20in%20Backus-Naur%20form.htm
RawTier<Op, NextTier>: RawExpr<'input> = {
<l:Tier<Op,NextTier>> <o:Spaned<Op>> <r:NextTier> => RawExpr::BinOp(Box::new(l), o, Box::new(r)),
}
Tier<Op,NextTier>: Expr<'input> = {
Spaned<RawTier<Op,NextTier>>,
NextTier
};
OrExpr = Tier<LogicalOr, AndExpr>;
AndExpr = Tier<LogicalAnd, EqualityExpr>;
EqualityExpr = Tier<EqualityOp, ComparisonExpr>;
ComparisonExpr = Tier<ComparisonOp, AdditiveExpr>;
AdditiveExpr = Tier<AdditionOp, MultiplicitveExpr>;
MultiplicitveExpr = Tier<MultiplicationOp, UnaryExpr>;
UnaryExpr = Spaned<UnaryExprRaw>;
UnaryExprRaw: RawExpr<'input> = {
<PostfixExprRaw>,
<o:Spaned<UnaryOp>> <e:UnaryExpr> => RawExpr::UnaryOp(o, Box::new(e)),
}
PostfixExpr = Spaned<PostfixExprRaw>;
PostfixExprRaw: RawExpr<'input> = {
<BaseExprRaw>,
<l:PostfixExpr> "[" <r:Expr> "]" => RawExpr::ArrayAccess(Box::new(l), Box::new(r)),
<l:PostfixExpr> "(" <r:Comma<Expr>> ")" => RawExpr::Call(Box::new(l), r),
<l:PostfixExpr> "." <r: Name> => RawExpr::FieldAccess(Box::new(l), r),
}
// BaseExpr = Spaned<BaseExprRaw>;
BaseExprRaw: RawExpr<'input> = {
// TODO: Else if
"if" <BExpr> <Body> <("else" <Body>)?> => RawExpr::If(<>),
"for" <Name> "in" <BExpr> <Body> => RawExpr::For(<>),
"while" <BExpr> <Body> => RawExpr::While(<>),
<Name> => RawExpr::Var(<>),
<Body> => RawExpr::Block(<>),
// This is a hack where we remove the span of the inner expr
// I'm not sure if this is good
"(" <e:Expr> ")" => e.node,
<Spaned<Literal>> => RawExpr::Literal(<>)
}
Literal: Literal<'input> = {
<s:StringLit> => Literal::String(&s[1..s.len()-1]),
<FloatLit> => Literal::Float(<>.parse().unwrap()),
<IntLit> => Literal::Integer(<>.parse().unwrap()),
<BoolLit> => Literal::Bool(<>)
}
// https://stackoverflow.com/a/2039820
StringLit = { r#""(\\.|[^"\\])*""# }
FloatLit = { r#"[+-]?[0-9]+[.][0-9]*"# }
IntLit = { r"[0-9]+" }
BoolLit: bool = {
"true" => true,
"false" => false
}
LogicalOr: BinOp = { "||" => BinOp::LogicalOr }
LogicalAnd: BinOp = { "&&" => BinOp::LogicalAnd }
Equals: BinOp = { "==" => BinOp::Equals }
NotEquals: BinOp = { "!=" => BinOp::NotEquals }
LessThan: BinOp = { "<" => BinOp::LessThan }
GreaterThan: BinOp = { ">" => BinOp::GreaterThan }
LessThanEquals: BinOp = { "<=" => BinOp::LessThanEquals }
GreaterThanEquals: BinOp = { ">=" => BinOp::GreaterThanEquals }
Plus: BinOp = { "+" => BinOp::Plus }
Minus: BinOp = { "-" => BinOp::Minus }
Times: BinOp = { "*" => BinOp::Times }
Devide: BinOp = { "/" => BinOp::Devide }
// UnaryMinus: UnaryOp = { "-" => UnaryOp::Minus }
// UnaryNot: UnaryOp = { "-" => UnaryOp::Not }
EqualityOp: BinOp = { <Equals>, <NotEquals> }
ComparisonOp: BinOp = { <LessThan>, <GreaterThan>, <LessThanEquals>, <GreaterThanEquals> }
AdditionOp: BinOp = { <Plus>, <Minus> }
MultiplicationOp: BinOp = { <Times>, <Devide> }
// Somehow, doing the thing we did with BinOps gives a lalrpop error about ambiguitys
UnaryOp: UnaryOp = {
"!" => UnaryOp::Not,
"-" => UnaryOp::Minus,
}