difftreelog
feat parse new object iteration syntax
in: master
10 files changed
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -31,7 +31,7 @@
]
# Destructuring of locals
exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
-# Iteration over objects yields [key, value] elements
+# Iteration over objects using [key]: value syntax
exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"]
# Bigint type
exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -40,8 +40,12 @@
"jrsonnet-peg-parser?/exp-destruct",
"jrsonnet-ir-parser?/exp-destruct",
]
-# Iteration over objects yields [key, value] elements
-exp-object-iteration = []
+# Iteration over objects using [key]: value syntax
+exp-object-iteration = [
+ "jrsonnet-ir/exp-object-iteration",
+ "jrsonnet-peg-parser?/exp-object-iteration",
+ "jrsonnet-ir-parser?/exp-object-iteration",
+]
# Bigint type
exp-bigint = ["num-bigint", "jrsonnet-types/exp-bigint"]
# obj?.field, obj?.['field']
crates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/analyze.rs
+++ b/crates/jrsonnet-evaluator/src/analyze.rs
@@ -1862,6 +1862,8 @@
);
(r, rest)
}
+ #[cfg(feature = "exp-object-iteration")]
+ CompSpec::ForObjSpec(_) => todo!(),
}
}
let outer_depth = stack.depth;
crates/jrsonnet-ir-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-ir-parser/Cargo.toml
+++ b/crates/jrsonnet-ir-parser/Cargo.toml
@@ -11,9 +11,10 @@
[features]
default = []
-experimental = ["exp-null-coaelse", "exp-destruct"]
+experimental = ["exp-null-coaelse", "exp-destruct", "exp-object-iteration"]
exp-null-coaelse = ["jrsonnet-ir/exp-null-coaelse"]
exp-destruct = ["jrsonnet-ir/exp-destruct"]
+exp-object-iteration = ["jrsonnet-ir/exp-object-iteration"]
[dependencies]
insta.workspace = true
crates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir-parser/src/lib.rs
+++ b/crates/jrsonnet-ir-parser/src/lib.rs
@@ -66,6 +66,13 @@
!self.at_eof() && self.peek() == kind
}
+ #[allow(dead_code)]
+ fn nth(&self, n: usize) -> SyntaxKind {
+ self.lexemes
+ .get(self.offset + n)
+ .map_or(SyntaxKind::EOF, |l| l.kind)
+ }
+
fn eat_any(&mut self) {
self.offset += 1;
}
@@ -561,20 +568,36 @@
}
}
-fn for_spec(p: &mut Parser<'_>) -> Result<ForSpecData> {
+fn for_spec(p: &mut Parser<'_>) -> Result<CompSpec> {
p.eat(T![for])?;
+ #[cfg(feature = "exp-object-iteration")]
+ if p.at(T!['[']) && p.nth(1) == SyntaxKind::IDENT && p.nth(2) == T![']'] && p.nth(3) == T![:] {
+ p.eat(T!['['])?;
+ let key = ident(p)?;
+ p.eat(T![']'])?;
+ let visibility = visibility(p)?;
+ let value = destruct(p)?;
+ p.eat(T![in])?;
+ let over = expr(p)?;
+ return Ok(CompSpec::ForObjSpec(jrsonnet_ir::ForObjSpecData {
+ key,
+ visibility,
+ value,
+ over,
+ }));
+ }
let d = destruct(p)?;
p.eat(T![in])?;
let over = expr(p)?;
- Ok(ForSpecData { destruct: d, over })
+ Ok(CompSpec::ForSpec(ForSpecData { destruct: d, over }))
}
fn compspecs(p: &mut Parser<'_>) -> Result<Vec<CompSpec>> {
let mut specs = Vec::new();
- specs.push(CompSpec::ForSpec(for_spec(p)?));
+ specs.push(for_spec(p)?);
loop {
if p.at(T![for]) {
- specs.push(CompSpec::ForSpec(for_spec(p)?));
+ specs.push(for_spec(p)?);
} else if p.at(T![if]) {
let isd = if_spec_data(p)?;
specs.push(CompSpec::IfSpec(isd));
crates/jrsonnet-ir/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-ir/Cargo.toml
+++ b/crates/jrsonnet-ir/Cargo.toml
@@ -12,9 +12,10 @@
[features]
default = []
-experimental = ["exp-destruct", "exp-null-coaelse"]
+experimental = ["exp-destruct", "exp-null-coaelse", "exp-object-iteration"]
exp-destruct = []
exp-null-coaelse = []
+exp-object-iteration = []
[dependencies]
jrsonnet-interner.workspace = true
crates/jrsonnet-ir/src/expr.rsdiffbeforeafterboth1use std::{2 fmt::{self, Debug, Display},3 ops::{Deref, RangeInclusive},4};56use jrsonnet_gcmodule::Acyclic;7use jrsonnet_interner::IStr;89use crate::{10 NumValue,11 function::{FunctionSignature, ParamDefault, ParamName, ParamParse},12 source::Source,13};1415#[derive(Debug, PartialEq, Acyclic)]16pub enum FieldName {17 /// {fixed: 2}18 Fixed(IStr),19 /// {["dyn"+"amic"]: 3}20 Dyn(Expr),21}2223#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]24#[repr(u8)]25pub enum Visibility {26 /// :27 Normal,28 /// ::29 Hidden,30 /// :::31 Unhide,32}3334impl Visibility {35 pub fn is_visible(&self) -> bool {36 matches!(self, Self::Normal | Self::Unhide)37 }38}3940#[derive(Debug, PartialEq, Acyclic)]41pub struct AssertStmt {42 pub assertion: Spanned<Expr>,43 pub message: Option<Expr>,44}4546#[derive(Debug, PartialEq, Acyclic)]47pub struct FieldMember {48 pub name: Spanned<FieldName>,49 pub plus: bool,50 pub params: Option<ExprParams>,51 pub visibility: Visibility,52 pub value: Expr,53}5455#[derive(Debug, PartialEq, Acyclic)]56pub enum Member {57 Field(FieldMember),58 BindStmt(BindSpec),59 AssertStmt(AssertStmt),60}6162#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]63pub enum UnaryOpType {64 Plus,65 Minus,66 BitNot,67 Not,68}6970impl Display for UnaryOpType {71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {72 use UnaryOpType::*;73 write!(74 f,75 "{}",76 match self {77 Plus => "+",78 Minus => "-",79 BitNot => "~",80 Not => "!",81 }82 )83 }84}8586#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]87pub enum BinaryOpType {88 Mul,89 Div,9091 /// Implemented as intrinsic, put here for completeness92 Mod,9394 Add,95 Sub,9697 Lhs,98 Rhs,99100 Lt,101 Gt,102 Lte,103 Gte,104105 BitAnd,106 BitOr,107 BitXor,108109 Eq,110 Neq,111112 And,113 Or,114 #[cfg(feature = "exp-null-coaelse")]115 NullCoaelse,116117 // Equialent to std.objectHasEx(a, b, true)118 In,119}120121impl Display for BinaryOpType {122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {123 use BinaryOpType::*;124 write!(125 f,126 "{}",127 match self {128 Mul => "*",129 Div => "/",130 Mod => "%",131 Add => "+",132 Sub => "-",133 Lhs => "<<",134 Rhs => ">>",135 Lt => "<",136 Gt => ">",137 Lte => "<=",138 Gte => ">=",139 BitAnd => "&",140 BitOr => "|",141 BitXor => "^",142 Eq => "==",143 Neq => "!=",144 And => "&&",145 Or => "||",146 In => "in",147 #[cfg(feature = "exp-null-coaelse")]148 NullCoaelse => "??",149 }150 )151 }152}153154/// name, default value155#[derive(Debug, PartialEq, Acyclic)]156pub struct ExprParam {157 pub destruct: Destruct,158 pub default: Option<Expr>,159}160161/// Defined function parameters162#[derive(Debug, PartialEq, Acyclic)]163pub struct ExprParams {164 pub exprs: Vec<ExprParam>,165 pub signature: FunctionSignature,166 pub(crate) binds_len: usize,167}168impl ExprParams {169 pub fn len(&self) -> usize {170 self.exprs.len()171 }172 pub fn is_empty(&self) -> bool {173 self.exprs.is_empty()174 }175176 pub fn binds_len(&self) -> usize {177 self.binds_len178 }179 pub fn new(exprs: Vec<ExprParam>) -> Self {180 Self {181 signature: FunctionSignature::new(182 exprs183 .iter()184 .map(|p| {185 ParamParse::new(186 p.destruct.name(),187 ParamDefault::exists(p.default.is_some()),188 )189 })190 .collect(),191 ),192 binds_len: exprs.iter().map(|v| v.destruct.binds_len()).sum(),193 exprs,194 }195 }196}197198#[derive(Debug, PartialEq, Acyclic)]199pub struct ArgsDesc {200 pub unnamed: Vec<Expr>,201 pub names: Vec<IStr>,202 pub values: Vec<Expr>,203}204impl ArgsDesc {205 pub fn new(unnamed: Vec<Expr>, names: Vec<IStr>, values: Vec<Expr>) -> Self {206 Self {207 unnamed,208 names,209 values,210 }211 }212}213214#[derive(Debug, PartialEq, Eq, Acyclic)]215pub enum DestructRest {216 /// ...rest217 Keep(IStr),218 /// ...219 Drop,220}221222#[derive(Debug, PartialEq, Acyclic)]223pub enum Destruct {224 Full(Spanned<IStr>),225 #[cfg(feature = "exp-destruct")]226 Skip,227 #[cfg(feature = "exp-destruct")]228 Array {229 start: Vec<Destruct>,230 rest: Option<DestructRest>,231 end: Vec<Destruct>,232 },233 #[cfg(feature = "exp-destruct")]234 Object {235 #[allow(clippy::type_complexity)]236 fields: Vec<(IStr, Option<Destruct>, Option<Spanned<Expr>>)>,237 rest: Option<DestructRest>,238 },239}240impl Destruct {241 /// Name of destructure, used for function parameter names242 pub fn name(&self) -> ParamName {243 match self {244 Self::Full(name) => ParamName::Named(name.value.clone()),245 #[cfg(feature = "exp-destruct")]246 _ => ParamName::Unnamed,247 }248 }249 pub fn binds_len(&self) -> usize {250 #[cfg(feature = "exp-destruct")]251 fn cap_rest(rest: &Option<DestructRest>) -> usize {252 match rest {253 Some(DestructRest::Keep(_)) => 1,254 Some(DestructRest::Drop) => 0,255 None => 0,256 }257 }258 match self {259 Self::Full(_) => 1,260 #[cfg(feature = "exp-destruct")]261 Self::Skip => 0,262 #[cfg(feature = "exp-destruct")]263 Self::Array { start, rest, end } => {264 start.iter().map(Destruct::binds_len).sum::<usize>()265 + end.iter().map(Destruct::binds_len).sum::<usize>()266 + cap_rest(rest)267 }268 #[cfg(feature = "exp-destruct")]269 Self::Object { fields, rest } => {270 let mut out = 0;271 for (_, into, _) in fields {272 match into {273 Some(v) => out += v.binds_len(),274 // Field is destructured to default name275 None => out += 1,276 }277 }278 out + cap_rest(rest)279 }280 }281 }282}283284#[derive(Debug, PartialEq, Acyclic)]285pub enum BindSpec {286 Field {287 into: Destruct,288 value: Expr,289 },290 Function {291 name: IStr,292 params: ExprParams,293 value: Expr,294 },295}296impl BindSpec {297 pub fn binds_len(&self) -> usize {298 match self {299 BindSpec::Field { into, .. } => into.binds_len(),300 BindSpec::Function { .. } => 1,301 }302 }303}304305#[derive(Debug, PartialEq, Acyclic)]306pub struct IfSpecData {307 pub span: Span,308 pub cond: Expr,309}310311#[derive(Debug, PartialEq, Acyclic)]312pub struct ForSpecData {313 pub destruct: Destruct,314 pub over: Expr,315}316317#[cfg(feature = "exp-object-iteration")]318#[derive(Debug, PartialEq, Acyclic)]319pub struct ForObjSpecData {320 pub key: IStr,321 pub visibility: Visibility,322 pub value: Destruct,323 pub over: Expr,324}325326#[derive(Debug, PartialEq, Acyclic)]327pub enum CompSpec {328 IfSpec(IfSpecData),329 ForSpec(ForSpecData),330 #[cfg(feature = "exp-object-iteration")]331 ForObjSpec(ForObjSpecData),332}333334#[derive(Debug, PartialEq, Acyclic)]335pub struct ObjComp {336 pub locals: Vec<BindSpec>,337 pub field: Box<FieldMember>,338 pub compspecs: Vec<CompSpec>,339}340341#[derive(Debug, PartialEq, Acyclic)]342pub struct ObjMembers {343 pub locals: Vec<BindSpec>,344 pub asserts: Vec<AssertStmt>,345 pub fields: Vec<FieldMember>,346}347348#[derive(Debug, PartialEq, Acyclic)]349pub enum ObjBody {350 MemberList(ObjMembers),351 ObjComp(ObjComp),352}353354#[derive(Debug, PartialEq, Eq, Clone, Copy, Acyclic)]355pub enum LiteralType {356 This,357 Super,358 Dollar,359 Null,360 True,361 False,362}363364#[derive(Debug, PartialEq, Acyclic)]365pub struct SliceDesc {366 pub start: Option<Spanned<Expr>>,367 pub end: Option<Spanned<Expr>>,368 pub step: Option<Spanned<Expr>>,369}370371#[derive(Debug, PartialEq, Acyclic)]372pub struct AssertExpr {373 pub assert: AssertStmt,374 pub rest: Expr,375}376377#[derive(Debug, PartialEq, Acyclic)]378pub struct BinaryOp {379 pub lhs: Expr,380 pub op: BinaryOpType,381 pub rhs: Expr,382}383384#[derive(Debug, PartialEq, Acyclic, Clone, Copy)]385pub enum ImportKind {386 Normal,387 Str,388 Bin,389}390391#[derive(Debug, PartialEq, Acyclic)]392pub struct IfElse {393 pub cond: IfSpecData,394 pub cond_then: Expr,395 pub cond_else: Option<Expr>,396}397398#[derive(Debug, PartialEq, Acyclic)]399pub struct Slice {400 pub value: Expr,401 pub slice: SliceDesc,402}403404/// Syntax base405#[derive(Debug, PartialEq, Acyclic)]406pub enum Expr {407 Literal(LiteralType),408409 /// String value: "hello"410 Str(IStr),411 /// Number: 1, 2.0, 2e+20412 Num(NumValue),413 /// Variable name: test414 Var(Spanned<IStr>),415416 /// Array of expressions: [1, 2, "Hello"]417 Arr(Vec<Expr>),418 /// Array comprehension:419 /// ```jsonnet420 /// ingredients: [421 /// { kind: kind, qty: 4 / 3 }422 /// for kind in [423 /// 'Honey Syrup',424 /// 'Lemon Juice',425 /// 'Farmers Gin',426 /// ]427 /// ],428 /// ```429 ArrComp(Box<Expr>, Vec<CompSpec>),430431 /// Object: {a: 2}432 Obj(ObjBody),433 /// Object extension: var1 {b: 2}434 ObjExtend(Box<Expr>, ObjBody),435436 /// -2437 UnaryOp(UnaryOpType, Box<Expr>),438 /// 2 - 2439 BinaryOp(Box<BinaryOp>),440 /// assert 2 == 2 : "Math is broken"441 AssertExpr(Box<AssertExpr>),442 /// local a = 2; { b: a }443 LocalExpr(Vec<BindSpec>, Box<Expr>),444445 /// import* "hello"446 Import(Spanned<ImportKind>, Box<Expr>),447 /// error "I'm broken"448 ErrorStmt(Span, Box<Expr>),449 /// a(b, c)450 Apply(Box<Expr>, Spanned<ArgsDesc>, bool),451 /// a[b], a.b, a?.b452 Index {453 indexable: Box<Expr>,454 parts: Vec<IndexPart>,455 },456 /// function(x) x457 Function(ExprParams, Box<Expr>),458 /// if true == false then 1 else 2459 IfElse(Box<IfElse>),460 Slice(Box<Slice>),461}462463#[derive(Debug, PartialEq, Acyclic)]464pub struct IndexPart {465 pub span: Span,466 pub value: Expr,467 #[cfg(feature = "exp-null-coaelse")]468 pub null_coaelse: bool,469}470471/// file, begin offset, end offset472#[derive(Clone, PartialEq, Eq, Acyclic)]473#[repr(C)]474pub struct Span(pub Source, pub u32, pub u32);475impl Span {476 pub fn belongs_to(&self, other: &Span) -> bool {477 other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2478 }479 pub fn range(&self) -> RangeInclusive<usize> {480 let start = self.1;481 let mut end = self.2;482 if end > start {483 // Because it is originally exclusive484 end -= 1;485 }486 start as usize..=end as usize487 }488}489490#[cfg(target_pointer_width = "64")]491static_assertions::assert_eq_size!(Span, (usize, usize));492493impl Debug for Span {494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {495 write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)496 }497}498499#[derive(Clone, PartialEq, Acyclic)]500pub struct Spanned<T: Acyclic> {501 pub value: T,502 pub span: Span,503}504impl<T: Acyclic> Deref for Spanned<T> {505 type Target = T;506 fn deref(&self) -> &Self::Target {507 &self.value508 }509}510impl<T: Acyclic> Spanned<T> {511 #[inline]512 pub fn new(value: T, span: Span) -> Self {513 Self { value, span }514 }515 pub fn map<U: Acyclic>(self, v: impl FnOnce(T) -> U) -> Spanned<U> {516 Spanned {517 span: self.span,518 value: v(self.value),519 }520 }521 pub fn as_ref<'a>(&'a self) -> Spanned<&'a T>522 where523 &'a T: Acyclic,524 {525 Spanned {526 span: self.span.clone(),527 value: &self.value,528 }529 }530}531532impl<T: Debug + Acyclic> Debug for Spanned<T> {533 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {534 let expr = &**self;535 if f.alternate() {536 write!(f, "{:#?}", expr)?;537 } else {538 write!(f, "{:?}", expr)?;539 }540 write!(f, " from {:?}", self.span)?;541 Ok(())542 }543}crates/jrsonnet-ir/src/visit.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir/src/visit.rs
+++ b/crates/jrsonnet-ir/src/visit.rs
@@ -1,5 +1,7 @@
use jrsonnet_interner::IStr;
+#[cfg(feature = "exp-object-iteration")]
+use crate::ForObjSpecData;
use crate::{
ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BindSpec, CompSpec, Destruct, Expr, ExprParam,
ExprParams, FieldMember, FieldName, ForSpecData, IfElse, IfSpecData, ImportKind, IndexPart,
@@ -69,6 +71,17 @@
visit_destruct(v, destruct);
v.visit_expr(over);
}
+ #[cfg(feature = "exp-object-iteration")]
+ CompSpec::ForObjSpec(for_obj_spec_data) => {
+ let ForObjSpecData {
+ key: _,
+ visibility: _,
+ value,
+ over,
+ } = for_obj_spec_data;
+ visit_destruct(v, value);
+ v.visit_expr(over);
+ }
}
}
pub fn visit_params<V: Visitor>(v: &mut V, par: &ExprParams) {
crates/jrsonnet-peg-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/Cargo.toml
+++ b/crates/jrsonnet-peg-parser/Cargo.toml
@@ -22,6 +22,7 @@
[features]
default = []
-experimental = ["exp-destruct", "exp-null-coaelse"]
+experimental = ["exp-destruct", "exp-null-coaelse", "exp-object-iteration"]
exp-destruct = ["jrsonnet-ir/exp-destruct"]
exp-null-coaelse = ["jrsonnet-ir/exp-null-coaelse"]
+exp-object-iteration = ["jrsonnet-ir/exp-object-iteration"]
crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -241,11 +241,21 @@
= i:spanned(<keyword("if")>, s) _ cond:expr(s) {IfSpecData { span: i.span, cond }}
pub rule forspec(s: &ParserSettings) -> ForSpecData
= keyword("for") _ destruct:destruct(s) _ keyword("in") _ over:expr(s) { ForSpecData { destruct, over } }
+ rule ensure_object_iteration()
+ = "" {?
+ #[cfg(not(feature = "exp-object-iteration"))] return Err("!!!experimental object iteration was not enabled");
+ #[cfg(feature = "exp-object-iteration")] Ok(())
+ }
+ pub rule forobjspec(s: &ParserSettings) -> CompSpec
+ = ensure_object_iteration() keyword("for") _ "[" _ key:id() _ "]" _ vis:visibility() _ value:destruct(s) _ keyword("in") _ over:expr(s) {
+ #[cfg(feature = "exp-object-iteration")] return CompSpec::ForObjSpec(jrsonnet_ir::ForObjSpecData { key, visibility: vis, value, over });
+ #[cfg(not(feature = "exp-object-iteration"))] unreachable!("ensure_object_iteration will fail if feature is not enabled")
+ }
rule compspec(s: &ParserSettings) -> CompSpec
- = i:ifspec(s) { CompSpec::IfSpec(i) } / f:forspec(s) {CompSpec::ForSpec(f)}
+ = i:ifspec(s) { CompSpec::IfSpec(i) } / f:forobjspec(s) { f } / f:forspec(s) {CompSpec::ForSpec(f)}
pub rule compspecs(s: &ParserSettings) -> Vec<CompSpec>
= specs:compspec(s) ++ _ {?
- if !matches!(specs[0], CompSpec::ForSpec(_)) {
+ if matches!(specs[0], CompSpec::IfSpec(_)) {
return Err("<first compspec should be for>")
}
Ok(specs)