1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
use std::rc::Rc;

use crate::{lang::*, InternalString};
use crate::{RantFunctionHandle, RantList, RantMap, RantValue};

use super::{RuntimeResult, VM, resolver::Weights};

/// Actions that can be queued on a stack frame that are performed before the frame runs.
///
/// ## "Call" or "Invoke"?
/// In the context of Rant runtime intents, "calling" and "invoking" have specific meanings:
/// * "Invoke" means that argument expressions potentially need to be evaluated before the call can proceed;
/// * "Call" means that all argument values are already known (either in the intent or on the value stack).
pub enum Intent {
  /// Pops a value off the value stack and print it to the current frame's output.
  PrintLast,
  /// Pops a value off the value stack and returns it from the current function.
  ReturnLast,
  /// Pops a value off the value stack and continues to the next repeater iteration with it.
  ContinueLast,
  /// Pops a value off the value stack and breaks from the current repeater with it.
  BreakLast,
  /// Pops a map off the stack and loads it as a module with the specified name.
  ImportLastAsModule { module_name: String, descope: usize },
  /// Check if the current block is finished and either continue the block or pop the state from the stack
  TickCurrentBlock,
  /// Pops a value off the stack and assign it to an existing variable.
  SetVar { vname: Identifier, access_kind: VarAccessMode, },
  /// Pops a value off the stack and assign it to a new variable.
  DefVar { vname: Identifier, access_kind: VarAccessMode, is_const: bool },
  /// Pops a block from `pending_exprs` and evaluate it. If there are no expressions left, switch intent to `GetValue`.
  BuildDynamicGetter { 
    path: Rc<AccessPath>, 
    dynamic_key_count: usize, 
    pending_exprs: Vec<Rc<Sequence>>, 
    override_print: bool, 
    prefer_function: bool,
    fallback: Option<Rc<Sequence>>,
  },
  /// Pops dynamic key values off the stack and runs a getter. If the getter fails, evaluates the fallback.
  GetValue { 
    path: Rc<AccessPath>, 
    dynamic_key_count: usize, 
    override_print: bool, 
    prefer_function: bool,
    fallback: Option<Rc<Sequence>>, 
  },
  /// Used to evaluate a setter path with dynamic keys.
  /// 
  /// Pops a block from `pending_exprs` and evaluate it. If there are no expressions left, switch intent to `SetValue`.
  BuildDynamicSetter { 
    path: Rc<AccessPath>,
    write_mode: VarWriteMode, 
    expr_count: usize, 
    pending_exprs: Vec<Rc<Sequence>>, 
    val_source: SetterValueSource,
  },
  /// Pops `expr_count` values off the stack and uses them for expression fields in a setter.
  SetValue { 
    path: Rc<AccessPath>,
    write_mode: VarWriteMode,
    expr_count: usize 
  },
  /// Evaluates `arg_exprs` in order, then pops the argument values off the stack, pops a function off the stack, and passes the arguments to the function.
  Invoke { 
    arg_exprs: Rc<Vec<ArgumentExpr>>, 
    arg_eval_count: usize,
    is_temporal: bool, 
  },
  /// Invokes a single function in a piped function call chain.
  InvokePipeStep { 
    /// All steps in the entire piped function call
    steps: Rc<Vec<FunctionCall>>,
    /// The current step being executed
    step_index: usize, 
    /// Current state of the intent.
    state: InvokePipeStepState,
    /// The pipe value from the last step
    pipeval: Option<RantValue>,
    /// Optional assignment pipe
    assignment_pipe: Option<Rc<AssignmentPipeTarget>>,
  },
  /// Evaluates each sequence in `default_arg_exprs` in order and assigns their results to local constants with their associated `Identifier`.
  CreateDefaultArgs { context: RantFunctionHandle, default_arg_exprs: Vec<(Rc<Sequence>, usize)>, eval_index: usize, },
  /// Pops `argc` args off the stack, then pops a function off the stack and calls it with the args.
  Call { argc: usize, override_print: bool },
  /// Calls a sequence without an inner variable scope, then pushes its output to the value stack.
  CallOperand { sequence: Rc<Sequence> },
  /// Calls a function for every variant of a temporal argument set and increments the provided temporal state.
  CallTemporal { func: RantFunctionHandle, arg_exprs: Rc<Vec<ArgumentExpr>>, args: Rc<Vec<RantValue>>, temporal_state: TemporalSpreadState, },
  /// Pops value from stack and adds it to a list. If `index` is out of range, prints the list.
  BuildList { init: Rc<Vec<Rc<Sequence>>>, index: usize, list: RantList },
  /// Pops a value from the stack and adds it to `items`. If `index` is out of range, produces a tuple from `items` and prints it.
  BuildTuple { init: Rc<Vec<Rc<Sequence>>>, index: usize, items: Vec<RantValue> },
  /// Pops a value and optional key from stack and adds them to `map`. If `pair_index` is out of range, prints `map`.
  BuildMap { init: Rc<Vec<(MapKeyExpr, Rc<Sequence>)>>, pair_index: usize, map: RantMap },
  /// Evaluates expressions in `weights` and then runs the block with the computed element weights.
  BuildWeightedBlock { block: Rc<Block>, weights: Weights, pop_next_weight: bool, },
  /// Calls a function that accepts a mutable reference to the current runtime. Optionally interrupts the intent loop to force another tick.
  RuntimeCall { function: Box<dyn FnOnce(&mut VM) -> RuntimeResult<()>>, interrupt: bool },
  /// Drops all unwind states that are no longer within the call stack.
  DropStaleUnwinds,
  /// Pops two values (RHS, LHS), performs addition, and pushes the result.
  Add,
  /// Pops two values (RHS, LHS), performs subtraction, and pushes the result.
  Subtract,
  /// Pops two values (RHS, LHS), performs multiplication, and pushes the result.
  Multiply,
  /// Pops two values (RHS, LHS), performs division, and pushes the result.
  Divide,
  /// Pops two values (RHS, LHS), performs modulo, and pushes the result.
  Modulo,
  /// Pops two values (RHS, LHS), performs exponentiation, and pushes the result.
  Power,
  /// Pops a value, performs logical NOT, and pushes the result.
  LogicNot,
  /// Pops a value, performs negation, and pushes the result.
  Negate,
  /// Pops a value off the value stack and compares its truthiness against `on_truthiness`, and re-pushes the value.
  /// If they match, do nothing and continue.
  /// If they don't match, calls `gen_op_intent` and pushes the returned intent onto the current frame, then evaluates `rhs` using the `CallOperand` intent.
  LogicShortCircuit { on_truthiness: bool, short_circuit_result: LogicShortCircuitHandling, gen_op_intent: Box<dyn FnOnce() -> Intent>, rhs: Rc<Sequence> },
  /// Pops two values, performs logical AND, and pushes the result.
  LogicAnd,
  /// Pops two values, performs logical OR, and pushes the result.
  LogicOr,
  /// Pops two values (RHS, LHS), performs logical XOR, and pushes the result.
  LogicXor,
  /// Pops two values (RHS, LHS), calculates `LHS == RHS`, and pushes the result.
  Equals,
  /// Pops two values (RHS, LHS), calculates `LHS != RHS`, and pushes the result.
  NotEquals,
  /// Pops two values (RHS, LHS), calculates `LHS > RHS`, and pushes the result. 
  Greater,
  /// Pops two values (RHS, LHS), calculates `LHS >= RHS`, and pushes the result.
  GreaterOrEqual,
  /// Pops two values (RHS, LHS), calculates `LHS < RHS`, and pushes the result.
  Less,
  /// Pops two values (RHS, LHS), calculates `LHS <= RHS`, and pushes the result.
  LessOrEqual,
  /// If `index` is nonzero, pops a value off the value stack and checks its truthiness. 
  /// If it's falsy, pushes the next condition (or fallback) to the call stack and re-pushes this intent with `index = index + 1`.
  /// If it's truthy, pushes it to the value stack and exits. 
  CheckCondition { conditions: Rc<Vec<(Rc<Sequence>, Rc<Block>)>>, fallback: Option<Rc<Block>>, index: usize },
}

