git.delta.rocks / jrsonnet / refs/commits / ee033ba226d8

difftreelog

source

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