1use bigdecimal::{BigDecimal, Zero};
8use std::collections::{HashMap, VecDeque};
9use std::fmt;
10use std::path::Path;
11use std::str::FromStr;
12
13use crate::ast::{
14 AddressAction, AssignTarget, BinOp, Clause, ClauseKind, Condition, ControlledLoop, DoBlock,
15 DoKind, Expr, NumericFormSetting, NumericSetting, ParseSource, ParseTemplate, Program,
16 SignalAction, TailElement, TemplateElement, UnaryOp,
17};
18use crate::env::{EnvVars, Environment};
19use crate::error::{RexxDiagnostic, RexxError, RexxResult};
20use crate::lexer::Lexer;
21use crate::parser::Parser;
22use crate::value::{NumericSettings, RexxValue};
23
24const MAX_INTERPRET_DEPTH: usize = 100;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
29enum TraceLevel {
30 Off,
31 Normal,
32 Failure,
33 Errors,
34 Commands,
35 Labels,
36 Results,
37 Intermediates,
38 All,
39}
40
41impl TraceLevel {
42 fn parse(s: &str) -> Option<Self> {
44 let upper = s.trim().to_uppercase();
45 match upper.as_str() {
46 "O" | "OFF" => Some(Self::Off),
47 "N" | "NORMAL" => Some(Self::Normal),
48 "F" | "FAILURE" => Some(Self::Failure),
49 "E" | "ERRORS" => Some(Self::Errors),
50 "C" | "COMMANDS" => Some(Self::Commands),
51 "L" | "LABELS" => Some(Self::Labels),
52 "R" | "RESULTS" => Some(Self::Results),
53 "I" | "INTERMEDIATES" => Some(Self::Intermediates),
54 "A" | "ALL" => Some(Self::All),
55 _ => None,
56 }
57 }
58
59 fn letter(self) -> char {
61 match self {
62 Self::Off => 'O',
63 Self::Normal => 'N',
64 Self::Failure => 'F',
65 Self::Errors => 'E',
66 Self::Commands => 'C',
67 Self::Labels => 'L',
68 Self::Results => 'R',
69 Self::Intermediates => 'I',
70 Self::All => 'A',
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
77struct TraceSetting {
78 level: TraceLevel,
79 interactive: bool,
80}
81
82enum TraceAction {
84 ToggleInteractive,
86 Set(TraceSetting),
88}
89
90impl TraceAction {
91 fn parse(s: &str) -> Option<Self> {
93 let trimmed = s.trim();
94 if trimmed.is_empty() {
95 return None;
96 }
97 if trimmed == "?" {
98 return Some(Self::ToggleInteractive);
99 }
100 if let Some(rest) = trimmed.strip_prefix('?') {
101 let level = TraceLevel::parse(rest)?;
102 Some(Self::Set(TraceSetting {
103 level,
104 interactive: true,
105 }))
106 } else {
107 let level = TraceLevel::parse(trimmed)?;
108 Some(Self::Set(TraceSetting {
109 level,
110 interactive: false,
111 }))
112 }
113 }
114}
115
116impl Default for TraceSetting {
117 fn default() -> Self {
118 Self {
119 level: TraceLevel::Normal,
120 interactive: false,
121 }
122 }
123}
124
125impl fmt::Display for TraceSetting {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 if self.interactive {
128 write!(f, "?{}", self.level.letter())
129 } else {
130 write!(f, "{}", self.level.letter())
131 }
132 }
133}
134
135pub enum ExecSignal {
137 Normal,
138 Leave(Option<String>),
139 Iterate(Option<String>),
140 Exit(Option<RexxValue>),
141 Return(Option<RexxValue>),
142 Signal(String),
144}
145
146enum PendingExit {
148 None,
149 WithValue(Option<RexxValue>),
150}
151
152impl PendingExit {
153 fn take_signal(&mut self) -> Option<ExecSignal> {
156 match std::mem::replace(self, PendingExit::None) {
157 PendingExit::None => Option::None,
158 PendingExit::WithValue(v) => Some(ExecSignal::Exit(v)),
159 }
160 }
161
162 const fn is_pending(&self) -> bool {
163 matches!(self, PendingExit::WithValue(_))
164 }
165}
166
167type CommandHandler = Box<dyn FnMut(&str, &str) -> Option<i32>>;
178
179type CommandHandlerWithEnv = Box<dyn FnMut(&str, &str, &mut EnvVars<'_>) -> Option<i32>>;
196
197pub struct Evaluator<'a> {
198 env: &'a mut Environment,
199 program: &'a Program,
200 settings: NumericSettings,
201 labels: HashMap<String, usize>,
202 arg_stack: Vec<Vec<RexxValue>>,
203 pending_exit: PendingExit,
204 traps: HashMap<Condition, String>,
206 pending_signal: Option<String>,
208 interpret_depth: usize,
210 external_depth: usize,
212 trace_setting: TraceSetting,
214 queue: VecDeque<String>,
216 command_handler: Option<CommandHandler>,
220 command_handler_with_env: Option<CommandHandlerWithEnv>,
224}
225
226impl<'a> Evaluator<'a> {
227 pub fn new(env: &'a mut Environment, program: &'a Program) -> Self {
228 let labels = Self::build_labels(program);
229 Self {
230 env,
231 program,
232 settings: NumericSettings::default(),
233 labels,
234 arg_stack: Vec::new(),
235 pending_exit: PendingExit::None,
236 traps: HashMap::new(),
237 pending_signal: None,
238 interpret_depth: 0,
239 external_depth: 0,
240 trace_setting: TraceSetting::default(),
241 queue: VecDeque::new(),
242 command_handler: None,
243 command_handler_with_env: None,
244 }
245 }
246
247 fn build_labels(program: &Program) -> HashMap<String, usize> {
248 let mut labels = HashMap::new();
249 for (i, clause) in program.clauses.iter().enumerate() {
250 if let ClauseKind::Label(name) = &clause.kind {
251 labels.entry(name.clone()).or_insert(i);
252 }
253 }
254 labels
255 }
256
257 pub fn exec(&mut self) -> RexxResult<ExecSignal> {
258 let mut start = 0;
259 loop {
260 match self.exec_from(start)? {
261 ExecSignal::Signal(label) => {
262 let &idx = self.labels.get(&label).ok_or_else(|| {
263 RexxDiagnostic::new(RexxError::LabelNotFound)
264 .with_detail(format!("label '{label}' not found"))
265 })?;
266 if matches!(
268 self.trace_setting.level,
269 TraceLevel::Labels | TraceLevel::All
270 ) {
271 self.trace_clause(&self.program.clauses[idx]);
272 }
273 start = idx + 1;
274 }
275 other => return Ok(other),
276 }
277 }
278 }
279
280 fn exec_from(&mut self, start: usize) -> RexxResult<ExecSignal> {
281 for clause in &self.program.clauses[start..] {
282 let signal = self.exec_clause_outer(clause)?;
283 if let Some(signal) = self.pending_exit.take_signal() {
284 return Ok(signal);
285 }
286 if let Some(label) = self.pending_signal.take() {
287 return Ok(ExecSignal::Signal(label));
288 }
289 if !matches!(signal, ExecSignal::Normal) {
290 return Ok(signal);
291 }
292 }
293 Ok(ExecSignal::Normal)
294 }
295
296 fn exec_body(&mut self, body: &[Clause]) -> RexxResult<ExecSignal> {
297 for clause in body {
298 let signal = self.exec_clause_outer(clause)?;
299 if let Some(signal) = self.pending_exit.take_signal() {
300 return Ok(signal);
301 }
302 if let Some(label) = self.pending_signal.take() {
303 return Ok(ExecSignal::Signal(label));
304 }
305 if !matches!(signal, ExecSignal::Normal) {
306 return Ok(signal);
307 }
308 }
309 Ok(ExecSignal::Normal)
310 }
311
312 fn exec_clause_outer(&mut self, clause: &Clause) -> RexxResult<ExecSignal> {
314 let trace_level = self.trace_setting.level;
315
316 let should_trace_source = match &clause.kind {
321 ClauseKind::Label(_) => {
322 matches!(trace_level, TraceLevel::Labels | TraceLevel::All)
323 }
324 ClauseKind::Command(_) => matches!(
325 trace_level,
326 TraceLevel::Commands
327 | TraceLevel::Results
328 | TraceLevel::Intermediates
329 | TraceLevel::All
330 ),
331 _ => matches!(
332 trace_level,
333 TraceLevel::Results | TraceLevel::Intermediates | TraceLevel::All
334 ),
335 };
336 if should_trace_source && !matches!(clause.kind, ClauseKind::Nop) {
337 self.trace_clause(clause);
338 }
339
340 match self.exec_clause(clause) {
341 Ok(signal) => {
342 if should_trace_source && !matches!(clause.kind, ClauseKind::Nop) {
344 self.trace_interactive_pause()?;
345 }
346 Ok(signal)
347 }
348 Err(diag) => {
349 if let Some(label) = self.traps.get(&Condition::Syntax).cloned() {
350 self.env
352 .set("RC", RexxValue::new(diag.error.number().to_string()));
353 let desc = diag.detail.unwrap_or_default();
355 self.env.set_condition_info(crate::env::ConditionInfoData {
356 condition: "SYNTAX".to_string(),
357 description: desc,
358 instruction: "SIGNAL".to_string(),
359 status: "ON".to_string(),
360 });
361 self.traps.remove(&Condition::Syntax);
363 Ok(ExecSignal::Signal(label))
364 } else {
365 Err(diag)
366 }
367 }
368 }
369 }
370
371 #[allow(clippy::too_many_lines)]
372 fn exec_clause(&mut self, clause: &Clause) -> RexxResult<ExecSignal> {
373 match &clause.kind {
374 ClauseKind::Say(expr) => {
375 let val = self.eval_expr(expr)?;
376 if self.pending_exit.is_pending() {
377 return Ok(ExecSignal::Normal);
378 }
379 if matches!(
380 self.trace_setting.level,
381 TraceLevel::Results | TraceLevel::Intermediates | TraceLevel::All
382 ) {
383 self.trace_tag(">>", val.as_str());
384 }
385 println!("{val}");
386 Ok(ExecSignal::Normal)
387 }
388 ClauseKind::Assignment { target, expr } => {
389 let val = self.eval_expr(expr)?;
390 if matches!(
391 self.trace_setting.level,
392 TraceLevel::Results | TraceLevel::Intermediates | TraceLevel::All
393 ) {
394 self.trace_tag(">>", val.as_str());
395 }
396 match target {
397 AssignTarget::Simple(name) => {
398 self.env.set(name, val);
399 }
400 AssignTarget::Stem { stem, tail } => {
401 let resolved_tail = self.resolve_tail(tail);
402 if resolved_tail.is_empty() {
403 self.env.set_stem_default(stem, val);
404 } else {
405 self.env.set_compound(stem, &resolved_tail, val);
406 }
407 }
408 }
409 Ok(ExecSignal::Normal)
410 }
411 ClauseKind::Command(expr) => {
412 let val = self.eval_expr(expr)?;
413 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
414 return Ok(ExecSignal::Normal);
415 }
416 Ok(self.exec_host_command(val.as_str()))
417 }
418 ClauseKind::If {
419 condition,
420 then_clause,
421 else_clause,
422 } => self.exec_if(condition, then_clause, else_clause.as_deref()),
423 ClauseKind::Do(block) => self.exec_do(block),
424 ClauseKind::Select {
425 when_clauses,
426 otherwise,
427 } => self.exec_select(when_clauses, otherwise.as_ref()),
428 ClauseKind::Leave(name) => Ok(ExecSignal::Leave(name.clone())),
429 ClauseKind::Iterate(name) => Ok(ExecSignal::Iterate(name.clone())),
430 ClauseKind::Exit(expr) => {
431 let val = if let Some(e) = expr {
432 Some(self.eval_expr(e)?)
433 } else {
434 None
435 };
436 Ok(ExecSignal::Exit(val))
437 }
438 ClauseKind::Return(expr) => {
439 let val = if let Some(e) = expr {
440 Some(self.eval_expr(e)?)
441 } else {
442 None
443 };
444 Ok(ExecSignal::Return(val))
445 }
446 ClauseKind::Call { name, args } => self.exec_call(name, args),
447 ClauseKind::Signal(action) => self.exec_signal(action),
448 ClauseKind::Label(_) | ClauseKind::Nop => Ok(ExecSignal::Normal),
449 ClauseKind::Procedure(_) => Err(RexxDiagnostic::new(RexxError::UnexpectedProcedure)
450 .with_detail("PROCEDURE must be the first instruction in a called routine")),
451 ClauseKind::Drop(names) => {
452 for name in names {
453 self.env.drop(name);
454 }
455 Ok(ExecSignal::Normal)
456 }
457 ClauseKind::Arg(template) => self.exec_arg(template),
458 ClauseKind::Parse {
459 upper,
460 source,
461 template,
462 } => self.exec_parse(*upper, source, template),
463 ClauseKind::Pull(template_opt) => {
464 let raw = self.pull_from_queue_or_stdin()?;
465 let text = raw.to_uppercase();
466 if let Some(template) = template_opt {
467 self.apply_template(&text, template)?;
468 }
469 Ok(ExecSignal::Normal)
470 }
471 ClauseKind::Interpret(expr) => self.exec_interpret(expr),
472 ClauseKind::Address(action) => self.exec_address(action),
473 ClauseKind::Trace(expr) => self.exec_trace(expr),
474 ClauseKind::Numeric(setting) => self.exec_numeric(setting),
475 ClauseKind::Push(expr) => self.exec_push(expr.as_ref()),
476 ClauseKind::Queue(expr) => self.exec_queue(expr.as_ref()),
477 }
478 }
479
480 fn exec_address(&mut self, action: &AddressAction) -> RexxResult<ExecSignal> {
484 match action {
485 AddressAction::SetEnvironment(name) => {
486 if name.is_empty() {
487 self.env.swap_address();
488 } else {
489 self.env.set_address(name);
490 }
491 Ok(ExecSignal::Normal)
492 }
493 AddressAction::Value(expr) => {
494 let val = self.eval_expr(expr)?;
495 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
496 return Ok(ExecSignal::Normal);
497 }
498 let name = val.as_str().to_uppercase();
499 self.env.set_address(&name);
500 Ok(ExecSignal::Normal)
501 }
502 AddressAction::Temporary {
503 environment,
504 command,
505 } => {
506 let cmd_val = self.eval_expr(command)?;
507 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
508 return Ok(ExecSignal::Normal);
509 }
510 let cmd_str = cmd_val.as_str().to_string();
511 let saved_default = self.env.address().to_string();
513 self.env.set_address(environment);
514 let signal = self.exec_host_command(&cmd_str);
515 self.env.set_address(&saved_default);
518 Ok(signal)
519 }
520 }
521 }
522
523 fn exec_host_command(&mut self, command: &str) -> ExecSignal {
526 let mut custom_rc = None;
533 if let Some(mut handler) = self.command_handler_with_env.take() {
534 let addr = self.env.address().to_string();
535 let mut vars = EnvVars::new(self.env);
536 custom_rc = handler(&addr, command, &mut vars);
537 self.command_handler_with_env = Some(handler);
538 }
539 if custom_rc.is_none()
541 && let Some(ref mut handler) = self.command_handler
542 {
543 let addr = self.env.address().to_string();
544 custom_rc = handler(&addr, command);
545 }
546
547 let rc = if let Some(rc) = custom_rc {
548 self.env.set("RC", RexxValue::new(rc.to_string()));
550 rc
551 } else {
552 let result = std::process::Command::new("sh")
554 .arg("-c")
555 .arg(command)
556 .status();
557 if let Ok(status) = result {
558 let code = status.code().unwrap_or(-1);
559 self.env.set("RC", RexxValue::new(code.to_string()));
560 code
561 } else {
562 self.env.set("RC", RexxValue::new("-1"));
563 return self.fire_failure_trap(command);
564 }
565 };
566
567 if rc < 0 {
569 return self.fire_failure_trap(command);
570 }
571 if rc > 0
572 && let Some(label) = self.traps.get(&Condition::Error).cloned()
573 {
574 self.env.set_condition_info(crate::env::ConditionInfoData {
575 condition: "ERROR".to_string(),
576 description: command.to_string(),
577 instruction: "SIGNAL".to_string(),
578 status: "ON".to_string(),
579 });
580 self.traps.remove(&Condition::Error);
581 return ExecSignal::Signal(label);
582 }
583 ExecSignal::Normal
584 }
585
586 fn fire_failure_trap(&mut self, command: &str) -> ExecSignal {
587 if let Some(label) = self.traps.get(&Condition::Failure).cloned() {
588 self.env.set_condition_info(crate::env::ConditionInfoData {
589 condition: "FAILURE".to_string(),
590 description: command.to_string(),
591 instruction: "SIGNAL".to_string(),
592 status: "ON".to_string(),
593 });
594 self.traps.remove(&Condition::Failure);
595 ExecSignal::Signal(label)
596 } else {
597 ExecSignal::Normal
598 }
599 }
600
601 fn apply_trace_setting(&mut self, s: &str) -> RexxResult<String> {
606 let old = self.trace_setting.to_string();
607 let action = TraceAction::parse(s).ok_or_else(|| {
608 RexxDiagnostic::new(RexxError::InvalidTrace)
609 .with_detail(format!("invalid trace setting '{s}'"))
610 })?;
611 match action {
612 TraceAction::ToggleInteractive => {
613 self.trace_setting.interactive = !self.trace_setting.interactive;
614 }
615 TraceAction::Set(new_setting) => {
616 self.trace_setting = new_setting;
617 }
618 }
619 Ok(old)
620 }
621
622 fn exec_trace(&mut self, expr: &Expr) -> RexxResult<ExecSignal> {
624 let val = self.eval_expr(expr)?;
625 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
626 return Ok(ExecSignal::Normal);
627 }
628 self.apply_trace_setting(val.as_str())?;
629 Ok(ExecSignal::Normal)
630 }
631
632 #[allow(clippy::unused_self)]
634 fn trace_clause(&self, clause: &Clause) {
635 let line_num = clause.loc.line;
636 let source = clause.loc.source_line.as_deref().unwrap_or("(unknown)");
637 eprintln!("{line_num:>6} *-* {source}");
638 }
639
640 #[allow(clippy::unused_self)]
642 fn trace_tag(&self, tag: &str, value: &str) {
643 eprintln!(" >{tag}> \"{value}\"");
644 }
645
646 fn trace_intermediates(&self, tag: &str, value: &str) {
648 if matches!(
649 self.trace_setting.level,
650 TraceLevel::Intermediates | TraceLevel::All
651 ) {
652 self.trace_tag(tag, value);
653 }
654 }
655
656 fn trace_interactive_pause(&mut self) -> RexxResult<()> {
660 if !self.trace_setting.interactive {
661 return Ok(());
662 }
663 loop {
664 let mut line = String::new();
665 match std::io::stdin().read_line(&mut line) {
666 Ok(0) | Err(_) => break, Ok(_) => {}
668 }
669 let content = line.trim_end_matches(['\n', '\r']);
671 if content.is_empty() {
673 break;
674 }
675 let source = content.trim().to_string();
677 let mut lexer = Lexer::new(&source);
678 let tokens = lexer.tokenize()?;
679 let mut parser = Parser::new(tokens);
680 let program = parser.parse()?;
681 let labels = HashMap::new();
682 self.exec_interpret_body(&program.clauses, &labels)?;
683 }
684 Ok(())
685 }
686
687 fn exec_interpret(&mut self, expr: &Expr) -> RexxResult<ExecSignal> {
691 let val = self.eval_expr(expr)?;
692 if self.pending_exit.is_pending() {
693 return Ok(ExecSignal::Normal);
694 }
695 if self.pending_signal.is_some() {
696 return Ok(ExecSignal::Normal);
697 }
698
699 let source = val.as_str().to_string();
700 if source.is_empty() {
701 return Ok(ExecSignal::Normal);
702 }
703
704 if self.interpret_depth >= MAX_INTERPRET_DEPTH {
706 return Err(RexxDiagnostic::new(RexxError::ResourceExhausted)
707 .with_detail("INTERPRET recursion depth limit exceeded"));
708 }
709
710 let mut lexer = Lexer::new(&source);
712 let tokens = lexer.tokenize()?;
713 let mut parser = Parser::new(tokens);
714 let program = parser.parse()?;
715
716 let mut labels = HashMap::new();
718 for (i, clause) in program.clauses.iter().enumerate() {
719 if let ClauseKind::Label(name) = &clause.kind {
720 labels.entry(name.clone()).or_insert(i);
721 }
722 }
723
724 self.interpret_depth += 1;
725 let result = self.exec_interpret_body(&program.clauses, &labels);
726 self.interpret_depth -= 1;
727
728 result
729 }
730
731 fn exec_interpret_body(
733 &mut self,
734 clauses: &[Clause],
735 labels: &HashMap<String, usize>,
736 ) -> RexxResult<ExecSignal> {
737 let mut start = 0;
738 loop {
739 match self.exec_interpret_from(clauses, start)? {
740 ExecSignal::Signal(ref label) => {
741 if let Some(&idx) = labels.get(label) {
742 start = idx + 1; } else {
744 return Ok(ExecSignal::Signal(label.clone()));
745 }
746 }
747 other => return Ok(other),
748 }
749 }
750 }
751
752 fn exec_interpret_from(&mut self, clauses: &[Clause], start: usize) -> RexxResult<ExecSignal> {
754 for clause in &clauses[start..] {
755 let signal = self.exec_clause_outer(clause)?;
756 if let Some(signal) = self.pending_exit.take_signal() {
757 return Ok(signal);
758 }
759 if let Some(label) = self.pending_signal.take() {
760 return Ok(ExecSignal::Signal(label));
761 }
762 if !matches!(signal, ExecSignal::Normal) {
763 return Ok(signal);
764 }
765 }
766 Ok(ExecSignal::Normal)
767 }
768
769 fn call_routine(&mut self, name: &str, args: Vec<RexxValue>) -> RexxResult<ExecSignal> {
770 let &label_idx = self.labels.get(name).ok_or_else(|| {
771 RexxDiagnostic::new(RexxError::RoutineNotFound)
772 .with_detail(format!("routine '{name}' not found"))
773 })?;
774
775 let start_idx = label_idx + 1; self.arg_stack.push(args);
777
778 let has_procedure = matches!(
780 self.program.clauses.get(start_idx).map(|c| &c.kind),
781 Some(ClauseKind::Procedure(_))
782 );
783
784 let exec_start = if has_procedure {
785 match &self.program.clauses[start_idx].kind {
786 ClauseKind::Procedure(Some(names)) => self.env.push_procedure_expose(names),
787 ClauseKind::Procedure(None) => self.env.push_procedure(),
788 _ => unreachable!(),
789 }
790 start_idx + 1
791 } else {
792 start_idx
793 };
794
795 let result = self.exec_from(exec_start);
796
797 if has_procedure {
798 self.env.pop_procedure();
799 }
800 self.arg_stack.pop();
801
802 result
803 }
804
805 fn try_call_external(
808 &mut self,
809 name: &str,
810 args: Vec<RexxValue>,
811 ) -> RexxResult<Option<ExecSignal>> {
812 let source_dir = self.env.source_dir();
814 let Some((program, path)) = crate::external::resolve_external(name, source_dir)? else {
815 return Ok(None);
816 };
817
818 if self.external_depth >= 100 {
820 return Err(RexxDiagnostic::new(RexxError::ResourceExhausted)
821 .with_detail("external function call recursion depth limit exceeded"));
822 }
823 self.external_depth += 1;
824
825 self.env.push_procedure();
827 self.arg_stack.push(args);
828
829 let old_source_path = self.env.source_path().map(Path::to_path_buf);
831 self.env.set_source_path(path);
832
833 let ext_labels = Self::build_labels(&program);
835 let result = self.exec_interpret_body(&program.clauses, &ext_labels);
836
837 match old_source_path {
839 Some(old) => self.env.set_source_path(old),
840 None => self.env.clear_source_path(),
841 }
842 self.arg_stack.pop();
843 self.env.pop_procedure();
844 self.external_depth -= 1;
845
846 Ok(Some(result?))
847 }
848
849 fn exec_call(&mut self, name: &str, arg_exprs: &[Expr]) -> RexxResult<ExecSignal> {
850 let mut args = Vec::with_capacity(arg_exprs.len());
851 for expr in arg_exprs {
852 args.push(self.eval_expr(expr)?);
853 if let Some(signal) = self.pending_exit.take_signal() {
854 return Ok(signal);
855 }
856 }
857
858 if name.eq_ignore_ascii_case("TRACE") {
860 if args.len() == 1 {
861 let old = self.apply_trace_setting(args[0].as_str())?;
862 self.env.set("RESULT", RexxValue::new(old));
863 } else if args.is_empty() {
864 let old = self.trace_setting.to_string();
865 self.env.set("RESULT", RexxValue::new(old));
866 } else {
867 return Err(RexxDiagnostic::new(RexxError::IncorrectCall)
868 .with_detail("TRACE expects 0 or 1 arguments"));
869 }
870 return Ok(ExecSignal::Normal);
871 }
872
873 if self.labels.contains_key(name) {
875 let signal = self.call_routine(name, args)?;
876 match signal {
877 ExecSignal::Return(Some(val)) => {
878 self.env.set("RESULT", val);
879 Ok(ExecSignal::Normal)
880 }
881 ExecSignal::Return(None) | ExecSignal::Normal => {
882 self.env.drop("RESULT");
883 Ok(ExecSignal::Normal)
884 }
885 ExecSignal::Exit(_) | ExecSignal::Signal(_) => Ok(signal),
886 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(ExecSignal::Normal),
887 }
888 } else if let Some(result) =
889 crate::builtins::call_builtin(name, &args, &self.settings, self.env, self.queue.len())
890 {
891 let val = result?;
892 self.env.set("RESULT", val);
893 Ok(ExecSignal::Normal)
894 } else {
895 match self.try_call_external(name, args)? {
897 Some(signal) => match signal {
898 ExecSignal::Return(Some(val)) => {
899 self.env.set("RESULT", val);
900 Ok(ExecSignal::Normal)
901 }
902 ExecSignal::Return(None) | ExecSignal::Normal => {
903 self.env.drop("RESULT");
904 Ok(ExecSignal::Normal)
905 }
906 ExecSignal::Exit(_) | ExecSignal::Signal(_) => Ok(signal),
907 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(ExecSignal::Normal),
908 },
909 None => Err(RexxDiagnostic::new(RexxError::RoutineNotFound)
910 .with_detail(format!("routine '{name}' not found"))),
911 }
912 }
913 }
914
915 fn exec_signal(&mut self, action: &SignalAction) -> RexxResult<ExecSignal> {
917 match action {
918 SignalAction::Label(label) => Ok(ExecSignal::Signal(label.clone())),
919 SignalAction::Value(expr) => {
920 let val = self.eval_expr(expr)?;
921 let label = val.as_str().to_uppercase();
922 Ok(ExecSignal::Signal(label))
923 }
924 SignalAction::On { condition, name } => {
925 let label = name
926 .clone()
927 .unwrap_or_else(|| Self::condition_default_label(condition));
928 self.traps.insert(condition.clone(), label);
929 Ok(ExecSignal::Normal)
930 }
931 SignalAction::Off(condition) => {
932 self.traps.remove(condition);
933 Ok(ExecSignal::Normal)
934 }
935 }
936 }
937
938 fn condition_default_label(condition: &Condition) -> String {
940 match condition {
941 Condition::Error => "ERROR".to_string(),
942 Condition::Failure => "FAILURE".to_string(),
943 Condition::Halt => "HALT".to_string(),
944 Condition::NoValue => "NOVALUE".to_string(),
945 Condition::NotReady => "NOTREADY".to_string(),
946 Condition::Syntax => "SYNTAX".to_string(),
947 Condition::LostDigits => "LOSTDIGITS".to_string(),
948 }
949 }
950
951 fn exec_arg(&mut self, template: &ParseTemplate) -> RexxResult<ExecSignal> {
953 self.exec_parse(true, &ParseSource::Arg, template)
954 }
955
956 pub fn set_main_args(&mut self, args: Vec<RexxValue>) {
958 self.arg_stack.push(args);
959 }
960
961 pub fn set_command_handler(&mut self, handler: CommandHandler) {
970 self.command_handler = Some(handler);
971 }
972
973 pub fn set_command_handler_with_env(&mut self, handler: CommandHandlerWithEnv) {
992 self.command_handler_with_env = Some(handler);
993 }
994
995 fn exec_parse(
999 &mut self,
1000 upper: bool,
1001 source: &ParseSource,
1002 template: &ParseTemplate,
1003 ) -> RexxResult<ExecSignal> {
1004 let sub_templates = Self::split_template_at_commas(template);
1005
1006 if let ParseSource::Arg = source {
1007 let args = self.arg_stack.last().cloned().unwrap_or_default();
1008 for (i, sub_t) in sub_templates.iter().enumerate() {
1009 let raw = args
1010 .get(i)
1011 .map(|v| v.as_str().to_string())
1012 .unwrap_or_default();
1013 let text = if upper { raw.to_uppercase() } else { raw };
1014 self.apply_template(&text, sub_t)?;
1015 }
1016 } else {
1017 let raw = match source {
1018 ParseSource::Var(name) => self.env.get(name).as_str().to_string(),
1019 ParseSource::Value(expr) => self.eval_expr(expr)?.as_str().to_string(),
1020 ParseSource::Pull => self.pull_from_queue_or_stdin()?,
1021 ParseSource::LineIn => self.read_stdin_line()?,
1022 ParseSource::Source => {
1023 let filename = self
1024 .env
1025 .source_path()
1026 .and_then(|p| p.file_name())
1027 .map_or_else(|| "rexx".to_string(), |f| f.to_string_lossy().into_owned());
1028 format!("UNIX COMMAND {filename}")
1029 }
1030 ParseSource::Version => {
1031 format!("REXX-patch-rexx {} 8 Feb 2026", env!("CARGO_PKG_VERSION"))
1032 }
1033 ParseSource::Arg => unreachable!(),
1034 };
1035 let text = if upper { raw.to_uppercase() } else { raw };
1036 for (i, sub_t) in sub_templates.iter().enumerate() {
1037 let s = if i == 0 { &text } else { "" };
1038 self.apply_template(s, sub_t)?;
1039 }
1040 }
1041
1042 Ok(ExecSignal::Normal)
1043 }
1044
1045 fn apply_template(&mut self, source: &str, template: &ParseTemplate) -> RexxResult<()> {
1047 let elements = &template.elements;
1048 let len = elements.len();
1049 let mut cursor: usize = 0;
1050 let mut i: usize = 0;
1051
1052 while i < len {
1053 let mut targets: Vec<&TemplateElement> = Vec::new();
1055 while i < len {
1056 match &elements[i] {
1057 e @ (TemplateElement::Variable(_) | TemplateElement::Dot) => {
1058 targets.push(e);
1059 i += 1;
1060 }
1061 _ => break,
1062 }
1063 }
1064
1065 if i >= len {
1067 let section = if cursor < source.len() {
1069 &source[cursor..]
1070 } else {
1071 ""
1072 };
1073 self.assign_section(section, &targets);
1074 break;
1075 }
1076
1077 match &elements[i] {
1078 TemplateElement::Literal(pat) => {
1079 cursor = self.match_pattern(source, cursor, pat, &targets);
1080 i += 1;
1081 }
1082 TemplateElement::AbsolutePos(expr) => {
1083 let pos_val = self.eval_expr(expr)?;
1084 let pos = self.to_position_value(&pos_val)?;
1085 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
1086 let char_pos = if pos > 0 { (pos - 1) as usize } else { 0 };
1087 let target = Self::char_pos_to_byte_offset(source, char_pos);
1088 let section = if target > cursor {
1089 &source[cursor..target]
1090 } else {
1091 ""
1092 };
1093 self.assign_section(section, &targets);
1094 cursor = target;
1095 i += 1;
1096 }
1097 TemplateElement::RelativePos(offset) => {
1098 #[allow(clippy::cast_sign_loss)]
1099 let target = if *offset >= 0 {
1100 Self::advance_chars(source, cursor, *offset as usize)
1101 } else {
1102 Self::retreat_chars(source, cursor, offset.unsigned_abs() as usize)
1103 };
1104 let section = if target > cursor {
1105 &source[cursor..target]
1106 } else {
1107 ""
1108 };
1109 self.assign_section(section, &targets);
1110 cursor = target;
1111 i += 1;
1112 }
1113 TemplateElement::VariablePattern(name) => {
1114 let pat = self.env.get(name).as_str().to_string();
1115 cursor = self.match_pattern(source, cursor, &pat, &targets);
1116 i += 1;
1117 }
1118 _ => {
1119 i += 1;
1120 }
1121 }
1122 }
1123 Ok(())
1124 }
1125
1126 fn assign_section(&mut self, section: &str, targets: &[&TemplateElement]) {
1129 match targets.len() {
1130 0 => {} 1 => {
1132 self.assign_target(targets[0], section);
1134 }
1135 _ => {
1136 let mut remaining = section;
1138 for (j, target) in targets.iter().enumerate() {
1139 if j == targets.len() - 1 {
1140 let trimmed = remaining.trim_start_matches([' ', '\t']);
1142 self.assign_target(target, trimmed);
1143 } else {
1144 let trimmed = remaining.trim_start_matches([' ', '\t']);
1146 if let Some(blank_pos) = trimmed.find([' ', '\t']) {
1147 let word = &trimmed[..blank_pos];
1148 self.assign_target(target, word);
1149 remaining = &trimmed[blank_pos..];
1150 } else {
1151 self.assign_target(target, trimmed);
1153 remaining = "";
1154 }
1155 }
1156 }
1157 }
1158 }
1159 }
1160
1161 fn match_pattern(
1165 &mut self,
1166 source: &str,
1167 cursor: usize,
1168 pat: &str,
1169 targets: &[&TemplateElement],
1170 ) -> usize {
1171 if !pat.is_empty()
1172 && let Some(found) = source[cursor..].find(pat)
1173 {
1174 let abs = cursor + found;
1175 self.assign_section(&source[cursor..abs], targets);
1176 abs + pat.len()
1177 } else {
1178 let section = if cursor < source.len() {
1179 &source[cursor..]
1180 } else {
1181 ""
1182 };
1183 self.assign_section(section, targets);
1184 source.len()
1185 }
1186 }
1187
1188 fn assign_target(&mut self, target: &TemplateElement, value: &str) {
1190 if let TemplateElement::Variable(name) = target {
1191 self.env.set(name, RexxValue::new(value));
1192 }
1193 }
1195
1196 fn split_template_at_commas(template: &ParseTemplate) -> Vec<ParseTemplate> {
1199 if !template
1200 .elements
1201 .iter()
1202 .any(|e| matches!(e, TemplateElement::Comma))
1203 {
1204 return vec![template.clone()];
1205 }
1206 let mut result = Vec::new();
1207 let mut current = Vec::new();
1208 for elem in &template.elements {
1209 if matches!(elem, TemplateElement::Comma) {
1210 result.push(ParseTemplate {
1211 elements: std::mem::take(&mut current),
1212 });
1213 } else {
1214 current.push(elem.clone());
1215 }
1216 }
1217 result.push(ParseTemplate { elements: current });
1218 result
1219 }
1220
1221 fn exec_numeric(&mut self, setting: &NumericSetting) -> RexxResult<ExecSignal> {
1225 match setting {
1226 NumericSetting::Digits(expr) => {
1227 let digits = if let Some(e) = expr {
1228 let val = self.eval_expr(e)?;
1229 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1230 return Ok(ExecSignal::Normal);
1231 }
1232 let n = self.to_integer(&val)?;
1233 if n < 1 {
1234 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1235 .with_detail("NUMERIC DIGITS value must be positive"));
1236 }
1237 if n > i64::from(u32::MAX) {
1238 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1239 .with_detail("NUMERIC DIGITS value too large"));
1240 }
1241 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1242 {
1243 n as u32
1244 }
1245 } else {
1246 9 };
1248 self.settings.digits = digits;
1249 }
1250 NumericSetting::Form(form_setting) => {
1251 let form = match form_setting {
1252 NumericFormSetting::Scientific => crate::value::NumericForm::Scientific,
1253 NumericFormSetting::Engineering => crate::value::NumericForm::Engineering,
1254 NumericFormSetting::Value(expr) => {
1255 let val = self.eval_expr(expr)?;
1256 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1257 return Ok(ExecSignal::Normal);
1258 }
1259 let s = val.as_str().to_uppercase();
1260 match s.as_str() {
1261 "SCIENTIFIC" => crate::value::NumericForm::Scientific,
1262 "ENGINEERING" => crate::value::NumericForm::Engineering,
1263 _ => {
1264 return Err(RexxDiagnostic::new(RexxError::InvalidSubKeyword)
1265 .with_detail(format!(
1266 "NUMERIC FORM value must be SCIENTIFIC or ENGINEERING; got '{s}'"
1267 )));
1268 }
1269 }
1270 }
1271 };
1272 self.settings.form = form;
1273 }
1274 NumericSetting::Fuzz(expr) => {
1275 let fuzz = if let Some(e) = expr {
1276 let val = self.eval_expr(e)?;
1277 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1278 return Ok(ExecSignal::Normal);
1279 }
1280 let n = self.to_integer(&val)?;
1281 if n < 0 {
1282 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1283 .with_detail("NUMERIC FUZZ value must not be negative"));
1284 }
1285 if n > i64::from(u32::MAX) {
1286 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1287 .with_detail("NUMERIC FUZZ value too large"));
1288 }
1289 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1290 {
1291 n as u32
1292 }
1293 } else {
1294 0 };
1296 if fuzz >= self.settings.digits {
1297 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1298 .with_detail("NUMERIC FUZZ must be less than NUMERIC DIGITS"));
1299 }
1300 self.settings.fuzz = fuzz;
1301 }
1302 }
1303 Ok(ExecSignal::Normal)
1304 }
1305
1306 fn exec_push(&mut self, expr: Option<&Expr>) -> RexxResult<ExecSignal> {
1310 let val = if let Some(e) = expr {
1311 let v = self.eval_expr(e)?;
1312 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1313 return Ok(ExecSignal::Normal);
1314 }
1315 v.as_str().to_string()
1316 } else {
1317 String::new()
1318 };
1319 self.queue.push_front(val);
1320 Ok(ExecSignal::Normal)
1321 }
1322
1323 fn exec_queue(&mut self, expr: Option<&Expr>) -> RexxResult<ExecSignal> {
1325 let val = if let Some(e) = expr {
1326 let v = self.eval_expr(e)?;
1327 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1328 return Ok(ExecSignal::Normal);
1329 }
1330 v.as_str().to_string()
1331 } else {
1332 String::new()
1333 };
1334 self.queue.push_back(val);
1335 Ok(ExecSignal::Normal)
1336 }
1337
1338 fn pull_from_queue_or_stdin(&mut self) -> RexxResult<String> {
1340 if let Some(line) = self.queue.pop_front() {
1341 Ok(line)
1342 } else {
1343 self.read_stdin_line()
1344 }
1345 }
1346
1347 pub fn queue_len(&self) -> usize {
1349 self.queue.len()
1350 }
1351
1352 #[allow(clippy::unused_self)]
1354 fn read_stdin_line(&self) -> RexxResult<String> {
1355 let mut line = String::new();
1356 std::io::stdin().read_line(&mut line).map_err(|e| {
1357 RexxDiagnostic::new(RexxError::SystemFailure)
1358 .with_detail(format!("failed to read stdin: {e}"))
1359 })?;
1360 if line.ends_with('\n') {
1361 line.pop();
1362 if line.ends_with('\r') {
1363 line.pop();
1364 }
1365 }
1366 Ok(line)
1367 }
1368
1369 fn char_pos_to_byte_offset(source: &str, char_pos: usize) -> usize {
1372 source
1373 .char_indices()
1374 .nth(char_pos)
1375 .map_or(source.len(), |(byte_offset, _)| byte_offset)
1376 }
1377
1378 fn advance_chars(source: &str, byte_cursor: usize, n: usize) -> usize {
1380 let clamped = byte_cursor.min(source.len());
1381 source[clamped..]
1382 .char_indices()
1383 .nth(n)
1384 .map_or(source.len(), |(offset, _)| clamped + offset)
1385 }
1386
1387 fn retreat_chars(source: &str, byte_cursor: usize, n: usize) -> usize {
1389 if n == 0 {
1390 return byte_cursor.min(source.len());
1391 }
1392 let clamped = byte_cursor.min(source.len());
1393 source[..clamped]
1394 .char_indices()
1395 .map(|(i, _)| i)
1396 .rev()
1397 .nth(n - 1)
1398 .unwrap_or(0)
1399 }
1400
1401 fn to_position_value(&self, val: &RexxValue) -> RexxResult<i64> {
1403 let d = self.to_number(val)?;
1404 let rounded = d.round(0);
1405 if d != rounded {
1406 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1407 .with_detail(format!("'{val}' is not a whole number")));
1408 }
1409 let s = rounded.to_string();
1410 s.parse::<i64>().map_err(|_| {
1411 RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1412 .with_detail(format!("'{val}' is not a valid position"))
1413 })
1414 }
1415
1416 fn exec_if(
1417 &mut self,
1418 condition: &Expr,
1419 then_clause: &Clause,
1420 else_clause: Option<&Clause>,
1421 ) -> RexxResult<ExecSignal> {
1422 let cond_val = self.eval_expr(condition)?;
1423 let b = to_logical(&cond_val)?;
1424 if b {
1425 self.exec_clause(then_clause)
1426 } else if let Some(else_c) = else_clause {
1427 self.exec_clause(else_c)
1428 } else {
1429 Ok(ExecSignal::Normal)
1430 }
1431 }
1432
1433 fn exec_do(&mut self, block: &DoBlock) -> RexxResult<ExecSignal> {
1434 match &block.kind {
1435 DoKind::Simple => {
1436 let signal = self.exec_body(&block.body)?;
1437 Ok(signal)
1438 }
1439 DoKind::Forever => self.exec_do_forever(block),
1440 DoKind::Count(expr) => self.exec_do_count(expr, block),
1441 DoKind::While(expr) => self.exec_do_while(expr, block),
1442 DoKind::Until(expr) => self.exec_do_until(expr, block),
1443 DoKind::Controlled(ctrl) => self.exec_do_controlled(ctrl, block),
1444 }
1445 }
1446
1447 fn exec_do_forever(&mut self, block: &DoBlock) -> RexxResult<ExecSignal> {
1448 loop {
1449 let signal = self.exec_body(&block.body)?;
1450 match signal {
1451 ExecSignal::Normal => {}
1452 ExecSignal::Leave(ref name) => {
1453 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1454 return Ok(ExecSignal::Normal);
1455 }
1456 return Ok(signal);
1457 }
1458 ExecSignal::Iterate(ref name) => {
1459 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1460 continue;
1461 }
1462 return Ok(signal);
1463 }
1464 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1465 return Ok(signal);
1466 }
1467 }
1468 }
1469 }
1470
1471 fn exec_do_count(&mut self, count_expr: &Expr, block: &DoBlock) -> RexxResult<ExecSignal> {
1472 let count_val = self.eval_expr(count_expr)?;
1473 let count = self.to_integer(&count_val)?;
1474 for _ in 0..count {
1475 let signal = self.exec_body(&block.body)?;
1476 match signal {
1477 ExecSignal::Normal => {}
1478 ExecSignal::Leave(ref name) => {
1479 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1480 return Ok(ExecSignal::Normal);
1481 }
1482 return Ok(signal);
1483 }
1484 ExecSignal::Iterate(ref name) => {
1485 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1486 continue;
1487 }
1488 return Ok(signal);
1489 }
1490 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1491 return Ok(signal);
1492 }
1493 }
1494 }
1495 Ok(ExecSignal::Normal)
1496 }
1497
1498 fn exec_do_while(&mut self, cond_expr: &Expr, block: &DoBlock) -> RexxResult<ExecSignal> {
1499 loop {
1500 let cond_val = self.eval_expr(cond_expr)?;
1501 if !to_logical(&cond_val)? {
1502 break;
1503 }
1504 let signal = self.exec_body(&block.body)?;
1505 match signal {
1506 ExecSignal::Normal => {}
1507 ExecSignal::Leave(ref name) => {
1508 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1509 return Ok(ExecSignal::Normal);
1510 }
1511 return Ok(signal);
1512 }
1513 ExecSignal::Iterate(ref name) => {
1514 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1515 continue;
1516 }
1517 return Ok(signal);
1518 }
1519 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1520 return Ok(signal);
1521 }
1522 }
1523 }
1524 Ok(ExecSignal::Normal)
1525 }
1526
1527 fn exec_do_until(&mut self, cond_expr: &Expr, block: &DoBlock) -> RexxResult<ExecSignal> {
1528 loop {
1529 let signal = self.exec_body(&block.body)?;
1530 match signal {
1531 ExecSignal::Normal => {}
1532 ExecSignal::Leave(ref name) => {
1533 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1534 return Ok(ExecSignal::Normal);
1535 }
1536 return Ok(signal);
1537 }
1538 ExecSignal::Iterate(ref name) => {
1539 if !Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1540 return Ok(signal);
1541 }
1542 }
1544 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1545 return Ok(signal);
1546 }
1547 }
1548 let cond_val = self.eval_expr(cond_expr)?;
1549 if to_logical(&cond_val)? {
1550 break;
1551 }
1552 }
1553 Ok(ExecSignal::Normal)
1554 }
1555
1556 #[allow(clippy::too_many_lines)]
1557 fn exec_do_controlled(
1558 &mut self,
1559 ctrl: &ControlledLoop,
1560 block: &DoBlock,
1561 ) -> RexxResult<ExecSignal> {
1562 let start_val = self.eval_expr(&ctrl.start)?;
1564 let start_num = self.to_number(&start_val)?;
1565
1566 let to_num = if let Some(ref to_expr) = ctrl.to {
1568 let v = self.eval_expr(to_expr)?;
1569 Some(self.to_number(&v)?)
1570 } else {
1571 None
1572 };
1573
1574 let by_num = if let Some(ref by_expr) = ctrl.by {
1576 let v = self.eval_expr(by_expr)?;
1577 self.to_number(&v)?
1578 } else {
1579 BigDecimal::from(1)
1580 };
1581
1582 if by_num.is_zero() {
1584 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1585 .with_detail("BY value in DO loop must not be zero"));
1586 }
1587
1588 let for_count = if let Some(ref for_expr) = ctrl.r#for {
1590 let v = self.eval_expr(for_expr)?;
1591 Some(self.to_integer(&v)?)
1592 } else {
1593 None
1594 };
1595
1596 let mut current = start_num;
1598 let mut iterations: i64 = 0;
1599
1600 loop {
1601 if let Some(ref limit) = to_num {
1604 let positive_step = by_num.sign() == bigdecimal::num_bigint::Sign::Plus;
1605 if positive_step {
1606 if current > *limit {
1607 break;
1608 }
1609 } else if current < *limit {
1610 break;
1611 }
1612 }
1613
1614 if let Some(max) = for_count
1616 && iterations >= max
1617 {
1618 break;
1619 }
1620
1621 self.env.set(
1623 &ctrl.var,
1624 RexxValue::from_decimal(¤t, self.settings.digits, self.settings.form),
1625 );
1626
1627 if let Some(ref while_expr) = ctrl.while_cond {
1629 let v = self.eval_expr(while_expr)?;
1630 if !to_logical(&v)? {
1631 break;
1632 }
1633 }
1634
1635 let signal = self.exec_body(&block.body)?;
1637 match signal {
1638 ExecSignal::Normal => {}
1639 ExecSignal::Leave(ref name) => {
1640 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1641 return Ok(ExecSignal::Normal);
1642 }
1643 return Ok(signal);
1644 }
1645 ExecSignal::Iterate(ref name) => {
1646 if !Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1647 return Ok(signal);
1648 }
1649 }
1651 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1652 return Ok(signal);
1653 }
1654 }
1655
1656 if let Some(ref until_expr) = ctrl.until_cond {
1661 let v = self.eval_expr(until_expr)?;
1662 if to_logical(&v)? {
1663 break;
1664 }
1665 }
1666
1667 current += &by_num;
1669 iterations = iterations.saturating_add(1);
1670 }
1671
1672 self.env.set(
1675 &ctrl.var,
1676 RexxValue::from_decimal(¤t, self.settings.digits, self.settings.form),
1677 );
1678
1679 Ok(ExecSignal::Normal)
1680 }
1681
1682 fn exec_select(
1683 &mut self,
1684 when_clauses: &[(Expr, Vec<Clause>)],
1685 otherwise: Option<&Vec<Clause>>,
1686 ) -> RexxResult<ExecSignal> {
1687 for (condition, body) in when_clauses {
1688 let val = self.eval_expr(condition)?;
1689 if to_logical(&val)? {
1690 return self.exec_body(body);
1691 }
1692 }
1693 if let Some(body) = otherwise {
1694 return self.exec_body(body);
1695 }
1696 Err(RexxDiagnostic::new(RexxError::ExpectedWhenOtherwise)
1697 .with_detail("no WHEN matched and no OTHERWISE in SELECT"))
1698 }
1699
1700 fn signal_matches(signal_name: Option<&String>, loop_name: Option<&String>) -> bool {
1704 match signal_name {
1705 None => true,
1706 Some(name) => loop_name.is_some_and(|ln| ln == name),
1707 }
1708 }
1709
1710 fn to_integer(&self, val: &RexxValue) -> RexxResult<i64> {
1713 let d = self.to_number(val)?;
1714 let rounded = d.round(0);
1715 if d != rounded {
1716 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1717 .with_detail("loop count must be a whole number"));
1718 }
1719 let s = rounded.to_string();
1720 let n = s.parse::<i64>().map_err(|_| {
1721 RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1722 .with_detail(format!("'{rounded}' is too large for a loop count"))
1723 })?;
1724 if n < 0 {
1725 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1726 .with_detail(format!("loop count must not be negative (got {n})")));
1727 }
1728 Ok(n)
1729 }
1730
1731 fn resolve_tail(&self, tail: &[TailElement]) -> String {
1732 tail.iter()
1733 .map(|elem| match elem {
1734 TailElement::Const(c) => c.clone(),
1735 TailElement::Var(v) => self.env.get(v).into_string(),
1736 })
1737 .collect::<Vec<_>>()
1738 .join(".")
1739 }
1740
1741 #[allow(clippy::too_many_lines)]
1742 fn eval_expr(&mut self, expr: &Expr) -> RexxResult<RexxValue> {
1743 match expr {
1744 Expr::StringLit(s) => {
1745 let val = RexxValue::new(s.clone());
1746 self.trace_intermediates("L", val.as_str());
1747 Ok(val)
1748 }
1749 Expr::Number(n) => {
1750 let val = RexxValue::new(n.clone());
1751 self.trace_intermediates("L", val.as_str());
1752 Ok(val)
1753 }
1754 Expr::Symbol(name) => {
1755 if !self.env.is_set(name)
1756 && let Some(label) = self.traps.get(&Condition::NoValue).cloned()
1757 {
1758 self.env.set_condition_info(crate::env::ConditionInfoData {
1760 condition: "NOVALUE".to_string(),
1761 description: name.clone(),
1762 instruction: "SIGNAL".to_string(),
1763 status: "ON".to_string(),
1764 });
1765 self.traps.remove(&Condition::NoValue);
1767 self.pending_signal = Some(label);
1768 }
1769 let val = self.env.get(name);
1770 self.trace_intermediates("V", val.as_str());
1771 Ok(val)
1772 }
1773 Expr::Compound { stem, tail } => {
1774 let resolved = self.resolve_tail(tail);
1775 if !self.env.is_compound_set(stem, &resolved)
1776 && let Some(label) = self.traps.get(&Condition::NoValue).cloned()
1777 {
1778 let compound_name = format!("{}.{}", stem.to_uppercase(), resolved);
1779 self.env.set_condition_info(crate::env::ConditionInfoData {
1780 condition: "NOVALUE".to_string(),
1781 description: compound_name,
1782 instruction: "SIGNAL".to_string(),
1783 status: "ON".to_string(),
1784 });
1785 self.traps.remove(&Condition::NoValue);
1786 self.pending_signal = Some(label);
1787 }
1788 let val = self.env.get_compound(stem, &resolved);
1789 self.trace_intermediates("C", val.as_str());
1790 Ok(val)
1791 }
1792 Expr::Paren(inner) => self.eval_expr(inner),
1793 Expr::UnaryOp { op, operand } => {
1794 let val = self.eval_expr(operand)?;
1795 if self.pending_signal.is_some() {
1796 return Ok(val);
1797 }
1798 let result = self.eval_unary(*op, &val)?;
1799 self.trace_intermediates("P", result.as_str());
1800 Ok(result)
1801 }
1802 Expr::BinOp { left, op, right } => {
1803 let lval = self.eval_expr(left)?;
1804 if self.pending_signal.is_some() {
1805 return Ok(lval);
1806 }
1807 let rval = self.eval_expr(right)?;
1808 if self.pending_signal.is_some() {
1809 return Ok(rval);
1810 }
1811 let result = self.eval_binop(*op, &lval, &rval)?;
1812 self.trace_intermediates("O", result.as_str());
1813 Ok(result)
1814 }
1815 Expr::FunctionCall { name, args } => {
1816 let mut evaluated_args = Vec::with_capacity(args.len());
1817 for arg_expr in args {
1818 evaluated_args.push(self.eval_expr(arg_expr)?);
1819 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1820 return Ok(RexxValue::new(""));
1821 }
1822 }
1823 if name.eq_ignore_ascii_case("TRACE") {
1825 if evaluated_args.len() == 1 {
1826 let old = self.apply_trace_setting(evaluated_args[0].as_str())?;
1827 return Ok(RexxValue::new(old));
1828 } else if !evaluated_args.is_empty() {
1829 return Err(RexxDiagnostic::new(RexxError::IncorrectCall)
1830 .with_detail("TRACE expects 0 or 1 arguments"));
1831 }
1832 return Ok(RexxValue::new(self.trace_setting.to_string()));
1833 }
1834
1835 if self.labels.contains_key(name.as_str()) {
1837 let signal = self.call_routine(name, evaluated_args)?;
1838 match signal {
1839 ExecSignal::Return(Some(val)) => {
1840 self.trace_intermediates("F", val.as_str());
1841 Ok(val)
1842 }
1843 ExecSignal::Return(None) | ExecSignal::Normal => {
1844 Err(RexxDiagnostic::new(RexxError::NoReturnData)
1845 .with_detail(format!("function '{name}' did not return data")))
1846 }
1847 ExecSignal::Exit(val) => {
1848 self.pending_exit = PendingExit::WithValue(val);
1849 Ok(RexxValue::new(""))
1850 }
1851 ExecSignal::Signal(_) => {
1852 if let ExecSignal::Signal(label) = signal {
1854 self.pending_signal = Some(label);
1855 }
1856 Ok(RexxValue::new(""))
1857 }
1858 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(RexxValue::new("")),
1859 }
1860 } else if let Some(result) = crate::builtins::call_builtin(
1861 name,
1862 &evaluated_args,
1863 &self.settings,
1864 self.env,
1865 self.queue.len(),
1866 ) {
1867 let val = result?;
1868 self.trace_intermediates("F", val.as_str());
1869 Ok(val)
1870 } else {
1871 match self.try_call_external(name, evaluated_args)? {
1873 Some(signal) => match signal {
1874 ExecSignal::Return(Some(val)) => {
1875 self.trace_intermediates("F", val.as_str());
1876 Ok(val)
1877 }
1878 ExecSignal::Return(None) | ExecSignal::Normal => {
1879 Err(RexxDiagnostic::new(RexxError::NoReturnData)
1880 .with_detail(format!("function '{name}' did not return data")))
1881 }
1882 ExecSignal::Exit(val) => {
1883 self.pending_exit = PendingExit::WithValue(val);
1884 Ok(RexxValue::new(""))
1885 }
1886 ExecSignal::Signal(_) => {
1887 if let ExecSignal::Signal(label) = signal {
1888 self.pending_signal = Some(label);
1889 }
1890 Ok(RexxValue::new(""))
1891 }
1892 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(RexxValue::new("")),
1893 },
1894 None => Err(RexxDiagnostic::new(RexxError::RoutineNotFound)
1895 .with_detail(format!("routine '{name}' not found"))),
1896 }
1897 }
1898 }
1899 }
1900 }
1901
1902 fn eval_unary(&self, op: UnaryOp, val: &RexxValue) -> RexxResult<RexxValue> {
1903 match op {
1904 UnaryOp::Plus => {
1905 let d = val.to_decimal().ok_or_else(|| {
1907 RexxDiagnostic::new(RexxError::BadArithmetic)
1908 .with_detail(format!("'{}' is not a number", val.as_str()))
1909 })?;
1910 Ok(RexxValue::from_decimal(
1911 &d,
1912 self.settings.digits,
1913 self.settings.form,
1914 ))
1915 }
1916 UnaryOp::Minus => {
1917 let d = val.to_decimal().ok_or_else(|| {
1918 RexxDiagnostic::new(RexxError::BadArithmetic)
1919 .with_detail(format!("'{}' is not a number", val.as_str()))
1920 })?;
1921 let neg = -d;
1922 Ok(RexxValue::from_decimal(
1923 &neg,
1924 self.settings.digits,
1925 self.settings.form,
1926 ))
1927 }
1928 UnaryOp::Not => {
1929 let s = val.as_str().trim();
1930 match s {
1931 "0" => Ok(RexxValue::new("1")),
1932 "1" => Ok(RexxValue::new("0")),
1933 _ => Err(RexxDiagnostic::new(RexxError::InvalidLogicalValue)
1934 .with_detail(format!("'{}' is not 0 or 1", val.as_str()))),
1935 }
1936 }
1937 }
1938 }
1939
1940 #[allow(clippy::too_many_lines)]
1941 fn eval_binop(&self, op: BinOp, left: &RexxValue, right: &RexxValue) -> RexxResult<RexxValue> {
1942 match op {
1943 BinOp::Add => self.arithmetic(left, right, |a, b| a + b),
1945 BinOp::Sub => self.arithmetic(left, right, |a, b| a - b),
1946 BinOp::Mul => self.arithmetic(left, right, |a, b| a * b),
1947 BinOp::Div => {
1948 let b = right.to_decimal().ok_or_else(|| {
1949 RexxDiagnostic::new(RexxError::BadArithmetic)
1950 .with_detail(format!("'{}' is not a number", right.as_str()))
1951 })?;
1952 if b.is_zero() {
1953 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1954 .with_detail("division by zero"));
1955 }
1956 let a = left.to_decimal().ok_or_else(|| {
1957 RexxDiagnostic::new(RexxError::BadArithmetic)
1958 .with_detail(format!("'{}' is not a number", left.as_str()))
1959 })?;
1960 let result = a / b;
1961 Ok(RexxValue::from_decimal(
1962 &result,
1963 self.settings.digits,
1964 self.settings.form,
1965 ))
1966 }
1967 BinOp::IntDiv => {
1968 let a = self.to_number(left)?;
1969 let b = self.to_number(right)?;
1970 if b.is_zero() {
1971 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1972 .with_detail("division by zero"));
1973 }
1974 let result = trunc_div(&a, &b);
1976 Ok(RexxValue::from_decimal(
1977 &result,
1978 self.settings.digits,
1979 self.settings.form,
1980 ))
1981 }
1982 BinOp::Remainder => {
1983 let a = self.to_number(left)?;
1984 let b = self.to_number(right)?;
1985 if b.is_zero() {
1986 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1987 .with_detail("division by zero"));
1988 }
1989 let int_div = trunc_div(&a, &b);
1991 let result = &a - &int_div * &b;
1992 Ok(RexxValue::from_decimal(
1993 &result,
1994 self.settings.digits,
1995 self.settings.form,
1996 ))
1997 }
1998 BinOp::Power => {
1999 let base = self.to_number(left)?;
2000 let exp_val = self.to_number(right)?;
2001 let exp_rounded = exp_val.round(0);
2003 if exp_val != exp_rounded {
2004 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
2005 .with_detail("exponent must be a whole number"));
2006 }
2007 let exp_i64: i64 = exp_rounded.to_string().parse().map_err(|_| {
2008 RexxDiagnostic::new(RexxError::ArithmeticOverflow)
2009 .with_detail("exponent too large")
2010 })?;
2011 if exp_i64.abs() > 1_000_000 {
2014 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
2015 .with_detail("exponent exceeds limits"));
2016 }
2017 if base.is_zero() && exp_i64 < 0 {
2018 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
2019 .with_detail("zero raised to a negative power"));
2020 }
2021 let result = pow_bigdecimal(&base, exp_i64);
2022 Ok(RexxValue::from_decimal(
2023 &result,
2024 self.settings.digits,
2025 self.settings.form,
2026 ))
2027 }
2028
2029 BinOp::Concat => {
2031 let s = format!("{}{}", left.as_str(), right.as_str());
2032 Ok(RexxValue::new(s))
2033 }
2034 BinOp::ConcatBlank => {
2035 let s = format!("{} {}", left.as_str(), right.as_str());
2036 Ok(RexxValue::new(s))
2037 }
2038
2039 BinOp::Eq => Ok(bool_val(
2041 normal_compare(left, right) == std::cmp::Ordering::Equal,
2042 )),
2043 BinOp::NotEq => Ok(bool_val(
2044 normal_compare(left, right) != std::cmp::Ordering::Equal,
2045 )),
2046 BinOp::Gt => Ok(bool_val(
2047 normal_compare(left, right) == std::cmp::Ordering::Greater,
2048 )),
2049 BinOp::Lt => Ok(bool_val(
2050 normal_compare(left, right) == std::cmp::Ordering::Less,
2051 )),
2052 BinOp::GtEq => Ok(bool_val(
2053 normal_compare(left, right) != std::cmp::Ordering::Less,
2054 )),
2055 BinOp::LtEq => Ok(bool_val(
2056 normal_compare(left, right) != std::cmp::Ordering::Greater,
2057 )),
2058
2059 BinOp::StrictEq => Ok(bool_val(left.as_str() == right.as_str())),
2061 BinOp::StrictNotEq => Ok(bool_val(left.as_str() != right.as_str())),
2062 BinOp::StrictGt => Ok(bool_val(left.as_str() > right.as_str())),
2063 BinOp::StrictLt => Ok(bool_val(left.as_str() < right.as_str())),
2064 BinOp::StrictGtEq => Ok(bool_val(left.as_str() >= right.as_str())),
2065 BinOp::StrictLtEq => Ok(bool_val(left.as_str() <= right.as_str())),
2066
2067 BinOp::And => {
2069 let l = to_logical(left)?;
2070 let r = to_logical(right)?;
2071 Ok(bool_val(l && r))
2072 }
2073 BinOp::Or => {
2074 let l = to_logical(left)?;
2075 let r = to_logical(right)?;
2076 Ok(bool_val(l || r))
2077 }
2078 BinOp::Xor => {
2079 let l = to_logical(left)?;
2080 let r = to_logical(right)?;
2081 Ok(bool_val(l ^ r))
2082 }
2083 }
2084 }
2085
2086 fn arithmetic(
2087 &self,
2088 left: &RexxValue,
2089 right: &RexxValue,
2090 f: impl FnOnce(BigDecimal, BigDecimal) -> BigDecimal,
2091 ) -> RexxResult<RexxValue> {
2092 let a = self.to_number(left)?;
2093 let b = self.to_number(right)?;
2094 let result = f(a, b);
2095 Ok(RexxValue::from_decimal(
2096 &result,
2097 self.settings.digits,
2098 self.settings.form,
2099 ))
2100 }
2101
2102 #[allow(clippy::unused_self)]
2103 fn to_number(&self, val: &RexxValue) -> RexxResult<BigDecimal> {
2104 val.to_decimal().ok_or_else(|| {
2105 RexxDiagnostic::new(RexxError::BadArithmetic)
2106 .with_detail(format!("'{}' is not a number", val.as_str()))
2107 })
2108 }
2109}
2110
2111fn to_logical(val: &RexxValue) -> RexxResult<bool> {
2113 match val.as_str().trim() {
2114 "0" => Ok(false),
2115 "1" => Ok(true),
2116 _ => Err(RexxDiagnostic::new(RexxError::InvalidLogicalValue)
2117 .with_detail(format!("'{}' is not 0 or 1", val.as_str()))),
2118 }
2119}
2120
2121fn bool_val(b: bool) -> RexxValue {
2123 RexxValue::new(if b { "1" } else { "0" })
2124}
2125
2126fn normal_compare(left: &RexxValue, right: &RexxValue) -> std::cmp::Ordering {
2130 let ls = left.as_str().trim();
2131 let rs = right.as_str().trim();
2132
2133 if let (Some(ld), Some(rd)) = (BigDecimal::from_str(ls).ok(), BigDecimal::from_str(rs).ok()) {
2135 return ld.cmp(&rd);
2136 }
2137
2138 let max_len = ls.len().max(rs.len());
2140 let lp: String = format!("{ls:<max_len$}");
2141 let rp: String = format!("{rs:<max_len$}");
2142 lp.cmp(&rp)
2143}
2144
2145fn trunc_div(a: &BigDecimal, b: &BigDecimal) -> BigDecimal {
2147 let quotient = a / b;
2148 quotient.with_scale_round(0, bigdecimal::RoundingMode::Down)
2152}
2153
2154fn pow_bigdecimal(base: &BigDecimal, exp: i64) -> BigDecimal {
2156 if exp == 0 {
2157 return BigDecimal::from(1);
2158 }
2159 if exp < 0 {
2160 let pos_result = pow_bigdecimal(base, -exp);
2161 return BigDecimal::from(1) / pos_result;
2162 }
2163 let mut result = BigDecimal::from(1);
2164 let mut b = base.clone();
2165 let mut e = exp;
2166 while e > 0 {
2168 if e & 1 == 1 {
2169 result *= &b;
2170 }
2171 b = &b * &b;
2172 e >>= 1;
2173 }
2174 result
2175}
2176
2177#[cfg(test)]
2178mod tests {
2179 use super::*;
2180 use crate::lexer::Lexer;
2181 use crate::parser::Parser;
2182
2183 fn eval_expr(src: &str) -> RexxValue {
2184 let mut env = Environment::new();
2185 let mut lexer = Lexer::new(src);
2186 let tokens = lexer.tokenize().unwrap();
2187 let mut parser = Parser::new(tokens);
2188 let program = parser.parse().unwrap();
2189 let mut eval = Evaluator::new(&mut env, &program);
2190 match &program.clauses[0].kind {
2192 ClauseKind::Command(expr) => eval.eval_expr(expr).unwrap(),
2193 _ => panic!("expected command clause"),
2194 }
2195 }
2196
2197 #[test]
2198 fn eval_addition() {
2199 let val = eval_expr("2 + 3");
2200 assert_eq!(val.as_str(), "5");
2201 }
2202
2203 #[test]
2204 fn eval_subtraction() {
2205 let val = eval_expr("10 - 4");
2206 assert_eq!(val.as_str(), "6");
2207 }
2208
2209 #[test]
2210 fn eval_multiplication() {
2211 let val = eval_expr("3 * 7");
2212 assert_eq!(val.as_str(), "21");
2213 }
2214
2215 #[test]
2216 fn eval_division() {
2217 let val = eval_expr("10 / 4");
2218 assert_eq!(val.as_str(), "2.5");
2219 }
2220
2221 #[test]
2222 fn eval_precedence() {
2223 let val = eval_expr("2 + 3 * 4");
2224 assert_eq!(val.as_str(), "14");
2225 }
2226
2227 #[test]
2228 fn eval_division_by_zero() {
2229 let mut env = Environment::new();
2230 let mut lexer = Lexer::new("1 / 0");
2231 let tokens = lexer.tokenize().unwrap();
2232 let mut parser = Parser::new(tokens);
2233 let program = parser.parse().unwrap();
2234 let mut eval = Evaluator::new(&mut env, &program);
2235 match &program.clauses[0].kind {
2236 ClauseKind::Command(expr) => {
2237 let result = eval.eval_expr(expr);
2238 assert!(result.is_err());
2239 assert_eq!(result.unwrap_err().error, RexxError::ArithmeticOverflow);
2240 }
2241 _ => panic!("expected command clause"),
2242 }
2243 }
2244
2245 #[test]
2246 fn eval_power() {
2247 let val = eval_expr("2 ** 10");
2248 assert_eq!(val.as_str(), "1024");
2249 }
2250
2251 #[test]
2252 fn eval_string_concat_blank() {
2253 let val = eval_expr("'hello' 'world'");
2254 assert_eq!(val.as_str(), "hello world");
2255 }
2256
2257 #[test]
2258 fn eval_string_concat_abuttal() {
2259 let val = eval_expr("'hello'||'world'");
2260 assert_eq!(val.as_str(), "helloworld");
2261 }
2262
2263 #[test]
2264 fn eval_comparison_numeric() {
2265 let val = eval_expr("3 > 2");
2266 assert_eq!(val.as_str(), "1");
2267 }
2268
2269 #[test]
2270 fn eval_comparison_equal() {
2271 let val = eval_expr("5 = 5");
2272 assert_eq!(val.as_str(), "1");
2273 }
2274
2275 #[test]
2276 fn eval_comparison_string() {
2277 let val = eval_expr("'abc' = 'abc'");
2278 assert_eq!(val.as_str(), "1");
2279 }
2280
2281 #[test]
2282 fn eval_strict_comparison() {
2283 let val = eval_expr("' abc' == 'abc'");
2284 assert_eq!(val.as_str(), "0");
2285 }
2286
2287 #[test]
2288 fn eval_logical_and() {
2289 let val = eval_expr("1 & 1");
2290 assert_eq!(val.as_str(), "1");
2291 let val = eval_expr("1 & 0");
2292 assert_eq!(val.as_str(), "0");
2293 }
2294
2295 #[test]
2296 fn eval_logical_or() {
2297 let val = eval_expr("0 | 1");
2298 assert_eq!(val.as_str(), "1");
2299 }
2300
2301 #[test]
2302 fn eval_logical_not() {
2303 let val = eval_expr("\\0");
2304 assert_eq!(val.as_str(), "1");
2305 }
2306
2307 #[test]
2308 fn eval_variable_assignment_and_lookup() {
2309 let mut env = Environment::new();
2310 let mut lexer = Lexer::new("x = 42; x + 1");
2311 let tokens = lexer.tokenize().unwrap();
2312 let mut parser = Parser::new(tokens);
2313 let program = parser.parse().unwrap();
2314 let mut eval = Evaluator::new(&mut env, &program);
2315 let signal = eval.exec().unwrap();
2316 assert!(matches!(signal, ExecSignal::Normal));
2317 assert_eq!(env.get("X").as_str(), "42");
2319 }
2320
2321 #[test]
2322 fn eval_unset_variable_returns_name() {
2323 let val = eval_expr("foo");
2324 assert_eq!(val.as_str(), "FOO");
2325 }
2326
2327 #[test]
2328 fn eval_say_runs() {
2329 let mut env = Environment::new();
2331 let mut lexer = Lexer::new("say 2 + 3");
2332 let tokens = lexer.tokenize().unwrap();
2333 let mut parser = Parser::new(tokens);
2334 let program = parser.parse().unwrap();
2335 let mut eval = Evaluator::new(&mut env, &program);
2336 let signal = eval.exec().unwrap();
2337 assert!(matches!(signal, ExecSignal::Normal));
2338 }
2339
2340 #[test]
2341 fn eval_negative_power() {
2342 let val = eval_expr("2 ** -1");
2343 assert_eq!(val.as_str(), "0.5");
2344 }
2345
2346 #[test]
2347 fn eval_unary_minus() {
2348 let val = eval_expr("-5 + 3");
2349 assert_eq!(val.as_str(), "-2");
2350 }
2351
2352 #[test]
2353 fn eval_remainder() {
2354 let val = eval_expr("17 // 5");
2355 assert_eq!(val.as_str(), "2");
2356 }
2357
2358 #[test]
2359 fn eval_integer_division() {
2360 let val = eval_expr("17 % 5");
2361 assert_eq!(val.as_str(), "3");
2362 }
2363
2364 #[test]
2365 fn command_handler_with_env_sets_compound() {
2366 let mut env = Environment::new();
2367 let src = "address XEDIT; 'EXTRACT /CURLINE/'; say CURLINE.1";
2368 let mut lexer = Lexer::new(src);
2369 let tokens = lexer.tokenize().unwrap();
2370 let mut parser = Parser::new(tokens);
2371 let program = parser.parse().unwrap();
2372 let mut eval = Evaluator::new(&mut env, &program);
2373 eval.set_command_handler_with_env(Box::new(|_addr, _cmd, vars| {
2374 vars.set_compound("CURLINE", "1", RexxValue::new("Hello from XEDIT"));
2375 Some(0)
2376 }));
2377 let signal = eval.exec().unwrap();
2378 assert!(matches!(signal, ExecSignal::Normal));
2379 assert_eq!(env.get("RC").as_str(), "0");
2380 assert_eq!(
2381 env.get_compound("CURLINE", "1").as_str(),
2382 "Hello from XEDIT"
2383 );
2384 }
2385
2386 #[test]
2387 fn command_handler_with_env_none_falls_through_to_basic() {
2388 let mut env = Environment::new();
2389 let src = "'some command'";
2391 let mut lexer = Lexer::new(src);
2392 let tokens = lexer.tokenize().unwrap();
2393 let mut parser = Parser::new(tokens);
2394 let program = parser.parse().unwrap();
2395 let mut eval = Evaluator::new(&mut env, &program);
2396 eval.set_command_handler_with_env(Box::new(|_addr, _cmd, _vars| None));
2397 eval.set_command_handler(Box::new(|_addr, _cmd| Some(7)));
2398 let signal = eval.exec().unwrap();
2399 assert!(matches!(signal, ExecSignal::Normal));
2400 assert_eq!(env.get("RC").as_str(), "7");
2401 }
2402
2403 #[test]
2404 fn command_handler_with_env_some_skips_basic() {
2405 let mut env = Environment::new();
2406 let src = "'some command'";
2407 let mut lexer = Lexer::new(src);
2408 let tokens = lexer.tokenize().unwrap();
2409 let mut parser = Parser::new(tokens);
2410 let program = parser.parse().unwrap();
2411 let mut eval = Evaluator::new(&mut env, &program);
2412 eval.set_command_handler_with_env(Box::new(|_addr, _cmd, _vars| Some(0)));
2413 eval.set_command_handler(Box::new(|_addr, _cmd| Some(99)));
2414 let signal = eval.exec().unwrap();
2415 assert!(matches!(signal, ExecSignal::Normal));
2416 assert_eq!(env.get("RC").as_str(), "0");
2418 }
2419
2420 #[test]
2421 fn command_handler_with_env_error_trap_fires() {
2422 let mut env = Environment::new();
2423 let src = "signal on error name oops; 'fail'; say 'NOT REACHED'; exit 0\noops: exit RC";
2425 let mut lexer = Lexer::new(src);
2426 let tokens = lexer.tokenize().unwrap();
2427 let mut parser = Parser::new(tokens);
2428 let program = parser.parse().unwrap();
2429 let mut eval = Evaluator::new(&mut env, &program);
2430 eval.set_command_handler_with_env(Box::new(|_addr, _cmd, _vars| Some(42)));
2431 let signal = eval.exec().unwrap();
2432 match &signal {
2434 ExecSignal::Exit(Some(val)) => assert_eq!(val.as_str(), "42"),
2435 _ => panic!("expected Exit(Some(42))"),
2436 }
2437 assert_eq!(env.get("RC").as_str(), "42");
2438 }
2439
2440 #[test]
2441 fn command_handler_with_env_failure_trap_fires_on_negative_rc() {
2442 let mut env = Environment::new();
2443 let src = "signal on failure name bad; 'notfound'; say 'NOT REACHED'; exit 0\nbad: exit RC";
2445 let mut lexer = Lexer::new(src);
2446 let tokens = lexer.tokenize().unwrap();
2447 let mut parser = Parser::new(tokens);
2448 let program = parser.parse().unwrap();
2449 let mut eval = Evaluator::new(&mut env, &program);
2450 eval.set_command_handler_with_env(Box::new(|_addr, _cmd, _vars| Some(-3)));
2451 let signal = eval.exec().unwrap();
2452 match &signal {
2454 ExecSignal::Exit(Some(val)) => assert_eq!(val.as_str(), "-3"),
2455 _ => panic!("expected Exit(Some(-3))"),
2456 }
2457 assert_eq!(env.get("RC").as_str(), "-3");
2458 }
2459}