1mod num;
2mod output;
3mod series_buffer;
4mod signature;
5
6pub use num::Num;
7pub use series_buffer::{SeriesBuffer, MAX_LOOKBACK};
8pub use signature::{BuiltinSignature, Param, ParamType};
9
10pub use output::{
12 AlertCondition, AlertConditionOutput, BoxOutput, Color, DefaultPineOutput, FillObject,
13 FillOutput, GlobalContext, GlobalOutput, Indicator, IndicatorOutput, Input, InputOutput,
14 InputValue, Label, LabelOutput, LineObject, LineOutput, LogEntry, LogLevel, LogOutput, PineBox,
15 PineOutput, Plot, PlotOutput, Plotarrow, Plotbar, Plotcandle, Plotchar, Plotshape, Table,
16 TableCell, TableOutput,
17};
18
19use pine_ast::{Argument, BinOp, Expr, Literal, MethodParam, Program, Stmt, TypeField, UnOp};
22use std::cell::RefCell;
23use std::collections::HashMap;
24use std::rc::Rc;
25use thiserror::Error;
26
27pub trait LibraryLoader {
29 fn load_library(&self, path: &str) -> Result<Program, String>;
31}
32
33fn push_history<O: PineOutput>(
39 history: &mut HashMap<String, Vec<Value<O>>>,
40 name: &str,
41 value: Value<O>,
42) {
43 let entries = history.entry(name.to_string()).or_default();
44 entries.push(value);
45 if entries.len() > MAX_LOOKBACK {
46 entries.drain(..entries.len() - MAX_LOOKBACK);
47 }
48}
49
50fn numeric_op<O: PineOutput>(
54 left: &Value<O>,
55 right: &Value<O>,
56 op: impl Fn(Num, Num) -> Option<Num>,
57) -> Result<Value<O>, RuntimeError> {
58 left.to_number()?;
60 right.to_number()?;
61
62 match (left.as_num(), right.as_num()) {
63 (Some(a), Some(b)) => Ok(op(a, b).map_or(Value::Na, Value::from)),
64 _ => Ok(Value::Na),
65 }
66}
67
68#[derive(Error, Debug)]
69pub enum RuntimeError {
70 #[error("Variable '{0}' not found")]
71 UndefinedVariable(String),
72
73 #[error("Type error: {0}")]
74 TypeError(String),
75
76 #[error("Division by zero")]
77 DivisionByZero,
78
79 #[error("Index out of bounds: {0}")]
80 IndexOutOfBounds(usize),
81
82 #[error("Cannot iterate: from={0}, to={1}")]
83 InvalidForLoop(f64, f64),
84
85 #[error("Break statement outside of loop")]
86 BreakOutsideLoop,
87
88 #[error("Continue statement outside of loop")]
89 ContinueOutsideLoop,
90
91 #[error("Library error: {0}")]
92 LibraryError(String),
93
94 #[error("Cannot reassign const variable '{0}'")]
95 ConstReassignment(String),
96}
97
98#[derive(Debug, Clone, PartialEq)]
100enum LoopControl {
101 None,
102 Break,
103 Continue,
104}
105
106#[derive(Clone)]
108struct Variable<O: PineOutput = DefaultPineOutput> {
109 value: Value<O>,
110 is_const: bool,
111 is_var_persistent: bool,
113}
114
115#[derive(Clone, Debug)]
117pub struct Series<O: PineOutput = DefaultPineOutput> {
118 pub id: String,
119 pub current: Box<Value<O>>,
120}
121
122#[derive(Clone)]
124pub enum Value<O: PineOutput> {
125 Int(i64),
126 Number(f64),
127 String(String),
128 Bool(bool),
129 Na, Array(Rc<RefCell<Vec<Value<O>>>>), Series(Series<O>), Object {
133 type_name: String, fields: Rc<RefCell<HashMap<String, Value<O>>>>, call: Option<BuiltinFn<O>>,
136 },
137 Function {
138 params: Vec<pine_ast::FunctionParam>,
139 body: Vec<Stmt>,
140 },
141 BuiltinFunction(Builtin<O>), Expr(Rc<Expr>),
145 Type {
146 name: String,
147 fields: Vec<TypeField>,
148 }, Enum {
150 enum_name: String, field_name: String, title: String, }, Color(Color), Matrix {
156 element_type: String, data: Rc<RefCell<Vec<Vec<Value<O>>>>>, },
159}
160
161impl<O: PineOutput> From<Num> for Value<O> {
162 fn from(n: Num) -> Self {
164 match n {
165 Num::Int(n) => Value::Int(n),
166 Num::Float(n) => Value::Number(n),
167 }
168 }
169}
170
171impl<O: PineOutput> Value<O> {
172 pub fn new_color(r: u8, g: u8, b: u8, t: u8) -> Value<O> {
173 Value::Color(Color::new(r, g, b, t))
174 }
175}
176
177impl<O: PineOutput> std::fmt::Debug for Value<O> {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 match self {
181 Value::Int(n) => write!(f, "Int({:?})", n),
182 Value::Number(n) => write!(f, "Number({:?})", n),
183 Value::String(s) => write!(f, "String({:?})", s),
184 Value::Bool(b) => write!(f, "Bool({:?})", b),
185 Value::Na => write!(f, "Na"),
186 Value::Array(a) => write!(f, "Array({:?})", a),
187 Value::Series(s) => write!(f, "Series({:?})", s),
188 Value::Object {
189 type_name, fields, ..
190 } => write!(f, "Object({}:{:?})", type_name, fields),
191 Value::Function { params, .. } => write!(f, "Function({} params)", params.len()),
192 Value::BuiltinFunction(_) => write!(f, "BuiltinFunction"),
193 Value::Expr(_) => write!(f, "Expr"),
194 Value::Type { name, .. } => write!(f, "Type({})", name),
195 Value::Enum {
196 enum_name,
197 field_name,
198 ..
199 } => write!(f, "Enum({}::{})", enum_name, field_name),
200 Value::Color(color) => write!(
201 f,
202 "Color(rgba({}, {}, {}, {}))",
203 color.r, color.g, color.b, color.t
204 ),
205 Value::Matrix { element_type, data } => {
206 write!(f, "Matrix<{}>({:?})", element_type, data)
207 }
208 }
209 }
210}
211
212impl<O: PineOutput> PartialEq for Value<O> {
213 fn eq(&self, other: &Self) -> bool {
214 match (self, other) {
215 (Value::Int(a), Value::Int(b)) => a == b,
216 (Value::Int(a), Value::Number(b)) | (Value::Number(b), Value::Int(a)) => {
218 (*a as f64 - b).abs() < f64::EPSILON
219 }
220 (Value::Number(a), Value::Number(b)) => (a - b).abs() < f64::EPSILON,
221 (Value::String(a), Value::String(b)) => a == b,
222 (Value::Bool(a), Value::Bool(b)) => a == b,
223 (Value::Na, Value::Na) => true,
224 (Value::Array(a), Value::Array(b)) => Rc::ptr_eq(a, b),
226 (Value::Series(a), Value::Series(b)) => a.id == b.id && *a.current == *b.current,
228 (Value::Object { fields: a, .. }, Value::Object { fields: b, .. }) => Rc::ptr_eq(a, b),
229 (Value::Function { .. }, Value::Function { .. }) => false,
231 (Value::BuiltinFunction(_), Value::BuiltinFunction(_)) => false,
232 (Value::Type { name: a, .. }, Value::Type { name: b, .. }) => a == b,
234 (
236 Value::Enum {
237 enum_name: a_enum,
238 field_name: a_field,
239 ..
240 },
241 Value::Enum {
242 enum_name: b_enum,
243 field_name: b_field,
244 ..
245 },
246 ) => a_enum == b_enum && a_field == b_field,
247 (Value::Color(c1), Value::Color(c2)) => c1 == c2,
249 (Value::Matrix { data: a, .. }, Value::Matrix { data: b, .. }) => Rc::ptr_eq(a, b),
251 _ => false,
252 }
253 }
254}
255
256#[derive(Debug, Clone)]
258pub enum EvaluatedArg<O: PineOutput = DefaultPineOutput> {
259 Positional(Value<O>),
260 Named { name: String, value: Value<O> },
261}
262
263#[derive(Debug, Clone)]
265pub struct FunctionCallArgs<O: PineOutput = DefaultPineOutput> {
266 pub type_args: Vec<String>,
267 pub args: Vec<EvaluatedArg<O>>,
268 pub call_id: u32,
269}
270
271impl<O: PineOutput> FunctionCallArgs<O> {
272 pub fn new(type_args: Vec<String>, args: Vec<EvaluatedArg<O>>) -> Self {
273 Self {
274 type_args,
275 args,
276 call_id: 0,
277 }
278 }
279
280 pub fn without_types(args: Vec<EvaluatedArg<O>>) -> Self {
281 Self {
282 type_args: vec![],
283 args,
284 call_id: 0,
285 }
286 }
287
288 pub fn with_call_id(mut self, call_id: u32) -> Self {
289 self.call_id = call_id;
290 self
291 }
292}
293
294pub type BuiltinFn<O> =
296 Rc<dyn Fn(&mut Interpreter<O>, FunctionCallArgs<O>) -> Result<Value<O>, RuntimeError>>;
297
298#[derive(Clone)]
302pub struct Builtin<O: PineOutput> {
303 pub call: BuiltinFn<O>,
304 pub signature: BuiltinSignature,
305}
306
307impl<O: PineOutput> Builtin<O> {
308 pub fn untyped(call: BuiltinFn<O>) -> Self {
311 Self {
312 call,
313 signature: BuiltinSignature::default(),
314 }
315 }
316}
317
318impl<O: PineOutput> Value<O> {
319 pub fn as_number(&self) -> Result<f64, RuntimeError> {
322 self.to_number().map(|opt| opt.unwrap_or(f64::NAN))
323 }
324
325 pub fn as_bool(&self) -> Result<bool, RuntimeError> {
328 Ok(self.to_bool()?.unwrap_or(false))
329 }
330
331 pub fn as_num(&self) -> Option<Num> {
335 match self {
336 Value::Int(n) => Some(Num::Int(*n)),
337 Value::Number(n) => Some(Num::Float(*n)),
338 Value::Bool(b) => Some(Num::Int(if *b { 1 } else { 0 })),
339 Value::Series(series) => series.current.as_num(),
340 _ => None,
341 }
342 }
343
344 fn as_int(&self) -> Option<i64> {
346 match self.as_num() {
347 Some(Num::Int(n)) => Some(n),
348 _ => None,
349 }
350 }
351
352 pub fn to_number(&self) -> Result<Option<f64>, RuntimeError> {
355 match self {
356 Value::Int(n) => Ok(Some(*n as f64)),
357 Value::Number(n) => Ok(Some(*n)),
358 Value::Bool(b) => Ok(Some(if *b { 1.0 } else { 0.0 })),
359 Value::Series(series) => series.current.to_number(),
360 Value::Na => Ok(None),
361 _ => Err(RuntimeError::TypeError(format!(
362 "Expected number, got {:?}",
363 self
364 ))),
365 }
366 }
367
368 pub fn to_bool(&self) -> Result<Option<bool>, RuntimeError> {
370 match self {
371 Value::Bool(b) => Ok(Some(*b)),
372 Value::Int(n) => Ok(Some(*n != 0)),
373 Value::Number(n) => Ok(Some(*n != 0.0 && !n.is_nan())),
375 Value::Na => Ok(None),
376 _ => Err(RuntimeError::TypeError(format!(
377 "Expected bool, got {:?}",
378 self
379 ))),
380 }
381 }
382
383 pub fn truthy_for_condition(&self) -> Result<bool, RuntimeError> {
386 Ok(self.to_bool()?.unwrap_or(false))
387 }
388
389 pub fn as_string(&self) -> Result<String, RuntimeError> {
390 match self {
391 Value::String(s) => Ok(s.clone()),
392 Value::Int(n) => Ok(n.to_string()),
393 Value::Number(n) => Ok(n.to_string()),
394 Value::Bool(b) => Ok(b.to_string()),
395 Value::Na => Ok("na".to_string()),
396 _ => Err(RuntimeError::TypeError(format!(
397 "Cannot convert {:?} to string",
398 self
399 ))),
400 }
401 }
402
403 pub fn as_array(&self) -> Result<&Rc<RefCell<Vec<Value<O>>>>, RuntimeError> {
404 match self {
405 Value::Array(arr) => Ok(arr),
406 _ => Err(RuntimeError::TypeError(format!(
407 "Expected array, got {:?}",
408 self
409 ))),
410 }
411 }
412
413 pub fn as_color(&self) -> Result<Color, RuntimeError> {
414 match self {
415 Value::Color(color) => Ok(color.clone()),
416 _ => Err(RuntimeError::TypeError(format!(
417 "Expected color, got {:?}",
418 self
419 ))),
420 }
421 }
422}
423
424#[derive(Clone)]
426struct MethodDef {
427 type_name: String, params: Vec<pine_ast::MethodParam>,
429 body: Vec<Stmt>,
430}
431
432pub struct Interpreter<O: PineOutput> {
434 variables: HashMap<String, Variable<O>>,
436 methods: HashMap<String, Vec<MethodDef>>,
438 library_loader: Option<Box<dyn LibraryLoader>>,
440 exports: HashMap<String, Value<O>>,
442 pub output: O,
444 pub user_series_history: HashMap<String, Vec<Value<O>>>,
448 function_local_state: HashMap<u32, HashMap<String, Variable<O>>>,
455 var_decls_initialized: HashMap<(u32, String), u64>,
466 current_call_id: u32,
469 bar_seq: u64,
472 pub broker: Option<Box<dyn pine_broker::Broker>>,
475 pub request_provider: Option<Rc<dyn pine_core::DataProvider>>,
480 pub chart_period: Option<i64>,
481}
482
483fn collect_assigned_names(body: &[Stmt], out: &mut std::collections::HashSet<String>) {
488 for s in body {
489 match s {
490 Stmt::VarDecl { name, .. } => {
491 out.insert(name.clone());
492 }
493 Stmt::Assignment {
494 target: Expr::Variable(n),
495 ..
496 } => {
497 out.insert(n.clone());
498 }
499 Stmt::TupleAssignment { names, .. } => {
500 for n in names {
501 out.insert(n.clone());
502 }
503 }
504 Stmt::If {
505 then_branch,
506 else_if_branches,
507 else_branch,
508 ..
509 } => {
510 collect_assigned_names(then_branch, out);
511 for (_, b) in else_if_branches {
512 collect_assigned_names(b, out);
513 }
514 if let Some(b) = else_branch {
515 collect_assigned_names(b, out);
516 }
517 }
518 Stmt::For { var_name, body, .. } => {
519 out.insert(var_name.clone());
520 collect_assigned_names(body, out);
521 }
522 Stmt::While { body, .. } | Stmt::ForIn { body, .. } => {
523 collect_assigned_names(body, out)
524 }
525 _ => {}
526 }
527 }
528}
529
530fn is_na_operand<O: PineOutput>(v: &Value<O>) -> bool {
534 matches!(v, Value::Na) || matches!(v, Value::Number(n) if n.is_nan())
535}
536
537impl<O: PineOutput> Interpreter<O> {
538 pub fn new() -> Self {
539 Self {
540 variables: HashMap::new(),
541 methods: HashMap::new(),
542 library_loader: None,
543 exports: HashMap::new(),
544 output: O::default(),
545 user_series_history: HashMap::new(),
546 function_local_state: HashMap::new(),
547 var_decls_initialized: HashMap::new(),
548 current_call_id: 0,
549 bar_seq: 0,
550 broker: None,
551 request_provider: None,
552 chart_period: None,
553 }
554 }
555
556 pub fn bar_seq(&self) -> u64 {
559 self.bar_seq
560 }
561
562 pub fn set_library_loader(&mut self, library_loader: Box<dyn LibraryLoader>) {
564 self.library_loader = Some(library_loader);
565 }
566
567 pub fn snapshot(&self) -> HashMap<String, Value<O>> {
571 self.variables
572 .iter()
573 .map(|(name, var)| (name.clone(), var.value.clone()))
574 .collect()
575 }
576
577 pub fn exports(&self) -> &HashMap<String, Value<O>> {
579 &self.exports
580 }
581
582 pub fn execute(&mut self, program: &Program) -> Result<O, RuntimeError> {
584 self.output.clear();
586 self.bar_seq += 1;
588
589 for stmt in &program.statements {
590 self.execute_stmt(stmt)?;
591 }
592
593 Ok(self.output.clone())
595 }
596
597 pub fn get_variable(&self, name: &str) -> Option<&Value<O>> {
599 self.variables.get(name).map(|var| &var.value)
600 }
601
602 pub fn set_variable(&mut self, name: &str, value: Value<O>) {
604 self.variables.insert(
605 name.to_string(),
606 Variable {
607 value,
608 is_const: false,
609 is_var_persistent: false,
610 },
611 );
612 }
613
614 pub fn advance_series(&mut self, name: &str, value: Value<O>) {
621 if let Some(existing) = self.variables.get(name) {
622 let previous = match &existing.value {
625 Value::Series(series) => (*series.current).clone(),
626 other => other.clone(),
627 };
628 push_history(&mut self.user_series_history, name, previous);
629 }
630 self.set_variable(name, value);
631 }
632
633 pub fn set_object_field(&mut self, object: &str, field: &str, value: Value<O>) {
637 if let Some(Variable {
638 value: Value::Object { fields, .. },
639 ..
640 }) = self.variables.get(object)
641 {
642 fields.borrow_mut().insert(field.to_string(), value);
643 }
644 }
645
646 pub fn set_const_variable(&mut self, name: &str, value: Value<O>) {
648 self.variables.insert(
649 name.to_string(),
650 Variable {
651 value,
652 is_const: true,
653 is_var_persistent: false,
654 },
655 );
656 }
657
658 fn evaluate_arguments(
662 &mut self,
663 args: &[Argument],
664 signature: Option<&BuiltinSignature>,
665 ) -> Result<Vec<EvaluatedArg<O>>, RuntimeError> {
666 let mut evaluated_args = Vec::new();
667 let mut seen_named = false;
668 let mut positional_index = 0;
669
670 for arg in args {
671 match arg {
672 Argument::Positional(expr) => {
673 if seen_named {
674 return Err(RuntimeError::TypeError(
675 "Positional arguments cannot follow named arguments".to_string(),
676 ));
677 }
678 let lazy = signature.is_some_and(|s| s.positional_is_lazy(positional_index));
679 let value = self.eval_or_capture(expr, lazy)?;
680 evaluated_args.push(EvaluatedArg::Positional(value));
681 positional_index += 1;
682 }
683 Argument::Named { name, value: expr } => {
684 seen_named = true;
685 let lazy = signature.is_some_and(|s| s.named_is_lazy(name));
686 let value = self.eval_or_capture(expr, lazy)?;
687 evaluated_args.push(EvaluatedArg::Named {
688 name: name.clone(),
689 value,
690 });
691 }
692 }
693 }
694
695 Ok(evaluated_args)
696 }
697
698 fn eval_or_capture(&mut self, expr: &Expr, lazy: bool) -> Result<Value<O>, RuntimeError> {
701 if lazy {
702 Ok(Value::Expr(Rc::new(expr.clone())))
703 } else {
704 self.eval_expr(expr)
705 }
706 }
707
708 fn execute_stmt(&mut self, stmt: &Stmt) -> Result<Option<Value<O>>, RuntimeError> {
709 match stmt {
710 Stmt::VarDecl {
711 name,
712 type_qualifier,
713 type_annotation: _,
714 initializer,
715 var_kind,
718 } => {
719 let is_var_persistent = var_kind.is_persistent();
720 if is_var_persistent {
727 let init_key = (self.current_call_id, name.clone());
728 if self.var_decls_initialized.contains_key(&init_key) {
729 return Ok(None);
730 }
731 self.var_decls_initialized.insert(init_key, self.bar_seq);
732 }
733 if !is_var_persistent {
737 if let Some(existing) = self.variables.get(name) {
738 push_history(&mut self.user_series_history, name, existing.value.clone());
739 }
740 }
741 let value = if let Some(init_expr) = initializer {
742 self.eval_expr(init_expr)?
743 } else {
744 Value::Na
745 };
746 let is_const = matches!(type_qualifier, Some(pine_ast::TypeQualifier::Const));
747 self.variables.insert(
748 name.clone(),
749 Variable {
750 value,
751 is_const,
752 is_var_persistent,
753 },
754 );
755 Ok(None)
756 }
757
758 Stmt::Assignment { target, value } => {
759 if let Expr::Variable(name) = target {
768 if let Some(var) = self.variables.get(name) {
769 let born_this_bar = self
770 .var_decls_initialized
771 .get(&(self.current_call_id, name.clone()))
772 == Some(&self.bar_seq);
773 if var.is_var_persistent && !born_this_bar {
774 push_history(&mut self.user_series_history, name, var.value.clone());
775 }
776 }
777 }
778
779 let val = self.eval_expr(value)?;
780 match target {
781 Expr::Variable(name) => {
782 let (is_const, is_var_persistent) =
784 if let Some(var) = self.variables.get(name) {
785 if var.is_const {
786 return Err(RuntimeError::ConstReassignment(name.clone()));
787 }
788 if !var.is_var_persistent {
789 push_history(
791 &mut self.user_series_history,
792 name,
793 var.value.clone(),
794 );
795 }
796 (false, var.is_var_persistent)
798 } else {
799 (false, false)
800 };
801
802 self.variables.insert(
803 name.clone(),
804 Variable {
805 value: val,
806 is_const,
807 is_var_persistent,
808 },
809 );
810 Ok(None)
811 }
812 Expr::MemberAccess { object, member } => {
813 if let Expr::Variable(var_name) = object.as_ref() {
815 if let Some(var) = self.variables.get(var_name) {
816 if var.is_const {
817 return Err(RuntimeError::ConstReassignment(format!(
818 "{}.{}",
819 var_name, member
820 )));
821 }
822 }
823 }
824
825 let obj_value = self.eval_expr(object)?;
827
828 if let Value::Object { fields, .. } = obj_value {
829 let mut obj = fields.borrow_mut();
830 obj.insert(member.clone(), val);
831 Ok(None)
832 } else {
833 Err(RuntimeError::TypeError(
834 "Cannot assign to member of non-object value".to_string(),
835 ))
836 }
837 }
838 _ => Err(RuntimeError::TypeError(
839 "Invalid assignment target".to_string(),
840 )),
841 }
842 }
843
844 Stmt::TupleAssignment { names, value } => {
845 let val = self.eval_expr(value)?;
846 if let Value::Array(arr_ref) = val {
847 let arr = arr_ref.borrow();
848 for (i, name) in names.iter().enumerate() {
849 if let Some(var) = self.variables.get(name) {
851 push_history(&mut self.user_series_history, name, var.value.clone());
852 }
853 let element_val = arr.get(i).cloned().unwrap_or(Value::Na);
854 self.variables.insert(
855 name.clone(),
856 Variable {
857 value: element_val,
858 is_const: false,
859 is_var_persistent: false,
860 },
861 );
862 }
863 Ok(None)
864 } else {
865 Err(RuntimeError::TypeError(
866 "Expected array for tuple destructuring".to_string(),
867 ))
868 }
869 }
870
871 Stmt::Expression(expr) => {
872 self.eval_expr(expr)?;
873 Ok(None)
874 }
875
876 Stmt::If {
877 condition,
878 then_branch,
879 else_if_branches,
880 else_branch,
881 } => {
882 let cond_value = self.eval_expr(condition)?;
883 if cond_value.truthy_for_condition()? {
884 for stmt in then_branch {
885 self.execute_stmt(stmt)?;
886 }
887 } else {
888 let mut executed = false;
890 for (else_if_cond, else_if_body) in else_if_branches {
891 let else_if_value = self.eval_expr(else_if_cond)?;
892 if else_if_value.truthy_for_condition()? {
893 for stmt in else_if_body {
894 self.execute_stmt(stmt)?;
895 }
896 executed = true;
897 break;
898 }
899 }
900
901 if !executed {
903 if let Some(else_stmts) = else_branch {
904 for stmt in else_stmts {
905 self.execute_stmt(stmt)?;
906 }
907 }
908 }
909 }
910 Ok(None)
911 }
912
913 Stmt::For {
914 var_name,
915 from,
916 to,
917 body,
918 } => {
919 let from_val = self.eval_expr(from)?.as_number()?;
920 let to_val = self.eval_expr(to)?.as_number()?;
921
922 if from_val > to_val {
923 return Err(RuntimeError::InvalidForLoop(from_val, to_val));
924 }
925
926 let mut i = from_val as i64;
927 let end = to_val as i64;
928
929 while i <= end {
930 self.variables.insert(
931 var_name.clone(),
932 Variable {
933 value: Value::Int(i),
934 is_const: false,
935 is_var_persistent: false,
936 },
937 );
938
939 let control = self.execute_loop_body(body)?;
940 if control == LoopControl::Break {
941 break;
942 }
943
944 i += 1;
945 }
946
947 Ok(None)
948 }
949
950 Stmt::ForIn {
951 index_var,
952 item_var,
953 collection,
954 body,
955 } => {
956 let collection_value = self.eval_expr(collection)?;
957 let arr = collection_value.as_array()?;
958 let arr_borrowed = arr.borrow();
959
960 for (index, item) in arr_borrowed.iter().enumerate() {
961 if let Some(idx_var) = index_var {
963 self.variables.insert(
964 idx_var.clone(),
965 Variable {
966 value: Value::Int(index as i64),
967 is_const: false,
968 is_var_persistent: false,
969 },
970 );
971 }
972
973 self.variables.insert(
975 item_var.clone(),
976 Variable {
977 value: item.clone(),
978 is_const: false,
979 is_var_persistent: false,
980 },
981 );
982
983 let control = self.execute_loop_body(body)?;
984 if control == LoopControl::Break {
985 break;
986 }
987 }
988
989 Ok(None)
990 }
991
992 Stmt::While { condition, body } => {
993 loop {
994 let cond_value = self.eval_expr(condition)?;
995 if !cond_value.truthy_for_condition()? {
996 break;
997 }
998
999 let control = self.execute_loop_body(body)?;
1000 if control == LoopControl::Break {
1001 break;
1002 }
1003 }
1004 Ok(None)
1005 }
1006
1007 Stmt::Break => Err(RuntimeError::BreakOutsideLoop),
1008 Stmt::Continue => Err(RuntimeError::ContinueOutsideLoop),
1009
1010 Stmt::TypeDecl {
1011 name,
1012 fields,
1013 export,
1014 } => {
1015 let type_value = Value::Type {
1017 name: name.clone(),
1018 fields: fields.clone(),
1019 };
1020 self.variables.insert(
1021 name.clone(),
1022 Variable {
1023 value: type_value.clone(),
1024 is_const: false,
1025 is_var_persistent: false,
1026 },
1027 );
1028
1029 if *export {
1031 self.exports.insert(name.clone(), type_value);
1032 }
1033 Ok(None)
1034 }
1035
1036 Stmt::EnumDecl {
1037 name,
1038 fields,
1039 export,
1040 } => {
1041 let mut enum_fields = HashMap::new();
1043
1044 for field in fields {
1045 let title = field.title.clone().unwrap_or_else(|| field.name.clone());
1046 let enum_value = Value::Enum {
1047 enum_name: name.clone(),
1048 field_name: field.name.clone(),
1049 title,
1050 };
1051 enum_fields.insert(field.name.clone(), enum_value);
1052 }
1053
1054 let enum_object = Value::Object {
1055 type_name: name.clone(),
1056 fields: Rc::new(RefCell::new(enum_fields)),
1057 call: None,
1058 };
1059 self.variables.insert(
1060 name.clone(),
1061 Variable {
1062 value: enum_object.clone(),
1063 is_const: false,
1064 is_var_persistent: false,
1065 },
1066 );
1067
1068 if *export {
1070 self.exports.insert(name.clone(), enum_object);
1071 }
1072 Ok(None)
1073 }
1074
1075 Stmt::Export { item } => {
1076 match item {
1078 pine_ast::ExportItem::Type(type_name) => {
1079 if let Some(var) = self.variables.get(type_name) {
1081 self.exports.insert(type_name.clone(), var.value.clone());
1082 }
1083 }
1084 pine_ast::ExportItem::Function(func_name) => {
1085 if let Some(var) = self.variables.get(func_name) {
1087 self.exports.insert(func_name.clone(), var.value.clone());
1088 }
1089 }
1090 }
1091 Ok(None)
1092 }
1093
1094 Stmt::Import { path, alias } => {
1095 if let Some(ref loader) = self.library_loader {
1097 match loader.load_library(path) {
1098 Ok(library_program) => {
1099 let mut library_interp = Interpreter::new();
1101
1102 library_interp.execute(&library_program)?;
1104
1105 let library_exports = library_interp.exports();
1107
1108 for (method_name, method_defs) in &library_interp.methods {
1110 for method_def in method_defs {
1111 self.methods
1112 .entry(method_name.clone())
1113 .or_default()
1114 .push(method_def.clone());
1115 }
1116 }
1117
1118 let namespace: Value<O> = Value::Object {
1120 type_name: alias.clone(),
1121 fields: Rc::new(RefCell::new(library_exports.clone())),
1122 call: None,
1123 };
1124 self.variables.insert(
1125 alias.clone(),
1126 Variable {
1127 value: namespace,
1128 is_const: false,
1129 is_var_persistent: false,
1130 },
1131 );
1132 }
1133 Err(e) => {
1134 return Err(RuntimeError::LibraryError(format!(
1135 "Failed to load library '{}': {}",
1136 path, e
1137 )));
1138 }
1139 }
1140 } else {
1141 return Err(RuntimeError::LibraryError(
1142 "Cannot import library: no library loader configured".to_string(),
1143 ));
1144 }
1145 Ok(None)
1146 }
1147
1148 Stmt::MethodDecl {
1149 name,
1150 params,
1151 body,
1152 export,
1153 } => {
1154 let type_name = if let Some(first_param) = params.first() {
1156 first_param.type_annotation.clone().ok_or_else(|| {
1157 RuntimeError::TypeError(
1158 "Method's first parameter must have a type annotation".to_string(),
1159 )
1160 })?
1161 } else {
1162 return Err(RuntimeError::TypeError(
1163 "Method must have at least one parameter (this)".to_string(),
1164 ));
1165 };
1166
1167 let method_def = MethodDef {
1169 type_name,
1170 params: params.clone(),
1171 body: body.clone(),
1172 };
1173
1174 self.methods
1175 .entry(name.clone())
1176 .or_default()
1177 .push(method_def);
1178
1179 if *export {
1183 }
1185
1186 Ok(None)
1187 }
1188
1189 Stmt::FunctionDecl {
1190 name,
1191 params,
1192 body,
1193 export,
1194 } => {
1195 let func_value = Value::Function {
1197 params: params.clone(),
1198 body: body.clone(),
1199 };
1200 self.variables.insert(
1201 name.clone(),
1202 Variable {
1203 value: func_value.clone(),
1204 is_const: false,
1205 is_var_persistent: false,
1206 },
1207 );
1208
1209 if *export {
1211 self.exports.insert(name.clone(), func_value);
1212 }
1213
1214 Ok(None)
1215 }
1216 }
1217 }
1218
1219 fn execute_loop_body(&mut self, body: &[Stmt]) -> Result<LoopControl, RuntimeError> {
1221 for stmt in body {
1222 match stmt {
1223 Stmt::Break => return Ok(LoopControl::Break),
1224 Stmt::Continue => return Ok(LoopControl::Continue),
1225 Stmt::If {
1226 condition,
1227 then_branch,
1228 else_if_branches,
1229 else_branch,
1230 } => {
1231 let cond_value = self.eval_expr(condition)?;
1232 let branch = if cond_value.truthy_for_condition()? {
1233 then_branch
1234 } else {
1235 let mut matched_branch = None;
1237 for (else_if_cond, else_if_body) in else_if_branches {
1238 let else_if_value = self.eval_expr(else_if_cond)?;
1239 if else_if_value.truthy_for_condition()? {
1240 matched_branch = Some(else_if_body);
1241 break;
1242 }
1243 }
1244
1245 if let Some(branch) = matched_branch {
1246 branch
1247 } else if let Some(else_stmts) = else_branch {
1248 else_stmts
1249 } else {
1250 continue;
1251 }
1252 };
1253
1254 let control = self.execute_loop_body(branch)?;
1255 if control != LoopControl::None {
1256 return Ok(control);
1257 }
1258 }
1259 Stmt::For { .. } | Stmt::ForIn { .. } | Stmt::While { .. } => {
1260 self.execute_stmt(stmt)?;
1262 }
1263 _ => {
1264 self.execute_stmt(stmt)?;
1265 }
1266 }
1267 }
1268 Ok(LoopControl::None)
1269 }
1270
1271 fn eval_expr(&mut self, expr: &Expr) -> Result<Value<O>, RuntimeError> {
1272 match expr {
1273 Expr::Literal(lit) => Ok(self.eval_literal(lit)),
1274
1275 Expr::Variable(name) => self
1276 .variables
1277 .get(name)
1278 .map(|var| var.value.clone())
1279 .ok_or_else(|| RuntimeError::UndefinedVariable(name.clone())),
1280
1281 Expr::Binary {
1282 left, op, right, ..
1283 } => {
1284 let left_val = self.eval_expr(left)?;
1285 if matches!(op, BinOp::And | BinOp::Or) {
1292 match (op, left_val.to_bool()?) {
1293 (BinOp::And, Some(false)) => return Ok(Value::Bool(false)),
1294 (BinOp::Or, Some(true)) => return Ok(Value::Bool(true)),
1295 _ => {}
1296 }
1297 }
1298 let right_val = self.eval_expr(right)?;
1299 self.eval_binary_op(&left_val, op, &right_val)
1300 }
1301
1302 Expr::Unary { op, expr } => {
1303 let val = self.eval_expr(expr)?;
1304 self.eval_unary_op(op, &val)
1305 }
1306
1307 Expr::Ternary {
1308 condition,
1309 then_expr,
1310 else_expr,
1311 } => {
1312 let cond_val = self.eval_expr(condition)?;
1313 if cond_val.truthy_for_condition()? {
1314 self.eval_expr(then_expr)
1315 } else {
1316 self.eval_expr(else_expr)
1317 }
1318 }
1319
1320 Expr::IfExpr {
1321 condition,
1322 then_expr,
1323 else_if_branches,
1324 else_expr,
1325 } => {
1326 let cond_val = self.eval_expr(condition)?;
1327 if cond_val.truthy_for_condition()? {
1328 self.eval_expr(then_expr)
1329 } else {
1330 for (else_if_cond, else_if_expr) in else_if_branches {
1332 let else_if_val = self.eval_expr(else_if_cond)?;
1333 if else_if_val.truthy_for_condition()? {
1334 return self.eval_expr(else_if_expr);
1335 }
1336 }
1337 if let Some(expr) = else_expr {
1339 self.eval_expr(expr)
1340 } else {
1341 Ok(Value::Na)
1342 }
1343 }
1344 }
1345
1346 Expr::Array(elements) => {
1347 let values: Result<Vec<_>, _> =
1348 elements.iter().map(|e| self.eval_expr(e)).collect();
1349 Ok(Value::Array(Rc::new(RefCell::new(values?))))
1350 }
1351
1352 Expr::Index { expr, index } => {
1353 let index_val = self.eval_expr(index)?.as_number()? as usize;
1354
1355 if index_val > 0 {
1362 if let Expr::Variable(var_name) = expr.as_ref() {
1363 if let Some(h) = self.user_series_history.get(var_name) {
1364 return Ok(if h.len() >= index_val {
1365 h[h.len() - index_val].clone()
1366 } else {
1367 Value::Na
1368 });
1369 }
1370 if let Some(var) = self.variables.get(var_name) {
1374 if !matches!(var.value, Value::Series(_) | Value::Array(_)) {
1375 return Ok(Value::Na);
1376 }
1377 }
1378 }
1379 }
1380
1381 let val = self.eval_expr(expr)?;
1382
1383 match val {
1384 Value::Array(arr_ref) => {
1385 let arr = arr_ref.borrow();
1386 arr.get(index_val)
1387 .cloned()
1388 .ok_or(RuntimeError::IndexOutOfBounds(index_val))
1389 }
1390 Value::Series(series) => {
1391 if index_val == 0 {
1396 Ok((*series.current).clone())
1397 } else {
1398 Ok(Value::Na)
1399 }
1400 }
1401 ref v => Err(RuntimeError::TypeError(format!(
1402 "Cannot index non-array/non-series value: {:?}",
1403 v
1404 ))),
1405 }
1406 }
1407
1408 Expr::Switch { value, cases } => {
1409 let switch_val = self.eval_expr(value)?;
1410
1411 for (pattern, result) in cases {
1412 let pattern_val = self.eval_expr(pattern)?;
1414
1415 if pattern_val == Value::Bool(true)
1417 && matches!(pattern, Expr::Literal(Literal::Bool(true)))
1418 {
1419 return self.eval_expr(result);
1420 }
1421
1422 if self.values_equal(&switch_val, &pattern_val)? {
1424 return self.eval_expr(result);
1425 }
1426 }
1427
1428 Ok(Value::Na)
1430 }
1431
1432 Expr::Call {
1433 callee,
1434 type_args,
1435 args,
1436 id,
1437 ..
1438 } => {
1439 if let Expr::MemberAccess { object, member } = callee.as_ref() {
1441 if let Some(method_defs) = self.methods.get(member).cloned() {
1443 let obj_value = self.eval_expr(object)?;
1445
1446 let obj_type = self.get_object_type_name(&obj_value)?;
1448
1449 if let Some(method_def) =
1450 method_defs.iter().find(|m| m.type_name == obj_type)
1451 {
1452 let mut evaluated_args: Vec<EvaluatedArg<O>> =
1454 vec![EvaluatedArg::Positional(obj_value)];
1455 evaluated_args.extend(self.evaluate_arguments(args, None)?);
1456
1457 return self.call_method(
1461 &method_def.params,
1462 &method_def.body,
1463 evaluated_args,
1464 *id,
1465 );
1466 }
1467 }
1468 }
1469
1470 let callee_value = self.eval_expr(callee)?;
1474 let signature = match &callee_value {
1475 Value::BuiltinFunction(builtin) => Some(builtin.signature.clone()),
1476 _ => None,
1477 };
1478 let evaluated_args = self.evaluate_arguments(args, signature.as_ref())?;
1479
1480 match callee_value {
1482 Value::Function { params, body } => {
1483 self.call_user_function(¶ms, &body, args, evaluated_args, *id)
1486 }
1487 Value::BuiltinFunction(builtin_fn) => {
1488 let call_args = FunctionCallArgs::new(type_args.clone(), evaluated_args)
1491 .with_call_id(*id);
1492 (builtin_fn.call)(self, call_args)
1493 }
1494 Value::Object {
1497 call: Some(builtin_fn),
1498 ..
1499 } => {
1500 let call_args = FunctionCallArgs::new(type_args.clone(), evaluated_args)
1501 .with_call_id(*id);
1502 (builtin_fn)(self, call_args)
1503 }
1504 Value::Na => {
1506 let is_na = matches!(
1507 evaluated_args.first(),
1508 Some(EvaluatedArg::Positional(Value::Na)) | None
1509 );
1510 Ok(Value::Bool(is_na))
1511 }
1512 _ => Err(RuntimeError::TypeError(
1513 "Attempted to call a non-function value".to_string(),
1514 )),
1515 }
1516 }
1517
1518 Expr::MemberAccess { object, member } => {
1519 let obj_value = self.eval_expr(object)?;
1520 match obj_value {
1521 Value::Object { fields, .. } => {
1522 let obj = fields.borrow();
1523 obj.get(member).cloned().ok_or_else(|| {
1524 RuntimeError::TypeError(format!("Object has no member '{}'", member))
1525 })
1526 }
1527 Value::Type { name, fields } => {
1528 if member == "new" {
1530 Ok(Value::BuiltinFunction(Builtin::untyped(
1532 Self::create_constructor(name, fields),
1533 )))
1534 } else if member == "copy" {
1535 Ok(Value::BuiltinFunction(Builtin::untyped(
1537 Self::create_copy_function(),
1538 )))
1539 } else {
1540 Err(RuntimeError::TypeError(format!(
1541 "Type '{}' has no member '{}' (only 'new' and 'copy' are supported)",
1542 name, member
1543 )))
1544 }
1545 }
1546 _ => Err(RuntimeError::TypeError(format!(
1547 "Cannot access member '{}' on non-object value",
1548 member
1549 ))),
1550 }
1551 }
1552
1553 Expr::Function { params, body } => {
1554 Ok(Value::Function {
1556 params: params.clone(),
1557 body: body.clone(),
1558 })
1559 }
1560 }
1561 }
1562
1563 fn eval_literal(&self, lit: &Literal) -> Value<O> {
1564 match lit {
1565 Literal::Int(n) => Value::Int(*n),
1566 Literal::Number(n) => Value::Number(*n),
1567 Literal::String(s) => Value::String(s.clone()),
1568 Literal::Bool(b) => Value::Bool(*b),
1569 Literal::Na => Value::Na,
1570 Literal::HexColor(hex) => Value::String(hex.clone()),
1571 }
1572 }
1573
1574 fn eval_binary_op(
1575 &self,
1576 left: &Value<O>,
1577 op: &BinOp,
1578 right: &Value<O>,
1579 ) -> Result<Value<O>, RuntimeError> {
1580 match op {
1581 BinOp::Add => {
1582 if matches!(left, Value::String(_)) || matches!(right, Value::String(_)) {
1584 Ok(Value::String(format!(
1585 "{}{}",
1586 left.as_string()?,
1587 right.as_string()?
1588 )))
1589 } else {
1590 numeric_op(left, right, |a, b| Some(a + b))
1591 }
1592 }
1593
1594 BinOp::Sub => numeric_op(left, right, |a, b| Some(a - b)),
1595
1596 BinOp::Mul => numeric_op(left, right, |a, b| Some(a * b)),
1597
1598 BinOp::Div => numeric_op(left, right, Num::checked_div),
1601
1602 BinOp::Mod => numeric_op(left, right, Num::checked_rem),
1603
1604 BinOp::Eq => {
1612 if is_na_operand(left) || is_na_operand(right) {
1613 return Ok(Value::Na);
1614 }
1615 Ok(Value::Bool(self.values_equal(left, right)?))
1616 }
1617
1618 BinOp::NotEq => {
1619 if is_na_operand(left) || is_na_operand(right) {
1620 return Ok(Value::Na);
1621 }
1622 Ok(Value::Bool(!self.values_equal(left, right)?))
1623 }
1624
1625 BinOp::Less => {
1631 if is_na_operand(left) || is_na_operand(right) {
1632 return Ok(Value::Na);
1633 }
1634 match (left.to_number()?, right.to_number()?) {
1635 (Some(l), Some(r)) => Ok(Value::Bool(l < r)),
1636 _ => Ok(Value::Na),
1637 }
1638 }
1639
1640 BinOp::Greater => {
1641 if is_na_operand(left) || is_na_operand(right) {
1642 return Ok(Value::Na);
1643 }
1644 match (left.to_number()?, right.to_number()?) {
1645 (Some(l), Some(r)) => Ok(Value::Bool(l > r)),
1646 _ => Ok(Value::Na),
1647 }
1648 }
1649
1650 BinOp::LessEq => {
1651 if is_na_operand(left) || is_na_operand(right) {
1652 return Ok(Value::Na);
1653 }
1654 match (left.to_number()?, right.to_number()?) {
1655 (Some(l), Some(r)) => Ok(Value::Bool(l <= r)),
1656 _ => Ok(Value::Na),
1657 }
1658 }
1659
1660 BinOp::GreaterEq => {
1661 if is_na_operand(left) || is_na_operand(right) {
1662 return Ok(Value::Na);
1663 }
1664 match (left.to_number()?, right.to_number()?) {
1665 (Some(l), Some(r)) => Ok(Value::Bool(l >= r)),
1666 _ => Ok(Value::Na),
1667 }
1668 }
1669
1670 BinOp::And => match (left.to_bool()?, right.to_bool()?) {
1672 (Some(false), _) | (_, Some(false)) => Ok(Value::Bool(false)),
1673 (Some(true), Some(true)) => Ok(Value::Bool(true)),
1674 _ => Ok(Value::Na),
1675 },
1676
1677 BinOp::Or => match (left.to_bool()?, right.to_bool()?) {
1679 (Some(true), _) | (_, Some(true)) => Ok(Value::Bool(true)),
1680 (Some(false), Some(false)) => Ok(Value::Bool(false)),
1681 _ => Ok(Value::Na),
1682 },
1683 }
1684 }
1685
1686 fn eval_unary_op(&self, op: &UnOp, val: &Value<O>) -> Result<Value<O>, RuntimeError> {
1687 match op {
1688 UnOp::Neg => match val.as_int() {
1690 Some(n) => Ok(Value::Int(-n)),
1691 None => match val.to_number()? {
1692 Some(n) => Ok(Value::Number(-n)),
1693 None => Ok(Value::Na),
1694 },
1695 },
1696 UnOp::Not => match val.to_bool()? {
1697 Some(b) => Ok(Value::Bool(!b)),
1698 None => Ok(Value::Na),
1699 },
1700 }
1701 }
1702
1703 fn values_equal(&self, left: &Value<O>, right: &Value<O>) -> Result<bool, RuntimeError> {
1704 match (left, right) {
1705 (Value::Int(l), Value::Int(r)) => Ok(l == r),
1706 (Value::Int(l), Value::Number(r)) | (Value::Number(r), Value::Int(l)) => {
1708 Ok((*l as f64 - r).abs() < f64::EPSILON)
1709 }
1710 (Value::Number(l), Value::Number(r)) => Ok((l - r).abs() < f64::EPSILON),
1711 (Value::String(l), Value::String(r)) => Ok(l == r),
1712 (Value::Bool(l), Value::Bool(r)) => Ok(l == r),
1713 (Value::Na, Value::Na) => Ok(true),
1714 (
1715 Value::Enum {
1716 enum_name: a_enum,
1717 field_name: a_field,
1718 ..
1719 },
1720 Value::Enum {
1721 enum_name: b_enum,
1722 field_name: b_field,
1723 ..
1724 },
1725 ) => Ok(a_enum == b_enum && a_field == b_field),
1726 _ => Ok(false),
1727 }
1728 }
1729
1730 fn is_const_expr(&self, expr: &Expr) -> bool {
1732 match expr {
1733 Expr::Literal(_) => true,
1735 Expr::Variable(name) => self
1737 .variables
1738 .get(name)
1739 .map(|var| var.is_const)
1740 .unwrap_or(false),
1741 Expr::MemberAccess { object, .. } => self.is_const_expr(object),
1743 _ => false,
1745 }
1746 }
1747
1748 fn call_user_function(
1749 &mut self,
1750 params: &[pine_ast::FunctionParam],
1751 body: &[Stmt],
1752 arg_exprs: &[Argument],
1753 args: Vec<EvaluatedArg<O>>,
1754 call_id: u32,
1755 ) -> Result<Value<O>, RuntimeError> {
1756 let mut positional_values = Vec::new();
1758 let mut positional_exprs = Vec::new();
1759
1760 for (i, arg) in args.iter().enumerate() {
1761 match arg {
1762 EvaluatedArg::Positional(value) => {
1763 positional_values.push(value.clone());
1764 if let Some(Argument::Positional(expr)) = arg_exprs.get(i) {
1765 positional_exprs.push(expr);
1766 }
1767 }
1768 EvaluatedArg::Named { .. } => {
1769 return Err(RuntimeError::TypeError(
1770 "User-defined functions do not support named arguments yet".to_string(),
1771 ))
1772 }
1773 }
1774 }
1775
1776 if positional_values.len() != params.len() {
1778 return Err(RuntimeError::TypeError(format!(
1779 "Expected {} arguments, got {}",
1780 params.len(),
1781 positional_values.len()
1782 )));
1783 }
1784
1785 for (i, param) in params.iter().enumerate() {
1787 if matches!(param.type_qualifier, Some(pine_ast::TypeQualifier::Const)) {
1788 if let Some(arg_expr) = positional_exprs.get(i) {
1789 if !self.is_const_expr(arg_expr) {
1790 return Err(RuntimeError::TypeError(format!(
1791 "Parameter '{}' requires a const argument, but received a non-const value",
1792 param.name
1793 )));
1794 }
1795 }
1796 }
1797 }
1798
1799 let param_bindings: Vec<(String, Variable<O>)> = params
1802 .iter()
1803 .zip(positional_values)
1804 .map(|(param, value)| {
1805 let is_const = matches!(param.type_qualifier, Some(pine_ast::TypeQualifier::Const));
1806 (
1807 param.name.clone(),
1808 Variable {
1809 value,
1810 is_const,
1811 is_var_persistent: false,
1812 },
1813 )
1814 })
1815 .collect();
1816
1817 self.run_call_site_body(call_id, param_bindings, body)
1818 }
1819
1820 fn run_call_site_body(
1828 &mut self,
1829 call_id: u32,
1830 param_bindings: Vec<(String, Variable<O>)>,
1831 body: &[Stmt],
1832 ) -> Result<Value<O>, RuntimeError> {
1833 let param_names: std::collections::HashSet<String> =
1834 param_bindings.iter().map(|(n, _)| n.clone()).collect();
1835
1836 let saved_vars = self.variables.clone();
1838
1839 if call_id != 0 {
1843 if let Some(local_state) = self.function_local_state.get(&call_id) {
1844 for (var_name, var) in local_state {
1845 if !param_names.contains(var_name) {
1846 self.variables.insert(var_name.clone(), var.clone());
1847 }
1848 }
1849 }
1850 }
1851
1852 for (name, var) in param_bindings {
1854 self.variables.insert(name, var);
1855 }
1856
1857 let prev_call_id = self.current_call_id;
1862 self.current_call_id = call_id;
1863 let mut result: Value<O> = Value::Na;
1864 for stmt in body {
1865 if let Some(return_value) = self.execute_stmt(stmt)? {
1866 result = return_value;
1867 } else if let Stmt::Expression(expr) = stmt {
1868 result = self.eval_expr(expr)?;
1870 }
1871 }
1872 self.current_call_id = prev_call_id;
1873
1874 let call_vars = std::mem::replace(&mut self.variables, saved_vars);
1878 if call_id != 0 {
1879 let mut assigned: std::collections::HashSet<String> = std::collections::HashSet::new();
1886 collect_assigned_names(body, &mut assigned);
1887 let local_state: HashMap<String, Variable<O>> = call_vars
1888 .into_iter()
1889 .filter(|(k, _)| !param_names.contains(k) && assigned.contains(k))
1890 .collect();
1891 self.function_local_state.insert(call_id, local_state);
1892 }
1893
1894 Ok(result)
1895 }
1896
1897 fn get_object_type_name(&self, value: &Value<O>) -> Result<String, RuntimeError> {
1899 match value {
1900 Value::Object { type_name, .. } => Ok(type_name.clone()),
1901 _ => Err(RuntimeError::TypeError(
1902 "Cannot determine type of non-object value".to_string(),
1903 )),
1904 }
1905 }
1906
1907 fn call_method(
1909 &mut self,
1910 params: &[MethodParam],
1911 body: &[Stmt],
1912 args: Vec<EvaluatedArg<O>>,
1913 call_id: u32,
1914 ) -> Result<Value<O>, RuntimeError> {
1915 let mut positional_idx = 0;
1919 let mut param_bindings: Vec<(String, Variable<O>)> = Vec::with_capacity(params.len());
1920
1921 for param in params {
1922 let param_value = if positional_idx < args.len() {
1923 match &args[positional_idx] {
1924 EvaluatedArg::Positional(value) => {
1925 positional_idx += 1;
1926 value.clone()
1927 }
1928 EvaluatedArg::Named { name, value } => {
1929 if name == ¶m.name {
1930 positional_idx += 1;
1931 value.clone()
1932 } else if let Some(default_expr) = ¶m.default_value {
1933 self.eval_expr(default_expr)?
1934 } else {
1935 Value::Na
1936 }
1937 }
1938 }
1939 } else if let Some(default_expr) = ¶m.default_value {
1940 self.eval_expr(default_expr)?
1941 } else {
1942 Value::Na
1943 };
1944
1945 param_bindings.push((
1946 param.name.clone(),
1947 Variable {
1948 value: param_value,
1949 is_const: false,
1950 is_var_persistent: false,
1951 },
1952 ));
1953 }
1954
1955 self.run_call_site_body(call_id, param_bindings, body)
1956 }
1957
1958 fn create_constructor(type_name: String, fields: Vec<TypeField>) -> BuiltinFn<O> {
1960 Rc::new(
1961 move |interp: &mut Interpreter<O>, call_args: FunctionCallArgs<O>| {
1962 let mut instance_fields = HashMap::new();
1963
1964 let mut positional_idx = 0;
1966
1967 for arg in &call_args.args {
1968 match arg {
1969 EvaluatedArg::Positional(value) => {
1970 if positional_idx < fields.len() {
1972 let field = &fields[positional_idx];
1973 instance_fields.insert(field.name.clone(), value.clone());
1974 positional_idx += 1;
1975 } else {
1976 return Err(RuntimeError::TypeError(format!(
1977 "Too many arguments for type '{}' (expected {} fields)",
1978 type_name,
1979 fields.len()
1980 )));
1981 }
1982 }
1983 EvaluatedArg::Named { name, value } => {
1984 if let Some(field) = fields.iter().find(|f| f.name == *name) {
1986 instance_fields.insert(field.name.clone(), value.clone());
1987 } else {
1988 return Err(RuntimeError::TypeError(format!(
1989 "Type '{}' has no field '{}'",
1990 type_name, name
1991 )));
1992 }
1993 }
1994 }
1995 }
1996
1997 for field in &fields {
1999 if !instance_fields.contains_key(&field.name) {
2000 if let Some(default_expr) = &field.default_value {
2001 let default_val = interp.eval_expr(default_expr)?;
2002 instance_fields.insert(field.name.clone(), default_val);
2003 } else {
2004 instance_fields.insert(field.name.clone(), Value::Na);
2006 }
2007 }
2008 }
2009
2010 Ok(Value::Object {
2011 type_name: type_name.clone(),
2012 fields: Rc::new(RefCell::new(instance_fields)),
2013 call: None,
2014 })
2015 },
2016 )
2017 }
2018
2019 fn create_copy_function() -> BuiltinFn<O> {
2021 Rc::new(
2022 |_interp: &mut Interpreter<O>, call_args: FunctionCallArgs<O>| {
2023 if call_args.args.len() != 1 {
2025 return Err(RuntimeError::TypeError(
2026 "copy() expects exactly one argument".to_string(),
2027 ));
2028 }
2029
2030 match &call_args.args[0] {
2031 EvaluatedArg::Positional(value) => {
2032 if let Value::Object {
2033 type_name,
2034 fields,
2035 call,
2036 } = value
2037 {
2038 let obj = fields.borrow();
2040 let copied_fields = obj.clone();
2041 Ok(Value::Object {
2042 type_name: type_name.clone(),
2043 fields: Rc::new(RefCell::new(copied_fields)),
2044 call: call.clone(),
2045 })
2046 } else {
2047 Err(RuntimeError::TypeError(
2048 "copy() expects an object argument".to_string(),
2049 ))
2050 }
2051 }
2052 EvaluatedArg::Named { .. } => Err(RuntimeError::TypeError(
2053 "copy() does not accept named arguments".to_string(),
2054 )),
2055 }
2056 },
2057 )
2058 }
2059}
2060
2061impl<O: PineOutput> Default for Interpreter<O> {
2062 fn default() -> Self {
2063 Self::new()
2064 }
2065}