difftreelog
feat(evaluator) stacktrace preparation
in: master
3 files changed
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -67,37 +67,25 @@
pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {
match (op, b) {
- (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),
- (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),
- (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),
+ (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()),
+ (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),
(op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),
}
}
pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {
match (a, op, b) {
- (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),
- (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),
+ (Val::Lazy(a), o, b) => evaluate_binary_op(&a.evaluate(), o, b),
+ (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.evaluate()),
(Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),
- (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),
(Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),
(Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),
(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
- (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
- bool_val(false)
- }
- (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
- bool_val(false)
- }
- (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
- bool_val(false)
- }
- (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
- bool_val(true)
- }
+ (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),
+ (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),
(Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
@@ -134,6 +122,8 @@
(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {
Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)
}
+ (a, BinaryOpType::Eq, b) => bool_val(a == b),
+ (a, BinaryOpType::Ne, b) => bool_val(a != b),
_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),
}
}
@@ -238,7 +228,7 @@
pub fn evaluate(context: Context, expr: &LocExpr) -> Val {
use Expr::*;
- let LocExpr(expr, _location) = expr;
+ let LocExpr(expr, loc) = expr;
match &**expr {
Literal(LiteralType::This) => Val::Obj(
context
@@ -252,7 +242,9 @@
.clone()
.unwrap_or_else(|| panic!("super not found")),
),
- Literal(t) => Val::Literal(t.clone()),
+ Literal(LiteralType::True) => Val::Bool(true),
+ Literal(LiteralType::False) => Val::Bool(false),
+ Literal(LiteralType::Null) => Val::Null,
Parened(e) => evaluate(context, e),
Str(v) => Val::Str(v.clone()),
Num(v) => Val::Num(*v),
@@ -383,13 +375,16 @@
cond_then,
cond_else,
} => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {
- Val::Literal(LiteralType::True) => evaluate(context, cond_then),
- Val::Literal(LiteralType::False) => match cond_else {
+ Val::Bool(true) => evaluate(context, cond_then),
+ Val::Bool(false) => match cond_else {
Some(v) => evaluate(context, v),
- None => Val::Literal(LiteralType::False),
+ None => Val::Bool(false),
},
v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),
},
- _ => panic!("evaluation not implemented: {:?}", expr),
+ _ => panic!(
+ "evaluation not implemented: {:?}",
+ LocExpr(expr.clone(), loc.clone())
+ ),
}
}
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -13,6 +13,7 @@
pub use evaluate::*;
use jsonnet_parser::*;
pub use obj::*;
+use std::{cell::RefCell, collections::HashMap, rc::Rc};
pub use val::*;
rc_fn_helper!(
@@ -32,48 +33,131 @@
dyn Fn(Context, LocExpr) -> Val
);
+pub struct ExitGuard<'s>(&'s EvaluationState);
+impl<'s> Drop for ExitGuard<'s> {
+ fn drop(&mut self) {
+ self.0.stack.borrow_mut().pop();
+ }
+}
+
+pub struct EvaluationState {
+ pub stack: Rc<RefCell<Vec<LocExpr>>>,
+ pub files: Rc<RefCell<HashMap<String, String>>>,
+}
+impl EvaluationState {
+ #[must_use = "should keep exit guard before exit from function"]
+ pub fn push(&self, e: LocExpr) -> ExitGuard {
+ self.stack.borrow_mut().push(e);
+ ExitGuard(self)
+ }
+ pub fn print_stack_trace(&self) {
+ for e in self
+ .stack
+ .borrow()
+ .iter()
+ .rev()
+ .map(|e| e.1.clone())
+ .flatten()
+ {
+ println!("{:?}", e)
+ }
+ }
+}
+impl Default for EvaluationState {
+ fn default() -> Self {
+ EvaluationState {
+ stack: Rc::new(RefCell::new(Vec::new())),
+ files: Rc::new(RefCell::new(HashMap::new())),
+ }
+ }
+}
+
#[cfg(test)]
pub mod tests {
use super::{evaluate, Context, Val};
+ use crate::EvaluationState;
use jsonnet_parser::*;
+ #[test]
+ fn eval_state_stacktrace() {
+ let state = EvaluationState::default();
+ let _v = state.push(loc_expr!(
+ Expr::Num(0.0),
+ true,
+ ("test.jsonnet".to_owned(), 10, 20)
+ ));
+
+ state.print_stack_trace()
+ }
+
macro_rules! eval {
($str: expr) => {
- evaluate(Context::new(), &parse($str, &ParserSettings {
- loc_data: false,
- file_name: "test.jsonnet".to_owned(),
- }).unwrap())
+ evaluate(
+ Context::new(),
+ &parse(
+ $str,
+ &ParserSettings {
+ loc_data: true,
+ file_name: "test.jsonnet".to_owned(),
+ },
+ )
+ .unwrap(),
+ )
};
}
macro_rules! eval_stdlib {
($str: expr) => {{
let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";
- evaluate(Context::new(), &parse(&(std + $str), &ParserSettings {
- loc_data: false,
- file_name: "test.jsonnet".to_owned(),
- }).unwrap())
+ evaluate(
+ Context::new(),
+ &parse(
+ &(std + $str),
+ &ParserSettings {
+ loc_data: true,
+ file_name: "test.jsonnet".to_owned(),
+ },
+ )
+ .unwrap(),
+ )
}};
}
macro_rules! assert_eval {
($str: expr) => {
assert_eq!(
- evaluate(Context::new(), &parse($str, &ParserSettings {
- loc_data: false,
- file_name: "test.jsonnet".to_owned(),
- }).unwrap()),
- Val::Literal(LiteralType::True)
+ evaluate(
+ Context::new(),
+ &parse(
+ $str,
+ &ParserSettings {
+ loc_data: true,
+ file_name: "test.jsonnet".to_owned(),
+ }
+ )
+ .unwrap()
+ ),
+ Val::Bool(true)
)
};
}
macro_rules! assert_json {
($str: expr, $out: expr) => {
assert_eq!(
- format!("{}", evaluate(Context::new(), &parse($str, &ParserSettings {
- loc_data: false,
- file_name: "test.jsonnet".to_owned(),
- }).unwrap())),
+ format!(
+ "{}",
+ evaluate(
+ Context::new(),
+ &parse(
+ $str,
+ &ParserSettings {
+ loc_data: true,
+ file_name: "test.jsonnet".to_owned(),
+ }
+ )
+ .unwrap()
+ )
+ ),
$out
)
};
@@ -86,11 +170,18 @@
macro_rules! assert_eval_neg {
($str: expr) => {
assert_eq!(
- evaluate(Context::new(), &parse($str, &ParserSettings {
- loc_data: false,
- file_name: "test.jsonnet".to_owned(),
- }).unwrap()),
- Val::Literal(LiteralType::False)
+ evaluate(
+ Context::new(),
+ &parse(
+ $str,
+ &ParserSettings {
+ loc_data: true,
+ file_name: "test.jsonnet".to_owned(),
+ }
+ )
+ .unwrap()
+ ),
+ Val::Bool(false)
)
};
}
@@ -241,7 +332,7 @@
fn string_is_string() {
assert_eq!(
eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),
- Val::Literal(LiteralType::False)
+ Val::Bool(false)
);
}
@@ -257,4 +348,40 @@
r#"{"c":128526,"l":1}"#
)
}
+
+ #[test]
+ fn json() {
+ println!("{:?}", eval_stdlib!(r#"std.manifestJson({a:3, b:4, c:6})"#));
+ }
+
+ #[test]
+ fn sjsonnet() {
+ eval!(
+ r#"
+ local x0 = {k: 1};
+ local x1 = {k: x0.k + x0.k};
+ local x2 = {k: x1.k + x1.k};
+ local x3 = {k: x2.k + x2.k};
+ local x4 = {k: x3.k + x3.k};
+ local x5 = {k: x4.k + x4.k};
+ local x6 = {k: x5.k + x5.k};
+ local x7 = {k: x6.k + x6.k};
+ local x8 = {k: x7.k + x7.k};
+ local x9 = {k: x8.k + x8.k};
+ local x10 = {k: x9.k + x9.k};
+ local x11 = {k: x10.k + x10.k};
+ local x12 = {k: x11.k + x11.k};
+ local x13 = {k: x12.k + x12.k};
+ local x14 = {k: x13.k + x13.k};
+ local x15 = {k: x14.k + x14.k};
+ local x16 = {k: x15.k + x15.k};
+ local x17 = {k: x16.k + x16.k};
+ local x18 = {k: x17.k + x17.k};
+ local x19 = {k: x18.k + x18.k};
+ local x20 = {k: x19.k + x19.k};
+ local x21 = {k: x20.k + x20.k};
+ x21.k
+ "#
+ );
+ }
}
crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth1use std::{2 fmt::{Debug, Display},3 rc::Rc,4};56#[derive(Debug, Clone, PartialEq)]7pub enum FieldName {8 /// {fixed: 2}9 Fixed(String),10 /// {["dyn"+"amic"]: 3}11 Dyn(LocExpr),12}1314#[derive(Debug, Clone, PartialEq)]15pub enum Visibility {16 /// :17 Normal,18 /// ::19 Hidden,20 /// :::21 Unhide,22}2324#[derive(Debug, Clone, PartialEq)]25pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2627#[derive(Debug, Clone, PartialEq)]28pub struct FieldMember {29 pub name: FieldName,30 pub plus: bool,31 pub params: Option<ParamsDesc>,32 pub visibility: Visibility,33 pub value: LocExpr,34}3536#[derive(Debug, Clone, PartialEq)]37pub enum Member {38 Field(FieldMember),39 BindStmt(BindSpec),40 AssertStmt(AssertStmt),41}4243#[derive(Debug, Clone, Copy, PartialEq)]44pub enum UnaryOpType {45 Plus,46 Minus,47 BitNot,48 Not,49}5051#[derive(Debug, Clone, Copy, PartialEq)]52pub enum BinaryOpType {53 Mul,54 Div,55 Mod,5657 Add,58 Sub,5960 Lhs,61 Rhs,6263 Lt,64 Gt,65 Lte,66 Gte,6768 In,6970 Eq,71 Ne,7273 BitAnd,74 BitOr,75 BitXor,7677 And,78 Or,79}8081/// name, default value82#[derive(Debug, Clone, PartialEq)]83pub struct Param(pub String, pub Option<LocExpr>);84/// Defined function parameters85#[derive(Debug, Clone, PartialEq)]86pub struct ParamsDesc(pub Vec<Param>);87impl ParamsDesc {88 pub fn with_defaults(&self) -> Vec<Param> {89 self.0.iter().filter(|e| e.1.is_some()).cloned().collect()90 }91}9293#[derive(Debug, Clone, PartialEq)]94pub struct Arg(pub Option<String>, pub LocExpr);95#[derive(Debug, Clone, PartialEq)]96pub struct ArgsDesc(pub Vec<Arg>);9798#[derive(Debug, Clone, PartialEq)]99pub struct BindSpec {100 pub name: String,101 pub params: Option<ParamsDesc>,102 pub value: LocExpr,103}104105#[derive(Debug, Clone, PartialEq)]106pub struct IfSpecData(pub LocExpr);107#[derive(Debug, Clone, PartialEq)]108pub struct ForSpecData(pub String, pub LocExpr);109110#[derive(Debug, Clone, PartialEq)]111pub enum CompSpec {112 IfSpec(IfSpecData),113 ForSpec(ForSpecData),114}115116#[derive(Debug, Clone, PartialEq)]117pub enum ObjBody {118 MemberList(Vec<Member>),119 ObjComp {120 pre_locals: Vec<BindSpec>,121 key: LocExpr,122 value: LocExpr,123 post_locals: Vec<BindSpec>,124 first: ForSpecData,125 rest: Vec<CompSpec>,126 },127}128129#[derive(Debug, Clone, PartialEq)]130pub enum LiteralType {131 This,132 Super,133 Dollar,134 Null,135 True,136 False,137}138impl Display for LiteralType {139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {140 use LiteralType::*;141 match self {142 This => write!(f, "this"),143 Null => write!(f, "null"),144 True => write!(f, "true"),145 False => write!(f, "false"),146 _ => panic!("non printable item"),147 }148 }149}150151#[derive(Debug, Clone, PartialEq)]152pub struct SliceDesc {153 pub start: Option<LocExpr>,154 pub end: Option<LocExpr>,155 pub step: Option<LocExpr>,156}157158/// Syntax base159#[derive(Debug, Clone, PartialEq)]160pub enum Expr {161 Literal(LiteralType),162163 /// String value: "hello"164 Str(String),165 /// Number: 1, 2.0, 2e+20166 Num(f64),167 /// Variable name: test168 Var(String),169170 /// Array of expressions: [1, 2, "Hello"]171 Arr(Vec<LocExpr>),172 /// Array comprehension:173 /// ```jsonnet174 /// ingredients: [175 /// { kind: kind, qty: 4 / 3 }176 /// for kind in [177 /// 'Honey Syrup',178 /// 'Lemon Juice',179 /// 'Farmers Gin',180 /// ]181 /// ],182 /// ```183 ArrComp(LocExpr, ForSpecData, Vec<CompSpec>),184185 /// Object: {a: 2}186 Obj(ObjBody),187 /// Object extension: var1 {b: 2}188 ObjExtend(LocExpr, ObjBody),189190 /// (obj)191 Parened(LocExpr),192193 /// Params in function definition194 /// hello, world, test = 2195 Params(ParamsDesc),196 /// Args in function call197 /// 2 + 2, 3, named = 6198 Args(ArgsDesc),199200 /// -2201 UnaryOp(UnaryOpType, LocExpr),202 /// 2 - 2203 BinaryOp(LocExpr, BinaryOpType, LocExpr),204 /// assert 2 == 2 : "Math is broken"205 AssertExpr(AssertStmt, LocExpr),206 /// local a = 2; { b: a }207 LocalExpr(Vec<BindSpec>, LocExpr),208209 /// a = 3210 Bind(BindSpec),211 /// import "hello"212 Import(String),213 /// importStr "file.txt"214 ImportStr(String),215 /// error "I'm broken"216 Error(LocExpr),217 /// a(b, c)218 Apply(LocExpr, ArgsDesc),219 ///220 Select(LocExpr, String),221 /// a[b]222 Index(LocExpr, LocExpr),223 /// a[1::2]224 Slice(LocExpr, SliceDesc),225 /// function(x) x226 Function(ParamsDesc, LocExpr),227 /// if true == false then 1 else 2228 IfElse {229 cond: IfSpecData,230 cond_then: LocExpr,231 cond_else: Option<LocExpr>,232 },233 /// if 2 = 3234 IfSpec(IfSpecData),235 /// for elem in array236 ForSpec(ForSpecData),237}238239/// file, begin offset, end offset240#[derive(Clone, PartialEq)]241pub struct ExprLocation(pub String, pub usize, pub usize);242impl Debug for ExprLocation {243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {244 write!(f, "{}:{:?}", self.0, self.1)245 }246}247248/// Holds AST expression and its location in source file+249#[derive(Clone, PartialEq)]250pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);251impl Debug for LocExpr {252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {253 write!(f, "{:?} from {:?}", self.0, self.1)254 }255}256257/// Creates LocExpr from Expr and ExprLocation components258#[macro_export]259macro_rules! loc_expr {260 ($expr:expr, $need_loc:expr, ($name:expr, $start:expr, $end:expr)) => {261 LocExpr(262 Rc::new($expr),263 if $need_loc {264 Some(Rc::new(ExprLocation($name.to_owned(), $start, $end)))265 } else {266 None267 },268 )269 };270}271272/// Creates LocExpr without location info273#[macro_export]274macro_rules! loc_expr_todo {275 ($expr:expr) => {276 LocExpr(Rc::new($expr), None)277 };278}1use std::{fmt::Debug, rc::Rc};23#[derive(Debug, Clone, PartialEq)]4pub enum FieldName {5 /// {fixed: 2}6 Fixed(String),7 /// {["dyn"+"amic"]: 3}8 Dyn(LocExpr),9}1011#[derive(Debug, Clone, PartialEq)]12pub enum Visibility {13 /// :14 Normal,15 /// ::16 Hidden,17 /// :::18 Unhide,19}2021#[derive(Debug, Clone, PartialEq)]22pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2324#[derive(Debug, Clone, PartialEq)]25pub struct FieldMember {26 pub name: FieldName,27 pub plus: bool,28 pub params: Option<ParamsDesc>,29 pub visibility: Visibility,30 pub value: LocExpr,31}3233#[derive(Debug, Clone, PartialEq)]34pub enum Member {35 Field(FieldMember),36 BindStmt(BindSpec),37 AssertStmt(AssertStmt),38}3940#[derive(Debug, Clone, Copy, PartialEq)]41pub enum UnaryOpType {42 Plus,43 Minus,44 BitNot,45 Not,46}4748#[derive(Debug, Clone, Copy, PartialEq)]49pub enum BinaryOpType {50 Mul,51 Div,52 Mod,5354 Add,55 Sub,5657 Lhs,58 Rhs,5960 Lt,61 Gt,62 Lte,63 Gte,6465 In,6667 Eq,68 Ne,6970 BitAnd,71 BitOr,72 BitXor,7374 And,75 Or,76}7778/// name, default value79#[derive(Debug, Clone, PartialEq)]80pub struct Param(pub String, pub Option<LocExpr>);81/// Defined function parameters82#[derive(Debug, Clone, PartialEq)]83pub struct ParamsDesc(pub Vec<Param>);84impl ParamsDesc {85 pub fn with_defaults(&self) -> Vec<Param> {86 self.0.iter().filter(|e| e.1.is_some()).cloned().collect()87 }88}8990#[derive(Debug, Clone, PartialEq)]91pub struct Arg(pub Option<String>, pub LocExpr);92#[derive(Debug, Clone, PartialEq)]93pub struct ArgsDesc(pub Vec<Arg>);9495#[derive(Debug, Clone, PartialEq)]96pub struct BindSpec {97 pub name: String,98 pub params: Option<ParamsDesc>,99 pub value: LocExpr,100}101102#[derive(Debug, Clone, PartialEq)]103pub struct IfSpecData(pub LocExpr);104#[derive(Debug, Clone, PartialEq)]105pub struct ForSpecData(pub String, pub LocExpr);106107#[derive(Debug, Clone, PartialEq)]108pub enum CompSpec {109 IfSpec(IfSpecData),110 ForSpec(ForSpecData),111}112113#[derive(Debug, Clone, PartialEq)]114pub enum ObjBody {115 MemberList(Vec<Member>),116 ObjComp {117 pre_locals: Vec<BindSpec>,118 key: LocExpr,119 value: LocExpr,120 post_locals: Vec<BindSpec>,121 first: ForSpecData,122 rest: Vec<CompSpec>,123 },124}125126#[derive(Debug, Clone, PartialEq)]127pub enum LiteralType {128 This,129 Super,130 Dollar,131 Null,132 True,133 False,134}135136#[derive(Debug, Clone, PartialEq)]137pub struct SliceDesc {138 pub start: Option<LocExpr>,139 pub end: Option<LocExpr>,140 pub step: Option<LocExpr>,141}142143/// Syntax base144#[derive(Debug, Clone, PartialEq)]145pub enum Expr {146 Literal(LiteralType),147148 /// String value: "hello"149 Str(String),150 /// Number: 1, 2.0, 2e+20151 Num(f64),152 /// Variable name: test153 Var(String),154155 /// Array of expressions: [1, 2, "Hello"]156 Arr(Vec<LocExpr>),157 /// Array comprehension:158 /// ```jsonnet159 /// ingredients: [160 /// { kind: kind, qty: 4 / 3 }161 /// for kind in [162 /// 'Honey Syrup',163 /// 'Lemon Juice',164 /// 'Farmers Gin',165 /// ]166 /// ],167 /// ```168 ArrComp(LocExpr, ForSpecData, Vec<CompSpec>),169170 /// Object: {a: 2}171 Obj(ObjBody),172 /// Object extension: var1 {b: 2}173 ObjExtend(LocExpr, ObjBody),174175 /// (obj)176 Parened(LocExpr),177178 /// Params in function definition179 /// hello, world, test = 2180 Params(ParamsDesc),181 /// Args in function call182 /// 2 + 2, 3, named = 6183 Args(ArgsDesc),184185 /// -2186 UnaryOp(UnaryOpType, LocExpr),187 /// 2 - 2188 BinaryOp(LocExpr, BinaryOpType, LocExpr),189 /// assert 2 == 2 : "Math is broken"190 AssertExpr(AssertStmt, LocExpr),191 /// local a = 2; { b: a }192 LocalExpr(Vec<BindSpec>, LocExpr),193194 /// a = 3195 Bind(BindSpec),196 /// import "hello"197 Import(String),198 /// importStr "file.txt"199 ImportStr(String),200 /// error "I'm broken"201 Error(LocExpr),202 /// a(b, c)203 Apply(LocExpr, ArgsDesc),204 ///205 Select(LocExpr, String),206 /// a[b]207 Index(LocExpr, LocExpr),208 /// a[1::2]209 Slice(LocExpr, SliceDesc),210 /// function(x) x211 Function(ParamsDesc, LocExpr),212 /// if true == false then 1 else 2213 IfElse {214 cond: IfSpecData,215 cond_then: LocExpr,216 cond_else: Option<LocExpr>,217 },218 /// if 2 = 3219 IfSpec(IfSpecData),220 /// for elem in array221 ForSpec(ForSpecData),222}223224/// file, begin offset, end offset225#[derive(Clone, PartialEq)]226pub struct ExprLocation(pub String, pub usize, pub usize);227impl Debug for ExprLocation {228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {229 write!(f, "{}:{:?}-{:?}", self.0, self.1, self.2)230 }231}232233/// Holds AST expression and its location in source file+234#[derive(Clone, PartialEq)]235pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);236impl Debug for LocExpr {237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {238 write!(f, "{:?} from {:?}", self.0, self.1)239 }240}241242/// Creates LocExpr from Expr and ExprLocation components243#[macro_export]244macro_rules! loc_expr {245 ($expr:expr, $need_loc:expr, ($name:expr, $start:expr, $end:expr)) => {246 LocExpr(247 std::rc::Rc::new($expr),248 if $need_loc {249 Some(std::rc::Rc::new(ExprLocation($name.to_owned(), $start, $end)))250 } else {251 None252 },253 )254 };255}256257/// Creates LocExpr without location info258#[macro_export]259macro_rules! loc_expr_todo {260 ($expr:expr) => {261 LocExpr(Rc::new($expr), None)262 };263}