json_eval_rs/parse_schema/
legacy.rs

1/// Legacy schema parsing for JSONEval (direct evaluation)
2
3use indexmap::{IndexMap, IndexSet};
4use serde_json::{Map, Value};
5use std::sync::Arc;
6
7use crate::parse_schema::common::compute_column_partitions;
8use crate::{topo_sort, JSONEval, LogicId, RLogic, path_utils};
9use crate::table_metadata::{ColumnMetadata, RepeatBoundMetadata, RowMetadata, TableMetadata};
10
11pub fn parse_schema(lib: &mut JSONEval) -> Result<(), String> {
12    /// Single-pass schema walker that collects everything
13    fn walk(
14        value: &Value,
15        path: &str,
16        engine: &mut RLogic,
17        evaluations: &mut IndexMap<String, LogicId>,
18        tables: &mut IndexMap<String, Value>,
19        deps: &mut IndexMap<String, IndexSet<String>>,
20        value_fields: &mut Vec<String>,
21        layout_paths: &mut Vec<String>,
22        dependents: &mut IndexMap<String, Vec<crate::DependentItem>>,
23        options_templates: &mut Vec<(String, String, String)>,
24        subforms: &mut Vec<(String, serde_json::Map<String, Value>, Value)>,
25        fields_with_rules: &mut Vec<String>,
26    ) -> Result<(), String> {
27        match value {
28            Value::Object(map) => {
29                // Check for $evaluation
30                if let Some(evaluation) = map.get("$evaluation") {
31                    let key = path.to_string();
32                    let logic_value = evaluation.get("logic").unwrap_or(evaluation);
33                    let logic_id = engine
34                        .compile(logic_value)
35                        .map_err(|e| format!("failed to compile evaluation at {key}: {e}"))?;
36                    evaluations.insert(key.clone(), logic_id);
37
38                    // Collect dependencies with smart table inheritance
39                    // Normalize all dependencies to JSON pointer format to avoid duplicates
40                    let mut refs: IndexSet<String> = engine
41                        .get_referenced_vars(&logic_id)
42                        .unwrap_or_default()
43                        .into_iter()
44                        .map(|dep| path_utils::normalize_to_json_pointer(&dep))
45                        .filter(|dep| {
46                            // Filter out simple column references (e.g., "/INSAGE_YEAR", "/PREM_PP")
47                            // These are FINDINDEX/MATCH column names, not actual data dependencies
48                            // Real dependencies have multiple path segments (e.g., "/illustration/properties/...")
49                            dep.matches('/').count() > 1 || dep.starts_with("/$")
50                        })
51                        .collect();
52                    let mut extra_refs = IndexSet::new();
53                    collect_refs(logic_value, &mut extra_refs);
54                    if !extra_refs.is_empty() {
55                        refs.extend(extra_refs.into_iter());
56                    }
57
58                    // For table dependencies, inherit parent table path instead of individual rows
59                    let refs: IndexSet<String> = refs
60                        .into_iter()
61                        .filter_map(|dep| {
62                            // If dependency is a table row (contains /$table/), inherit table path
63                            if let Some(table_idx) = dep.find("/$table/") {
64                                let table_path = &dep[..table_idx];
65                                Some(table_path.to_string())
66                            } else {
67                                Some(dep)
68                            }
69                        })
70                        .collect();
71
72                    if !refs.is_empty() {
73                        deps.insert(key.clone(), refs);
74                    }
75                }
76
77                // Check for $table
78                if let Some(table) = map.get("$table") {
79                    let key = path.to_string();
80
81                    let rows = table.clone();
82                    let datas = map
83                        .get("$datas")
84                        .cloned()
85                        .unwrap_or_else(|| Value::Array(vec![]));
86                    let skip = map.get("$skip").cloned().unwrap_or(Value::Bool(false));
87                    let clear = map.get("$clear").cloned().unwrap_or(Value::Bool(false));
88
89                    let mut table_entry = Map::new();
90                    table_entry.insert("rows".to_string(), rows);
91                    table_entry.insert("datas".to_string(), datas);
92                    table_entry.insert("skip".to_string(), skip);
93                    table_entry.insert("clear".to_string(), clear);
94
95                    tables.insert(key, Value::Object(table_entry));
96                }
97
98                // Check for $layout with elements
99                if let Some(layout_obj) = map.get("$layout") {
100                    if let Some(Value::Array(_)) = layout_obj.get("elements") {
101                        let layout_elements_path = format!("{}/$layout/elements", path);
102                        layout_paths.push(layout_elements_path);
103                    }
104                }
105                
106                // Check for rules object - collect field path for efficient validation
107                if map.contains_key("rules") && !path.is_empty() && !path.starts_with("#/$") {
108                    // Convert JSON pointer path to dotted notation for validation
109                    // E.g., "#/properties/form/properties/name" -> "form.name"
110                    let field_path = path
111                        .trim_start_matches('#')
112                        .replace("/properties/", ".")
113                        .trim_start_matches('/')
114                        .trim_start_matches('.')
115                        .to_string();
116                    
117                    if !field_path.is_empty() && !field_path.starts_with("$") {
118                        fields_with_rules.push(field_path);
119                    }
120                }
121
122                // Check for options with URL templates
123                if let Some(Value::String(url)) = map.get("url") {
124                    // Check if URL contains template pattern {variable}
125                    if url.contains('{') && url.contains('}') {
126                        // Convert to JSON pointer format for evaluated_schema access
127                        let url_path = path_utils::normalize_to_json_pointer(&format!("{}/url", path));
128                        let params_path = path_utils::normalize_to_json_pointer(&format!("{}/params", path));
129                        options_templates.push((url_path, url.clone(), params_path));
130                    }
131                }
132                
133                // Check for array fields with items (subforms)
134                if let Some(Value::String(type_str)) = map.get("type") {
135                    if type_str == "array" {
136                        if let Some(items) = map.get("items") {
137                            // Store subform info for later creation (after walk completes)
138                            subforms.push((path.to_string(), map.clone(), items.clone()));
139                            // Don't recurse into items - it will be processed as a separate subform
140                            return Ok(());
141                        }
142                    }
143                }
144
145                // Check for dependents array
146                if let Some(Value::Array(dependents_arr)) = map.get("dependents") {
147                    let mut dependent_items = Vec::new();
148                    
149                    for (dep_idx, dep_item) in dependents_arr.iter().enumerate() {
150                        if let Value::Object(dep_obj) = dep_item {
151                            if let Some(Value::String(ref_path)) = dep_obj.get("$ref") {
152                                // Process clear - compile if it's an $evaluation
153                                let clear_val = if let Some(clear) = dep_obj.get("clear") {
154                                    if let Value::Object(clear_obj) = clear {
155                                        if clear_obj.contains_key("$evaluation") {
156                                            // Compile and store the evaluation
157                                            let clear_eval = clear_obj.get("$evaluation").unwrap();
158                                            let clear_key = format!("{}/dependents/{}/clear", path, dep_idx);
159                                            let logic_id = engine.compile(clear_eval)
160                                                .map_err(|e| format!("Failed to compile dependent clear at {}: {}", clear_key, e))?;
161                                            evaluations.insert(clear_key.clone(), logic_id);
162                                            // Replace with eval key reference
163                                            Some(Value::String(clear_key))
164                                        } else {
165                                            Some(clear.clone())
166                                        }
167                                    } else {
168                                        Some(clear.clone())
169                                    }
170                                } else {
171                                    None
172                                };
173                                
174                                // Process value - compile if it's an $evaluation
175                                let value_val = if let Some(value) = dep_obj.get("value") {
176                                    if let Value::Object(value_obj) = value {
177                                        if value_obj.contains_key("$evaluation") {
178                                            // Compile and store the evaluation
179                                            let value_eval = value_obj.get("$evaluation").unwrap();
180                                            let value_key = format!("{}/dependents/{}/value", path, dep_idx);
181                                            let logic_id = engine.compile(value_eval)
182                                                .map_err(|e| format!("Failed to compile dependent value at {}: {}", value_key, e))?;
183                                            evaluations.insert(value_key.clone(), logic_id);
184                                            // Replace with eval key reference
185                                            Some(Value::String(value_key))
186                                        } else {
187                                            Some(value.clone())
188                                        }
189                                    } else {
190                                        Some(value.clone())
191                                    }
192                                } else {
193                                    None
194                                };
195                                
196                                dependent_items.push(crate::DependentItem {
197                                    ref_path: ref_path.clone(),
198                                    clear: clear_val,
199                                    value: value_val,
200                                });
201                            }
202                        }
203                    }
204                    
205                    if !dependent_items.is_empty() {
206                        dependents.insert(path.to_string(), dependent_items);
207                    }
208                }
209
210                // Recurse into children
211                Ok(for (key, val) in map {
212                    // Skip special evaluation and dependents keys from recursion (already processed above)
213                    if key == "$evaluation" || key == "dependents" {
214                        continue;
215                    }
216                    
217                    let next_path = if path == "#" {
218                        format!("#/{key}")
219                    } else {
220                        format!("{path}/{key}")
221                    };
222                    
223                    // Check if this is a "value" field
224                    if key == "value" && !next_path.starts_with("#/$") && !next_path.contains("/$layout/") && !next_path.contains("/items/") && !next_path.contains("/options/") && !next_path.contains("/dependents/") && !next_path.contains("/rules/") {
225                        value_fields.push(next_path.clone());
226                    }
227                    
228                    // Recurse into all children (including $ keys like $table, $datas, etc.)
229                    walk(val, &next_path, engine, evaluations, tables, deps, value_fields, layout_paths, dependents, options_templates, subforms, fields_with_rules)?;
230                })
231            }
232            Value::Array(arr) => Ok(for (index, item) in arr.iter().enumerate() {
233                let next_path = if path == "#" {
234                    format!("#/{index}")
235                } else {
236                    format!("{path}/{index}")
237                };
238                walk(item, &next_path, engine, evaluations, tables, deps, value_fields, layout_paths, dependents, options_templates, subforms, fields_with_rules)?;
239            }),
240            _ => Ok(()),
241        }
242    }
243
244    fn collect_refs(value: &Value, refs: &mut IndexSet<String>) {
245        match value {
246            Value::Object(map) => {
247                if let Some(path) = map.get("$ref").and_then(Value::as_str) {
248                    refs.insert(path_utils::normalize_to_json_pointer(path));
249                }
250                if let Some(path) = map.get("ref").and_then(Value::as_str) {
251                    refs.insert(path_utils::normalize_to_json_pointer(path));
252                }
253                if let Some(var_val) = map.get("var") {
254                    match var_val {
255                        Value::String(s) => {
256                            refs.insert(s.clone());
257                        }
258                        Value::Array(arr) => {
259                            if let Some(path) = arr.get(0).and_then(Value::as_str) {
260                                refs.insert(path.to_string());
261                            }
262                        }
263                        _ => {}
264                    }
265                }
266                for val in map.values() {
267                    collect_refs(val, refs);
268                }
269            }
270            Value::Array(arr) => {
271                for val in arr {
272                    collect_refs(val, refs);
273                }
274            }
275            _ => {}
276        }
277    }
278
279    // Use centralized path normalization from path_utils
280    // This ensures consistent $ref/var handling across the entire pipeline
281
282    // Single-pass collection: walk schema once and collect everything
283    let mut evaluations = IndexMap::new();
284    let mut tables = IndexMap::new();
285    let mut dependencies = IndexMap::new();
286    let mut value_fields = Vec::new();
287    let mut layout_paths = Vec::new();
288    let mut dependents_evaluations = IndexMap::new();
289    let mut options_templates = Vec::new();
290    let mut subforms_data = Vec::new();
291    
292    // Get mutable access to engine through Arc
293    let engine = Arc::get_mut(&mut lib.engine)
294        .ok_or("Cannot get mutable reference to engine - JSONEval engine is shared")?;
295    
296    let mut fields_with_rules = Vec::new();
297    
298    walk(
299        &lib.schema,
300        "#",
301        engine,
302        &mut evaluations,
303        &mut tables,
304        &mut dependencies,
305        &mut value_fields,
306        &mut layout_paths,
307        &mut dependents_evaluations,
308        &mut options_templates,
309        &mut subforms_data,
310        &mut fields_with_rules,
311    )?;
312    
313    lib.evaluations = Arc::new(evaluations);
314    lib.tables = Arc::new(tables);
315    lib.dependencies = Arc::new(dependencies);
316    lib.layout_paths = Arc::new(layout_paths);
317    lib.dependents_evaluations = Arc::new(dependents_evaluations);
318    lib.options_templates = Arc::new(options_templates);
319    lib.fields_with_rules = Arc::new(fields_with_rules);
320    
321    // Build subforms from collected data (after walk completes)
322    lib.subforms = build_subforms_from_data(subforms_data, lib)?;
323    
324    // Collect table-level dependencies by aggregating all column dependencies
325    collect_table_dependencies(lib);
326    
327    lib.sorted_evaluations = Arc::new(topo_sort::legacy::topological_sort(lib)?);
328    
329    // Categorize evaluations for result handling
330    categorize_evaluations(lib);
331    
332    // Process collected value fields
333    process_value_fields(lib, value_fields);
334    
335    // Pre-compile all table metadata for zero-copy evaluation
336    build_table_metadata(lib)?;
337    
338    Ok(())
339}
340
341/// Build subforms from collected data during walk
342fn build_subforms_from_data(
343    subforms_data: Vec<(String, serde_json::Map<String, Value>, Value)>,
344    parent: &JSONEval,
345) -> Result<IndexMap<String, Box<JSONEval>>, String> {
346    let mut subforms = IndexMap::new();
347    
348    for (path, field_map, items) in subforms_data {
349        create_subform(&path, &field_map, &items, &mut subforms, parent)?;
350    }
351    
352    Ok(subforms)
353}
354
355/// Create an isolated sub-JSONEval for a subform
356fn create_subform(
357    path: &str,
358    field_map: &serde_json::Map<String, Value>,
359    items: &Value,
360    subforms: &mut IndexMap<String, Box<JSONEval>>,
361    parent: &JSONEval,
362) -> Result<(), String> {
363    // Extract field key from path (e.g., "#/riders" -> "riders")
364    let field_key = path.trim_start_matches('#').trim_start_matches('/');
365    
366    // Build subform schema: { $params: from parent, [field_key]: items content }
367    let mut subform_schema = serde_json::Map::new();
368    
369    // Copy $params from parent schema
370    if let Some(params) = parent.schema.get("$params") {
371        subform_schema.insert("$params".to_string(), params.clone());
372    }
373    
374    // Create field object with items content
375    let mut field_obj = serde_json::Map::new();
376    
377    // Copy properties from items
378    if let Value::Object(items_map) = items {
379        for (key, value) in items_map {
380            field_obj.insert(key.clone(), value.clone());
381        }
382    }
383    
384    // Copy field-level properties (title, etc.) but exclude items and type="array"
385    for (key, value) in field_map {
386        if key != "items" && key != "type" {
387            field_obj.insert(key.clone(), value.clone());
388        }
389    }
390    
391    // Set type to "object" for the subform root
392    field_obj.insert("type".to_string(), Value::String("object".to_string()));
393    
394    subform_schema.insert(field_key.to_string(), Value::Object(field_obj));
395    
396    // Create sub-JSONEval with isolated schema
397    let subform_schema_json = serde_json::to_string(&subform_schema)
398        .map_err(|e| format!("Failed to serialize subform schema: {}", e))?;
399    
400    let sub_eval = crate::JSONEval::new(
401        &subform_schema_json,
402        Some(&serde_json::to_string(&parent.context).unwrap_or("{}".to_string())),
403        None, // No data initially
404    ).map_err(|e| format!("Failed to create subform for {}: {}", field_key, e))?;
405    
406    subforms.insert(path.to_string(), Box::new(sub_eval));
407    
408    Ok(())
409}
410
411/// Collect dependencies for tables by aggregating all column/cell dependencies
412fn collect_table_dependencies(lib: &mut JSONEval) {
413    // Clone the dependencies to a mutable map
414    let mut dependencies = (*lib.dependencies).clone();
415    
416    for (table_key, _) in lib.tables.iter() {
417        let mut table_deps = IndexSet::new();
418        
419        // Collect dependencies from all evaluations that belong to this table
420        for (eval_key, deps) in &dependencies {
421            // Check if this evaluation is within the table
422            if eval_key.starts_with(table_key) && eval_key != table_key {
423                // Add all dependencies from table cells/columns
424                for dep in deps {
425                    // Filter out self-references and internal table paths
426                    if !dep.starts_with(table_key) {
427                        table_deps.insert(dep.clone());
428                    }
429                }
430            }
431        }
432        
433        // Store aggregated dependencies for the table
434        if !table_deps.is_empty() {
435            dependencies.insert(table_key.clone(), table_deps);
436        }
437    }
438    
439    // Wrap the updated dependencies in Arc
440    lib.dependencies = Arc::new(dependencies);
441}
442
443/// Categorize evaluations for different result handling
444fn categorize_evaluations(lib: &mut JSONEval) {
445    // Collect all evaluation keys that are in sorted_evaluations (batches)
446    let batched_keys: IndexSet<String> = lib.sorted_evaluations
447        .iter()
448        .flatten()
449        .cloned()
450        .collect();
451    
452    // Find evaluations NOT in batches and categorize them
453    let mut rules_evaluations = Vec::new();
454    let mut others_evaluations = Vec::new();
455    
456    for eval_key in lib.evaluations.keys() {
457        // Skip if already in sorted_evaluations batches
458        if batched_keys.contains(eval_key) {
459            continue;
460        }
461        
462        // Skip table-related evaluations
463        if lib.tables.iter().any(|(key, _)| eval_key.starts_with(key)) {
464            continue;
465        }
466
467        // Categorize based on path patterns
468        if eval_key.contains("/rules/") {
469            rules_evaluations.push(eval_key.clone());
470        } else if !eval_key.contains("/dependents/") {
471            // Don't add dependents to others_evaluations
472            others_evaluations.push(eval_key.clone());
473        }
474    }
475    
476    // Update Arc-wrapped collections
477    lib.rules_evaluations = Arc::new(rules_evaluations);
478    lib.others_evaluations = Arc::new(others_evaluations);
479}
480
481/// Process collected value fields and add non-duplicate, non-table, non-dependent ones
482fn process_value_fields(lib: &mut JSONEval, value_fields: Vec<String>) {
483    let mut value_evaluations = Vec::new();
484    
485    for path in value_fields {
486        // Skip if already collected from evaluations in categorize_evaluations
487        if value_evaluations.contains(&path) {
488            continue;
489        }
490        
491        // Skip table-related paths
492        if lib.tables.iter().any(|(key, _)| path.starts_with(key)) {
493            continue;
494        }
495        
496        value_evaluations.push(path);
497    }
498    
499    // Update Arc-wrapped collection
500    lib.value_evaluations = Arc::new(value_evaluations);
501}
502
503/// Build pre-compiled table metadata at parse time (moves heavy operations from evaluation)
504fn build_table_metadata(lib: &mut JSONEval) -> Result<(), String> {
505    let mut table_metadata = IndexMap::new();
506    
507    for (eval_key, table) in lib.tables.iter() {
508        let metadata = compile_table_metadata(lib, eval_key, table)?;
509        table_metadata.insert(eval_key.to_string(), metadata);
510    }
511    
512    lib.table_metadata = Arc::new(table_metadata);
513    Ok(())
514}
515
516/// Compile table metadata at parse time (zero-copy design)
517fn compile_table_metadata(
518    lib: &JSONEval,
519    eval_key: &str,
520    table: &Value,
521) -> Result<TableMetadata, String> {
522    let rows = table
523        .get("rows")
524        .and_then(|v| v.as_array())
525        .ok_or("table missing rows")?;
526    let empty_datas = Vec::new();
527    let datas = table
528        .get("datas")
529        .and_then(|v| v.as_array())
530        .unwrap_or(&empty_datas);
531
532    // Pre-compile data plans with Arc sharing
533    let mut data_plans = Vec::with_capacity(datas.len());
534    for (idx, entry) in datas.iter().enumerate() {
535        let Some(name) = entry.get("name").and_then(|v| v.as_str()) else { continue };
536        let logic_path = format!("{eval_key}/$datas/{idx}/data");
537        let logic = lib.evaluations.get(&logic_path).copied();
538        let literal = entry.get("data").map(|v| Arc::new(v.clone()));
539        data_plans.push((Arc::from(name), logic, literal));
540    }
541
542    // Pre-compile row plans with dependency analysis
543    let mut row_plans = Vec::with_capacity(rows.len());
544    for (row_idx, row_val) in rows.iter().enumerate() {
545        let Some(row_obj) = row_val.as_object() else {
546            continue;
547        };
548
549        if let Some(repeat_arr) = row_obj.get("$repeat").and_then(|v| v.as_array()) {
550            if repeat_arr.len() == 3 {
551                let start_logic_path = format!("{eval_key}/$table/{row_idx}/$repeat/0");
552                let end_logic_path = format!("{eval_key}/$table/{row_idx}/$repeat/1");
553                let start_logic = lib.evaluations.get(&start_logic_path).copied();
554                let end_logic = lib.evaluations.get(&end_logic_path).copied();
555
556                let start_literal = Arc::new(repeat_arr.get(0).cloned().unwrap_or(Value::Null));
557                let end_literal = Arc::new(repeat_arr.get(1).cloned().unwrap_or(Value::Null));
558
559                if let Some(template) = repeat_arr.get(2).and_then(|v| v.as_object()) {
560                    let mut columns = Vec::with_capacity(template.len());
561                    for (col_name, col_val) in template {
562                        let col_eval_path =
563                            format!("{eval_key}/$table/{row_idx}/$repeat/2/{col_name}");
564                        let logic = lib.evaluations.get(&col_eval_path).copied();
565                        let literal = if logic.is_none() {
566                            Some(col_val.clone())
567                        } else {
568                            None
569                        };
570                        
571                        // Extract dependencies ONCE at parse time (not during evaluation)
572                        let (dependencies, has_forward_ref) = if let Some(logic_id) = logic {
573                            let deps = lib.engine.get_referenced_vars(&logic_id)
574                                .unwrap_or_default()
575                                .into_iter()
576                                .filter(|v| v.starts_with('$') && v != "$iteration" && v != "$threshold")
577                                .collect();
578                            let has_fwd = lib.engine.has_forward_reference(&logic_id);
579                            (deps, has_fwd)
580                        } else {
581                            (Vec::new(), false)
582                        };
583                        
584                        columns.push(ColumnMetadata::new(col_name, logic, literal, dependencies, has_forward_ref));
585                    }
586
587                    // Pre-compute forward column propagation (transitive closure)
588                    let (forward_cols, normal_cols) = compute_column_partitions(&columns);
589
590                    row_plans.push(RowMetadata::Repeat {
591                        start: RepeatBoundMetadata {
592                            logic: start_logic,
593                            literal: start_literal,
594                        },
595                        end: RepeatBoundMetadata {
596                            logic: end_logic,
597                            literal: end_literal,
598                        },
599                        columns: columns.into(),
600                        forward_cols: forward_cols.into(),
601                        normal_cols: normal_cols.into(),
602                    });
603                    continue;
604                }
605            }
606        }
607
608        // Static row
609        let mut columns = Vec::with_capacity(row_obj.len());
610        for (col_name, col_val) in row_obj {
611            if col_name == "$repeat" {
612                continue;
613            }
614            let col_eval_path = format!("{eval_key}/$table/{row_idx}/{col_name}");
615            let logic = lib.evaluations.get(&col_eval_path).copied();
616            let literal = if logic.is_none() {
617                Some(col_val.clone())
618            } else {
619                None
620            };
621            
622            // Extract dependencies ONCE at parse time
623            let (dependencies, has_forward_ref) = if let Some(logic_id) = logic {
624                let deps = lib.engine.get_referenced_vars(&logic_id)
625                    .unwrap_or_default()
626                    .into_iter()
627                    .filter(|v| v.starts_with('$') && v != "$iteration" && v != "$threshold")
628                    .collect();
629                let has_fwd = lib.engine.has_forward_reference(&logic_id);
630                (deps, has_fwd)
631            } else {
632                (Vec::new(), false)
633            };
634            
635            columns.push(ColumnMetadata::new(col_name, logic, literal, dependencies, has_forward_ref));
636        }
637        row_plans.push(RowMetadata::Static { columns: columns.into() });
638    }
639
640    // Pre-compile skip/clear logic
641    let skip_logic = lib.evaluations.get(&format!("{eval_key}/$skip")).copied();
642    let skip_literal = table.get("skip").and_then(Value::as_bool).unwrap_or(false);
643    let clear_logic = lib.evaluations.get(&format!("{eval_key}/$clear")).copied();
644    let clear_literal = table.get("clear").and_then(Value::as_bool).unwrap_or(false);
645
646    Ok(TableMetadata {
647        data_plans: data_plans.into(),
648        row_plans: row_plans.into(),
649        skip_logic,
650        skip_literal,
651        clear_logic,
652        clear_literal,
653    })
654}