impl Intent {
  pub(crate) fn name(&self) -> &'static str {
    match self {
      Self::PrintLast => "print",
      Self::TickCurrentBlock => "check_block",
      Self::SetVar { .. } => "set_var",
      Self::DefVar { .. } => "def_var",
      Self::BuildDynamicGetter { .. } => "build_dyn_getter",
      Self::GetValue { .. } => "get_value",
      Self::BuildDynamicSetter { .. } => "build_dyn_setter",
      Self::SetValue { .. } => "set_value",
      Self::Invoke { .. } => "invoke",
      Self::InvokePipeStep { .. } => "invoke_pipe_step",
      Self::Call { .. } => "call",
      Self::CallOperand { .. } => "call_operand",
      Self::CallTemporal { .. } => "call_temporal",
      Self::BuildList { .. } => "build_list",
      Self::BuildTuple { .. } => "build_tuple",
      Self::BuildMap { .. } => "build_map",
      Self::ImportLastAsModule { .. } => "load_module",
      Self::RuntimeCall { .. } => "runtime_call",
      Self::DropStaleUnwinds => "drop_stale_unwinds",
      Self::ReturnLast => "return_last",
      Self::ContinueLast => "continue_last",
      Self::BreakLast => "break_last",
      Self::BuildWeightedBlock { .. } => "build_weighted_block",
      Self::CreateDefaultArgs { .. } => "create_default_args",
      Self::Add => "add",
      Self::Subtract => "subtract",
      Self::Multiply => "multiply",
      Self::Divide => "divide",
      Self::Modulo => "modulo",
      Self::Power => "power",
      Self::Negate => "negate",
      Self::LogicShortCircuit { .. } => "logic_short_circuit",
      Self::LogicNot => "not",
      Self::LogicAnd => "and",
      Self::LogicOr => "or",
      Self::LogicXor => "xor",
      Self::Equals => "equals",
      Self::NotEquals => "not_equals",
      Self::Less => "less",
      Self::LessOrEqual => "less_or_equal",
      Self::Greater => "greater",
      Self::GreaterOrEqual => "greater_or_equal",
      Self::CheckCondition { .. } => "conditional",
    }
  }
}

