diff options
Diffstat (limited to 'crates/rebel-parse/src/grammar/recipe.rs')
-rw-r--r-- | crates/rebel-parse/src/grammar/recipe.rs | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/crates/rebel-parse/src/grammar/recipe.rs b/crates/rebel-parse/src/grammar/recipe.rs new file mode 100644 index 0000000..5ae6b8b --- /dev/null +++ b/crates/rebel-parse/src/grammar/recipe.rs @@ -0,0 +1,162 @@ +use crate::ast::*; + +pub use rules::*; + +peg::parser! { + pub grammar rules() for str { + use OpBinary::*; + use OpUnary::*; + + pub rule recipe() -> Recipe<'input> + = _ recipe:recipe_stmt()* { recipe } + + pub rule recipe_stmt() -> RecipeStmt<'input> + = stmt:body_stmt() { + RecipeStmt::BodyStmt(stmt) + } + / "fetch" __ name:ident() _ "{" _ body:body() _ "}" _ { + RecipeStmt::Fetch { name, body } + } + / "task" __ name:ident() _ "(" _ args:argtypes() _ ")" _ "{" _ body:body() _ "}" _ { + RecipeStmt::Task { name, args, body } + } + + pub rule body() -> Body<'input> + = recipe:body_stmt()* { recipe } + + pub rule body_stmt() -> BodyStmt<'input> + = left:typed_expr() _ op:assign_op() _ right:expr() _ ";" _ { + BodyStmt::assign(left, op, right) + } + + rule assign_op() -> Option<OpBinary> + = "+=" { Some(Add) } + / "-=" { Some(Sub) } + / "*=" { Some(Mul) } + / "/=" { Some(Div) } + / "%=" { Some(Rem) } + / "=" { None } + + rule typed_expr() -> TypedExpr<'input> + = expr:expr() typ:tagged(<_ ":" _>, <typ()>)? { TypedExpr { expr, typ } } + + + rule typ() -> Path<'input> + = path() + + pub rule expr() -> Expr<'input> = precedence! { + left:(@) _ "||" _ right:@ { Expr::binary(left, Or, right) } + -- + left:(@) _ "&&" _ right:@ { Expr::binary(left, And, right) } + -- + left:(@) _ "==" _ right:@ { Expr::binary(left, Eq, right) } + left:(@) _ "!=" _ right:@ { Expr::binary(left, Ne, right) } + left:(@) _ "<" _ right:@ { Expr::binary(left, Lt, right) } + left:(@) _ ">" _ right:@ { Expr::binary(left, Gt, right) } + left:(@) _ "<=" _ right:@ { Expr::binary(left, Le, right) } + left:(@) _ ">=" _ right:@ { Expr::binary(left, Ge, right) } + -- + left:(@) _ "+" _ right:@ { Expr::binary(left, Add, right) } + left:(@) _ "-" _ right:@ { Expr::binary(left, Sub, right) } + -- + left:(@) _ "*" _ right:@ { Expr::binary(left, Mul, right) } + left:(@) _ "/" _ right:@ { Expr::binary(left, Div, right) } + left:(@) _ "%" _ right:@ { Expr::binary(left, Rem, right) } + -- + "-" _ expr:@ { Expr::unary(Neg, expr) } + "!" _ expr:@ { Expr::unary(Not, expr) } + -- + expr:@ _ "(" _ args:args() _ ")" { Expr::apply(expr, args) } + expr:@ _ "[" _ index:expr() _ "]" { Expr::index(expr, index) } + -- + expr:@ _ "." _ field:field() { Expr::field(expr, field) } + -- + "(" _ e:expr() _ ")" { Expr::paren(e) } + e:atom() { e } + } + + rule atom() -> Expr<'input> + = lit:literal() { Expr::Literal(lit) } + / path:path() { Expr::Path(path) } + + rule args() -> Vec<Arg<'input>> + = args:delimited(<arg()>, <_ "," _>) { args } + + rule arg() -> Arg<'input> + = expr:expr() { Arg { expr } } + + rule argtypes() -> Vec<ArgType<'input>> + = args:delimited(<argtype()>, <_ "," _>) { args } + + rule argtype() -> ArgType<'input> + = expr:typed_expr() { ArgType { expr } } + + rule literal() -> Literal<'input> + = "true" { Literal::Boolean(true) } + / "false" { Literal::Boolean(false) } + / "0x" s:$((['0'..='9' | 'a'..='f' | 'A'..='F']+) ++ "_") { ? + Literal::integer(s, 16) + } + / "0o" s:$((['0'..='7']+) ++ "_") { ? + Literal::integer(s, 8) + } + / "0b" s:$((['0'..='1']+) ++ "_") { ? + Literal::integer(s, 2) + } + / s:$((['0'..='9']+) ++ "_") { ? + Literal::integer(s, 10) + } + / "\"" s:$(string_char()*) "\"" { Literal::String(s) } + / "r\"" s:$([^'"']*) "\"" { Literal::RawString(s) } + / "```" newline() s:$((!"```" [_])+) "```" { Literal::ScriptString(s) } + / "(" _ ")" { Literal::Unit } + / "(" _ elements:(expr() ** (_ "," _)) (_ ",")? _ ")" { Literal::Tuple(elements) } + / "[" _ elements:delimited(<expr()>, <_ "," _>) _ "]" { Literal::Array(elements) } + / "{" _ entries:delimited(<map_entry()>, <_ "," _>) _ "}" { Literal::Map(entries) } + + rule map_entry() -> MapEntry<'input> + = left:typed_expr() _ "=" _ right:expr() { + MapEntry { left, right } + } + + rule string_char() + = [^'"' | '\\'] + / "\\" [_] + + rule path() -> Path<'input> + = components:ident() ++ (_ "::" _) { Path { components } } + + rule field() -> Ident<'input> + = name:$( + ['a'..='z' | 'A' ..='Z' | '0'..='9' | '_']* + ) { Ident { name } } + + rule ident() -> Ident<'input> + = name:$( + ['a'..='z' | 'A' ..='Z' | '_' ] + ['a'..='z' | 'A' ..='Z' | '0'..='9' | '_']* + ) { Ident { name } } + + /// Mandatory whitespace + rule __ + = ([' ' | '\t'] / quiet!{newline()} / quiet!{comment()})+ + + /// Optional whitespace + rule _ + = quiet!{__?} + + rule comment() + = "//" (!newline() [_])* (newline() / ![_]) + / "/*" (!"*/" [_])* "*/" + + rule newline() + = ['\n' | '\r'] + + rule delimited<T>(expr: rule<T>, delim: rule<()>) -> Vec<T> + = values:(expr() ++ delim()) delim()? { values } + / { Vec::new() } + + rule tagged<T>(tag: rule<()>, value: rule<T>) -> T + = tag() v:value() { v } + } +} |