difftreelog
feat(evaluator) create evaluator crate BREAKING CHANGE: rename Expr internals to not match Expr variants
in: master
6 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,13 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
+name = "jsonnet-evaluator"
+version = "0.1.0"
+dependencies = [
+ "jsonnet-parser",
+]
+
+[[package]]
name = "jsonnet-parser"
version = "0.1.0"
dependencies = [
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,2 +1,5 @@
[workspace]
-members = ["crates/jsonnet-parser"]
+members = [
+ "crates/jsonnet-parser",
+ "crates/jsonnet-evaluator",
+]
crates/jsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-evaluator/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "jsonnet-evaluator"
+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]
+jsonnet-parser = { path = "../jsonnet-parser" }
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -0,0 +1,59 @@
+#![feature(box_syntax, box_patterns)]
+
+use jsonnet_parser::*;
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum Val {
+ Str(String),
+ Num(f64),
+}
+
+pub fn evaluate(expr: &Expr) -> Val {
+ use Expr::*;
+ match expr {
+ Parened(e) => evaluate(e),
+ Str(v) => Val::Str(v.clone()),
+ Num(v) => Val::Num(*v),
+ BinaryOp(v1, o, v2) => match (evaluate(v1), o, evaluate(v2)) {
+ (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1 + &v2),
+ (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => {
+ Val::Str(v1.repeat(v2 as usize))
+ },
+ (Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),
+ (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),
+ _ => panic!("Can't evaluate binary op: {:?} {:?} {:?}", v1, o, v2),
+ },
+ _ => panic!("Can't evaluate: {:?}", expr),
+ }
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::{evaluate, Val};
+ use jsonnet_parser::parse;
+ #[test]
+ fn math_evaluation() {
+ assert_eq!(evaluate(&parse("2+2*2").unwrap()), Val::Num(6.0));
+ }
+
+ #[test]
+ fn math_evaluation_with_parened() {
+ assert_eq!(evaluate(&parse("3+(2+2*2)").unwrap()), Val::Num(9.0));
+ }
+
+ #[test]
+ fn string_concat() {
+ assert_eq!(
+ evaluate(&parse("\"Hello\"+\"World\"").unwrap()),
+ Val::Str("HelloWorld".to_owned()),
+ );
+ }
+
+ #[test]
+ fn string_repeat() {
+ assert_eq!(
+ evaluate(&parse("\"Hello\"*3").unwrap()),
+ Val::Str("HelloHelloHello".to_owned()),
+ );
+ }
+}
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--- a/crates/jsonnet-parser/src/lib.rs
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -1,7 +1,7 @@
use peg::parser;
-pub mod expr;
-use expr::{Expr, Literal};
+mod expr;
+pub use expr::*;
enum Suffix {
String(String),
@@ -101,10 +101,11 @@
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)}
+ = "null" {Expr::Value(ValueType::Null)}
+ / "true" {Expr::Value(ValueType::True)} / "false" {Expr::Value(ValueType::False)}
+
+ / "self" {Expr::Literal(LiteralType::This)} / "$" {Expr::Literal(LiteralType::Dollar)}
+ / "super" {Expr::Literal(LiteralType::Super)}
/ string_expr() / number_expr()
/ array_expr()
@@ -124,36 +125,36 @@
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::BinaryOpType::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::BinaryOpType::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::BinaryOpType::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::BinaryOpType::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::BinaryOpType::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::BinaryOpType::Eq, Box::new(b))}
+ a:(@) __() "!=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::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::BinaryOpType::Lt, Box::new(b))}
+ a:(@) __() ">" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gt, Box::new(b))}
+ a:(@) __() "<=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lte, Box::new(b))}
+ a:(@) __() ">=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::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::BinaryOpType::Lhs, Box::new(b))}
+ a:(@) __() ">>" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::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::BinaryOpType::Add, Box::new(b))}
+ a:(@) __() "-" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::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))}
+ a:(@) __() "*" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mul, Box::new(b))}
+ a:(@) __() "/" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Div, Box::new(b))}
+ a:(@) __() "%" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mod, Box::new(b))}
--
e:expr_basic() {e}
- "(" __() e:expr_basic() __() ")" {Expr::Parened(Box::new(e))}
+ "(" __() e:boxed_expr() __() ")" {Expr::Parened(e)}
} suffixes:(__() suffix:expr_suffix() {suffix})* {
let mut cur = a;
for suffix in suffixes {
@@ -196,10 +197,10 @@
parse("2+2*2").unwrap(),
Expr::BinaryOp(
Box::new(Expr::Num(2.0)),
- BinaryOp::Add,
+ BinaryOpType::Add,
Box::new(Expr::BinaryOp(
Box::new(Expr::Num(2.0)),
- BinaryOp::Mul,
+ BinaryOpType::Mul,
Box::new(Expr::Num(2.0))
))
)