Skip to main content

mittens_engine/scripting/repl/
formatter.rs

1use 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}