1#![feature(box_syntax)]23use peg::parser;45mod expr;6pub use expr::*;78enum Suffix {9 String(String),10 Expression(Expr),11 Apply(expr::ArgsDesc),12}1314parser! {15 grammar jsonnet_parser() for str {16 rule delimiter() = quiet!{__() "," __()} / expected!("<elements delimiter>")17 rule _() = quiet!{[' ' | '\n' | '\t']+} / expected!("<whitespace>")18 rule __() = quiet!{[' ' | '\n' | '\t']*}19 rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().nth(0).unwrap()}20 rule digit() -> char = d:$(['0'..='9']) {d.chars().nth(0).unwrap()}21 rule int() -> u32 = a:$(digit()+) { a.parse().unwrap() }22 rule number() -> f64 = quiet!{a:$((['-'|'+'])? int() ("." int())? (['e'|'E'] (s:['+'|'-'])? int())?) { a.parse().unwrap() }} / expected!("<number>")23 rule id() -> String = quiet!{ !("local" / "super" / "self" / "true" / "false" / "null" / "$" / "if" / "then" / "else" / "function") s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")2425 pub rule positional_param() -> expr::Param = name:id() {expr::Param::Positional(name)}26 pub rule named_param() -> expr::Param = name:id() __() "=" __() expr:boxed_expr() {expr::Param::Named(name, expr)}27 pub rule params() -> expr::ParamsDesc28 = positionals:(positional_param() ** delimiter()) named: (delimiter() named:(named_param() ** delimiter()) {named})? {29 if named.is_some() {30 expr::ParamsDesc([&positionals[..], &named.unwrap()[..]].concat())31 } else {32 expr::ParamsDesc(positionals)33 }34 }35 / named:(named_param() ** delimiter()) {expr::ParamsDesc(named)}36 / {expr::ParamsDesc(Vec::new())}3738 pub rule positional_arg() -> expr::Arg = quiet!{name:boxed_expr() {expr::Arg::Positional(name)}}/expected!("<positional arg>")39 pub rule named_arg() -> expr::Arg = quiet!{name:id() __() "=" __() expr:boxed_expr() {expr::Arg::Named(name, expr)}}/expected!("<named arg>")40 pub rule args() -> expr::ArgsDesc41 = positionals:(positional_arg() ** delimiter()) named: (delimiter() named:(named_arg() ** delimiter()) {named})? {42 if named.is_some() {43 expr::ArgsDesc([&positionals[..], &named.unwrap()[..]].concat())44 } else {45 expr::ArgsDesc(positionals)46 }47 }48 / named:(named_arg() ** delimiter()) {expr::ArgsDesc(named)}49 / {expr::ArgsDesc(Vec::new())}5051 pub rule bind() -> expr::Bind52 = name:id() __() "=" __() expr:boxed_expr() {expr::Bind::Value(name, expr)}53 / name:id() __() "(" __() params:params() __() ")" __() "=" __() expr:boxed_expr() {expr::Bind::Function(name, params, expr)}54 pub rule assertion() -> expr::AssertStmt = "assert" _() cond:boxed_expr() msg:(__() ":" __() e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }55 pub rule string() -> String56 = "\"" str:$((!['"'][_])+) "\"" {str.to_owned()}57 / "'" str:$((!['\''][_])+) "'" {str.to_owned()}58 pub rule field_name() -> expr::FieldName59 = name:id() {expr::FieldName::Fixed(name)}60 / name:string() {expr::FieldName::Fixed(name)}61 / "[" __() expr:boxed_expr() __() "]" {expr::FieldName::Dyn(expr)}62 pub rule visibility() -> expr::Visibility63 = ":::" {expr::Visibility::Unhide}64 / "::" {expr::Visibility::Hidden}65 / ":" {expr::Visibility::Normal}66 pub rule field() -> expr::FieldMember67 = name:field_name() __() plus:"+"? __() visibility:visibility() __() value:expr() {expr::FieldMember::Value{68 name,69 plus: plus.is_some(),70 visibility,71 value,72 }}73 / name:field_name() __() "(" __() params:params() __() ")" __() visibility:visibility() __() value:expr() {expr::FieldMember::Function{74 name,75 params,76 visibility,77 value,78 }}79 pub rule member() -> expr::Member80 = "local" _() bind:bind() {expr::Member::BindStmt(bind)}81 / assertion:assertion() {expr::Member::AssertStmt(assertion)}82 / field:field() {expr::Member::Field(field)}83 pub rule obj_body() -> expr::ObjBody = members:(member() ** delimiter()) delimiter()? {expr::ObjBody::MemberList(members)}84 pub rule ifspec() -> expr::IfSpec = "if" _() expr:boxed_expr() {expr::IfSpec(expr)}85 pub rule forspec() -> expr::ForSpec = "for" _() id:id() _() "in" _() ifs:ifspec()* {expr::ForSpec(id, ifs)}86 pub rule bind_expr() -> Expr = bind:bind() {Expr::Bind(bind)}87 pub rule local_expr() -> Expr = "local" _() binds:(bind() ** delimiter()) __() ";" __() expr:boxed_expr() { Expr::LocalExpr(binds, expr) }88 pub rule string_expr() -> Expr = s:string() {Expr::Str(s)}89 pub rule parened_expr() -> Expr = "(" e:boxed_expr() ")" {Expr::Parened(e)}90 pub rule obj_expr() -> Expr = "{" __() body:obj_body() __() "}" {Expr::Obj(body)}91 pub rule array_expr() -> Expr = "[" __() elems:(expr() ** delimiter()) __() delimiter()? "]" {Expr::Arr(elems)}92 pub rule array_comp_expr() -> Expr = "[" __() expr:boxed_expr() delimiter()? fors:forspec()+ __() "]" {Expr::ArrComp(expr, fors)}93 pub rule index_expr() -> Expr94 = val:boxed_expr() "." idx:id() {Expr::Index(val, Box::new(Expr::Str(idx)))}95 / val:boxed_expr() "[" key:boxed_expr() "]" {Expr::Index(val, key)}96 pub rule slice_expr() -> Expr97 = value:boxed_expr() "[" start:boxed_expr()? ":" pair:(end:boxed_expr()? step:(":" e:boxed_expr() {e})? {(end, step)})? "]" {98 if let Some((end, step)) = pair {99 Expr::Slice { value, start, end, step }100 }else{101 Expr::Slice{ value, start, end: None, step: None }102 }103 }104 pub rule number_expr() -> Expr = n:number() { expr::Expr::Num(n) }105 pub rule var_expr() -> Expr = n:id() { expr::Expr::Var(n) }106 pub rule if_then_else_expr() -> Expr = cond:ifspec() _() "then" _() cond_then:boxed_expr() cond_else:(_() "else" _() e:boxed_expr() {e})? {Expr::IfElse{107 cond,108 cond_then,109 cond_else,110 }}111 pub rule expr_basic() -> Expr112 = "null" {Expr::Value(ValueType::Null)}113 / "true" {Expr::Value(ValueType::True)} / "false" {Expr::Value(ValueType::False)}114115 / "self" {Expr::Literal(LiteralType::This)} / "$" {Expr::Literal(LiteralType::Dollar)}116 / "super" {Expr::Literal(LiteralType::Super)}117118 / string_expr() / number_expr()119 / array_expr()120 / array_comp_expr()121 / obj_expr()122 / array_expr()123 / array_comp_expr()124125 / var_expr()126 / if_then_else_expr()127 / local_expr()128129 / "function" __() "(" __() params:params() __() ")" __() expr:boxed_expr() {Expr::Function(params, expr)}130131 rule expr_basic_with_suffix() -> Expr132 = a:expr_basic() suffixes:(__() suffix:expr_suffix() {suffix})* {133 let mut cur = a;134 for suffix in suffixes {135 match suffix {136 Suffix::String(index) => {137 cur = Expr::Index(Box::new(cur), Box::new(Expr::Str(index)))138 },139 Suffix::Expression(index) => {140 cur = Expr::Index(Box::new(cur), Box::new(index))141 },142 Suffix::Apply(args) => {143 cur = Expr::Apply(Box::new(cur), args)144 }145 }146 }147 cur148 }149150 rule expr_suffix() -> Suffix151 = "." __() s:id() { Suffix::String(s) }152 / "[" __() s:expr() __() "]" { Suffix::Expression(s) }153 / "(" __() args:args() __() ")" { Suffix::Apply(args) }154155 rule expr() -> Expr156 = a:precedence! {157 a:(@) __() "||" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Or, Box::new(b))}158 --159 a:(@) __() "&&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::And, Box::new(b))}160 --161 a:(@) __() "|" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitOr, Box::new(b))}162 --163 a:@ __() "^" __() b:(@) {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitXor, Box::new(b))}164 --165 a:(@) __() "&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitAnd, Box::new(b))}166 --167 a:(@) __() "==" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Eq, Box::new(b))}168 a:(@) __() "!=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Ne, Box::new(b))}169 --170 a:(@) __() "<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lt, Box::new(b))}171 a:(@) __() ">" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gt, Box::new(b))}172 a:(@) __() "<=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lte, Box::new(b))}173 a:(@) __() ">=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gte, Box::new(b))}174 --175 a:(@) __() "<<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lhs, Box::new(b))}176 a:(@) __() ">>" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Rhs, Box::new(b))}177 --178 a:(@) __() "+" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Add, Box::new(b))}179 a:(@) __() "-" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Sub, Box::new(b))}180 --181 a:(@) __() "*" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mul, Box::new(b))}182 a:(@) __() "/" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Div, Box::new(b))}183 a:(@) __() "%" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mod, Box::new(b))}184 --185 e:expr_basic_with_suffix() {e}186 "(" __() e:boxed_expr() __() ")" {Expr::Parened(e)}187 }188 / e:expr_basic_with_suffix() {e}189190 pub rule boxed_expr() -> Box<Expr> = e:expr() {Box::new(e)}191 pub rule jsonnet() -> Expr = __() e:expr() __() {e}192 }193}194195196pub fn parse(str: &str) -> Result<Expr, peg::error::ParseError<peg::str::LineCol>> {197 jsonnet_parser::jsonnet(str)198}199200#[cfg(test)]201pub mod tests {202 use super::{expr::*, parse};203 #[test]204 fn empty_object() {205 assert_eq!(parse("{}").unwrap(), Expr::Obj(ObjBody::MemberList(vec![])),);206 }207 #[test]208 fn basic_math() {209 assert_eq!(210 parse("2+2*2").unwrap(),211 Expr::BinaryOp(212 Box::new(Expr::Num(2.0)),213 BinaryOpType::Add,214 Box::new(Expr::BinaryOp(215 Box::new(Expr::Num(2.0)),216 BinaryOpType::Mul,217 Box::new(Expr::Num(2.0))218 ))219 )220 );221 }222223 #[test]224 fn suffix_comparsion() {225 use Expr::*;226 assert_eq!(227 parse("std.type(a) == \"string\"").unwrap(),228 BinaryOp(229 box Apply(230 box Index(box Var("std".to_owned()), box Str("type".to_owned())),231 ArgsDesc(vec![Arg::Positional(box Var("a".to_owned()))])232 ),233 BinaryOpType::Eq,234 box Str("string".to_owned())235 )236 );237 }238}