// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: MIT
use crate::ast::{CascadeString, Policy, Declaration, Expression, Statement, TypeDecl, FuncDecl, Argument, Annotation, LetBinding, Virtualable, FuncCall, DeclaredArgument};
use lalrpop_util::ErrorRecovery;
grammar<'err>(errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, &'static str>>);
// http://lalrpop.github.io/lalrpop/tutorial/006_macros.html
Comma<T>: Vec<T> = {
<mut v:(<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
};
pub Policy: Box<Policy> = {
Expr+ => Box::new(Policy::new(<>)),
}
Annotated<T>: T = {
<a:Ann> <mut t:Annotated<T>> => {
t.add_annotation(a);
t
},
T
}
pub Expr: Expression = {
Annotated<BaseExpr>,
// On error, report and fast forward to the next expression
! => { errors.push(<>); Expression::Error },
}
BaseExpr: Expression = {
// TODO: other keywords beside virtual?
<v: "virtual"?> <mut d: Decl> => {
if v.is_some() {
d.set_virtual();
}
Expression::Decl(d)
},
<Stmt> => Expression::Stmt(<>),
};
Decl: Declaration = {
TypeDecl => Declaration::Type(<>),
FuncDecl => Declaration::Func(<>),
}
TypeDecl: Box<TypeDecl> = {
<dr:BuiltInType> <n:NameDecl> <i:InheritList?> "{" <mut v:Expr*> "}" => {
let inherits = match i {
None => vec![dr],
Some(mut i) => {
i.push(dr);
i
}
};
v.iter_mut().for_each(|e| e.set_class_name_if_decl(n.clone()));
Box::new(TypeDecl::new(n, inherits, v))
},
}
InheritList: Vec<CascadeString> = {
"inherits" <Comma<Symbol>>,
}
BuiltInType: CascadeString = {
<start: @L> <s: "domain"> <end: @R> => CascadeString::new(s.to_string(), start..end),
<start: @L> <s: "resource"> <end: @R> => CascadeString::new(s.to_string(), start..end),
}
FuncDecl: Box<FuncDecl> = {
"fn" <n: NameDecl> "(" <a: Comma<FuncDeclArg>> ")" "{" <b: Stmt*> "}" => Box::new(FuncDecl::new(n, a, b)),
}
FuncDeclArg: DeclaredArgument = {
<t: Symbol> <n: NameDecl> <v: DefaultArg?> => DeclaredArgument { param_type: t, is_list_param: false, name: n, default: v },
"[" <t: Symbol> "]" <n: NameDecl> <v: DefaultArg?> => DeclaredArgument { param_type: t, is_list_param: false, name: n, default: v },
}
#[inline]
DefaultArg: Argument = {
"=" <Arg> => <>
}
Stmt: Statement = {
<StmtBody> ";",
IfBlock => Statement::IfBlock, // TODO
}
StmtBody: Statement = {
<c:Symbol> "." <n:Symbol> "(" <a:Comma<Arg>> ")" => Statement::Call(Box::new(FuncCall::new(Some(c), n, a))),
<n:Symbol> "(" <a:Comma<Arg>> ")" => Statement::Call(Box::new(FuncCall::new(None, n, a))),
"let" <n:Symbol> "=" <a:Arg> => Statement::LetBinding(Box::new(LetBinding::new(n, a))),
}
Ann: Annotation = {
"@" <s:NameDecl> "(" <a:Comma<Arg>> ")" => Annotation::new(s).set_arguments(a),
"@" <s:NameDecl> => Annotation::new(s),
}
pub NameDecl: CascadeString = {
// Naming rules:
// * must start with a letter
// * must not end with an underscore
// * must not contain consecutive underscores
// * can contain letters, digits and underscores
<start: @L> <s: r"[a-zA-Z](_?([0-9a-zA-Z]+_)*[0-9a-zA-Z]+)?"> <end: @R> => CascadeString::new(s.to_string(), start..end),
}
Symbol: CascadeString = {
// Less constrained than NameDecl and accepts '-', which enables to use synthetic resources.
<start: @L> <s: r"[a-zA-Z]+-[0-9a-zA-Z_-]*[0-9a-zA-Z]"> <end: @R> => CascadeString::new(s.to_string(), start..end),
NameDecl,
BuiltInType
}
List: Vec<CascadeString> = {
"[" <Symbol+> "]"
}
// TODO: Define boolean struct
BooleanExpr: () = {
Symbol,
BooleanExpr "&&" Symbol,
BooleanExpr "||" Symbol,
"(" BooleanExpr ")",
}
// TODO: Define if block struct
IfBlock: () = {
"if" "(" BooleanExpr ")" "{" <Stmt+> "}" ElseBlock?,
}
#[inline]
ElseBlock: () = {
"else" "{" <Stmt+> "}"
}
Arg: Argument = {
Symbol => Argument::Var(<>),
<s:Symbol> "=" <a: Arg> => Argument::Named(s, Box::new(a)),
List => Argument::List(<>),
Quoted_String => Argument::Quote(<>),
}
Quoted_String: CascadeString = {
<start: @L> <s: r#""[^"]*""#> <end: @R> => CascadeString::new(s.to_string(), start..end),
}
// lexing precedence
match {
r"\s*" => { },
r"//[^\n\r]*[\n\r]*" => { },
} else {
_
}