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::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
179pub struct Evaluator<'a> {
180 env: &'a mut Environment,
181 program: &'a Program,
182 settings: NumericSettings,
183 labels: HashMap<String, usize>,
184 arg_stack: Vec<Vec<RexxValue>>,
185 pending_exit: PendingExit,
186 traps: HashMap<Condition, String>,
188 pending_signal: Option<String>,
190 interpret_depth: usize,
192 external_depth: usize,
194 trace_setting: TraceSetting,
196 queue: VecDeque<String>,
198 command_handler: Option<CommandHandler>,
202}
203
204impl<'a> Evaluator<'a> {
205 pub fn new(env: &'a mut Environment, program: &'a Program) -> Self {
206 let labels = Self::build_labels(program);
207 Self {
208 env,
209 program,
210 settings: NumericSettings::default(),
211 labels,
212 arg_stack: Vec::new(),
213 pending_exit: PendingExit::None,
214 traps: HashMap::new(),
215 pending_signal: None,
216 interpret_depth: 0,
217 external_depth: 0,
218 trace_setting: TraceSetting::default(),
219 queue: VecDeque::new(),
220 command_handler: None,
221 }
222 }
223
224 fn build_labels(program: &Program) -> HashMap<String, usize> {
225 let mut labels = HashMap::new();
226 for (i, clause) in program.clauses.iter().enumerate() {
227 if let ClauseKind::Label(name) = &clause.kind {
228 labels.entry(name.clone()).or_insert(i);
229 }
230 }
231 labels
232 }
233
234 pub fn exec(&mut self) -> RexxResult<ExecSignal> {
235 let mut start = 0;
236 loop {
237 match self.exec_from(start)? {
238 ExecSignal::Signal(label) => {
239 let &idx = self.labels.get(&label).ok_or_else(|| {
240 RexxDiagnostic::new(RexxError::LabelNotFound)
241 .with_detail(format!("label '{label}' not found"))
242 })?;
243 if matches!(
245 self.trace_setting.level,
246 TraceLevel::Labels | TraceLevel::All
247 ) {
248 self.trace_clause(&self.program.clauses[idx]);
249 }
250 start = idx + 1;
251 }
252 other => return Ok(other),
253 }
254 }
255 }
256
257 fn exec_from(&mut self, start: usize) -> RexxResult<ExecSignal> {
258 for clause in &self.program.clauses[start..] {
259 let signal = self.exec_clause_outer(clause)?;
260 if let Some(signal) = self.pending_exit.take_signal() {
261 return Ok(signal);
262 }
263 if let Some(label) = self.pending_signal.take() {
264 return Ok(ExecSignal::Signal(label));
265 }
266 if !matches!(signal, ExecSignal::Normal) {
267 return Ok(signal);
268 }
269 }
270 Ok(ExecSignal::Normal)
271 }
272
273 fn exec_body(&mut self, body: &[Clause]) -> RexxResult<ExecSignal> {
274 for clause in body {
275 let signal = self.exec_clause_outer(clause)?;
276 if let Some(signal) = self.pending_exit.take_signal() {
277 return Ok(signal);
278 }
279 if let Some(label) = self.pending_signal.take() {
280 return Ok(ExecSignal::Signal(label));
281 }
282 if !matches!(signal, ExecSignal::Normal) {
283 return Ok(signal);
284 }
285 }
286 Ok(ExecSignal::Normal)
287 }
288
289 fn exec_clause_outer(&mut self, clause: &Clause) -> RexxResult<ExecSignal> {
291 let trace_level = self.trace_setting.level;
292
293 let should_trace_source = match &clause.kind {
298 ClauseKind::Label(_) => {
299 matches!(trace_level, TraceLevel::Labels | TraceLevel::All)
300 }
301 ClauseKind::Command(_) => matches!(
302 trace_level,
303 TraceLevel::Commands
304 | TraceLevel::Results
305 | TraceLevel::Intermediates
306 | TraceLevel::All
307 ),
308 _ => matches!(
309 trace_level,
310 TraceLevel::Results | TraceLevel::Intermediates | TraceLevel::All
311 ),
312 };
313 if should_trace_source && !matches!(clause.kind, ClauseKind::Nop) {
314 self.trace_clause(clause);
315 }
316
317 match self.exec_clause(clause) {
318 Ok(signal) => {
319 if should_trace_source && !matches!(clause.kind, ClauseKind::Nop) {
321 self.trace_interactive_pause()?;
322 }
323 Ok(signal)
324 }
325 Err(diag) => {
326 if let Some(label) = self.traps.get(&Condition::Syntax).cloned() {
327 self.env
329 .set("RC", RexxValue::new(diag.error.number().to_string()));
330 let desc = diag.detail.unwrap_or_default();
332 self.env.set_condition_info(crate::env::ConditionInfoData {
333 condition: "SYNTAX".to_string(),
334 description: desc,
335 instruction: "SIGNAL".to_string(),
336 status: "ON".to_string(),
337 });
338 self.traps.remove(&Condition::Syntax);
340 Ok(ExecSignal::Signal(label))
341 } else {
342 Err(diag)
343 }
344 }
345 }
346 }
347
348 #[allow(clippy::too_many_lines)]
349 fn exec_clause(&mut self, clause: &Clause) -> RexxResult<ExecSignal> {
350 match &clause.kind {
351 ClauseKind::Say(expr) => {
352 let val = self.eval_expr(expr)?;
353 if self.pending_exit.is_pending() {
354 return Ok(ExecSignal::Normal);
355 }
356 if matches!(
357 self.trace_setting.level,
358 TraceLevel::Results | TraceLevel::Intermediates | TraceLevel::All
359 ) {
360 self.trace_tag(">>", val.as_str());
361 }
362 println!("{val}");
363 Ok(ExecSignal::Normal)
364 }
365 ClauseKind::Assignment { target, expr } => {
366 let val = self.eval_expr(expr)?;
367 if matches!(
368 self.trace_setting.level,
369 TraceLevel::Results | TraceLevel::Intermediates | TraceLevel::All
370 ) {
371 self.trace_tag(">>", val.as_str());
372 }
373 match target {
374 AssignTarget::Simple(name) => {
375 self.env.set(name, val);
376 }
377 AssignTarget::Stem { stem, tail } => {
378 let resolved_tail = self.resolve_tail(tail);
379 if resolved_tail.is_empty() {
380 self.env.set_stem_default(stem, val);
381 } else {
382 self.env.set_compound(stem, &resolved_tail, val);
383 }
384 }
385 }
386 Ok(ExecSignal::Normal)
387 }
388 ClauseKind::Command(expr) => {
389 let val = self.eval_expr(expr)?;
390 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
391 return Ok(ExecSignal::Normal);
392 }
393 Ok(self.exec_host_command(val.as_str()))
394 }
395 ClauseKind::If {
396 condition,
397 then_clause,
398 else_clause,
399 } => self.exec_if(condition, then_clause, else_clause.as_deref()),
400 ClauseKind::Do(block) => self.exec_do(block),
401 ClauseKind::Select {
402 when_clauses,
403 otherwise,
404 } => self.exec_select(when_clauses, otherwise.as_ref()),
405 ClauseKind::Leave(name) => Ok(ExecSignal::Leave(name.clone())),
406 ClauseKind::Iterate(name) => Ok(ExecSignal::Iterate(name.clone())),
407 ClauseKind::Exit(expr) => {
408 let val = if let Some(e) = expr {
409 Some(self.eval_expr(e)?)
410 } else {
411 None
412 };
413 Ok(ExecSignal::Exit(val))
414 }
415 ClauseKind::Return(expr) => {
416 let val = if let Some(e) = expr {
417 Some(self.eval_expr(e)?)
418 } else {
419 None
420 };
421 Ok(ExecSignal::Return(val))
422 }
423 ClauseKind::Call { name, args } => self.exec_call(name, args),
424 ClauseKind::Signal(action) => self.exec_signal(action),
425 ClauseKind::Label(_) | ClauseKind::Nop => Ok(ExecSignal::Normal),
426 ClauseKind::Procedure(_) => Err(RexxDiagnostic::new(RexxError::UnexpectedProcedure)
427 .with_detail("PROCEDURE must be the first instruction in a called routine")),
428 ClauseKind::Drop(names) => {
429 for name in names {
430 self.env.drop(name);
431 }
432 Ok(ExecSignal::Normal)
433 }
434 ClauseKind::Arg(template) => self.exec_arg(template),
435 ClauseKind::Parse {
436 upper,
437 source,
438 template,
439 } => self.exec_parse(*upper, source, template),
440 ClauseKind::Pull(template_opt) => {
441 let raw = self.pull_from_queue_or_stdin()?;
442 let text = raw.to_uppercase();
443 if let Some(template) = template_opt {
444 self.apply_template(&text, template)?;
445 }
446 Ok(ExecSignal::Normal)
447 }
448 ClauseKind::Interpret(expr) => self.exec_interpret(expr),
449 ClauseKind::Address(action) => self.exec_address(action),
450 ClauseKind::Trace(expr) => self.exec_trace(expr),
451 ClauseKind::Numeric(setting) => self.exec_numeric(setting),
452 ClauseKind::Push(expr) => self.exec_push(expr.as_ref()),
453 ClauseKind::Queue(expr) => self.exec_queue(expr.as_ref()),
454 }
455 }
456
457 fn exec_address(&mut self, action: &AddressAction) -> RexxResult<ExecSignal> {
461 match action {
462 AddressAction::SetEnvironment(name) => {
463 if name.is_empty() {
464 self.env.swap_address();
465 } else {
466 self.env.set_address(name);
467 }
468 Ok(ExecSignal::Normal)
469 }
470 AddressAction::Value(expr) => {
471 let val = self.eval_expr(expr)?;
472 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
473 return Ok(ExecSignal::Normal);
474 }
475 let name = val.as_str().to_uppercase();
476 self.env.set_address(&name);
477 Ok(ExecSignal::Normal)
478 }
479 AddressAction::Temporary {
480 environment,
481 command,
482 } => {
483 let cmd_val = self.eval_expr(command)?;
484 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
485 return Ok(ExecSignal::Normal);
486 }
487 let cmd_str = cmd_val.as_str().to_string();
488 let saved_default = self.env.address().to_string();
490 self.env.set_address(environment);
491 let signal = self.exec_host_command(&cmd_str);
492 self.env.set_address(&saved_default);
495 Ok(signal)
496 }
497 }
498 }
499
500 fn exec_host_command(&mut self, command: &str) -> ExecSignal {
503 let custom_rc = if let Some(ref mut handler) = self.command_handler {
505 let addr = self.env.address().to_string();
506 handler(&addr, command)
507 } else {
508 None
509 };
510
511 let rc = if let Some(rc) = custom_rc {
512 self.env.set("RC", RexxValue::new(rc.to_string()));
514 rc
515 } else {
516 let result = std::process::Command::new("sh")
518 .arg("-c")
519 .arg(command)
520 .status();
521 if let Ok(status) = result {
522 let code = status.code().unwrap_or(-1);
523 self.env.set("RC", RexxValue::new(code.to_string()));
524 code
525 } else {
526 self.env.set("RC", RexxValue::new("-1"));
527 return self.fire_failure_trap(command);
528 }
529 };
530
531 if rc != 0
532 && let Some(label) = self.traps.get(&Condition::Error).cloned()
533 {
534 self.env.set_condition_info(crate::env::ConditionInfoData {
535 condition: "ERROR".to_string(),
536 description: command.to_string(),
537 instruction: "SIGNAL".to_string(),
538 status: "ON".to_string(),
539 });
540 self.traps.remove(&Condition::Error);
541 return ExecSignal::Signal(label);
542 }
543 ExecSignal::Normal
544 }
545
546 fn fire_failure_trap(&mut self, command: &str) -> ExecSignal {
547 if let Some(label) = self.traps.get(&Condition::Failure).cloned() {
548 self.env.set_condition_info(crate::env::ConditionInfoData {
549 condition: "FAILURE".to_string(),
550 description: command.to_string(),
551 instruction: "SIGNAL".to_string(),
552 status: "ON".to_string(),
553 });
554 self.traps.remove(&Condition::Failure);
555 ExecSignal::Signal(label)
556 } else {
557 ExecSignal::Normal
558 }
559 }
560
561 fn apply_trace_setting(&mut self, s: &str) -> RexxResult<String> {
566 let old = self.trace_setting.to_string();
567 let action = TraceAction::parse(s).ok_or_else(|| {
568 RexxDiagnostic::new(RexxError::InvalidTrace)
569 .with_detail(format!("invalid trace setting '{s}'"))
570 })?;
571 match action {
572 TraceAction::ToggleInteractive => {
573 self.trace_setting.interactive = !self.trace_setting.interactive;
574 }
575 TraceAction::Set(new_setting) => {
576 self.trace_setting = new_setting;
577 }
578 }
579 Ok(old)
580 }
581
582 fn exec_trace(&mut self, expr: &Expr) -> RexxResult<ExecSignal> {
584 let val = self.eval_expr(expr)?;
585 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
586 return Ok(ExecSignal::Normal);
587 }
588 self.apply_trace_setting(val.as_str())?;
589 Ok(ExecSignal::Normal)
590 }
591
592 #[allow(clippy::unused_self)]
594 fn trace_clause(&self, clause: &Clause) {
595 let line_num = clause.loc.line;
596 let source = clause.loc.source_line.as_deref().unwrap_or("(unknown)");
597 eprintln!("{line_num:>6} *-* {source}");
598 }
599
600 #[allow(clippy::unused_self)]
602 fn trace_tag(&self, tag: &str, value: &str) {
603 eprintln!(" >{tag}> \"{value}\"");
604 }
605
606 fn trace_intermediates(&self, tag: &str, value: &str) {
608 if matches!(
609 self.trace_setting.level,
610 TraceLevel::Intermediates | TraceLevel::All
611 ) {
612 self.trace_tag(tag, value);
613 }
614 }
615
616 fn trace_interactive_pause(&mut self) -> RexxResult<()> {
620 if !self.trace_setting.interactive {
621 return Ok(());
622 }
623 loop {
624 let mut line = String::new();
625 match std::io::stdin().read_line(&mut line) {
626 Ok(0) | Err(_) => break, Ok(_) => {}
628 }
629 let content = line.trim_end_matches(['\n', '\r']);
631 if content.is_empty() {
633 break;
634 }
635 let source = content.trim().to_string();
637 let mut lexer = Lexer::new(&source);
638 let tokens = lexer.tokenize()?;
639 let mut parser = Parser::new(tokens);
640 let program = parser.parse()?;
641 let labels = HashMap::new();
642 self.exec_interpret_body(&program.clauses, &labels)?;
643 }
644 Ok(())
645 }
646
647 fn exec_interpret(&mut self, expr: &Expr) -> RexxResult<ExecSignal> {
651 let val = self.eval_expr(expr)?;
652 if self.pending_exit.is_pending() {
653 return Ok(ExecSignal::Normal);
654 }
655 if self.pending_signal.is_some() {
656 return Ok(ExecSignal::Normal);
657 }
658
659 let source = val.as_str().to_string();
660 if source.is_empty() {
661 return Ok(ExecSignal::Normal);
662 }
663
664 if self.interpret_depth >= MAX_INTERPRET_DEPTH {
666 return Err(RexxDiagnostic::new(RexxError::ResourceExhausted)
667 .with_detail("INTERPRET recursion depth limit exceeded"));
668 }
669
670 let mut lexer = Lexer::new(&source);
672 let tokens = lexer.tokenize()?;
673 let mut parser = Parser::new(tokens);
674 let program = parser.parse()?;
675
676 let mut labels = HashMap::new();
678 for (i, clause) in program.clauses.iter().enumerate() {
679 if let ClauseKind::Label(name) = &clause.kind {
680 labels.entry(name.clone()).or_insert(i);
681 }
682 }
683
684 self.interpret_depth += 1;
685 let result = self.exec_interpret_body(&program.clauses, &labels);
686 self.interpret_depth -= 1;
687
688 result
689 }
690
691 fn exec_interpret_body(
693 &mut self,
694 clauses: &[Clause],
695 labels: &HashMap<String, usize>,
696 ) -> RexxResult<ExecSignal> {
697 let mut start = 0;
698 loop {
699 match self.exec_interpret_from(clauses, start)? {
700 ExecSignal::Signal(ref label) => {
701 if let Some(&idx) = labels.get(label) {
702 start = idx + 1; } else {
704 return Ok(ExecSignal::Signal(label.clone()));
705 }
706 }
707 other => return Ok(other),
708 }
709 }
710 }
711
712 fn exec_interpret_from(&mut self, clauses: &[Clause], start: usize) -> RexxResult<ExecSignal> {
714 for clause in &clauses[start..] {
715 let signal = self.exec_clause_outer(clause)?;
716 if let Some(signal) = self.pending_exit.take_signal() {
717 return Ok(signal);
718 }
719 if let Some(label) = self.pending_signal.take() {
720 return Ok(ExecSignal::Signal(label));
721 }
722 if !matches!(signal, ExecSignal::Normal) {
723 return Ok(signal);
724 }
725 }
726 Ok(ExecSignal::Normal)
727 }
728
729 fn call_routine(&mut self, name: &str, args: Vec<RexxValue>) -> RexxResult<ExecSignal> {
730 let &label_idx = self.labels.get(name).ok_or_else(|| {
731 RexxDiagnostic::new(RexxError::RoutineNotFound)
732 .with_detail(format!("routine '{name}' not found"))
733 })?;
734
735 let start_idx = label_idx + 1; self.arg_stack.push(args);
737
738 let has_procedure = matches!(
740 self.program.clauses.get(start_idx).map(|c| &c.kind),
741 Some(ClauseKind::Procedure(_))
742 );
743
744 let exec_start = if has_procedure {
745 match &self.program.clauses[start_idx].kind {
746 ClauseKind::Procedure(Some(names)) => self.env.push_procedure_expose(names),
747 ClauseKind::Procedure(None) => self.env.push_procedure(),
748 _ => unreachable!(),
749 }
750 start_idx + 1
751 } else {
752 start_idx
753 };
754
755 let result = self.exec_from(exec_start);
756
757 if has_procedure {
758 self.env.pop_procedure();
759 }
760 self.arg_stack.pop();
761
762 result
763 }
764
765 fn try_call_external(
768 &mut self,
769 name: &str,
770 args: Vec<RexxValue>,
771 ) -> RexxResult<Option<ExecSignal>> {
772 let source_dir = self.env.source_dir();
774 let Some((program, path)) = crate::external::resolve_external(name, source_dir)? else {
775 return Ok(None);
776 };
777
778 if self.external_depth >= 100 {
780 return Err(RexxDiagnostic::new(RexxError::ResourceExhausted)
781 .with_detail("external function call recursion depth limit exceeded"));
782 }
783 self.external_depth += 1;
784
785 self.env.push_procedure();
787 self.arg_stack.push(args);
788
789 let old_source_path = self.env.source_path().map(Path::to_path_buf);
791 self.env.set_source_path(path);
792
793 let ext_labels = Self::build_labels(&program);
795 let result = self.exec_interpret_body(&program.clauses, &ext_labels);
796
797 match old_source_path {
799 Some(old) => self.env.set_source_path(old),
800 None => self.env.clear_source_path(),
801 }
802 self.arg_stack.pop();
803 self.env.pop_procedure();
804 self.external_depth -= 1;
805
806 Ok(Some(result?))
807 }
808
809 fn exec_call(&mut self, name: &str, arg_exprs: &[Expr]) -> RexxResult<ExecSignal> {
810 let mut args = Vec::with_capacity(arg_exprs.len());
811 for expr in arg_exprs {
812 args.push(self.eval_expr(expr)?);
813 if let Some(signal) = self.pending_exit.take_signal() {
814 return Ok(signal);
815 }
816 }
817
818 if name.eq_ignore_ascii_case("TRACE") {
820 if args.len() == 1 {
821 let old = self.apply_trace_setting(args[0].as_str())?;
822 self.env.set("RESULT", RexxValue::new(old));
823 } else if args.is_empty() {
824 let old = self.trace_setting.to_string();
825 self.env.set("RESULT", RexxValue::new(old));
826 } else {
827 return Err(RexxDiagnostic::new(RexxError::IncorrectCall)
828 .with_detail("TRACE expects 0 or 1 arguments"));
829 }
830 return Ok(ExecSignal::Normal);
831 }
832
833 if self.labels.contains_key(name) {
835 let signal = self.call_routine(name, args)?;
836 match signal {
837 ExecSignal::Return(Some(val)) => {
838 self.env.set("RESULT", val);
839 Ok(ExecSignal::Normal)
840 }
841 ExecSignal::Return(None) | ExecSignal::Normal => {
842 self.env.drop("RESULT");
843 Ok(ExecSignal::Normal)
844 }
845 ExecSignal::Exit(_) | ExecSignal::Signal(_) => Ok(signal),
846 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(ExecSignal::Normal),
847 }
848 } else if let Some(result) =
849 crate::builtins::call_builtin(name, &args, &self.settings, self.env, self.queue.len())
850 {
851 let val = result?;
852 self.env.set("RESULT", val);
853 Ok(ExecSignal::Normal)
854 } else {
855 match self.try_call_external(name, args)? {
857 Some(signal) => match signal {
858 ExecSignal::Return(Some(val)) => {
859 self.env.set("RESULT", val);
860 Ok(ExecSignal::Normal)
861 }
862 ExecSignal::Return(None) | ExecSignal::Normal => {
863 self.env.drop("RESULT");
864 Ok(ExecSignal::Normal)
865 }
866 ExecSignal::Exit(_) | ExecSignal::Signal(_) => Ok(signal),
867 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(ExecSignal::Normal),
868 },
869 None => Err(RexxDiagnostic::new(RexxError::RoutineNotFound)
870 .with_detail(format!("routine '{name}' not found"))),
871 }
872 }
873 }
874
875 fn exec_signal(&mut self, action: &SignalAction) -> RexxResult<ExecSignal> {
877 match action {
878 SignalAction::Label(label) => Ok(ExecSignal::Signal(label.clone())),
879 SignalAction::Value(expr) => {
880 let val = self.eval_expr(expr)?;
881 let label = val.as_str().to_uppercase();
882 Ok(ExecSignal::Signal(label))
883 }
884 SignalAction::On { condition, name } => {
885 let label = name
886 .clone()
887 .unwrap_or_else(|| Self::condition_default_label(condition));
888 self.traps.insert(condition.clone(), label);
889 Ok(ExecSignal::Normal)
890 }
891 SignalAction::Off(condition) => {
892 self.traps.remove(condition);
893 Ok(ExecSignal::Normal)
894 }
895 }
896 }
897
898 fn condition_default_label(condition: &Condition) -> String {
900 match condition {
901 Condition::Error => "ERROR".to_string(),
902 Condition::Failure => "FAILURE".to_string(),
903 Condition::Halt => "HALT".to_string(),
904 Condition::NoValue => "NOVALUE".to_string(),
905 Condition::NotReady => "NOTREADY".to_string(),
906 Condition::Syntax => "SYNTAX".to_string(),
907 Condition::LostDigits => "LOSTDIGITS".to_string(),
908 }
909 }
910
911 fn exec_arg(&mut self, template: &ParseTemplate) -> RexxResult<ExecSignal> {
913 self.exec_parse(true, &ParseSource::Arg, template)
914 }
915
916 pub fn set_main_args(&mut self, args: Vec<RexxValue>) {
918 self.arg_stack.push(args);
919 }
920
921 pub fn set_command_handler(&mut self, handler: CommandHandler) {
930 self.command_handler = Some(handler);
931 }
932
933 fn exec_parse(
937 &mut self,
938 upper: bool,
939 source: &ParseSource,
940 template: &ParseTemplate,
941 ) -> RexxResult<ExecSignal> {
942 let sub_templates = Self::split_template_at_commas(template);
943
944 if let ParseSource::Arg = source {
945 let args = self.arg_stack.last().cloned().unwrap_or_default();
946 for (i, sub_t) in sub_templates.iter().enumerate() {
947 let raw = args
948 .get(i)
949 .map(|v| v.as_str().to_string())
950 .unwrap_or_default();
951 let text = if upper { raw.to_uppercase() } else { raw };
952 self.apply_template(&text, sub_t)?;
953 }
954 } else {
955 let raw = match source {
956 ParseSource::Var(name) => self.env.get(name).as_str().to_string(),
957 ParseSource::Value(expr) => self.eval_expr(expr)?.as_str().to_string(),
958 ParseSource::Pull => self.pull_from_queue_or_stdin()?,
959 ParseSource::LineIn => self.read_stdin_line()?,
960 ParseSource::Source => {
961 let filename = self
962 .env
963 .source_path()
964 .and_then(|p| p.file_name())
965 .map_or_else(|| "rexx".to_string(), |f| f.to_string_lossy().into_owned());
966 format!("UNIX COMMAND {filename}")
967 }
968 ParseSource::Version => {
969 format!("REXX-patch-rexx {} 8 Feb 2026", env!("CARGO_PKG_VERSION"))
970 }
971 ParseSource::Arg => unreachable!(),
972 };
973 let text = if upper { raw.to_uppercase() } else { raw };
974 for (i, sub_t) in sub_templates.iter().enumerate() {
975 let s = if i == 0 { &text } else { "" };
976 self.apply_template(s, sub_t)?;
977 }
978 }
979
980 Ok(ExecSignal::Normal)
981 }
982
983 fn apply_template(&mut self, source: &str, template: &ParseTemplate) -> RexxResult<()> {
985 let elements = &template.elements;
986 let len = elements.len();
987 let mut cursor: usize = 0;
988 let mut i: usize = 0;
989
990 while i < len {
991 let mut targets: Vec<&TemplateElement> = Vec::new();
993 while i < len {
994 match &elements[i] {
995 e @ (TemplateElement::Variable(_) | TemplateElement::Dot) => {
996 targets.push(e);
997 i += 1;
998 }
999 _ => break,
1000 }
1001 }
1002
1003 if i >= len {
1005 let section = if cursor < source.len() {
1007 &source[cursor..]
1008 } else {
1009 ""
1010 };
1011 self.assign_section(section, &targets);
1012 break;
1013 }
1014
1015 match &elements[i] {
1016 TemplateElement::Literal(pat) => {
1017 cursor = self.match_pattern(source, cursor, pat, &targets);
1018 i += 1;
1019 }
1020 TemplateElement::AbsolutePos(expr) => {
1021 let pos_val = self.eval_expr(expr)?;
1022 let pos = self.to_position_value(&pos_val)?;
1023 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
1024 let char_pos = if pos > 0 { (pos - 1) as usize } else { 0 };
1025 let target = Self::char_pos_to_byte_offset(source, char_pos);
1026 let section = if target > cursor {
1027 &source[cursor..target]
1028 } else {
1029 ""
1030 };
1031 self.assign_section(section, &targets);
1032 cursor = target;
1033 i += 1;
1034 }
1035 TemplateElement::RelativePos(offset) => {
1036 #[allow(clippy::cast_sign_loss)]
1037 let target = if *offset >= 0 {
1038 Self::advance_chars(source, cursor, *offset as usize)
1039 } else {
1040 Self::retreat_chars(source, cursor, offset.unsigned_abs() as usize)
1041 };
1042 let section = if target > cursor {
1043 &source[cursor..target]
1044 } else {
1045 ""
1046 };
1047 self.assign_section(section, &targets);
1048 cursor = target;
1049 i += 1;
1050 }
1051 TemplateElement::VariablePattern(name) => {
1052 let pat = self.env.get(name).as_str().to_string();
1053 cursor = self.match_pattern(source, cursor, &pat, &targets);
1054 i += 1;
1055 }
1056 _ => {
1057 i += 1;
1058 }
1059 }
1060 }
1061 Ok(())
1062 }
1063
1064 fn assign_section(&mut self, section: &str, targets: &[&TemplateElement]) {
1067 match targets.len() {
1068 0 => {} 1 => {
1070 self.assign_target(targets[0], section);
1072 }
1073 _ => {
1074 let mut remaining = section;
1076 for (j, target) in targets.iter().enumerate() {
1077 if j == targets.len() - 1 {
1078 let trimmed = remaining.trim_start_matches([' ', '\t']);
1080 self.assign_target(target, trimmed);
1081 } else {
1082 let trimmed = remaining.trim_start_matches([' ', '\t']);
1084 if let Some(blank_pos) = trimmed.find([' ', '\t']) {
1085 let word = &trimmed[..blank_pos];
1086 self.assign_target(target, word);
1087 remaining = &trimmed[blank_pos..];
1088 } else {
1089 self.assign_target(target, trimmed);
1091 remaining = "";
1092 }
1093 }
1094 }
1095 }
1096 }
1097 }
1098
1099 fn match_pattern(
1103 &mut self,
1104 source: &str,
1105 cursor: usize,
1106 pat: &str,
1107 targets: &[&TemplateElement],
1108 ) -> usize {
1109 if !pat.is_empty()
1110 && let Some(found) = source[cursor..].find(pat)
1111 {
1112 let abs = cursor + found;
1113 self.assign_section(&source[cursor..abs], targets);
1114 abs + pat.len()
1115 } else {
1116 let section = if cursor < source.len() {
1117 &source[cursor..]
1118 } else {
1119 ""
1120 };
1121 self.assign_section(section, targets);
1122 source.len()
1123 }
1124 }
1125
1126 fn assign_target(&mut self, target: &TemplateElement, value: &str) {
1128 if let TemplateElement::Variable(name) = target {
1129 self.env.set(name, RexxValue::new(value));
1130 }
1131 }
1133
1134 fn split_template_at_commas(template: &ParseTemplate) -> Vec<ParseTemplate> {
1137 if !template
1138 .elements
1139 .iter()
1140 .any(|e| matches!(e, TemplateElement::Comma))
1141 {
1142 return vec![template.clone()];
1143 }
1144 let mut result = Vec::new();
1145 let mut current = Vec::new();
1146 for elem in &template.elements {
1147 if matches!(elem, TemplateElement::Comma) {
1148 result.push(ParseTemplate {
1149 elements: std::mem::take(&mut current),
1150 });
1151 } else {
1152 current.push(elem.clone());
1153 }
1154 }
1155 result.push(ParseTemplate { elements: current });
1156 result
1157 }
1158
1159 fn exec_numeric(&mut self, setting: &NumericSetting) -> RexxResult<ExecSignal> {
1163 match setting {
1164 NumericSetting::Digits(expr) => {
1165 let digits = if let Some(e) = expr {
1166 let val = self.eval_expr(e)?;
1167 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1168 return Ok(ExecSignal::Normal);
1169 }
1170 let n = self.to_integer(&val)?;
1171 if n < 1 {
1172 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1173 .with_detail("NUMERIC DIGITS value must be positive"));
1174 }
1175 if n > i64::from(u32::MAX) {
1176 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1177 .with_detail("NUMERIC DIGITS value too large"));
1178 }
1179 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1180 {
1181 n as u32
1182 }
1183 } else {
1184 9 };
1186 self.settings.digits = digits;
1187 }
1188 NumericSetting::Form(form_setting) => {
1189 let form = match form_setting {
1190 NumericFormSetting::Scientific => crate::value::NumericForm::Scientific,
1191 NumericFormSetting::Engineering => crate::value::NumericForm::Engineering,
1192 NumericFormSetting::Value(expr) => {
1193 let val = self.eval_expr(expr)?;
1194 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1195 return Ok(ExecSignal::Normal);
1196 }
1197 let s = val.as_str().to_uppercase();
1198 match s.as_str() {
1199 "SCIENTIFIC" => crate::value::NumericForm::Scientific,
1200 "ENGINEERING" => crate::value::NumericForm::Engineering,
1201 _ => {
1202 return Err(RexxDiagnostic::new(RexxError::InvalidSubKeyword)
1203 .with_detail(format!(
1204 "NUMERIC FORM value must be SCIENTIFIC or ENGINEERING; got '{s}'"
1205 )));
1206 }
1207 }
1208 }
1209 };
1210 self.settings.form = form;
1211 }
1212 NumericSetting::Fuzz(expr) => {
1213 let fuzz = if let Some(e) = expr {
1214 let val = self.eval_expr(e)?;
1215 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1216 return Ok(ExecSignal::Normal);
1217 }
1218 let n = self.to_integer(&val)?;
1219 if n < 0 {
1220 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1221 .with_detail("NUMERIC FUZZ value must not be negative"));
1222 }
1223 if n > i64::from(u32::MAX) {
1224 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1225 .with_detail("NUMERIC FUZZ value too large"));
1226 }
1227 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
1228 {
1229 n as u32
1230 }
1231 } else {
1232 0 };
1234 if fuzz >= self.settings.digits {
1235 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1236 .with_detail("NUMERIC FUZZ must be less than NUMERIC DIGITS"));
1237 }
1238 self.settings.fuzz = fuzz;
1239 }
1240 }
1241 Ok(ExecSignal::Normal)
1242 }
1243
1244 fn exec_push(&mut self, expr: Option<&Expr>) -> RexxResult<ExecSignal> {
1248 let val = if let Some(e) = expr {
1249 let v = self.eval_expr(e)?;
1250 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1251 return Ok(ExecSignal::Normal);
1252 }
1253 v.as_str().to_string()
1254 } else {
1255 String::new()
1256 };
1257 self.queue.push_front(val);
1258 Ok(ExecSignal::Normal)
1259 }
1260
1261 fn exec_queue(&mut self, expr: Option<&Expr>) -> RexxResult<ExecSignal> {
1263 let val = if let Some(e) = expr {
1264 let v = self.eval_expr(e)?;
1265 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1266 return Ok(ExecSignal::Normal);
1267 }
1268 v.as_str().to_string()
1269 } else {
1270 String::new()
1271 };
1272 self.queue.push_back(val);
1273 Ok(ExecSignal::Normal)
1274 }
1275
1276 fn pull_from_queue_or_stdin(&mut self) -> RexxResult<String> {
1278 if let Some(line) = self.queue.pop_front() {
1279 Ok(line)
1280 } else {
1281 self.read_stdin_line()
1282 }
1283 }
1284
1285 pub fn queue_len(&self) -> usize {
1287 self.queue.len()
1288 }
1289
1290 #[allow(clippy::unused_self)]
1292 fn read_stdin_line(&self) -> RexxResult<String> {
1293 let mut line = String::new();
1294 std::io::stdin().read_line(&mut line).map_err(|e| {
1295 RexxDiagnostic::new(RexxError::SystemFailure)
1296 .with_detail(format!("failed to read stdin: {e}"))
1297 })?;
1298 if line.ends_with('\n') {
1299 line.pop();
1300 if line.ends_with('\r') {
1301 line.pop();
1302 }
1303 }
1304 Ok(line)
1305 }
1306
1307 fn char_pos_to_byte_offset(source: &str, char_pos: usize) -> usize {
1310 source
1311 .char_indices()
1312 .nth(char_pos)
1313 .map_or(source.len(), |(byte_offset, _)| byte_offset)
1314 }
1315
1316 fn advance_chars(source: &str, byte_cursor: usize, n: usize) -> usize {
1318 let clamped = byte_cursor.min(source.len());
1319 source[clamped..]
1320 .char_indices()
1321 .nth(n)
1322 .map_or(source.len(), |(offset, _)| clamped + offset)
1323 }
1324
1325 fn retreat_chars(source: &str, byte_cursor: usize, n: usize) -> usize {
1327 if n == 0 {
1328 return byte_cursor.min(source.len());
1329 }
1330 let clamped = byte_cursor.min(source.len());
1331 source[..clamped]
1332 .char_indices()
1333 .map(|(i, _)| i)
1334 .rev()
1335 .nth(n - 1)
1336 .unwrap_or(0)
1337 }
1338
1339 fn to_position_value(&self, val: &RexxValue) -> RexxResult<i64> {
1341 let d = self.to_number(val)?;
1342 let rounded = d.round(0);
1343 if d != rounded {
1344 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1345 .with_detail(format!("'{val}' is not a whole number")));
1346 }
1347 let s = rounded.to_string();
1348 s.parse::<i64>().map_err(|_| {
1349 RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1350 .with_detail(format!("'{val}' is not a valid position"))
1351 })
1352 }
1353
1354 fn exec_if(
1355 &mut self,
1356 condition: &Expr,
1357 then_clause: &Clause,
1358 else_clause: Option<&Clause>,
1359 ) -> RexxResult<ExecSignal> {
1360 let cond_val = self.eval_expr(condition)?;
1361 let b = to_logical(&cond_val)?;
1362 if b {
1363 self.exec_clause(then_clause)
1364 } else if let Some(else_c) = else_clause {
1365 self.exec_clause(else_c)
1366 } else {
1367 Ok(ExecSignal::Normal)
1368 }
1369 }
1370
1371 fn exec_do(&mut self, block: &DoBlock) -> RexxResult<ExecSignal> {
1372 match &block.kind {
1373 DoKind::Simple => {
1374 let signal = self.exec_body(&block.body)?;
1375 Ok(signal)
1376 }
1377 DoKind::Forever => self.exec_do_forever(block),
1378 DoKind::Count(expr) => self.exec_do_count(expr, block),
1379 DoKind::While(expr) => self.exec_do_while(expr, block),
1380 DoKind::Until(expr) => self.exec_do_until(expr, block),
1381 DoKind::Controlled(ctrl) => self.exec_do_controlled(ctrl, block),
1382 }
1383 }
1384
1385 fn exec_do_forever(&mut self, block: &DoBlock) -> RexxResult<ExecSignal> {
1386 loop {
1387 let signal = self.exec_body(&block.body)?;
1388 match signal {
1389 ExecSignal::Normal => {}
1390 ExecSignal::Leave(ref name) => {
1391 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1392 return Ok(ExecSignal::Normal);
1393 }
1394 return Ok(signal);
1395 }
1396 ExecSignal::Iterate(ref name) => {
1397 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1398 continue;
1399 }
1400 return Ok(signal);
1401 }
1402 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1403 return Ok(signal);
1404 }
1405 }
1406 }
1407 }
1408
1409 fn exec_do_count(&mut self, count_expr: &Expr, block: &DoBlock) -> RexxResult<ExecSignal> {
1410 let count_val = self.eval_expr(count_expr)?;
1411 let count = self.to_integer(&count_val)?;
1412 for _ in 0..count {
1413 let signal = self.exec_body(&block.body)?;
1414 match signal {
1415 ExecSignal::Normal => {}
1416 ExecSignal::Leave(ref name) => {
1417 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1418 return Ok(ExecSignal::Normal);
1419 }
1420 return Ok(signal);
1421 }
1422 ExecSignal::Iterate(ref name) => {
1423 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1424 continue;
1425 }
1426 return Ok(signal);
1427 }
1428 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1429 return Ok(signal);
1430 }
1431 }
1432 }
1433 Ok(ExecSignal::Normal)
1434 }
1435
1436 fn exec_do_while(&mut self, cond_expr: &Expr, block: &DoBlock) -> RexxResult<ExecSignal> {
1437 loop {
1438 let cond_val = self.eval_expr(cond_expr)?;
1439 if !to_logical(&cond_val)? {
1440 break;
1441 }
1442 let signal = self.exec_body(&block.body)?;
1443 match signal {
1444 ExecSignal::Normal => {}
1445 ExecSignal::Leave(ref name) => {
1446 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1447 return Ok(ExecSignal::Normal);
1448 }
1449 return Ok(signal);
1450 }
1451 ExecSignal::Iterate(ref name) => {
1452 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1453 continue;
1454 }
1455 return Ok(signal);
1456 }
1457 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1458 return Ok(signal);
1459 }
1460 }
1461 }
1462 Ok(ExecSignal::Normal)
1463 }
1464
1465 fn exec_do_until(&mut self, cond_expr: &Expr, block: &DoBlock) -> RexxResult<ExecSignal> {
1466 loop {
1467 let signal = self.exec_body(&block.body)?;
1468 match signal {
1469 ExecSignal::Normal => {}
1470 ExecSignal::Leave(ref name) => {
1471 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1472 return Ok(ExecSignal::Normal);
1473 }
1474 return Ok(signal);
1475 }
1476 ExecSignal::Iterate(ref name) => {
1477 if !Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1478 return Ok(signal);
1479 }
1480 }
1482 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1483 return Ok(signal);
1484 }
1485 }
1486 let cond_val = self.eval_expr(cond_expr)?;
1487 if to_logical(&cond_val)? {
1488 break;
1489 }
1490 }
1491 Ok(ExecSignal::Normal)
1492 }
1493
1494 #[allow(clippy::too_many_lines)]
1495 fn exec_do_controlled(
1496 &mut self,
1497 ctrl: &ControlledLoop,
1498 block: &DoBlock,
1499 ) -> RexxResult<ExecSignal> {
1500 let start_val = self.eval_expr(&ctrl.start)?;
1502 let start_num = self.to_number(&start_val)?;
1503
1504 let to_num = if let Some(ref to_expr) = ctrl.to {
1506 let v = self.eval_expr(to_expr)?;
1507 Some(self.to_number(&v)?)
1508 } else {
1509 None
1510 };
1511
1512 let by_num = if let Some(ref by_expr) = ctrl.by {
1514 let v = self.eval_expr(by_expr)?;
1515 self.to_number(&v)?
1516 } else {
1517 BigDecimal::from(1)
1518 };
1519
1520 if by_num.is_zero() {
1522 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1523 .with_detail("BY value in DO loop must not be zero"));
1524 }
1525
1526 let for_count = if let Some(ref for_expr) = ctrl.r#for {
1528 let v = self.eval_expr(for_expr)?;
1529 Some(self.to_integer(&v)?)
1530 } else {
1531 None
1532 };
1533
1534 let mut current = start_num;
1536 let mut iterations: i64 = 0;
1537
1538 loop {
1539 if let Some(ref limit) = to_num {
1542 let positive_step = by_num.sign() == bigdecimal::num_bigint::Sign::Plus;
1543 if positive_step {
1544 if current > *limit {
1545 break;
1546 }
1547 } else if current < *limit {
1548 break;
1549 }
1550 }
1551
1552 if let Some(max) = for_count
1554 && iterations >= max
1555 {
1556 break;
1557 }
1558
1559 self.env.set(
1561 &ctrl.var,
1562 RexxValue::from_decimal(¤t, self.settings.digits, self.settings.form),
1563 );
1564
1565 if let Some(ref while_expr) = ctrl.while_cond {
1567 let v = self.eval_expr(while_expr)?;
1568 if !to_logical(&v)? {
1569 break;
1570 }
1571 }
1572
1573 let signal = self.exec_body(&block.body)?;
1575 match signal {
1576 ExecSignal::Normal => {}
1577 ExecSignal::Leave(ref name) => {
1578 if Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1579 return Ok(ExecSignal::Normal);
1580 }
1581 return Ok(signal);
1582 }
1583 ExecSignal::Iterate(ref name) => {
1584 if !Self::signal_matches(name.as_ref(), block.name.as_ref()) {
1585 return Ok(signal);
1586 }
1587 }
1589 ExecSignal::Exit(_) | ExecSignal::Return(_) | ExecSignal::Signal(_) => {
1590 return Ok(signal);
1591 }
1592 }
1593
1594 if let Some(ref until_expr) = ctrl.until_cond {
1599 let v = self.eval_expr(until_expr)?;
1600 if to_logical(&v)? {
1601 break;
1602 }
1603 }
1604
1605 current += &by_num;
1607 iterations = iterations.saturating_add(1);
1608 }
1609
1610 self.env.set(
1613 &ctrl.var,
1614 RexxValue::from_decimal(¤t, self.settings.digits, self.settings.form),
1615 );
1616
1617 Ok(ExecSignal::Normal)
1618 }
1619
1620 fn exec_select(
1621 &mut self,
1622 when_clauses: &[(Expr, Vec<Clause>)],
1623 otherwise: Option<&Vec<Clause>>,
1624 ) -> RexxResult<ExecSignal> {
1625 for (condition, body) in when_clauses {
1626 let val = self.eval_expr(condition)?;
1627 if to_logical(&val)? {
1628 return self.exec_body(body);
1629 }
1630 }
1631 if let Some(body) = otherwise {
1632 return self.exec_body(body);
1633 }
1634 Err(RexxDiagnostic::new(RexxError::ExpectedWhenOtherwise)
1635 .with_detail("no WHEN matched and no OTHERWISE in SELECT"))
1636 }
1637
1638 fn signal_matches(signal_name: Option<&String>, loop_name: Option<&String>) -> bool {
1642 match signal_name {
1643 None => true,
1644 Some(name) => loop_name.is_some_and(|ln| ln == name),
1645 }
1646 }
1647
1648 fn to_integer(&self, val: &RexxValue) -> RexxResult<i64> {
1651 let d = self.to_number(val)?;
1652 let rounded = d.round(0);
1653 if d != rounded {
1654 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1655 .with_detail("loop count must be a whole number"));
1656 }
1657 let s = rounded.to_string();
1658 let n = s.parse::<i64>().map_err(|_| {
1659 RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1660 .with_detail(format!("'{rounded}' is too large for a loop count"))
1661 })?;
1662 if n < 0 {
1663 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1664 .with_detail(format!("loop count must not be negative (got {n})")));
1665 }
1666 Ok(n)
1667 }
1668
1669 fn resolve_tail(&self, tail: &[TailElement]) -> String {
1670 tail.iter()
1671 .map(|elem| match elem {
1672 TailElement::Const(c) => c.clone(),
1673 TailElement::Var(v) => self.env.get(v).into_string(),
1674 })
1675 .collect::<Vec<_>>()
1676 .join(".")
1677 }
1678
1679 #[allow(clippy::too_many_lines)]
1680 fn eval_expr(&mut self, expr: &Expr) -> RexxResult<RexxValue> {
1681 match expr {
1682 Expr::StringLit(s) => {
1683 let val = RexxValue::new(s.clone());
1684 self.trace_intermediates("L", val.as_str());
1685 Ok(val)
1686 }
1687 Expr::Number(n) => {
1688 let val = RexxValue::new(n.clone());
1689 self.trace_intermediates("L", val.as_str());
1690 Ok(val)
1691 }
1692 Expr::Symbol(name) => {
1693 if !self.env.is_set(name)
1694 && let Some(label) = self.traps.get(&Condition::NoValue).cloned()
1695 {
1696 self.env.set_condition_info(crate::env::ConditionInfoData {
1698 condition: "NOVALUE".to_string(),
1699 description: name.clone(),
1700 instruction: "SIGNAL".to_string(),
1701 status: "ON".to_string(),
1702 });
1703 self.traps.remove(&Condition::NoValue);
1705 self.pending_signal = Some(label);
1706 }
1707 let val = self.env.get(name);
1708 self.trace_intermediates("V", val.as_str());
1709 Ok(val)
1710 }
1711 Expr::Compound { stem, tail } => {
1712 let resolved = self.resolve_tail(tail);
1713 if !self.env.is_compound_set(stem, &resolved)
1714 && let Some(label) = self.traps.get(&Condition::NoValue).cloned()
1715 {
1716 let compound_name = format!("{}.{}", stem.to_uppercase(), resolved);
1717 self.env.set_condition_info(crate::env::ConditionInfoData {
1718 condition: "NOVALUE".to_string(),
1719 description: compound_name,
1720 instruction: "SIGNAL".to_string(),
1721 status: "ON".to_string(),
1722 });
1723 self.traps.remove(&Condition::NoValue);
1724 self.pending_signal = Some(label);
1725 }
1726 let val = self.env.get_compound(stem, &resolved);
1727 self.trace_intermediates("C", val.as_str());
1728 Ok(val)
1729 }
1730 Expr::Paren(inner) => self.eval_expr(inner),
1731 Expr::UnaryOp { op, operand } => {
1732 let val = self.eval_expr(operand)?;
1733 if self.pending_signal.is_some() {
1734 return Ok(val);
1735 }
1736 let result = self.eval_unary(*op, &val)?;
1737 self.trace_intermediates("P", result.as_str());
1738 Ok(result)
1739 }
1740 Expr::BinOp { left, op, right } => {
1741 let lval = self.eval_expr(left)?;
1742 if self.pending_signal.is_some() {
1743 return Ok(lval);
1744 }
1745 let rval = self.eval_expr(right)?;
1746 if self.pending_signal.is_some() {
1747 return Ok(rval);
1748 }
1749 let result = self.eval_binop(*op, &lval, &rval)?;
1750 self.trace_intermediates("O", result.as_str());
1751 Ok(result)
1752 }
1753 Expr::FunctionCall { name, args } => {
1754 let mut evaluated_args = Vec::with_capacity(args.len());
1755 for arg_expr in args {
1756 evaluated_args.push(self.eval_expr(arg_expr)?);
1757 if self.pending_exit.is_pending() || self.pending_signal.is_some() {
1758 return Ok(RexxValue::new(""));
1759 }
1760 }
1761 if name.eq_ignore_ascii_case("TRACE") {
1763 if evaluated_args.len() == 1 {
1764 let old = self.apply_trace_setting(evaluated_args[0].as_str())?;
1765 return Ok(RexxValue::new(old));
1766 } else if !evaluated_args.is_empty() {
1767 return Err(RexxDiagnostic::new(RexxError::IncorrectCall)
1768 .with_detail("TRACE expects 0 or 1 arguments"));
1769 }
1770 return Ok(RexxValue::new(self.trace_setting.to_string()));
1771 }
1772
1773 if self.labels.contains_key(name.as_str()) {
1775 let signal = self.call_routine(name, evaluated_args)?;
1776 match signal {
1777 ExecSignal::Return(Some(val)) => {
1778 self.trace_intermediates("F", val.as_str());
1779 Ok(val)
1780 }
1781 ExecSignal::Return(None) | ExecSignal::Normal => {
1782 Err(RexxDiagnostic::new(RexxError::NoReturnData)
1783 .with_detail(format!("function '{name}' did not return data")))
1784 }
1785 ExecSignal::Exit(val) => {
1786 self.pending_exit = PendingExit::WithValue(val);
1787 Ok(RexxValue::new(""))
1788 }
1789 ExecSignal::Signal(_) => {
1790 if let ExecSignal::Signal(label) = signal {
1792 self.pending_signal = Some(label);
1793 }
1794 Ok(RexxValue::new(""))
1795 }
1796 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(RexxValue::new("")),
1797 }
1798 } else if let Some(result) = crate::builtins::call_builtin(
1799 name,
1800 &evaluated_args,
1801 &self.settings,
1802 self.env,
1803 self.queue.len(),
1804 ) {
1805 let val = result?;
1806 self.trace_intermediates("F", val.as_str());
1807 Ok(val)
1808 } else {
1809 match self.try_call_external(name, evaluated_args)? {
1811 Some(signal) => match signal {
1812 ExecSignal::Return(Some(val)) => {
1813 self.trace_intermediates("F", val.as_str());
1814 Ok(val)
1815 }
1816 ExecSignal::Return(None) | ExecSignal::Normal => {
1817 Err(RexxDiagnostic::new(RexxError::NoReturnData)
1818 .with_detail(format!("function '{name}' did not return data")))
1819 }
1820 ExecSignal::Exit(val) => {
1821 self.pending_exit = PendingExit::WithValue(val);
1822 Ok(RexxValue::new(""))
1823 }
1824 ExecSignal::Signal(_) => {
1825 if let ExecSignal::Signal(label) = signal {
1826 self.pending_signal = Some(label);
1827 }
1828 Ok(RexxValue::new(""))
1829 }
1830 ExecSignal::Leave(_) | ExecSignal::Iterate(_) => Ok(RexxValue::new("")),
1831 },
1832 None => Err(RexxDiagnostic::new(RexxError::RoutineNotFound)
1833 .with_detail(format!("routine '{name}' not found"))),
1834 }
1835 }
1836 }
1837 }
1838 }
1839
1840 fn eval_unary(&self, op: UnaryOp, val: &RexxValue) -> RexxResult<RexxValue> {
1841 match op {
1842 UnaryOp::Plus => {
1843 let d = val.to_decimal().ok_or_else(|| {
1845 RexxDiagnostic::new(RexxError::BadArithmetic)
1846 .with_detail(format!("'{}' is not a number", val.as_str()))
1847 })?;
1848 Ok(RexxValue::from_decimal(
1849 &d,
1850 self.settings.digits,
1851 self.settings.form,
1852 ))
1853 }
1854 UnaryOp::Minus => {
1855 let d = val.to_decimal().ok_or_else(|| {
1856 RexxDiagnostic::new(RexxError::BadArithmetic)
1857 .with_detail(format!("'{}' is not a number", val.as_str()))
1858 })?;
1859 let neg = -d;
1860 Ok(RexxValue::from_decimal(
1861 &neg,
1862 self.settings.digits,
1863 self.settings.form,
1864 ))
1865 }
1866 UnaryOp::Not => {
1867 let s = val.as_str().trim();
1868 match s {
1869 "0" => Ok(RexxValue::new("1")),
1870 "1" => Ok(RexxValue::new("0")),
1871 _ => Err(RexxDiagnostic::new(RexxError::InvalidLogicalValue)
1872 .with_detail(format!("'{}' is not 0 or 1", val.as_str()))),
1873 }
1874 }
1875 }
1876 }
1877
1878 #[allow(clippy::too_many_lines)]
1879 fn eval_binop(&self, op: BinOp, left: &RexxValue, right: &RexxValue) -> RexxResult<RexxValue> {
1880 match op {
1881 BinOp::Add => self.arithmetic(left, right, |a, b| a + b),
1883 BinOp::Sub => self.arithmetic(left, right, |a, b| a - b),
1884 BinOp::Mul => self.arithmetic(left, right, |a, b| a * b),
1885 BinOp::Div => {
1886 let b = right.to_decimal().ok_or_else(|| {
1887 RexxDiagnostic::new(RexxError::BadArithmetic)
1888 .with_detail(format!("'{}' is not a number", right.as_str()))
1889 })?;
1890 if b.is_zero() {
1891 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1892 .with_detail("division by zero"));
1893 }
1894 let a = left.to_decimal().ok_or_else(|| {
1895 RexxDiagnostic::new(RexxError::BadArithmetic)
1896 .with_detail(format!("'{}' is not a number", left.as_str()))
1897 })?;
1898 let result = a / b;
1899 Ok(RexxValue::from_decimal(
1900 &result,
1901 self.settings.digits,
1902 self.settings.form,
1903 ))
1904 }
1905 BinOp::IntDiv => {
1906 let a = self.to_number(left)?;
1907 let b = self.to_number(right)?;
1908 if b.is_zero() {
1909 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1910 .with_detail("division by zero"));
1911 }
1912 let result = trunc_div(&a, &b);
1914 Ok(RexxValue::from_decimal(
1915 &result,
1916 self.settings.digits,
1917 self.settings.form,
1918 ))
1919 }
1920 BinOp::Remainder => {
1921 let a = self.to_number(left)?;
1922 let b = self.to_number(right)?;
1923 if b.is_zero() {
1924 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1925 .with_detail("division by zero"));
1926 }
1927 let int_div = trunc_div(&a, &b);
1929 let result = &a - &int_div * &b;
1930 Ok(RexxValue::from_decimal(
1931 &result,
1932 self.settings.digits,
1933 self.settings.form,
1934 ))
1935 }
1936 BinOp::Power => {
1937 let base = self.to_number(left)?;
1938 let exp_val = self.to_number(right)?;
1939 let exp_rounded = exp_val.round(0);
1941 if exp_val != exp_rounded {
1942 return Err(RexxDiagnostic::new(RexxError::InvalidWholeNumber)
1943 .with_detail("exponent must be a whole number"));
1944 }
1945 let exp_i64: i64 = exp_rounded.to_string().parse().map_err(|_| {
1946 RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1947 .with_detail("exponent too large")
1948 })?;
1949 if exp_i64.abs() > 1_000_000 {
1952 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1953 .with_detail("exponent exceeds limits"));
1954 }
1955 if base.is_zero() && exp_i64 < 0 {
1956 return Err(RexxDiagnostic::new(RexxError::ArithmeticOverflow)
1957 .with_detail("zero raised to a negative power"));
1958 }
1959 let result = pow_bigdecimal(&base, exp_i64);
1960 Ok(RexxValue::from_decimal(
1961 &result,
1962 self.settings.digits,
1963 self.settings.form,
1964 ))
1965 }
1966
1967 BinOp::Concat => {
1969 let s = format!("{}{}", left.as_str(), right.as_str());
1970 Ok(RexxValue::new(s))
1971 }
1972 BinOp::ConcatBlank => {
1973 let s = format!("{} {}", left.as_str(), right.as_str());
1974 Ok(RexxValue::new(s))
1975 }
1976
1977 BinOp::Eq => Ok(bool_val(
1979 normal_compare(left, right) == std::cmp::Ordering::Equal,
1980 )),
1981 BinOp::NotEq => Ok(bool_val(
1982 normal_compare(left, right) != std::cmp::Ordering::Equal,
1983 )),
1984 BinOp::Gt => Ok(bool_val(
1985 normal_compare(left, right) == std::cmp::Ordering::Greater,
1986 )),
1987 BinOp::Lt => Ok(bool_val(
1988 normal_compare(left, right) == std::cmp::Ordering::Less,
1989 )),
1990 BinOp::GtEq => Ok(bool_val(
1991 normal_compare(left, right) != std::cmp::Ordering::Less,
1992 )),
1993 BinOp::LtEq => Ok(bool_val(
1994 normal_compare(left, right) != std::cmp::Ordering::Greater,
1995 )),
1996
1997 BinOp::StrictEq => Ok(bool_val(left.as_str() == right.as_str())),
1999 BinOp::StrictNotEq => Ok(bool_val(left.as_str() != right.as_str())),
2000 BinOp::StrictGt => Ok(bool_val(left.as_str() > right.as_str())),
2001 BinOp::StrictLt => Ok(bool_val(left.as_str() < right.as_str())),
2002 BinOp::StrictGtEq => Ok(bool_val(left.as_str() >= right.as_str())),
2003 BinOp::StrictLtEq => Ok(bool_val(left.as_str() <= right.as_str())),
2004
2005 BinOp::And => {
2007 let l = to_logical(left)?;
2008 let r = to_logical(right)?;
2009 Ok(bool_val(l && r))
2010 }
2011 BinOp::Or => {
2012 let l = to_logical(left)?;
2013 let r = to_logical(right)?;
2014 Ok(bool_val(l || r))
2015 }
2016 BinOp::Xor => {
2017 let l = to_logical(left)?;
2018 let r = to_logical(right)?;
2019 Ok(bool_val(l ^ r))
2020 }
2021 }
2022 }
2023
2024 fn arithmetic(
2025 &self,
2026 left: &RexxValue,
2027 right: &RexxValue,
2028 f: impl FnOnce(BigDecimal, BigDecimal) -> BigDecimal,
2029 ) -> RexxResult<RexxValue> {
2030 let a = self.to_number(left)?;
2031 let b = self.to_number(right)?;
2032 let result = f(a, b);
2033 Ok(RexxValue::from_decimal(
2034 &result,
2035 self.settings.digits,
2036 self.settings.form,
2037 ))
2038 }
2039
2040 #[allow(clippy::unused_self)]
2041 fn to_number(&self, val: &RexxValue) -> RexxResult<BigDecimal> {
2042 val.to_decimal().ok_or_else(|| {
2043 RexxDiagnostic::new(RexxError::BadArithmetic)
2044 .with_detail(format!("'{}' is not a number", val.as_str()))
2045 })
2046 }
2047}
2048
2049fn to_logical(val: &RexxValue) -> RexxResult<bool> {
2051 match val.as_str().trim() {
2052 "0" => Ok(false),
2053 "1" => Ok(true),
2054 _ => Err(RexxDiagnostic::new(RexxError::InvalidLogicalValue)
2055 .with_detail(format!("'{}' is not 0 or 1", val.as_str()))),
2056 }
2057}
2058
2059fn bool_val(b: bool) -> RexxValue {
2061 RexxValue::new(if b { "1" } else { "0" })
2062}
2063
2064fn normal_compare(left: &RexxValue, right: &RexxValue) -> std::cmp::Ordering {
2068 let ls = left.as_str().trim();
2069 let rs = right.as_str().trim();
2070
2071 if let (Some(ld), Some(rd)) = (BigDecimal::from_str(ls).ok(), BigDecimal::from_str(rs).ok()) {
2073 return ld.cmp(&rd);
2074 }
2075
2076 let max_len = ls.len().max(rs.len());
2078 let lp: String = format!("{ls:<max_len$}");
2079 let rp: String = format!("{rs:<max_len$}");
2080 lp.cmp(&rp)
2081}
2082
2083fn trunc_div(a: &BigDecimal, b: &BigDecimal) -> BigDecimal {
2085 let quotient = a / b;
2086 quotient.with_scale_round(0, bigdecimal::RoundingMode::Down)
2090}
2091
2092fn pow_bigdecimal(base: &BigDecimal, exp: i64) -> BigDecimal {
2094 if exp == 0 {
2095 return BigDecimal::from(1);
2096 }
2097 if exp < 0 {
2098 let pos_result = pow_bigdecimal(base, -exp);
2099 return BigDecimal::from(1) / pos_result;
2100 }
2101 let mut result = BigDecimal::from(1);
2102 let mut b = base.clone();
2103 let mut e = exp;
2104 while e > 0 {
2106 if e & 1 == 1 {
2107 result *= &b;
2108 }
2109 b = &b * &b;
2110 e >>= 1;
2111 }
2112 result
2113}
2114
2115#[cfg(test)]
2116mod tests {
2117 use super::*;
2118 use crate::lexer::Lexer;
2119 use crate::parser::Parser;
2120
2121 fn eval_expr(src: &str) -> RexxValue {
2122 let mut env = Environment::new();
2123 let mut lexer = Lexer::new(src);
2124 let tokens = lexer.tokenize().unwrap();
2125 let mut parser = Parser::new(tokens);
2126 let program = parser.parse().unwrap();
2127 let mut eval = Evaluator::new(&mut env, &program);
2128 match &program.clauses[0].kind {
2130 ClauseKind::Command(expr) => eval.eval_expr(expr).unwrap(),
2131 _ => panic!("expected command clause"),
2132 }
2133 }
2134
2135 #[test]
2136 fn eval_addition() {
2137 let val = eval_expr("2 + 3");
2138 assert_eq!(val.as_str(), "5");
2139 }
2140
2141 #[test]
2142 fn eval_subtraction() {
2143 let val = eval_expr("10 - 4");
2144 assert_eq!(val.as_str(), "6");
2145 }
2146
2147 #[test]
2148 fn eval_multiplication() {
2149 let val = eval_expr("3 * 7");
2150 assert_eq!(val.as_str(), "21");
2151 }
2152
2153 #[test]
2154 fn eval_division() {
2155 let val = eval_expr("10 / 4");
2156 assert_eq!(val.as_str(), "2.5");
2157 }
2158
2159 #[test]
2160 fn eval_precedence() {
2161 let val = eval_expr("2 + 3 * 4");
2162 assert_eq!(val.as_str(), "14");
2163 }
2164
2165 #[test]
2166 fn eval_division_by_zero() {
2167 let mut env = Environment::new();
2168 let mut lexer = Lexer::new("1 / 0");
2169 let tokens = lexer.tokenize().unwrap();
2170 let mut parser = Parser::new(tokens);
2171 let program = parser.parse().unwrap();
2172 let mut eval = Evaluator::new(&mut env, &program);
2173 match &program.clauses[0].kind {
2174 ClauseKind::Command(expr) => {
2175 let result = eval.eval_expr(expr);
2176 assert!(result.is_err());
2177 assert_eq!(result.unwrap_err().error, RexxError::ArithmeticOverflow);
2178 }
2179 _ => panic!("expected command clause"),
2180 }
2181 }
2182
2183 #[test]
2184 fn eval_power() {
2185 let val = eval_expr("2 ** 10");
2186 assert_eq!(val.as_str(), "1024");
2187 }
2188
2189 #[test]
2190 fn eval_string_concat_blank() {
2191 let val = eval_expr("'hello' 'world'");
2192 assert_eq!(val.as_str(), "hello world");
2193 }
2194
2195 #[test]
2196 fn eval_string_concat_abuttal() {
2197 let val = eval_expr("'hello'||'world'");
2198 assert_eq!(val.as_str(), "helloworld");
2199 }
2200
2201 #[test]
2202 fn eval_comparison_numeric() {
2203 let val = eval_expr("3 > 2");
2204 assert_eq!(val.as_str(), "1");
2205 }
2206
2207 #[test]
2208 fn eval_comparison_equal() {
2209 let val = eval_expr("5 = 5");
2210 assert_eq!(val.as_str(), "1");
2211 }
2212
2213 #[test]
2214 fn eval_comparison_string() {
2215 let val = eval_expr("'abc' = 'abc'");
2216 assert_eq!(val.as_str(), "1");
2217 }
2218
2219 #[test]
2220 fn eval_strict_comparison() {
2221 let val = eval_expr("' abc' == 'abc'");
2222 assert_eq!(val.as_str(), "0");
2223 }
2224
2225 #[test]
2226 fn eval_logical_and() {
2227 let val = eval_expr("1 & 1");
2228 assert_eq!(val.as_str(), "1");
2229 let val = eval_expr("1 & 0");
2230 assert_eq!(val.as_str(), "0");
2231 }
2232
2233 #[test]
2234 fn eval_logical_or() {
2235 let val = eval_expr("0 | 1");
2236 assert_eq!(val.as_str(), "1");
2237 }
2238
2239 #[test]
2240 fn eval_logical_not() {
2241 let val = eval_expr("\\0");
2242 assert_eq!(val.as_str(), "1");
2243 }
2244
2245 #[test]
2246 fn eval_variable_assignment_and_lookup() {
2247 let mut env = Environment::new();
2248 let mut lexer = Lexer::new("x = 42; x + 1");
2249 let tokens = lexer.tokenize().unwrap();
2250 let mut parser = Parser::new(tokens);
2251 let program = parser.parse().unwrap();
2252 let mut eval = Evaluator::new(&mut env, &program);
2253 let signal = eval.exec().unwrap();
2254 assert!(matches!(signal, ExecSignal::Normal));
2255 assert_eq!(env.get("X").as_str(), "42");
2257 }
2258
2259 #[test]
2260 fn eval_unset_variable_returns_name() {
2261 let val = eval_expr("foo");
2262 assert_eq!(val.as_str(), "FOO");
2263 }
2264
2265 #[test]
2266 fn eval_say_runs() {
2267 let mut env = Environment::new();
2269 let mut lexer = Lexer::new("say 2 + 3");
2270 let tokens = lexer.tokenize().unwrap();
2271 let mut parser = Parser::new(tokens);
2272 let program = parser.parse().unwrap();
2273 let mut eval = Evaluator::new(&mut env, &program);
2274 let signal = eval.exec().unwrap();
2275 assert!(matches!(signal, ExecSignal::Normal));
2276 }
2277
2278 #[test]
2279 fn eval_negative_power() {
2280 let val = eval_expr("2 ** -1");
2281 assert_eq!(val.as_str(), "0.5");
2282 }
2283
2284 #[test]
2285 fn eval_unary_minus() {
2286 let val = eval_expr("-5 + 3");
2287 assert_eq!(val.as_str(), "-2");
2288 }
2289
2290 #[test]
2291 fn eval_remainder() {
2292 let val = eval_expr("17 // 5");
2293 assert_eq!(val.as_str(), "2");
2294 }
2295
2296 #[test]
2297 fn eval_integer_division() {
2298 let val = eval_expr("17 % 5");
2299 assert_eq!(val.as_str(), "3");
2300 }
2301}