difftreelog
refactor(parser) make parser parse stdlib successfully
in: master
4 files changed
crates/jsonnet-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jsonnet-parser/Cargo.toml
+++ b/crates/jsonnet-parser/Cargo.toml
@@ -11,3 +11,6 @@
[dependencies]
peg = "0.6.2"
+
+[dev-dependencies]
+jsonnet-stdlib = { version = "0.1.0", path = "../jsonnet-stdlib" }
crates/jsonnet-parser/README.mddiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-parser/README.md
@@ -0,0 +1,3 @@
+# jsonnet-parser
+
+Parser for jsonnet language
crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
#[derive(Debug, Clone, PartialEq)]
pub enum FieldName {
/// {fixed: 2}
@@ -20,25 +22,18 @@
pub struct AssertStmt(pub Box<Expr>, pub Option<Box<Expr>>);
#[derive(Debug, Clone, PartialEq)]
-pub enum FieldMember {
- Value {
- name: FieldName,
- plus: bool,
- visibility: Visibility,
- value: Expr,
- },
- Function {
- name: FieldName,
- params: ParamsDesc,
- visibility: Visibility,
- value: Expr,
- },
+pub struct FieldMember {
+ pub name: FieldName,
+ pub plus: bool,
+ pub params: Option<ParamsDesc>,
+ pub visibility: Visibility,
+ pub value: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Member {
Field(FieldMember),
- BindStmt(Bind),
+ BindStmt(BindSpec),
AssertStmt(AssertStmt),
}
@@ -50,7 +45,7 @@
Not,
}
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BinaryOpType {
Mul,
Div,
@@ -80,73 +75,90 @@
Or,
}
+/// name, default value
#[derive(Debug, Clone, PartialEq)]
-pub enum Param {
- Positional(String),
- Named(String, Box<Expr>),
-}
-
+pub struct Param(pub String, pub Option<Box<Expr>>);
+/// Defined function parameters
#[derive(Debug, Clone, PartialEq)]
pub struct ParamsDesc(pub Vec<Param>);
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Arg {
- Positional(Box<Expr>),
- Named(String, Box<Expr>),
+impl ParamsDesc {
+ pub fn with_defaults(&self) -> Vec<Param> {
+ self.0
+ .iter()
+ .filter(|e| e.1.is_some())
+ .map(|e| e.clone())
+ .collect()
+ }
}
#[derive(Debug, Clone, PartialEq)]
+pub struct Arg(pub Option<String>, pub Box<Expr>);
+#[derive(Debug, Clone, PartialEq)]
pub struct ArgsDesc(pub Vec<Arg>);
#[derive(Debug, Clone, PartialEq)]
-pub enum Bind {
- Value(String, Box<Expr>),
- Function(String, ParamsDesc, Box<Expr>),
+pub struct BindSpec {
+ pub name: String,
+ pub params: Option<ParamsDesc>,
+ pub value: Box<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
-pub struct IfSpec(pub Box<Expr>);
+pub struct IfSpecData(pub Box<Expr>);
#[derive(Debug, Clone, PartialEq)]
-pub struct ForSpec(pub String, pub Vec<IfSpec>);
+pub struct ForSpecData(pub String, pub Box<Expr>);
#[derive(Debug, Clone, PartialEq)]
pub enum CompSpec {
- IfSpec(IfSpec),
- ForSpec(ForSpec),
+ IfSpec(IfSpecData),
+ ForSpec(ForSpecData),
}
#[derive(Debug, Clone, PartialEq)]
pub enum ObjBody {
MemberList(Vec<Member>),
ObjComp {
- pre_locals: Vec<Bind>,
+ pre_locals: Vec<BindSpec>,
key: Box<Expr>,
value: Box<Expr>,
- post_locals: Vec<Bind>,
- first: ForSpec,
+ post_locals: Vec<BindSpec>,
+ first: ForSpecData,
rest: Vec<CompSpec>,
},
}
#[derive(Debug, Clone, PartialEq)]
-pub enum ValueType {
+pub enum LiteralType {
+ This,
+ Super,
+ Dollar,
Null,
True,
False,
}
+impl Display for LiteralType {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use LiteralType::*;
+ match self {
+ This => write!(f, "this"),
+ Null => write!(f, "null"),
+ True => write!(f, "true"),
+ False => write!(f, "false"),
+ _ => panic!("non printable item"),
+ }
+ }
+}
#[derive(Debug, Clone, PartialEq)]
-pub enum LiteralType {
- This,
- Super,
- Dollar,
+pub struct SliceDesc {
+ pub start: Option<Box<Expr>>,
+ pub end: Option<Box<Expr>>,
+ pub step: Option<Box<Expr>>,
}
/// Syntax base
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
- Value(ValueType),
- /// Plain value: null/true/false
Literal(LiteralType),
/// String value: "hello"
@@ -169,7 +181,7 @@
/// ]
/// ],
/// ```
- ArrComp(Box<Expr>, Vec<ForSpec>),
+ ArrComp(Box<Expr>, ForSpecData, Vec<CompSpec>),
/// Object: {a: 2}
Obj(ObjBody),
@@ -179,33 +191,48 @@
/// (obj)
Parened(Box<Expr>),
+ /// Params in function definition
+ /// hello, world, test = 2
Params(ParamsDesc),
+ /// Args in function call
+ /// 2 + 2, 3, named = 6
Args(ArgsDesc),
+ /// -2
UnaryOp(UnaryOpType, Box<Expr>),
+ /// 2 - 2
BinaryOp(Box<Expr>, BinaryOpType, Box<Expr>),
+ /// assert 2 == 2 : "Math is broken"
AssertExpr(AssertStmt, Box<Expr>),
- LocalExpr(Vec<Bind>, Box<Expr>),
+ /// local a = 2; { b: a }
+ LocalExpr(Vec<BindSpec>, Box<Expr>),
- Bind(Bind),
+ /// a = 3
+ Bind(BindSpec),
+ /// import "hello"
Import(String),
+ /// importStr "file.txt"
ImportStr(String),
+ /// error "I'm broken"
Error(Box<Expr>),
+ /// a(b, c)
Apply(Box<Expr>, ArgsDesc),
+ ///
Select(Box<Expr>, String),
+ /// a[b]
Index(Box<Expr>, Box<Expr>),
- Slice {
- value: Box<Expr>,
- start: Option<Box<Expr>>,
- end: Option<Box<Expr>>,
- step: Option<Box<Expr>>,
- },
+ /// a[1::2]
+ Slice(Box<Expr>, SliceDesc),
+ /// function(x) x
Function(ParamsDesc, Box<Expr>),
+ /// if true == false then 1 else 2
IfElse {
- cond: IfSpec,
+ cond: IfSpecData,
cond_then: Box<Expr>,
cond_else: Option<Box<Expr>>,
},
- IfSpec(IfSpec),
- ForSpec(ForSpec),
+ /// if 2 = 3
+ IfSpec(IfSpecData),
+ /// for elem in array
+ ForSpec(ForSpecData),
}
crates/jsonnet-parser/src/lib.rsdiffbeforeafterboth1#![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}1#![feature(box_syntax)]23use peg::parser;45mod expr;6pub use expr::*;78enum Suffix {9 String(String),10 Slice(SliceDesc),11 Expression(Expr),12 Apply(expr::ArgsDesc),13 Extend(expr::ObjBody),14}1516parser! {17 grammar jsonnet_parser() for str {18 use peg::ParseLiteral;1920 /// Standard C-like comments21 rule comment() = "//" (!['\n'][_])* "\n" / "/*" ((!("*/")[_][_])/("\\" "*/"))* "*/"22 rule _() = ([' ' | '\n' | '\t'] / comment())*2324 /// For comma-delimited elements25 rule comma() = quiet!{_ "," _} / expected!("<comma>")26 rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().nth(0).unwrap()}27 rule digit() -> char = d:$(['0'..='9']) {d.chars().nth(0).unwrap()}28 rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']29 /// Sequence of digits30 rule uint() -> u32 = a:$(digit()+) { a.parse().unwrap() }31 /// Number in scientific notation format32 rule number() -> f64 = quiet!{a:$(uint() ("." uint())? (['e'|'E'] (s:['+'|'-'])? uint())?) { a.parse().unwrap() }} / expected!("<number>")3334 /// Reserved word followed by any non-alphanumberic35 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()36 rule id() -> String = quiet!{ !reserved() s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")37 rule keyword(id: &'static str) = ##parse_string_literal(id) end_of_ident()3839 pub rule param() -> expr::Param = name:id() expr:(_ "=" _ expr:boxed_expr(){expr})? { expr::Param(name, expr) }40 pub rule params() -> expr::ParamsDesc41 = params:(param() ** comma()) {42 let mut defaults_started = false;43 for param in ¶ms {44 defaults_started = defaults_started || param.1.is_some();45 assert_eq!(defaults_started, param.1.is_some(), "defauld parameters should be used after all positionals");46 }47 expr::ParamsDesc(params)48 }49 / { expr::ParamsDesc(Vec::new()) }5051 pub rule arg() -> expr::Arg52 = name:id() _ "=" _ expr:boxed_expr() {expr::Arg(Some(name), expr)}53 / expr:boxed_expr() {expr::Arg(None, expr)}54 pub rule args() -> expr::ArgsDesc55 = args:arg() ** comma() comma()? {56 let mut named_started = false;57 for arg in &args {58 named_started = named_started || arg.0.is_some();59 assert_eq!(named_started, arg.0.is_some(), "named args should be used after all positionals");60 }61 expr::ArgsDesc(args)62 }63 / { expr::ArgsDesc(Vec::new()) }6465 pub rule bind() -> expr::BindSpec66 = name:id() _ "=" _ expr:boxed_expr() {expr::BindSpec{name, params: None, value: expr}}67 / name:id() _ "(" _ params:params() _ ")" _ "=" _ expr:boxed_expr() {expr::BindSpec{name, params: Some(params), value: expr}}68 pub rule assertion() -> expr::AssertStmt = keyword("assert") _ cond:boxed_expr() msg:(_ ":" _ e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }69 pub rule string() -> String70 = "\"" str:$(("\\\"" / !['"'][_])*) "\"" {str.to_owned()}71 / "'" str:$((!['\''][_])*) "'" {str.to_owned()}72 pub rule field_name() -> expr::FieldName73 = name:id() {expr::FieldName::Fixed(name)}74 / name:string() {expr::FieldName::Fixed(name)}75 / "[" _ expr:boxed_expr() _ "]" {expr::FieldName::Dyn(expr)}76 pub rule visibility() -> expr::Visibility77 = ":::" {expr::Visibility::Unhide}78 / "::" {expr::Visibility::Hidden}79 / ":" {expr::Visibility::Normal}80 pub rule field() -> expr::FieldMember81 = name:field_name() _ plus:"+"? _ visibility:visibility() _ value:expr() {expr::FieldMember{82 name,83 plus: plus.is_some(),84 params: None,85 visibility,86 value,87 }}88 / name:field_name() _ "(" _ params:params() _ ")" _ visibility:visibility() _ value:expr() {expr::FieldMember{89 name,90 plus: false,91 params: Some(params),92 visibility,93 value,94 }}95 pub rule obj_local() -> BindSpec96 = keyword("local") _ bind:bind() {bind}97 pub rule member() -> expr::Member98 = bind:obj_local() {expr::Member::BindStmt(bind)}99 / assertion:assertion() {expr::Member::AssertStmt(assertion)}100 / field:field() {expr::Member::Field(field)}101 pub rule objinside() -> expr::ObjBody102 = pre_locals:(b: obj_local() comma() {b})* "[" _ key:boxed_expr() _ "]" _ ":" _ value:boxed_expr() post_locals:(comma() b:obj_local() {b})* _ first:forspec() rest:(_ rest:compspec() {rest})? {103 expr::ObjBody::ObjComp {104 pre_locals,105 key,106 value,107 post_locals,108 first,109 rest: rest.unwrap_or(Vec::new()),110 }111 }112 / members:(member() ** comma()) comma()? {expr::ObjBody::MemberList(members)}113 pub rule ifspec() -> IfSpecData = keyword("if") _ expr:boxed_expr() {IfSpecData(expr)}114 pub rule forspec() -> ForSpecData = keyword("for") _ id:id() _ keyword("in") _ cond:boxed_expr() {ForSpecData(id, cond)}115 pub rule compspec() -> Vec<expr::CompSpec> = s:(i:ifspec() { expr::CompSpec::IfSpec(i) } / f:forspec() {expr::CompSpec::ForSpec(f)} )+ {s}116 pub rule bind_expr() -> Expr = bind:bind() {Expr::Bind(bind)}117 pub rule local_expr() -> Expr = keyword("local") _ binds:bind() ** comma() _ ";" _ expr:boxed_expr() { Expr::LocalExpr(binds, expr) }118 pub rule string_expr() -> Expr = s:string() {Expr::Str(s)}119 pub rule parened_expr() -> Expr = "(" e:boxed_expr() ")" {Expr::Parened(e)}120 pub rule obj_expr() -> Expr = "{" _ body:objinside() _ "}" {Expr::Obj(body)}121 pub rule array_expr() -> Expr = "[" _ elems:(expr() ** comma()) _ comma()? "]" {Expr::Arr(elems)}122 pub rule array_comp_expr() -> Expr = "[" _ expr:boxed_expr() _ comma()? _ forspec:forspec() _ others:(others: compspec() _ {others})? "]" {Expr::ArrComp(expr, forspec, others.unwrap_or(vec![]))}123 pub rule index_expr() -> Expr124 = val:boxed_expr() "." idx:id() {Expr::Index(val, Box::new(Expr::Str(idx)))}125 / val:boxed_expr() "[" key:boxed_expr() "]" {Expr::Index(val, key)}126 pub rule number_expr() -> Expr = n:number() { expr::Expr::Num(n) }127 pub rule var_expr() -> Expr = n:id() { expr::Expr::Var(n) }128 pub rule if_then_else_expr() -> Expr = cond:ifspec() _ keyword("then") _ cond_then:boxed_expr() cond_else:(_ keyword("else") _ e:boxed_expr() {e})? {Expr::IfElse{129 cond,130 cond_then,131 cond_else,132 }}133134 pub rule literal() -> Expr135 = v:(136 keyword("null") {LiteralType::Null}137 / keyword("true") {LiteralType::True}138 / keyword("false") {LiteralType::False}139 / keyword("self") {LiteralType::This}140 / keyword("$") {LiteralType::Dollar}141 / keyword("super") {LiteralType::Super}142 ) {Expr::Literal(v)}143144 pub rule expr_basic() -> Expr145 = literal()146147 / string_expr() / number_expr()148 / array_expr()149 / obj_expr()150 / array_expr()151 / array_comp_expr()152153 / var_expr()154 / local_expr()155 / if_then_else_expr()156 / "-" _ expr:boxed_expr() { Expr::UnaryOp(UnaryOpType::Minus, expr) }157 / "!" _ expr:boxed_expr() { Expr::UnaryOp(UnaryOpType::Not, expr) }158159 / keyword("function") _ "(" _ params:params() _ ")" _ expr:boxed_expr() {Expr::Function(params, expr)}160 / assertion:assertion() _ ";" _ expr:boxed_expr() { Expr::AssertExpr(assertion, expr) }161162 / keyword("error") _ expr:boxed_expr() { Expr::Error(expr) }163164 rule expr_basic_with_suffix() -> Expr165 = a:expr_basic() suffixes:(_ suffix:expr_suffix() {suffix})* {166 let mut cur = a;167 for suffix in suffixes {168 cur = match suffix {169 Suffix::String(index) => Expr::Index(Box::new(cur), Box::new(Expr::Str(index))),170 Suffix::Slice(desc) => Expr::Slice(Box::new(cur), desc),171 Suffix::Expression(index) => Expr::Index(Box::new(cur), Box::new(index)),172 Suffix::Apply(args) => Expr::Apply(Box::new(cur), args),173 Suffix::Extend(body) => Expr::ObjExtend(box cur, body),174 }175 }176 cur177 }178179 pub rule slice_desc() -> SliceDesc180 = start:boxed_expr()? _ ":" _ pair:(end:boxed_expr()? _ step:(":" _ e:boxed_expr() {e})? {(end, step)})? {181 if let Some((end, step)) = pair {182 SliceDesc { start, end, step }183 }else{184 SliceDesc { start, end: None, step: None }185 }186 }187188 rule expr_suffix() -> Suffix189 = "." _ s:id() { Suffix::String(s) }190 / "[" _ s:slice_desc() _ "]" { Suffix::Slice(s) }191 / "[" _ s:expr() _ "]" { Suffix::Expression(s) }192 / "(" _ args:args() _ ")" (_ keyword("tailstrict"))? { Suffix::Apply(args) }193 / "{" _ body:objinside() _ "}" { Suffix::Extend(body) }194195 rule expr() -> Expr196 = a:precedence! {197 a:(@) _ "||" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Or, Box::new(b))}198 --199 a:(@) _ "&&" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::And, Box::new(b))}200 --201 a:(@) _ "|" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::BitOr, Box::new(b))}202 --203 a:@ _ "^" _ b:(@) {Expr::BinaryOp(Box::new(a), BinaryOpType::BitXor, Box::new(b))}204 --205 a:(@) _ "&" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::BitAnd, Box::new(b))}206 --207 a:(@) _ "==" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Eq, Box::new(b))}208 a:(@) _ "!=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Ne, Box::new(b))}209 --210 a:(@) _ "<" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lt, Box::new(b))}211 a:(@) _ ">" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Gt, Box::new(b))}212 a:(@) _ "<=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lte, Box::new(b))}213 a:(@) _ ">=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Gte, Box::new(b))}214 --215 a:(@) _ "<<" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lhs, Box::new(b))}216 a:(@) _ ">>" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Rhs, Box::new(b))}217 --218 a:(@) _ "+" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Add, Box::new(b))}219 a:(@) _ "-" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Sub, Box::new(b))}220 --221 a:(@) _ "*" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Mul, Box::new(b))}222 a:(@) _ "/" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Div, Box::new(b))}223 a:(@) _ "%" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Mod, Box::new(b))}224 --225 e:expr_basic_with_suffix() {e}226 "(" _ e:boxed_expr() _ ")" {Expr::Parened(e)}227 }228 / e:expr_basic_with_suffix() {e}229230 pub rule boxed_expr() -> Box<Expr> = e:expr() {Box::new(e)}231 pub rule jsonnet() -> Expr = _ e:expr() _ {e}232 }233}234235// TODO: impl FromStr from Expr236pub fn parse(str: &str) -> Result<Expr, peg::error::ParseError<peg::str::LineCol>> {237 jsonnet_parser::jsonnet(str)238}239240#[cfg(test)]241pub mod tests {242 use super::{expr::*, parse};243 #[test]244 fn empty_object() {245 assert_eq!(parse("{}").unwrap(), Expr::Obj(ObjBody::MemberList(vec![])),);246 }247 #[test]248 fn basic_math() {249 assert_eq!(250 parse("2+2*2").unwrap(),251 Expr::BinaryOp(252 Box::new(Expr::Num(2.0)),253 BinaryOpType::Add,254 Box::new(Expr::BinaryOp(255 Box::new(Expr::Num(2.0)),256 BinaryOpType::Mul,257 Box::new(Expr::Num(2.0))258 ))259 )260 );261 }262263 /// Comments should not affect parsing264 #[test]265 fn comments() {266 assert_eq!(267 parse("2//comment\n+//comment\n3/*test*/*/*test*/4").unwrap(),268 Expr::BinaryOp(269 box Expr::Num(2.0),270 BinaryOpType::Add,271 box Expr::BinaryOp(box Expr::Num(3.0), BinaryOpType::Mul, box Expr::Num(4.0))272 )273 );274 }275276 /// Comments should be able to be escaped277 #[test]278 fn comment_escaping() {279 assert_eq!(280 parse("2/*\\*/+*/ - 22").unwrap(),281 Expr::BinaryOp(box Expr::Num(2.0), BinaryOpType::Sub, box Expr::Num(22.0))282 );283 }284285 #[test]286 fn suffix_comparsion() {287 use Expr::*;288 assert_eq!(289 parse("std.type(a) == \"string\"").unwrap(),290 BinaryOp(291 box Apply(292 box Index(box Var("std".to_owned()), box Str("type".to_owned())),293 ArgsDesc(vec![Arg(None, box Var("a".to_owned()))])294 ),295 BinaryOpType::Eq,296 box Str("string".to_owned())297 )298 );299 }300301 #[test]302 fn array_comp() {303 use Expr::*;304 assert_eq!(305 parse("[std.deepJoin(x) for x in arr]").unwrap(),306 ArrComp(307 box Apply(308 box Index(box Var("std".to_owned()), box Str("deepJoin".to_owned())),309 ArgsDesc(vec![Arg(None, box Var("x".to_owned()))])310 ),311 ForSpecData("x".to_owned(), box Var("arr".to_owned())),312 vec![]313 ),314 )315 }316317 #[test]318 fn array_comp_with_ifs() {319 use Expr::*;320 assert_eq!(321 parse("[k for k in std.objectFields(patch) if patch[k] == null]").unwrap(),322 ArrComp(323 box Var("k".to_owned()),324 ForSpecData(325 "k".to_owned(),326 box Apply(327 box Index(328 box Var("std".to_owned()),329 box Str("objectFields".to_owned())330 ),331 ArgsDesc(vec![Arg(None, box Var("patch".to_owned()))])332 )333 ),334 vec![CompSpec::IfSpec(IfSpecData(box BinaryOp(335 box Index(box Var("patch".to_owned()), box Var("k".to_owned())),336 BinaryOpType::Eq,337 box Literal(LiteralType::Null)338 )))]339 ),340 );341 }342343 #[test]344 fn reserved() {345 use Expr::*;346 assert_eq!(parse("null").unwrap(), Literal(LiteralType::Null));347 assert_eq!(parse("nulla").unwrap(), Var("nulla".to_owned()));348 }349350 #[test]351 fn multiple_args_buf() {352 parse("a(b, null_fields)").unwrap();353 }354355 #[test]356 fn can_parse_stdlib() {357 parse(jsonnet_stdlib::STDLIB_STR).unwrap();358 }359}