From 0b0d703c6d0521153d3fddf070a6752a9c5a8298 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 04 Jul 2021 21:20:07 +0000 Subject: [PATCH] refactor: do not desugar mod/slice --- --- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -1,13 +1,14 @@ use crate::{ equals, error::{Error::*, Result}, + operator::evaluate_mod_op, parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, EvaluationState, - FuncVal, LazyVal, Val, + FuncVal, IndexableVal, LazyVal, Val, }; use format::{format_arr, format_obj}; use jrsonnet_gc::Gc; use jrsonnet_interner::IStr; -use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation}; +use jrsonnet_parser::{ArgsDesc, ExprLocation}; use jrsonnet_types::ty; use std::{collections::HashMap, path::PathBuf, rc::Rc}; @@ -20,7 +21,7 @@ pub mod manifest; pub mod sort; -fn std_format(str: IStr, vals: Val) -> Result { +pub fn std_format(str: IStr, vals: Val) -> Result { push( Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), || format!("std.format of {}", str), @@ -34,6 +35,38 @@ ) } +pub fn std_slice( + indexable: IndexableVal, + index: Option, + end: Option, + step: Option, +) -> Result { + let index = index.unwrap_or(0); + let end = end.unwrap_or_else(|| match &indexable { + IndexableVal::Str(_) => usize::MAX, + IndexableVal::Arr(v) => v.len(), + }); + let step = step.unwrap_or(1); + match &indexable { + IndexableVal::Str(s) => Ok(Val::Str( + (s.chars() + .skip(index) + .take(end - index) + .step_by(step) + .collect::()) + .into(), + )), + IndexableVal::Arr(arr) => Ok(Val::Arr( + (arr.iter() + .skip(index) + .take(end - index) + .step_by(step) + .collect::>>()?) + .into(), + )), + } +} + type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result; type BuiltinsType = HashMap, Builtin>; @@ -188,34 +221,12 @@ 2, end: ty!((number | null)); 3, step: ty!((number | null)); ], { - let index = match index { - Val::Num(v) => v as usize, - Val::Null => 0, - _ => unreachable!(), - }; - let end = match end { - Val::Num(v) => v as usize, - Val::Null => match &indexable { - Val::Str(s) => s.chars().count(), - Val::Arr(v) => v.len(), - _ => unreachable!() - }, - _ => unreachable!() - }; - let step = match step { - Val::Num(v) => v as usize, - Val::Null => 1, - _ => unreachable!() - }; - match &indexable { - Val::Str(s) => { - Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::()).into())) - } - Val::Arr(arr) => { - Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::>>()?).into())) - } - _ => unreachable!() - } + std_slice( + indexable.to_indexable()?, + index.try_cast_nullable_num("index")?.map(|v| v as usize), + end.try_cast_nullable_num("end")?.map(|v| v as usize), + step.try_cast_nullable_num("step")?.map(|v| v as usize), + ) }) } @@ -257,11 +268,7 @@ 0, a: ty!((number | string)); 1, b: ty!(any); ], { - match (a, b) { - (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)), - (Val::Str(str), vals) => std_format(str, vals), - (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type())) - } + evaluate_mod_op(&a, &b) }) } --- a/crates/jrsonnet-evaluator/src/error.rs +++ b/crates/jrsonnet-evaluator/src/error.rs @@ -73,6 +73,8 @@ ValueIndexMustBeTypeGot(ValType, ValType, ValType), #[error("cant index into {0}")] CantIndexInto(ValType), + #[error("{0} is not indexable")] + ValueIsNotIndexable(ValType), #[error("super can't be used standalone")] StandaloneSuper, --- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs @@ -1,4 +1,5 @@ use crate::{ + builtin::std_slice, error::Error::*, evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op}, push, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal, @@ -679,6 +680,28 @@ } } } + Slice(value, desc) => { + let indexable = evaluate(context.clone(), value)?; + + fn parse_num( + context: &Context, + expr: Option<&LocExpr>, + desc: &'static str, + ) -> Result> { + Ok(match expr { + Some(s) => evaluate(context.clone(), &s)? + .try_cast_nullable_num(desc)? + .map(|v| v as usize), + None => None, + }) + } + + let start = parse_num(&context, desc.start.as_ref(), "start")?; + let end = parse_num(&context, desc.end.as_ref(), "end")?; + let step = parse_num(&context, desc.step.as_ref(), "step")?; + + std_slice(indexable.to_indexable()?, start, end, step)? + } Import(path) => { let tmp = loc .clone() --- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs +++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs @@ -1,3 +1,4 @@ +use crate::builtin::std_format; use crate::{equals, evaluate, Context, Val}; use crate::{error::Error::*, throw, Result}; use jrsonnet_parser::{BinaryOpType, LocExpr, UnaryOpType}; @@ -41,6 +42,19 @@ }) } +pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result { + use Val::*; + match (a, b) { + (Num(a), Num(b)) => Ok(Num(a % b)), + (Str(str), vals) => std_format(str.clone(), vals.clone()), + (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues( + BinaryOpType::Mod, + a.value_type(), + b.value_type() + )), + } +} + pub fn evaluate_binary_op_special( context: Context, a: &LocExpr, @@ -60,13 +74,14 @@ use BinaryOpType::*; use Val::*; Ok(match (a, op, b) { - (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)), - (a, Add, b) => evaluate_add_op(a, b)?, (a, Eq, b) => Bool(equals(a, b)?), (a, Neq, b) => Bool(!equals(a, b)?), + (Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)), + (a, Mod, b) => evaluate_mod_op(a, b)?, + (Str(v1), Mul, Num(v2)) => Str(v1.repeat(*v2 as usize).into()), // Bool X Bool --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -345,6 +345,11 @@ } } +pub enum IndexableVal { + Str(IStr), + Arr(ArrValue), +} + #[derive(Debug, Clone, Trace)] #[trivially_drop] pub enum Val { @@ -402,6 +407,17 @@ self.assert_type(context, ValType::Num)?; self.unwrap_num() } + pub fn try_cast_nullable_num(self, context: &'static str) -> Result> { + Ok(match self { + Val::Null => None, + Val::Num(num) => Some(num), + _ => throw!(TypeMismatch( + context, + vec![ValType::Null, ValType::Num], + self.value_type() + )), + }) + } pub const fn value_type(&self) -> ValType { match self { Self::Str(..) => ValType::Str, @@ -580,6 +596,13 @@ .try_cast_str("to json") }) } + pub fn to_indexable(self) -> Result { + Ok(match self { + Val::Str(s) => IndexableVal::Str(s), + Val::Arr(arr) => IndexableVal::Arr(arr), + _ => throw!(ValueIsNotIndexable(self.value_type())), + }) + } } const fn is_function_like(val: &Val) -> bool { --- a/crates/jrsonnet-parser/src/expr.rs +++ b/crates/jrsonnet-parser/src/expr.rs @@ -274,6 +274,8 @@ False, } +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] #[derive(Debug, PartialEq, Trace)] #[trivially_drop] pub struct SliceDesc { @@ -349,6 +351,7 @@ cond_then: LocExpr, cond_else: Option, }, + Slice(LocExpr, SliceDesc), } /// file, begin offset, end offset --- a/crates/jrsonnet-parser/src/lib.rs +++ b/crates/jrsonnet-parser/src/lib.rs @@ -14,6 +14,17 @@ pub file_name: Rc, } +macro_rules! expr_bin { + ($a:ident $op:ident $b:ident) => { + loc_expr_todo!(Expr::BinaryOp($a, $op, $b)) + }; +} +macro_rules! expr_un { + ($op:ident $a:ident) => { + loc_expr_todo!(Expr::UnaryOp($op, $a)) + }; +} + parser! { grammar jsonnet_parser() for str { use peg::ParseLiteral; @@ -219,54 +230,43 @@ use BinaryOpType::*; + use UnaryOpType::*; rule expr(s: &ParserSettings) -> LocExpr = start:position!() a:precedence! { - a:(@) _ binop(<"||">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Or, b))} + a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)} -- - a:(@) _ binop(<"&&">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, And, b))} + a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)} -- - a:(@) _ binop(<"|">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BitOr, b))} + a:(@) _ binop(<"|">) _ b:@ {expr_bin!(a BitOr b)} -- - a:@ _ binop(<"^">) _ b:(@) {loc_expr_todo!(Expr::BinaryOp(a, BitXor, b))} + a:@ _ binop(<"^">) _ b:(@) {expr_bin!(a BitXor b)} -- - a:(@) _ binop(<"&">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BitAnd, b))} + a:(@) _ binop(<"&">) _ b:@ {expr_bin!(a BitAnd b)} -- - a:(@) _ binop(<"==">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Eq, b))} - a:(@) _ binop(<"!=">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Neq, b))} + a:(@) _ binop(<"==">) _ b:@ {expr_bin!(a Eq b)} + a:(@) _ binop(<"!=">) _ b:@ {expr_bin!(a Neq b)} -- - a:(@) _ binop(<"<">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Lt, b))} - a:(@) _ binop(<">">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Gt, b))} - a:(@) _ binop(<"<=">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Lte, b))} - a:(@) _ binop(<">=">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Gte, b))} - a:(@) _ binop() _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, In, b))} + a:(@) _ binop(<"<">) _ b:@ {expr_bin!(a Lt b)} + a:(@) _ binop(<">">) _ b:@ {expr_bin!(a Gt b)} + a:(@) _ binop(<"<=">) _ b:@ {expr_bin!(a Lte b)} + a:(@) _ binop(<">=">) _ b:@ {expr_bin!(a Gte b)} + a:(@) _ binop() _ b:@ {expr_bin!(a In b)} -- - a:(@) _ binop(<"<<">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Lhs, b))} - a:(@) _ binop(<">>">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Rhs, b))} + a:(@) _ binop(<"<<">) _ b:@ {expr_bin!(a Lhs b)} + a:(@) _ binop(<">>">) _ b:@ {expr_bin!(a Rhs b)} -- - a:(@) _ binop(<"+">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Add, b))} - a:(@) _ binop(<"-">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Sub, b))} + a:(@) _ binop(<"+">) _ b:@ {expr_bin!(a Add b)} + a:(@) _ binop(<"-">) _ b:@ {expr_bin!(a Sub b)} -- - a:(@) _ binop(<"*">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Mul, b))} - a:(@) _ binop(<"/">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Div, b))} - a:(@) _ binop(<"%">) _ b:@ {loc_expr_todo!(Expr::Apply( - el!(Expr::Intrinsic("mod".into())), ArgsDesc(vec![Arg(None, a), Arg(None, b)]), - false - ))} + a:(@) _ binop(<"*">) _ b:@ {expr_bin!(a Mul b)} + a:(@) _ binop(<"/">) _ b:@ {expr_bin!(a Div b)} + a:(@) _ binop(<"%">) _ b:@ {expr_bin!(a Mod b)} -- - unaryop(<"-">) _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Minus, b))} - unaryop(<"!">) _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, b))} - unaryop(<"~">) _ b:@ { loc_expr_todo!(Expr::UnaryOp(UnaryOpType::BitNot, b)) } + unaryop(<"-">) _ b:@ {expr_un!(Minus b)} + unaryop(<"!">) _ b:@ {expr_un!(Not b)} + unaryop(<"~">) _ b:@ {expr_un!(BitNot b)} -- - a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Apply( - el!(Expr::Intrinsic("slice".into())), - ArgsDesc(vec![ - Arg(None, a), - Arg(None, s.start.unwrap_or_else(||el!(Expr::Literal(LiteralType::Null)))), - Arg(None, s.end.unwrap_or_else(||el!(Expr::Literal(LiteralType::Null)))), - Arg(None, s.step.unwrap_or_else(||el!(Expr::Literal(LiteralType::Null)))), - ]), - true, - ))} + a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Slice(a, s))} a:(@) _ "." _ s:$(id()) {loc_expr_todo!(Expr::Index(a, el!(Expr::Str(s.into()))))} a:(@) _ "[" _ s:expr(s) _ "]" {loc_expr_todo!(Expr::Index(a, s))} a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {loc_expr_todo!(Expr::Apply(a, args, ts.is_some()))} -- gitstuff