1use crate::options::InfoOptions;
4use crate::rule::Rule;
5use crate::token::{TIN_ST, TIN_TX};
6use crate::value::{ListRef, MapRef, Text, Value};
7use crate::Context;
8use indexmap::IndexMap;
9use std::cell::RefCell;
10use std::rc::Rc;
11use std::sync::Arc;
12
13pub(crate) fn is_builtin_action(name: &str) -> bool {
14 matches!(
15 name,
16 "@node$"
17 | "@capture$"
18 | "@bubble$"
19 | "@fold$"
20 | "@probeInit$"
21 | "@probeDecide$"
22 | "@object$"
23 | "@array$"
24 | "@reset$"
25 | "@key$"
26 | "@setval$"
27 | "@push$"
28 | "@value$"
29 | "@map-bo"
30 | "@list-bo"
31 | "@pairkey"
32 | "@pair-bc"
33 | "@elem-bc"
34 | "@val-bo"
35 | "@val-bc"
36 )
37}
38
39fn config_object(config: Option<&Value>) -> Option<&IndexMap<String, Value>> {
40 config.and_then(|value| match value {
41 Value::Object(map) => Some(map.as_ref()),
42 _ => None,
43 })
44}
45
46fn config_bool(config: Option<&Value>, name: &str) -> bool {
47 matches!(
48 config_object(config).and_then(|map| map.get(name)),
49 Some(Value::Bool(true))
50 )
51}
52
53fn config_string(config: Option<&Value>, name: &str) -> String {
54 match config_object(config).and_then(|map| map.get(name)) {
55 Some(Value::String(value)) => value.clone(),
56 _ => String::new(),
57 }
58}
59
60fn config_str_opt(config: Option<&Value>, name: &str) -> Option<String> {
68 match config_object(config).and_then(|map| map.get(name)) {
69 Some(Value::String(value)) => Some(value.clone()),
70 _ => None,
71 }
72}
73
74fn config_usize(config: Option<&Value>, name: &str) -> usize {
75 match config_object(config).and_then(|map| map.get(name)) {
76 Some(Value::Number(value)) if value.is_finite() && *value >= 0.0 => *value as usize,
77 _ => 0,
78 }
79}
80
81fn config_index(config: Option<&Value>, name: &str) -> Option<usize> {
82 match config_object(config).and_then(|map| map.get(name)) {
83 None => Some(0),
84 Some(Value::Number(value))
85 if value.is_finite()
86 && value.fract() == 0.0
87 && *value >= 0.0
88 && *value <= usize::MAX as f64 =>
89 {
90 Some(*value as usize)
91 }
92 _ => None,
93 }
94}
95
96fn ast_node(rule: String, kind: String) -> Value {
97 let mut node = IndexMap::new();
98 if kind == "user" {
99 node.insert("rule".into(), Value::String(rule));
100 }
101 node.insert("src".into(), Value::String(String::new()));
102 node.insert("kids".into(), Value::array(Vec::new()));
103 Value::object(node)
104}
105
106fn src_val(node: Value) -> Value {
116 if let Value::Object(map) = &node {
117 if let Some(Value::String(source)) = map.get("src") {
118 return Value::String(source.clone());
119 }
120 }
121 node
122}
123
124fn append_src(node: &mut IndexMap<String, Value>, source: &str) {
125 if let Some(Value::String(current)) = node.get_mut("src") {
126 current.push_str(source);
127 }
128}
129
130fn append_kid(node: &mut IndexMap<String, Value>, child: Value) {
131 if let Some(kids) = node.get_mut("kids").and_then(Value::as_array_mut) {
132 kids.push(child);
133 }
134}
135
136fn capture_child(node: &mut IndexMap<String, Value>, child: Value) {
137 if let Value::Object(child_map) = &child {
138 if let Some(Value::String(source)) = child_map.get("src") {
139 append_src(node, source);
140 if child_map
141 .get("rule")
142 .is_some_and(|value| !matches!(value, Value::String(rule) if rule.is_empty()))
143 {
144 append_kid(node, child);
145 } else if let Some(Value::Array(children)) = child_map.get("kids") {
146 extend_kids(node, children);
147 }
148 return;
149 }
150 }
151 append_kid(node, child);
152}
153
154fn extend_kids(node: &mut IndexMap<String, Value>, children: &Arc<Vec<Value>>) {
167 match node.get_mut("kids") {
168 Some(Value::Array(kids)) if kids.is_empty() => *kids = Arc::clone(children),
169 Some(Value::Array(kids)) => Arc::make_mut(kids).extend(children.iter().cloned()),
170 _ => {}
171 }
172}
173
174fn map_value(info: &InfoOptions, implicit: bool) -> Value {
175 if info.map {
176 Value::MapRef(Arc::new(MapRef {
177 value: IndexMap::new(),
178 implicit,
179 meta: IndexMap::new(),
180 }))
181 } else {
182 Value::object(IndexMap::new())
183 }
184}
185
186fn list_value(info: &InfoOptions, implicit: bool) -> Value {
187 if info.list {
188 Value::ListRef(Arc::new(ListRef {
189 value: Vec::new(),
190 implicit,
191 child: None,
192 meta: IndexMap::new(),
193 }))
194 } else {
195 Value::array(Vec::new())
196 }
197}
198
199fn token_value(rule: &mut Rule, context: &mut Context, index: usize, info: &InfoOptions) -> Value {
200 let Some(token) = rule.o.get(index).cloned() else {
201 return Value::Undefined;
202 };
203 let value = token.resolve_val(rule, context);
204 if info.text && matches!(token.tin, TIN_ST | TIN_TX) {
205 let string = match &value {
206 Value::String(value) => value.clone(),
207 Value::Text(value) => value.string.clone(),
208 _ => return value,
209 };
210 let quote = if token.tin == TIN_ST {
211 token
212 .src
213 .chars()
214 .next()
215 .map_or_else(String::new, |ch| ch.to_string())
216 } else {
217 String::new()
218 };
219 Value::Text(Text { quote, string })
220 } else {
221 value
222 }
223}
224
225fn map_insert(node: &mut Value, key: String, value: Value) {
226 match node {
227 Value::Object(map) => {
228 Arc::make_mut(map).insert(key, value);
229 }
230 Value::MapRef(map) => {
231 Arc::make_mut(map).value.insert(key, value);
232 }
233 _ => {}
234 }
235}
236
237fn list_push(node: &mut Value, value: Value) {
238 match node {
239 Value::Array(array) => Arc::make_mut(array).push(value),
240 Value::ListRef(list) => Arc::make_mut(list).value.push(value),
241 _ => {}
242 }
243}
244
245pub fn run_builtin_action(name: &str, rule: &mut Rule, config: Option<&Value>) -> bool {
253 let options = std::sync::Arc::new(crate::Options::default());
254 let mut context = Context::new(
255 options.rewind.history,
256 "",
257 Value::Undefined,
258 std::sync::Arc::clone(&options),
259 crate::InstanceInfo::default(),
260 );
261 run_builtin_action_with_info(name, rule, &mut context, config, &InfoOptions::default())
262}
263
264pub fn run_builtin_action_with_context(
267 name: &str,
268 rule: &mut Rule,
269 context: &mut Context,
270 config: Option<&Value>,
271) -> bool {
272 let info = context.options.info.clone();
273 run_builtin_action_with_info(name, rule, context, config, &info)
274}
275
276pub(crate) fn run_builtin_action_with_info(
277 name: &str,
278 rule: &mut Rule,
279 context: &mut Context,
280 config: Option<&Value>,
281 info: &InfoOptions,
282) -> bool {
283 match name {
284 "@node$" => {
285 if config_bool(config, "init") {
286 rule.node = Rc::new(RefCell::new(ast_node(
287 config_string(config, "rule"),
288 config_string(config, "kind"),
289 )));
290 }
291 let nterms = config_usize(config, "nterms");
292 if let Some(node) = rule.node.borrow_mut().as_object_mut() {
293 for token in rule.o.iter().take(nterms) {
294 append_src(node, &token.src);
295 }
296 }
297 }
298 "@capture$" => {
299 if rule.node.borrow().is_undefined() {
300 rule.node = Rc::new(RefCell::new(ast_node(
301 config_string(config, "rule"),
302 config_string(config, "kind"),
303 )));
304 }
305 if !rule.child_node.is_undefined() && !rule.child_node_is_self {
306 if let Some(node) = rule.node.borrow_mut().as_object_mut() {
307 capture_child(node, rule.child_node.clone());
308 }
309 }
310 }
311 "@bubble$" => {
312 if rule.has_child_value() {
313 rule.node = Rc::new(RefCell::new(rule.child_value()));
314 }
315 }
316 "@fold$" => {
317 if let Some(parent_node) = &rule.parent_node {
318 let same_node = Rc::ptr_eq(parent_node, &rule.node);
319 let own = rule.node.borrow().clone();
320 if let Some(parent) = parent_node.borrow_mut().as_object_mut() {
321 if !same_node {
322 capture_child(parent, own);
323 }
324 let close_count = config_usize(config, "cN");
325 for token in rule.c.iter().take(close_count) {
326 append_src(parent, &token.src);
327 }
328 }
329 }
330 rule.node = Rc::new(RefCell::new(Value::Undefined));
331 }
332 "@val-bo" => {
333 rule.node = Rc::new(RefCell::new(Value::Undefined));
334 }
335 "@val-bc" => {
336 let is_undef = rule.node.borrow().is_undefined();
337 if is_undef {
338 if rule.has_child_value() {
339 rule.node = Rc::new(RefCell::new(rule.child_value()));
340 } else if rule.os() > 0 {
341 rule.node = Rc::new(RefCell::new(token_value(rule, context, 0, info)));
342 }
343 }
344 }
345 "@value$" => {
346 if rule.has_child_value() {
347 rule.node = Rc::new(RefCell::new(rule.child_value()));
348 } else {
349 let value = config_index(config, "from").map_or(Value::Undefined, |index| {
350 token_value(rule, context, index, info)
351 });
352 rule.node = Rc::new(RefCell::new(value));
353 }
354 }
355 "@object$" => {
356 rule.node = Rc::new(RefCell::new(map_value(
357 info,
358 config_bool(config, "implicit"),
359 )));
360 }
361 "@array$" => {
362 rule.node = Rc::new(RefCell::new(list_value(
363 info,
364 config_bool(config, "implicit"),
365 )));
366 }
367 "@reset$" => {
368 rule.node = Rc::new(RefCell::new(Value::Undefined));
369 rule.child_node = Value::Undefined;
370 rule.child_node_is_self = false;
371 }
372 "@key$" => {
373 let slot = {
374 let configured = config_string(config, "slot");
375 if configured.is_empty() {
376 "key".to_string()
377 } else {
378 configured
379 }
380 };
381 if let Some(lit) = config_str_opt(config, "lit") {
388 rule.u_mut().insert(slot, Value::String(lit));
389 } else if let Some(value) = config_index(config, "from")
390 .and_then(|index| rule.o.get(index))
391 .map(|token| token.val.clone())
392 {
393 rule.u_mut().insert(slot, value);
394 }
395 }
396 "@setval$" => {
397 let slot = {
398 let configured = config_string(config, "slot");
399 if configured.is_empty() {
400 "key".to_string()
401 } else {
402 configured
403 }
404 };
405 if let Some(Value::String(key)) = rule.u.get(&slot).cloned() {
406 let mut val = rule.child_value();
407 if config_bool(config, "src") {
408 val = src_val(val);
409 }
410 map_insert(&mut rule.node.borrow_mut(), key, val);
411 }
412 }
413 "@push$" => {
414 if rule.has_child_value() {
415 let mut val = rule.child_value();
416 if config_bool(config, "src") {
417 val = src_val(val);
418 }
419 list_push(&mut rule.node.borrow_mut(), val);
420 }
421 }
422 "@map-bo" => {
423 rule.node = Rc::new(RefCell::new(map_value(info, false)));
424 }
425 "@list-bo" => {
426 rule.node = Rc::new(RefCell::new(list_value(info, false)));
427 }
428 "@pairkey" => {
432 if let Some(t0) = rule.o0() {
433 let key = match &t0.val {
434 Value::String(s) => s.clone(),
435 _ => t0.src.to_string(),
436 };
437 rule.u_mut().insert("key".to_string(), Value::String(key));
438 }
439 }
440 "@pair-bc" => {
441 if let Some(Value::String(key)) = rule.u.get("key").cloned() {
442 let child = rule.child_value();
443 let mut node = rule.node.borrow_mut();
444 map_insert(&mut node, key, child);
445 }
446 }
447 "@elem-bc" if rule.has_child_value() => {
448 let child = rule.child_value();
449 let mut node = rule.node.borrow_mut();
450 list_push(&mut node, child);
451 }
452 _ => return false,
453 }
454 true
455}