git.delta.rocks / jrsonnet / refs/commits / 2711ddb28fc9

difftreelog

source

crates/jsonnet-parser/src/lib.rs9.4 KiBsourcehistory
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}181182// TODO: impl FromStr from Expr183pub 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}