mittens_engine/scripting/repl/
formatter.rs1use crate::engine::ecs::World;
2use crate::scripting::ast::{
3 BlockStatement, ComponentExpression, ConstructorCall, Expression, Ident, Statement,
4 TableFieldValue,
5};
6use crate::scripting::component_registry::subtree_to_ce_ast;
7use crate::scripting::object::{BuiltinTableKind, CeChild, MaterializedCE, Value};
8use crate::scripting::unparser::{unparse_component, unparse_expression};
9
10pub fn format_repl_value(value: &Value, world: &World) -> Result<String, String> {
11 match value {
12 Value::Identifier(name) if name == "__mms_world__" => Ok("<world>".into()),
13 Value::ComponentObject { id, .. } => {
14 let ce = subtree_to_ce_ast(world, *id)
15 .map_err(|_| format!("stale component handle: component {id:?} is not live"))?;
16 Ok(unparse_component(&ce))
17 }
18 Value::ComponentExpr(ce) => Ok(unparse_component(&materialized_to_ast(ce)?)),
19 Value::Array(values) => {
20 let items = values
21 .iter()
22 .map(|v| value_to_expression(v, world))
23 .collect::<Result<Vec<_>, _>>()?;
24 Ok(unparse_expression(&Expression::Array(items)))
25 }
26 Value::Map(map) => {
27 let mut keys = map.keys().collect::<Vec<_>>();
28 keys.sort();
29 let fields = keys
30 .into_iter()
31 .map(|key| {
32 Ok(TableFieldValue {
33 name: Ident(key.clone()),
34 value: value_to_expression(&map[key], world)?,
35 })
36 })
37 .collect::<Result<Vec<_>, String>>()?;
38 Ok(unparse_expression(&Expression::Table(fields)))
39 }
40 Value::Object(id) => id
41 .with_map(|map| format_repl_value(&Value::Map(map.clone()), world))
42 .ok_or_else(|| "<stale object>".to_string())?,
43 Value::Function { .. } => Ok("<fn>".into()),
44 Value::Module { .. } => Ok("<module>".into()),
45 Value::BuiltinTable(BuiltinTableKind::Math) => Ok("<builtin Math>".into()),
46 Value::BuiltinTable(BuiltinTableKind::MusicNote) => Ok("<builtin MusicNote>".into()),
47 other => Ok(unparse_expression(&value_to_expression(other, world)?)),
48 }
49}
50
51fn value_to_expression(value: &Value, world: &World) -> Result<Expression, String> {
52 Ok(match value {
53 Value::Null => Expression::Null,
54 Value::Bool(v) => Expression::Bool(*v),
55 Value::Number(v) => Expression::Number(*v),
56 Value::Dimension { value, unit } => Expression::Dimension(*value, *unit),
57 Value::String(v) => Expression::String(v.clone()),
58 Value::Identifier(v) => Expression::Identifier(Ident(v.clone())),
59 Value::Array(v) => Expression::Array(
60 v.iter()
61 .map(|x| value_to_expression(x, world))
62 .collect::<Result<_, _>>()?,
63 ),
64 Value::Map(v) => Expression::Table(
65 v.iter()
66 .map(|(k, x)| {
67 Ok(TableFieldValue {
68 name: Ident(k.clone()),
69 value: value_to_expression(x, world)?,
70 })
71 })
72 .collect::<Result<_, String>>()?,
73 ),
74 Value::ComponentObject { id, .. } => Expression::Component(
75 subtree_to_ce_ast(world, *id)
76 .map_err(|_| format!("stale component handle: component {id:?} is not live"))?,
77 ),
78 Value::ComponentExpr(ce) => Expression::Component(materialized_to_ast(ce)?),
79 other => return Err(format!("value has no MMS source form: {other:?}")),
80 })
81}
82
83fn materialized_to_ast(ce: &MaterializedCE) -> Result<ComponentExpression, String> {
84 let mut constructors = Vec::new();
85 if let Some(method) = &ce.ctor_method {
86 constructors.push(ConstructorCall {
87 method: Ident(method.clone()),
88 args: ce
89 .ctor_args
90 .iter()
91 .map(|v| value_to_expression(v, &World::default()))
92 .collect::<Result<_, _>>()?,
93 });
94 }
95 let mut body = Vec::new();
96 for (method, args) in &ce.calls {
97 body.push(Statement::Expression(Expression::Call(
98 crate::scripting::ast::CallExpression {
99 callee: Box::new(Expression::Identifier(Ident(method.clone()))),
100 args: args
101 .iter()
102 .map(|v| value_to_expression(v, &World::default()))
103 .collect::<Result<_, _>>()?,
104 },
105 )));
106 }
107 for (name, value) in &ce.named {
108 body.push(Statement::Reassign {
109 target: Expression::Identifier(Ident(name.clone())),
110 value: value_to_expression(value, &World::default())?,
111 });
112 }
113 for value in &ce.positionals {
114 body.push(Statement::Expression(value_to_expression(
115 value,
116 &World::default(),
117 )?));
118 }
119 if let Some(deferred) = &ce.deferred_block {
120 body.extend(deferred.body.statements.clone());
121 }
122 for child in &ce.children {
123 match child {
124 CeChild::Spawn(child) => body.push(Statement::Expression(Expression::Component(
125 materialized_to_ast(child)?,
126 ))),
127 CeChild::Attach(id) => {
128 return Err(format!(
129 "detached component expression references live component {id:?}"
130 ));
131 }
132 }
133 }
134 Ok(ComponentExpression {
135 component_type: Ident(ce.component_type.clone()),
136 constructors,
137 body: BlockStatement { statements: body },
138 })
139}