1use peg::parser;23mod expr;4pub use expr::*;56enum Suffix {7 String(String),8 Expression(Expr),9 Apply(expr::Args),10}1112parser! {13 grammar jsonnet_parser() for str {14 rule delimiter() = quiet!{__() "," __()} / expected!("<elements delimiter>")15 rule _() = quiet!{[' ' | '\n' | '\t']+} / expected!("<whitespace>")16 rule __() = quiet!{[' ' | '\n' | '\t']*}17 rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().nth(0).unwrap()}18 rule digit() -> char = d:$(['0'..='9']) {d.chars().nth(0).unwrap()}19 rule int() -> u32 = a:$(digit()+) { a.parse().unwrap() }20 rule number() -> f64 = quiet!{a:$((['-'|'+'])? int() ("." int())? (['e'|'E'] (s:['+'|'-'])? int())?) { a.parse().unwrap() }} / expected!("<number>")21 rule id() -> String = quiet!{ !("local" / "super" / "self" / "true" / "false" / "null" / "$" / "if" / "then" / "else") s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")2223 pub rule positional_param() -> expr::Param = name:id() {expr::Param::Positional(name)}24 pub rule named_param() -> expr::Param = name:id() __() "=" __() expr:boxed_expr() {expr::Param::Named(name, expr)}25 pub rule params() -> expr::Params26 = positionals:(positional_param() ** delimiter()) delimiter() named:(named_param() ** delimiter()) {27 expr::Params([&positionals[..], &named[..]].concat())28 }29 / named:(named_param() ** delimiter()) {expr::Params(named)}30 / positionals:(positional_param() ** delimiter()) {expr::Params(positionals)}31 / {expr::Params(Vec::new())}3233 pub rule positional_arg() -> expr::Arg = quiet!{name:boxed_expr() {expr::Arg::Positional(name)}}/expected!("<positional arg>")34 pub rule named_arg() -> expr::Arg = quiet!{name:id() __() "=" __() expr:boxed_expr() {expr::Arg::Named(name, expr)}}/expected!("<named arg>")35 pub rule args() -> expr::Args36 = positionals:(positional_arg() ** delimiter()) delimiter() named:(named_arg() ** delimiter()) {37 expr::Args([&positionals[..], &named[..]].concat())38 }39 / named:(named_arg() ** delimiter()) {expr::Args(named)}40 / positionals:(positional_arg() ** delimiter()) {expr::Args(positionals)}41 / {expr::Args(Vec::new())}4243 pub rule bind() -> expr::Bind44 = name:id() __() "=" __() expr:boxed_expr() {expr::Bind::Value(name, expr)}45 / name:id() __() "(" __() params:params() __() ")" __() "=" __() expr:boxed_expr() {expr::Bind::Function(name, params, expr)}46 pub rule assertion() -> expr::AssertStmt = "assert" _() cond:boxed_expr() msg:(__() ":" __() e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }47 pub rule string() -> String48 = "\"" str:$((!['"'][_])+) "\"" {str.to_owned()}49 / "'" str:$((!['\''][_])+) "'" {str.to_owned()}50 pub rule field_name() -> expr::FieldName51 = name:id() {expr::FieldName::Fixed(name)}52 / name:string() {expr::FieldName::Fixed(name)}53 / "[" __() expr:boxed_expr() __() "]" {expr::FieldName::Dyn(expr)}54 pub rule visibility() -> expr::Visibility55 = ":::" {expr::Visibility::Unhide}56 / "::" {expr::Visibility::Hidden}57 / ":" {expr::Visibility::Normal}58 pub rule field() -> expr::FieldMember59 = name:field_name() __() plus:"+"? __() visibility:visibility() __() value:expr() {expr::FieldMember::Value{60 name,61 plus: plus.is_some(),62 visibility,63 value,64 }}65 / name:field_name() __() "(" __() params:params() __() ")" __() visibility:visibility() __() value:expr() {expr::FieldMember::Function{66 name,67 params,68 visibility,69 value,70 }}71 pub rule member() -> expr::Member72 = "local" _() bind:bind() {expr::Member::BindStmt(bind)}73 / assertion:assertion() {expr::Member::AssertStmt(assertion)}74 / field:field() {expr::Member::Field(field)}75 pub rule obj_body() -> expr::ObjBody = members:(member() ** delimiter()) delimiter()? {expr::ObjBody::MemberList(members)}76 pub rule ifspec() -> expr::IfSpec = "if" _() expr:boxed_expr() {expr::IfSpec(expr)}77 pub rule forspec() -> expr::ForSpec = "for" _() id:id() _() "in" _() ifs:ifspec()* {expr::ForSpec(id, ifs)}78 pub rule bind_expr() -> Expr = bind:bind() {Expr::Bind(bind)}79 pub rule local_expr() -> Expr = "local" _() binds:(bind() ** delimiter()) __() ";" __() expr:boxed_expr() { Expr::LocalExpr(binds, expr) }80 pub rule string_expr() -> Expr = s:string() {Expr::Str(s)}81 pub rule parened_expr() -> Expr = "(" e:boxed_expr() ")" {Expr::Parened(e)}82 pub rule obj_expr() -> Expr = "{" __() body:obj_body() __() "}" {Expr::Obj(body)}83 pub rule array_expr() -> Expr = "[" __() elems:(expr() ** delimiter()) __() delimiter()? "]" {Expr::Arr(elems)}84 pub rule array_comp_expr() -> Expr = "[" __() expr:boxed_expr() delimiter()? fors:forspec()+ __() "]" {Expr::ArrComp(expr, fors)}85 pub rule index_expr() -> Expr86 = val:boxed_expr() "." idx:id() {Expr::Index(val, Box::new(Expr::Str(idx)))}87 / val:boxed_expr() "[" key:boxed_expr() "]" {Expr::Index(val, key)}88 pub rule slice_expr() -> Expr89 = value:boxed_expr() "[" start:boxed_expr()? ":" pair:(end:boxed_expr()? step:(":" e:boxed_expr() {e})? {(end, step)})? "]" {90 if let Some((end, step)) = pair {91 Expr::Slice { value, start, end, step }92 }else{93 Expr::Slice{ value, start, end: None, step: None }94 }95 }96 pub rule number_expr() -> Expr = n:number() { expr::Expr::Num(n) }97 pub rule var_expr() -> Expr = n:id() { expr::Expr::Var(n) }98 pub rule if_then_else_expr() -> Expr = cond:ifspec() _() "then" _() cond_then:boxed_expr() cond_else:(_() "else" _() e:boxed_expr() {e})? {Expr::IfElse{99 cond,100 cond_then,101 cond_else,102 }}103 pub rule expr_basic() -> Expr104 = "null" {Expr::Value(ValueType::Null)}105 / "true" {Expr::Value(ValueType::True)} / "false" {Expr::Value(ValueType::False)}106107 / "self" {Expr::Literal(LiteralType::This)} / "$" {Expr::Literal(LiteralType::Dollar)}108 / "super" {Expr::Literal(LiteralType::Super)}109110 / string_expr() / number_expr()111 / array_expr()112 / array_comp_expr()113 / obj_expr()114 / array_expr()115 / array_comp_expr()116117 / var_expr()118 / if_then_else_expr()119 / local_expr()120121 rule expr_suffix() -> Suffix122 = "." __() s:id() { Suffix::String(s) }123 / "[" __() s:expr() __() "]" { Suffix::Expression(s) }124 / "(" __() args:args() __() ")" { Suffix::Apply(args) }125126 rule expr() -> Expr127 = a:precedence! {128 a:(@) __() "||" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Or, Box::new(b))}129 --130 a:(@) __() "&&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::And, Box::new(b))}131 --132 a:(@) __() "|" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitOr, Box::new(b))}133 --134 a:@ __() "^" __() b:(@) {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitXor, Box::new(b))}135 --136 a:(@) __() "&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitAnd, Box::new(b))}137 --138 a:(@) __() "==" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Eq, Box::new(b))}139 a:(@) __() "!=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Ne, Box::new(b))}140 --141 a:(@) __() "<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lt, Box::new(b))}142 a:(@) __() ">" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gt, Box::new(b))}143 a:(@) __() "<=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lte, Box::new(b))}144 a:(@) __() ">=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gte, Box::new(b))}145 --146 a:(@) __() "<<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lhs, Box::new(b))}147 a:(@) __() ">>" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Rhs, Box::new(b))}148 --149 a:(@) __() "+" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Add, Box::new(b))}150 a:(@) __() "-" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Sub, Box::new(b))}151 --152 a:(@) __() "*" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mul, Box::new(b))}153 a:(@) __() "/" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Div, Box::new(b))}154 a:(@) __() "%" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mod, Box::new(b))}155 --156 e:expr_basic() {e}157 "(" __() e:boxed_expr() __() ")" {Expr::Parened(e)}158 } suffixes:(__() suffix:expr_suffix() {suffix})* {159 let mut cur = a;160 for suffix in suffixes {161 match suffix {162 Suffix::String(index) => {163 cur = Expr::Index(Box::new(cur), Box::new(Expr::Str(index)))164 },165 Suffix::Expression(index) => {166 cur = Expr::Index(Box::new(cur), Box::new(index))167 },168 Suffix::Apply(args) => {169 cur = Expr::Apply(Box::new(cur), args)170 }171 }172 }173 cur174 }175 / e:expr_basic() {e}176177 pub rule boxed_expr() -> Box<Expr> = e:expr() {Box::new(e)}178 pub rule jsonnet() -> Expr = __() e:expr() __() {e}179 }180}181182183pub fn parse(str: &str) -> Result<Expr, peg::error::ParseError<peg::str::LineCol>> {184 jsonnet_parser::jsonnet(str)185}186187#[cfg(test)]188pub mod tests {189 use super::{expr::*, parse};190191 #[test]192 fn empty_object() {193 assert_eq!(parse("{}").unwrap(), Expr::Obj(ObjBody::MemberList(vec![])));194 }195196 #[test]197 fn basic_math() {198 assert_eq!(199 parse("2+2*2").unwrap(),200 Expr::BinaryOp(201 Box::new(Expr::Num(2.0)),202 BinaryOpType::Add,203 Box::new(Expr::BinaryOp(204 Box::new(Expr::Num(2.0)),205 BinaryOpType::Mul,206 Box::new(Expr::Num(2.0))207 ))208 )209 );210 }211212 #[test]213 fn basic_math_with_indents() {214 assert_eq!(215 parse("2 + 2 * 2 ").unwrap(),216 Expr::BinaryOp(217 Box::new(Expr::Num(2.0)),218 BinaryOp::Add,219 Box::new(Expr::BinaryOp(220 Box::new(Expr::Num(2.0)),221 BinaryOp::Mul,222 Box::new(Expr::Num(2.0)),223 )),224 )225 );226 }227}