1use crate::error::{EvalError, EvalResult};
7use crate::evaluator::Evaluator;
8use crate::test_runner::MockResponse;
9use pepl_stdlib::{ResultValue, Value};
10use pepl_types::ast::*;
11use std::collections::BTreeMap;
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct SurfaceNode {
16 pub component: String,
18 pub props: BTreeMap<String, Value>,
20 pub children: Vec<SurfaceNode>,
22}
23
24#[derive(Debug)]
26pub struct ActionResult {
27 pub committed: bool,
29 pub invariant_error: Option<String>,
31}
32
33pub struct SpaceInstance {
39 eval: Evaluator,
41 state_fields: Vec<String>,
43 derived_fields: Vec<(String, Expr)>,
45 invariants: Vec<(String, Expr)>,
47 actions: Vec<ActionDecl>,
49 views: Vec<ViewDecl>,
51 credentials: BTreeMap<String, Value>,
53 update_decl: Option<UpdateDecl>,
55 handle_event_decl: Option<HandleEventDecl>,
57 mock_responses: Vec<MockResponse>,
59}
60
61impl SpaceInstance {
62 pub fn new(program: &Program) -> EvalResult<Self> {
67 Self::with_gas_limit(program, 1_000_000)
68 }
69
70 pub fn with_gas_limit(program: &Program, gas_limit: u64) -> EvalResult<Self> {
72 let body = &program.space.body;
73 let mut eval = Evaluator::new(gas_limit);
74
75 eval.action_names = body.actions.iter().map(|a| a.name.name.clone()).collect();
77
78 let mut state_fields = Vec::new();
80 for field in &body.state.fields {
81 let default = eval.eval_expr(&field.default)?;
82 eval.env.define(&field.name.name, default);
83 state_fields.push(field.name.name.clone());
84 }
85
86 let mut credentials = BTreeMap::new();
88 if let Some(creds) = &body.credentials {
89 for field in &creds.fields {
90 eval.env.define(&field.name.name, Value::Nil);
91 credentials.insert(field.name.name.clone(), Value::Nil);
92 }
93 }
94
95 let derived_fields: Vec<(String, Expr)> = body
97 .derived
98 .as_ref()
99 .map(|d| {
100 d.fields
101 .iter()
102 .map(|f| (f.name.name.clone(), f.value.clone()))
103 .collect()
104 })
105 .unwrap_or_default();
106
107 let invariants: Vec<(String, Expr)> = body
109 .invariants
110 .iter()
111 .map(|inv| (inv.name.name.clone(), inv.condition.clone()))
112 .collect();
113
114 let mut instance = Self {
115 eval,
116 state_fields,
117 derived_fields,
118 invariants,
119 actions: body.actions.clone(),
120 views: body.views.clone(),
121 credentials,
122 update_decl: body.update.clone(),
123 handle_event_decl: body.handle_event.clone(),
124 mock_responses: Vec::new(),
125 };
126
127 instance.recompute_derived()?;
129
130 Ok(instance)
131 }
132
133 pub fn get_state(&self, name: &str) -> Option<&Value> {
139 self.eval.env.get(name)
140 }
141
142 pub fn state_snapshot(&self) -> BTreeMap<String, Value> {
144 let mut snap = BTreeMap::new();
145 for name in &self.state_fields {
146 if let Some(val) = self.eval.env.get(name) {
147 snap.insert(name.clone(), val.clone());
148 }
149 }
150 snap
151 }
152
153 pub fn log_output(&self) -> &[String] {
155 &self.eval.log_output
156 }
157
158 pub fn clear_log(&mut self) {
160 self.eval.log_output.clear();
161 }
162
163 pub fn set_credential(&mut self, name: &str, value: Value) {
165 self.credentials.insert(name.to_string(), value.clone());
166 self.eval.env.define(name, value);
167 }
168
169 pub fn dispatch(&mut self, action_name: &str, args: Vec<Value>) -> EvalResult<ActionResult> {
181 let action = self
183 .actions
184 .iter()
185 .find(|a| a.name.name == action_name)
186 .cloned()
187 .ok_or_else(|| EvalError::UndefinedAction(action_name.to_string()))?;
188
189 let snapshot = self.eval.env.global_bindings().clone();
191
192 self.eval.env.push_scope();
194 for (param, arg) in action.params.iter().zip(args.into_iter()) {
195 self.eval.env.define(¶m.name.name, arg);
196 }
197
198 let exec_result = self.eval.eval_block(&action.body);
200 self.eval.env.pop_scope();
201
202 match exec_result {
204 Ok(_) => {}
205 Err(EvalError::Return(_)) => {} Err(e) => return Err(e),
207 }
208
209 self.recompute_derived()?;
211
212 match self.check_invariants() {
214 Ok(()) => Ok(ActionResult {
215 committed: true,
216 invariant_error: None,
217 }),
218 Err(msg) => {
219 self.eval.env.restore_global(snapshot);
221 self.recompute_derived()?;
223 Ok(ActionResult {
224 committed: false,
225 invariant_error: Some(msg),
226 })
227 }
228 }
229 }
230
231 fn recompute_derived(&mut self) -> EvalResult<()> {
237 for (name, expr) in &self.derived_fields.clone() {
238 let val = self.eval.eval_expr(expr)?;
239 if !self.eval.env.set(name, val.clone()) {
241 self.eval.env.define(name, val);
242 }
243 }
244 Ok(())
245 }
246
247 fn check_invariants(&mut self) -> Result<(), String> {
253 for (name, condition) in &self.invariants.clone() {
254 match self.eval.eval_expr(condition) {
255 Ok(val) => {
256 if !val.is_truthy() {
257 return Err(format!("invariant '{name}' violated"));
258 }
259 }
260 Err(e) => {
261 return Err(format!("invariant '{name}' evaluation error: {e}"));
262 }
263 }
264 }
265 Ok(())
266 }
267
268 pub fn render_view(&mut self, view_name: &str) -> EvalResult<Vec<SurfaceNode>> {
274 let view = self
275 .views
276 .iter()
277 .find(|v| v.name.name == view_name)
278 .cloned()
279 .ok_or_else(|| EvalError::Runtime(format!("unknown view '{view_name}'")))?;
280
281 self.eval_ui_block(&view.body)
282 }
283
284 pub fn render(&mut self) -> EvalResult<Vec<SurfaceNode>> {
286 self.render_view("main")
287 }
288
289 fn eval_ui_block(&mut self, block: &UIBlock) -> EvalResult<Vec<SurfaceNode>> {
290 let mut nodes = Vec::new();
291 for elem in &block.elements {
292 self.eval_ui_element(elem, &mut nodes)?;
293 }
294 Ok(nodes)
295 }
296
297 fn eval_ui_element(&mut self, elem: &UIElement, out: &mut Vec<SurfaceNode>) -> EvalResult<()> {
298 match elem {
299 UIElement::Component(comp) => {
300 let node = self.eval_component(comp)?;
301 out.push(node);
302 }
303 UIElement::Let(binding) => {
304 let value = self.eval.eval_expr(&binding.value)?;
305 if let Some(name) = &binding.name {
306 self.eval.env.define(&name.name, value);
307 }
308 }
309 UIElement::If(ui_if) => {
310 self.eval_ui_if(ui_if, out)?;
311 }
312 UIElement::For(ui_for) => {
313 self.eval_ui_for(ui_for, out)?;
314 }
315 }
316 Ok(())
317 }
318
319 fn eval_component(&mut self, comp: &ComponentExpr) -> EvalResult<SurfaceNode> {
320 let mut props = BTreeMap::new();
321 for prop in &comp.props {
322 let name = &prop.name.name;
323 if name.starts_with("on_") {
325 match &prop.value.kind {
326 ExprKind::Identifier(action_name) => {
327 let mut action_props = BTreeMap::new();
329 action_props
330 .insert("__action".to_string(), Value::String(action_name.clone()));
331 props.insert(
332 name.clone(),
333 Value::Record {
334 type_name: None,
335 fields: action_props,
336 },
337 );
338 continue;
339 }
340 ExprKind::Call {
341 name: fn_name,
342 args,
343 } => {
344 let mut action_props = BTreeMap::new();
346 action_props
347 .insert("__action".to_string(), Value::String(fn_name.name.clone()));
348 let mut arg_vals = Vec::new();
349 for arg in args {
350 arg_vals.push(self.eval.eval_expr(arg)?);
351 }
352 action_props.insert("__args".to_string(), Value::List(arg_vals));
353 props.insert(
354 name.clone(),
355 Value::Record {
356 type_name: None,
357 fields: action_props,
358 },
359 );
360 continue;
361 }
362 ExprKind::Lambda(lambda) => {
363 let closure = self.eval.eval_lambda(lambda)?;
366 let mut lambda_props = BTreeMap::new();
367 lambda_props.insert("__lambda".to_string(), closure);
368 props.insert(
369 name.clone(),
370 Value::Record {
371 type_name: None,
372 fields: lambda_props,
373 },
374 );
375 continue;
376 }
377 _ => {}
378 }
379 }
380
381 let val = self.eval.eval_expr(&prop.value)?;
383 props.insert(name.clone(), val);
384 }
385
386 let children = if let Some(child_block) = &comp.children {
387 self.eval_ui_block(child_block)?
388 } else {
389 Vec::new()
390 };
391
392 Ok(SurfaceNode {
393 component: comp.name.name.clone(),
394 props,
395 children,
396 })
397 }
398
399 fn eval_ui_if(&mut self, ui_if: &UIIf, out: &mut Vec<SurfaceNode>) -> EvalResult<()> {
400 let cond = self.eval.eval_expr(&ui_if.condition)?;
401 if cond.is_truthy() {
402 let nodes = self.eval_ui_block(&ui_if.then_block)?;
403 out.extend(nodes);
404 } else if let Some(else_block) = &ui_if.else_block {
405 match else_block {
406 UIElse::ElseIf(elif) => self.eval_ui_if(elif, out)?,
407 UIElse::Block(block) => {
408 let nodes = self.eval_ui_block(block)?;
409 out.extend(nodes);
410 }
411 }
412 }
413 Ok(())
414 }
415
416 fn eval_ui_for(&mut self, ui_for: &UIFor, out: &mut Vec<SurfaceNode>) -> EvalResult<()> {
417 let iterable = self.eval.eval_expr(&ui_for.iterable)?;
418 let items = match iterable {
419 Value::List(items) => items,
420 _ => {
421 return Err(EvalError::TypeMismatch(format!(
422 "for loop requires list, got {}",
423 iterable.type_name()
424 )));
425 }
426 };
427
428 self.eval.env.push_scope();
429 for (i, item) in items.iter().enumerate() {
430 self.eval.env.define(&ui_for.item.name, item.clone());
431 if let Some(idx) = &ui_for.index {
432 self.eval.env.define(&idx.name, Value::Number(i as f64));
433 }
434 let nodes = self.eval_ui_block(&ui_for.body)?;
435 out.extend(nodes);
436 }
437 self.eval.env.pop_scope();
438 Ok(())
439 }
440
441 pub fn set_mock_responses(&mut self, mocks: Vec<MockResponse>) {
447 self.eval.mock_responses = mocks
449 .iter()
450 .map(|m| (m.module.clone(), m.function.clone(), m.response.clone()))
451 .collect();
452 self.mock_responses = mocks;
453 }
454
455 pub fn eval_expr_public(&mut self, expr: &Expr) -> EvalResult<Value> {
457 self.eval.eval_expr(expr)
458 }
459
460 pub fn eval_stmt_public(&mut self, stmt: &Stmt) -> EvalResult<Value> {
462 self.eval.eval_stmt(stmt)
463 }
464
465 pub fn define_in_env(&mut self, name: &str, value: Value) {
467 self.eval.env.define(name, value);
468 }
469
470 pub fn push_scope(&mut self) {
472 self.eval.env.push_scope();
473 }
474
475 pub fn pop_scope(&mut self) {
477 self.eval.env.pop_scope();
478 }
479
480 pub fn call_update(&mut self, dt: f64) -> EvalResult<ActionResult> {
488 let update = self
489 .update_decl
490 .clone()
491 .ok_or_else(|| EvalError::Runtime("space has no update() declaration".into()))?;
492
493 let snapshot = self.eval.env.global_bindings().clone();
494
495 self.eval.env.push_scope();
496 self.eval
497 .env
498 .define(&update.param.name.name, Value::Number(dt));
499
500 let exec_result = self.eval.eval_block(&update.body);
501 self.eval.env.pop_scope();
502
503 match exec_result {
504 Ok(_) => {}
505 Err(EvalError::Return(_)) => {}
506 Err(e) => return Err(e),
507 }
508
509 self.recompute_derived()?;
510
511 match self.check_invariants() {
512 Ok(()) => Ok(ActionResult {
513 committed: true,
514 invariant_error: None,
515 }),
516 Err(msg) => {
517 self.eval.env.restore_global(snapshot);
518 self.recompute_derived()?;
519 Ok(ActionResult {
520 committed: false,
521 invariant_error: Some(msg),
522 })
523 }
524 }
525 }
526
527 pub fn call_handle_event(&mut self, event: Value) -> EvalResult<ActionResult> {
531 let handler = self
532 .handle_event_decl
533 .clone()
534 .ok_or_else(|| EvalError::Runtime("space has no handleEvent() declaration".into()))?;
535
536 let snapshot = self.eval.env.global_bindings().clone();
537
538 self.eval.env.push_scope();
539 self.eval.env.define(&handler.param.name.name, event);
540
541 let exec_result = self.eval.eval_block(&handler.body);
542 self.eval.env.pop_scope();
543
544 match exec_result {
545 Ok(_) => {}
546 Err(EvalError::Return(_)) => {}
547 Err(e) => return Err(e),
548 }
549
550 self.recompute_derived()?;
551
552 match self.check_invariants() {
553 Ok(()) => Ok(ActionResult {
554 committed: true,
555 invariant_error: None,
556 }),
557 Err(msg) => {
558 self.eval.env.restore_global(snapshot);
559 self.recompute_derived()?;
560 Ok(ActionResult {
561 committed: false,
562 invariant_error: Some(msg),
563 })
564 }
565 }
566 }
567
568 pub fn surface_to_json(nodes: &[SurfaceNode]) -> serde_json::Value {
574 serde_json::Value::Array(nodes.iter().map(Self::node_to_json).collect())
575 }
576
577 fn node_to_json(node: &SurfaceNode) -> serde_json::Value {
578 let mut map = serde_json::Map::new();
579 map.insert(
580 "component".to_string(),
581 serde_json::Value::String(node.component.clone()),
582 );
583
584 let mut props_map = serde_json::Map::new();
585 for (k, v) in &node.props {
586 props_map.insert(k.clone(), Self::value_to_json(v));
587 }
588 map.insert("props".to_string(), serde_json::Value::Object(props_map));
589
590 if !node.children.is_empty() {
591 map.insert(
592 "children".to_string(),
593 serde_json::Value::Array(node.children.iter().map(Self::node_to_json).collect()),
594 );
595 }
596
597 serde_json::Value::Object(map)
598 }
599
600 fn value_to_json(val: &Value) -> serde_json::Value {
601 Self::value_to_json_public(val)
602 }
603
604 pub fn value_to_json_public(val: &Value) -> serde_json::Value {
606 match val {
607 Value::Number(n) => {
608 if n.fract() == 0.0
609 && n.is_finite()
610 && *n >= i64::MIN as f64
611 && *n <= i64::MAX as f64
612 {
613 serde_json::Value::Number(serde_json::Number::from(*n as i64))
614 } else {
615 serde_json::json!(*n)
616 }
617 }
618 Value::String(s) => serde_json::Value::String(s.clone()),
619 Value::Bool(b) => serde_json::Value::Bool(*b),
620 Value::Nil => serde_json::Value::Null,
621 Value::List(items) => {
622 serde_json::Value::Array(items.iter().map(Self::value_to_json).collect())
623 }
624 Value::Record { fields, .. } => {
625 let mut map = serde_json::Map::new();
626 for (k, v) in fields {
627 map.insert(k.clone(), Self::value_to_json(v));
628 }
629 serde_json::Value::Object(map)
630 }
631 Value::Result(r) => match r.as_ref() {
632 ResultValue::Ok(v) => serde_json::json!({"Ok": Self::value_to_json(v)}),
633 ResultValue::Err(v) => serde_json::json!({"Err": Self::value_to_json(v)}),
634 },
635 Value::SumVariant {
636 variant, fields, ..
637 } => {
638 if fields.is_empty() {
639 serde_json::Value::String(variant.clone())
640 } else {
641 serde_json::json!({
642 variant: fields.iter().map(Self::value_to_json).collect::<Vec<_>>()
643 })
644 }
645 }
646 Value::Function(_) => serde_json::Value::String("<function>".to_string()),
647 Value::Color { r, g, b, a } => {
648 serde_json::json!({"r": r, "g": g, "b": b, "a": a})
649 }
650 }
651 }
652}