difftreelog
chore(parser) initial parser commit
in: master
6 files changed
.gitignorediffbeforeafterboth--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
Cargo.lockdiffbeforeafterboth--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,59 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "jsonnet-parser"
+version = "0.1.0"
+dependencies = [
+ "peg",
+]
+
+[[package]]
+name = "peg"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9075875c14bb21f25f11cad4b6ad2e4dd443b8fb83900b2fbdd6ebd744b82e97"
+dependencies = [
+ "peg-macros",
+ "peg-runtime",
+]
+
+[[package]]
+name = "peg-macros"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c24c165fd39e995246140cc78df55c56c6733ba87e6658cb3e197b8856c62852"
+dependencies = [
+ "peg-runtime",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "peg-runtime"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c1a2897e69d986c7986747ebad425cf03746ec5e3e09bb3b2600f91301ba864"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,2 @@
+[workspace]
+members = ["crates/jsonnet-parser"]
crates/jsonnet-parser/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-parser/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "jsonnet-parser"
+version = "0.1.0"
+authors = ["Лач <iam@lach.pw>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+peg = "0.6.2"
crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth1#[derive(Debug, Clone, PartialEq)]2pub enum FieldName {3 /// {fixed: 2}4 Fixed(String),5 /// {["dyn"+"amic"]: 3}6 Dyn(Box<Expr>),7}89#[derive(Debug, Clone, PartialEq)]10pub enum Visibility {11 /// :12 Normal,13 /// ::14 Hidden,15 /// :::16 Unhide,17}1819#[derive(Debug, Clone, PartialEq)]20pub struct AssertStmt(pub Box<Expr>, pub Option<Box<Expr>>);2122#[derive(Debug, Clone, PartialEq)]23pub enum FieldMember {24 Value {25 name: FieldName,26 plus: bool,27 visibility: Visibility,28 value: Expr,29 },30 Function {31 name: FieldName,32 params: Params,33 visibility: Visibility,34 value: Expr,35 },36}3738#[derive(Debug, Clone, PartialEq)]39pub enum Member {40 Field(FieldMember),41 BindStmt(Bind),42 AssertStmt(AssertStmt),43}4445#[derive(Debug, Clone, PartialEq)]46pub enum UnaryOp {47 Plus,48 Minus,49 BitNot,50 Not,51}5253#[derive(Debug, Clone, PartialEq)]54pub enum BinaryOp {55 Mul,56 Div,57 Mod,5859 Add,60 Sub,6162 Lhs,63 Rhs,6465 Lt,66 Gt,67 Lte,68 Gte,6970 In,7172 Eq,73 Ne,7475 BitAnd,76 BitOr,77 And,78 Or,7980 BitXor,81}8283#[derive(Debug, Clone, PartialEq)]84pub enum Param {85 Positional(String),86 Named(String, Box<Expr>),87}8889#[derive(Debug, Clone, PartialEq)]90pub struct Params(pub Vec<Param>);9192#[derive(Debug, Clone, PartialEq)]93pub enum Arg {94 Positional(Box<Expr>),95 Named(String, Box<Expr>),96}9798#[derive(Debug, Clone, PartialEq)]99pub struct Args(pub Vec<Arg>);100101#[derive(Debug, Clone, PartialEq)]102pub enum Bind {103 Value(String, Box<Expr>),104 Function(String, Params, Box<Expr>),105}106107#[derive(Debug, Clone, PartialEq)]108pub struct IfSpec(pub Box<Expr>);109#[derive(Debug, Clone, PartialEq)]110pub struct ForSpec(pub String, pub Vec<IfSpec>);111112#[derive(Debug, Clone, PartialEq)]113pub enum CompSpec {114 IfSpec(IfSpec),115 ForSpec(ForSpec),116}117118#[derive(Debug, Clone, PartialEq)]119pub enum ObjBody {120 MemberList(Vec<Member>),121 ObjComp {122 pre_locals: Vec<Bind>,123 key: Box<Expr>,124 value: Box<Expr>,125 post_locals: Vec<Bind>,126 first: ForSpec,127 rest: Vec<CompSpec>,128 },129}130131#[derive(Debug, Clone, PartialEq)]132pub enum Literal {133 Null,134 True,135 False,136 This,137 Super,138 Dollar,139}140141/// Syntax base142#[derive(Debug, Clone, PartialEq)]143pub enum Expr {144 /// Plain value: null/true/false145 Literal(Literal),146147 /// String value: "hello"148 Str(String),149 /// Number: 1, 2.0, 2e+20150 Num(f64),151 /// Variable name: test152 Var(String),153154 /// Array of expressions: [1, 2, "Hello"]155 Arr(Vec<Expr>),156 /// Array comprehension:157 /// ```jsonnet158 /// ingredients: [159 /// { kind: kind, qty: 4 / 3 }160 /// for kind in [161 /// 'Honey Syrup',162 /// 'Lemon Juice',163 /// 'Farmers Gin',164 /// ]165 /// ],166 /// ```167 ArrComp(Box<Expr>, Vec<ForSpec>),168169 /// Object: {a: 2}170 Obj(ObjBody),171 /// Object extension: var1 {b: 2}172 ObjExtend(Box<Expr>, ObjBody),173174 /// (obj)175 Parened(Box<Expr>),176177 Params(Params),178 Args(Args),179180 UnaryOp(UnaryOp, Box<Expr>),181 BinaryOp(Box<Expr>, BinaryOp, Box<Expr>),182 AssertExpr(AssertStmt, Box<Expr>),183 LocalExpr(Vec<Bind>, Box<Expr>),184185 Bind(Bind),186 Import(String),187 ImportStr(String),188 Error(Box<Expr>),189 Apply(Box<Expr>, Args),190 Select(Box<Expr>, String),191 Index(Box<Expr>, Box<Expr>),192 Slice {193 value: Box<Expr>,194 start: Option<Box<Expr>>,195 end: Option<Box<Expr>>,196 step: Option<Box<Expr>>,197 },198 Function(Params, Box<Expr>),199 IfElse {200 cond: IfSpec,201 cond_then: Box<Expr>,202 cond_else: Option<Box<Expr>>,203 },204 IfSpec(IfSpec),205 ForSpec(ForSpec),206}crates/jsonnet-parser/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -0,0 +1,208 @@
+use peg::parser;
+
+pub mod expr;
+use expr::{Expr, Literal};
+
+enum Suffix {
+ String(String),
+ Expression(Expr),
+ Apply(expr::Args),
+}
+
+parser! {
+ grammar jsonnet_parser() for str {
+ rule delimiter() = quiet!{__() "," __()} / expected!("<elements delimiter>")
+ rule _() = quiet!{[' ' | '\n' | '\t']+} / expected!("<whitespace>")
+ rule __() = quiet!{[' ' | '\n' | '\t']*}
+ rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().nth(0).unwrap()}
+ rule digit() -> char = d:$(['0'..='9']) {d.chars().nth(0).unwrap()}
+ rule int() -> u32 = a:$(digit()+) { a.parse().unwrap() }
+ rule number() -> f64 = quiet!{a:$((['-'|'+'])? int() ("." int())? (['e'|'E'] (s:['+'|'-'])? int())?) { a.parse().unwrap() }} / expected!("<number>")
+ rule id() -> String = quiet!{ !("local" / "super" / "self" / "true" / "false" / "null" / "$" / "if" / "then" / "else") s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")
+
+ pub rule positional_param() -> expr::Param = name:id() {expr::Param::Positional(name)}
+ pub rule named_param() -> expr::Param = name:id() __() "=" __() expr:boxed_expr() {expr::Param::Named(name, expr)}
+ pub rule params() -> expr::Params
+ = positionals:(positional_param() ** delimiter()) delimiter() named:(named_param() ** delimiter()) {
+ expr::Params([&positionals[..], &named[..]].concat())
+ }
+ / named:(named_param() ** delimiter()) {expr::Params(named)}
+ / positionals:(positional_param() ** delimiter()) {expr::Params(positionals)}
+ / {expr::Params(Vec::new())}
+
+ pub rule positional_arg() -> expr::Arg = quiet!{name:boxed_expr() {expr::Arg::Positional(name)}}/expected!("<positional arg>")
+ pub rule named_arg() -> expr::Arg = quiet!{name:id() __() "=" __() expr:boxed_expr() {expr::Arg::Named(name, expr)}}/expected!("<named arg>")
+ pub rule args() -> expr::Args
+ = positionals:(positional_arg() ** delimiter()) delimiter() named:(named_arg() ** delimiter()) {
+ expr::Args([&positionals[..], &named[..]].concat())
+ }
+ / named:(named_arg() ** delimiter()) {expr::Args(named)}
+ / positionals:(positional_arg() ** delimiter()) {expr::Args(positionals)}
+ / {expr::Args(Vec::new())}
+
+ pub rule bind() -> expr::Bind
+ = name:id() __() "=" __() expr:boxed_expr() {expr::Bind::Value(name, expr)}
+ / name:id() __() "(" __() params:params() __() ")" __() "=" __() expr:boxed_expr() {expr::Bind::Function(name, params, expr)}
+ pub rule assertion() -> expr::AssertStmt = "assert" _() cond:boxed_expr() msg:(__() ":" __() e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }
+ pub rule string() -> String
+ = "\"" str:$((!['"'][_])+) "\"" {str.to_owned()}
+ / "'" str:$((!['\''][_])+) "'" {str.to_owned()}
+ pub rule field_name() -> expr::FieldName
+ = name:id() {expr::FieldName::Fixed(name)}
+ / name:string() {expr::FieldName::Fixed(name)}
+ / "[" __() expr:boxed_expr() __() "]" {expr::FieldName::Dyn(expr)}
+ pub rule visibility() -> expr::Visibility
+ = ":::" {expr::Visibility::Unhide}
+ / "::" {expr::Visibility::Hidden}
+ / ":" {expr::Visibility::Normal}
+ pub rule field() -> expr::FieldMember
+ = name:field_name() __() plus:"+"? __() visibility:visibility() __() value:expr() {expr::FieldMember::Value{
+ name,
+ plus: plus.is_some(),
+ visibility,
+ value,
+ }}
+ / name:field_name() __() "(" __() params:params() __() ")" __() visibility:visibility() __() value:expr() {expr::FieldMember::Function{
+ name,
+ params,
+ visibility,
+ value,
+ }}
+ pub rule member() -> expr::Member
+ = "local" _() bind:bind() {expr::Member::BindStmt(bind)}
+ / assertion:assertion() {expr::Member::AssertStmt(assertion)}
+ / field:field() {expr::Member::Field(field)}
+ pub rule obj_body() -> expr::ObjBody = members:(member() ** delimiter()) delimiter()? {expr::ObjBody::MemberList(members)}
+ pub rule ifspec() -> expr::IfSpec = "if" _() expr:boxed_expr() {expr::IfSpec(expr)}
+ pub rule forspec() -> expr::ForSpec = "for" _() id:id() _() "in" _() ifs:ifspec()* {expr::ForSpec(id, ifs)}
+ pub rule bind_expr() -> Expr = bind:bind() {Expr::Bind(bind)}
+ pub rule local_expr() -> Expr = "local" _() binds:(bind() ** delimiter()) __() ";" __() expr:boxed_expr() { Expr::LocalExpr(binds, expr) }
+ pub rule string_expr() -> Expr = s:string() {Expr::Str(s)}
+ pub rule parened_expr() -> Expr = "(" e:boxed_expr() ")" {Expr::Parened(e)}
+ pub rule obj_expr() -> Expr = "{" __() body:obj_body() __() "}" {Expr::Obj(body)}
+ pub rule array_expr() -> Expr = "[" __() elems:(expr() ** delimiter()) __() delimiter()? "]" {Expr::Arr(elems)}
+ pub rule array_comp_expr() -> Expr = "[" __() expr:boxed_expr() delimiter()? fors:forspec()+ __() "]" {Expr::ArrComp(expr, fors)}
+ pub rule index_expr() -> Expr
+ = val:boxed_expr() "." idx:id() {Expr::Index(val, Box::new(Expr::Str(idx)))}
+ / val:boxed_expr() "[" key:boxed_expr() "]" {Expr::Index(val, key)}
+ pub rule slice_expr() -> Expr
+ = value:boxed_expr() "[" start:boxed_expr()? ":" pair:(end:boxed_expr()? step:(":" e:boxed_expr() {e})? {(end, step)})? "]" {
+ if let Some((end, step)) = pair {
+ Expr::Slice { value, start, end, step }
+ }else{
+ Expr::Slice{ value, start, end: None, step: None }
+ }
+ }
+ pub rule number_expr() -> Expr = n:number() { expr::Expr::Num(n) }
+ pub rule var_expr() -> Expr = n:id() { expr::Expr::Var(n) }
+ pub rule if_then_else_expr() -> Expr = cond:ifspec() _() "then" _() cond_then:boxed_expr() cond_else:(_() "else" _() e:boxed_expr() {e})? {Expr::IfElse{
+ cond,
+ cond_then,
+ cond_else,
+ }}
+ pub rule expr_basic() -> Expr
+ = "null" {Expr::Literal(Literal::Null)}
+ / "true" {Expr::Literal(Literal::True)} / "false" {Expr::Literal(Literal::False)}
+ / "self" {Expr::Literal(Literal::This)} / "$" {Expr::Literal(Literal::Dollar)}
+ / "super" {Expr::Literal(Literal::Super)}
+
+ / string_expr() / number_expr()
+ / array_expr()
+ / array_comp_expr()
+ / obj_expr()
+ / array_expr()
+ / array_comp_expr()
+
+ / var_expr()
+ / if_then_else_expr()
+ / local_expr()
+
+ rule expr_suffix() -> Suffix
+ = "." __() s:id() { Suffix::String(s) }
+ / "[" __() s:expr() __() "]" { Suffix::Expression(s) }
+ / "(" __() args:args() __() ")" { Suffix::Apply(args) }
+
+ rule expr() -> Expr
+ = a:precedence! {
+ a:(@) __() "||" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Or, Box::new(b))}
+ --
+ a:(@) __() "&&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::And, Box::new(b))}
+ --
+ a:(@) __() "|" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::BitOr, Box::new(b))}
+ --
+ a:@ __() "^" __() b:(@) {Expr::BinaryOp(Box::new(a), expr::BinaryOp::BitXor, Box::new(b))}
+ --
+ a:(@) __() "&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::BitAnd, Box::new(b))}
+ --
+ a:(@) __() "==" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Eq, Box::new(b))}
+ a:(@) __() "!=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Ne, Box::new(b))}
+ --
+ a:(@) __() "<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Lt, Box::new(b))}
+ a:(@) __() ">" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Gt, Box::new(b))}
+ a:(@) __() "<=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Lte, Box::new(b))}
+ a:(@) __() ">=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Gte, Box::new(b))}
+ --
+ a:(@) __() "<<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Lhs, Box::new(b))}
+ a:(@) __() ">>" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Rhs, Box::new(b))}
+ --
+ a:(@) __() "+" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Add, Box::new(b))}
+ a:(@) __() "-" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Sub, Box::new(b))}
+ --
+ a:(@) __() "*" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Mul, Box::new(b))}
+ a:(@) __() "/" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Div, Box::new(b))}
+ a:(@) __() "%" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOp::Mod, Box::new(b))}
+ --
+ e:expr_basic() {e}
+ "(" __() e:expr_basic() __() ")" {Expr::Parened(Box::new(e))}
+ } suffixes:(__() suffix:expr_suffix() {suffix})* {
+ let mut cur = a;
+ for suffix in suffixes {
+ match suffix {
+ Suffix::String(index) => {
+ cur = Expr::Index(Box::new(cur), Box::new(Expr::Str(index)))
+ },
+ Suffix::Expression(index) => {
+ cur = Expr::Index(Box::new(cur), Box::new(index))
+ },
+ Suffix::Apply(args) => {
+ cur = Expr::Apply(Box::new(cur), args)
+ }
+ }
+ }
+ cur
+ }
+ / e:expr_basic() {e}
+
+ pub rule boxed_expr() -> Box<Expr> = e:expr() {Box::new(e)}
+ pub rule jsonnet() -> Expr = __() e:expr() __() {e}
+ }
+}
+
+// TODO: impl FromStr from Expr
+pub fn parse(str: &str) -> Result<Expr, peg::error::ParseError<peg::str::LineCol>> {
+ jsonnet_parser::jsonnet(str)
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::{expr::*, parse};
+ #[test]
+ fn empty_object() {
+ assert_eq!(parse("{}").unwrap(), Expr::Obj(ObjBody::MemberList(vec![])),);
+ }
+ #[test]
+ fn basic_math() {
+ assert_eq!(
+ parse("2+2*2").unwrap(),
+ Expr::BinaryOp(
+ Box::new(Expr::Num(2.0)),
+ BinaryOp::Add,
+ Box::new(Expr::BinaryOp(
+ Box::new(Expr::Num(2.0)),
+ BinaryOp::Mul,
+ Box::new(Expr::Num(2.0))
+ ))
+ )
+ );
+ }
+}