summaryrefslogtreecommitdiffstats
path: root/crates/rebel-parse/src/grammar/recipe.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/rebel-parse/src/grammar/recipe.rs')
-rw-r--r--crates/rebel-parse/src/grammar/recipe.rs162
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 }
+ }
+}