/// Defines short-circuiting behaviors for logic operators.
#[derive(Debug, Copy, Clone)]
pub enum LogicShortCircuitHandling {
  /// Pass through the LHS untouched.
  Passthrough,
  /// Use the specified boolean value as the LHS.
  OverrideWith(bool),
}

/// States for the `InvokePipeStep` intent.
#[derive(Debug)]
pub enum InvokePipeStepState {
  /// Evaluate step function and leave it on the value stack.
  ///
  /// Transitions to `EvaluatingArgs`.
  EvaluatingFunc,
  /// Evaluate argument expressions, then pop them off the value stack.
  /// Then, before transitioning, pop the function off the value stack and store it.
  ///
  /// Transitions to `PreCall` or `PreTemporalCall`.
  EvaluatingArgs { 
    /// Number of arguments that have already been evaluated.
    num_evaluated: usize 
  },
  /// Temporal step function is ready to iterate.
  ///
  /// Transitions to `PostTemporalCall`.
  PreTemporalCall {
    step_function: RantFunctionHandle,
    temporal_state: TemporalSpreadState,
    args: Vec<RantValue>,
  },
  /// Step function is ready to call.
  ///
  /// Transitions to `PostCall`.
  PreCall { 
    step_function: RantFunctionHandle,
    args: Vec<RantValue>,
  },
  /// Step function has returned and output can be used.
  PostCall,
  /// Temporal step function has iterated and output can be used.
  ///
  /// Might transition to `PreTemporalCall`.
  PostTemporalCall {
    step_function: RantFunctionHandle,
    temporal_state: TemporalSpreadState,
    args: Vec<RantValue>,
  }
}

/// Defines variable write modes for setter intents.
/// Used by function definitions to control conditional definition behavior.
#[derive(Debug, Copy, Clone)]
pub enum VarWriteMode {
  /// Only set existing variables.
  SetOnly,
  /// Defines and sets a variable.
  Define,
  /// Defines and sets a new constant.
  DefineConst,
}

#[derive(Debug)]
pub enum SetterKey<'a> {
  Index(i64),
  Slice(Slice),
  KeyRef(&'a str),
  KeyString(InternalString),
}

/// Describes where a setter gets its RHS value.
#[derive(Debug)]
pub enum SetterValueSource {
  /// Setter RHS is evaluated from an expression.
  FromExpression(Rc<Sequence>),
  /// Setter RHS is a value.
  FromValue(RantValue),
  /// Setter RHS was already consumed and pushed to the value stack.
  FromStack
}