1mod num;
2mod signature;
3
4pub use num::Num;
5pub use signature::{BuiltinSignature, Param, ParamType};
6
7use pine_core::{Color, DefaultPineOutput, PineOutput, MAX_LOOKBACK};
8
9use pine_ast::{Argument, BinOp, Expr, Literal, MethodParam, Program, Stmt, TypeField, UnOp};
10use std::cell::RefCell;
11use std::collections::HashMap;
12use std::rc::Rc;
13use thiserror::Error;
14
15pub use pine_core::LibraryLoader;
16
17fn push_history<O: PineOutput>(
23 history: &mut HashMap<String, Vec<Value<O>>>,
24 name: &str,
25 value: Value<O>,
26) {
27 let entries = history.entry(name.to_string()).or_default();
28 entries.push(value);
29 if entries.len() > MAX_LOOKBACK {
30 entries.drain(..entries.len() - MAX_LOOKBACK);
31 }
32}
33
34fn numeric_op<O: PineOutput>(
38 left: &Value<O>,
39 right: &Value<O>,
40 op: impl Fn(Num, Num) -> Option<Num>,
41) -> Result<Value<O>, RuntimeError> {
42 left.to_number()?;
44 right.to_number()?;
45
46 match (left.as_num(), right.as_num()) {
47 (Some(a), Some(b)) => Ok(op(a, b).map_or(Value::Na, Value::from)),
48 _ => Ok(Value::Na),
49 }
50}
51
52#[derive(Error, Debug)]
53pub enum RuntimeError {
54 #[error("Variable '{0}' not found")]
55 UndefinedVariable(String),
56
57 #[error("Type error: {0}")]
58 TypeError(String),
59
60 #[error("Division by zero")]
61 DivisionByZero,
62
63 #[error("Index out of bounds: {0}")]
64 IndexOutOfBounds(usize),
65
66 #[error("Cannot iterate: from={0}, to={1}")]
67 InvalidForLoop(f64, f64),
68
69 #[error("Break statement outside of loop")]
70 BreakOutsideLoop,
71
72 #[error("Continue statement outside of loop")]
73 ContinueOutsideLoop,
74
75 #[error("Library error: {0}")]
76 LibraryError(String),
77
78 #[error("Cannot reassign const variable '{0}'")]
79 ConstReassignment(String),
80}
81
82#[derive(Debug, Clone, PartialEq)]
84enum LoopControl {
85 None,
86 Break,
87 Continue,
88}
89
90#[derive(Clone)]
92struct Variable<O: PineOutput = DefaultPineOutput> {
93 value: Value<O>,
94 is_const: bool,
95 is_var_persistent: bool,
97}
98
99#[derive(Clone, Debug)]
101pub struct Series<O: PineOutput = DefaultPineOutput> {
102 pub id: String,
103 pub current: Box<Value<O>>,
104}
105
106#[derive(Clone)]
108pub enum Value<O: PineOutput> {
109 Int(i64),
110 Number(f64),
111 String(String),
112 Bool(bool),
113 Na, Array(Rc<RefCell<Vec<Value<O>>>>), Series(Series<O>), Object {
117 type_name: String, fields: Rc<RefCell<HashMap<String, Value<O>>>>, call: Option<BuiltinFn<O>>,
120 },
121 Function {
122 params: Vec<pine_ast::FunctionParam>,
123 body: Vec<Stmt>,
124 },
125 BuiltinFunction(Builtin<O>), Expr(Rc<Expr>),
129 Type {
130 name: String,
131 fields: Vec<TypeField>,
132 }, Enum {
134 enum_name: String, field_name: String, title: String, }, Color(Color), Matrix {
140 element_type: String, data: Rc<RefCell<Vec<Vec<Value<O>>>>>, },
143}
144
145impl<O: PineOutput> From<Num> for Value<O> {
146 fn from(n: Num) -> Self {
148 match n {
149 Num::Int(n) => Value::Int(n),
150 Num::Float(n) => Value::Number(n),
151 }
152 }
153}
154
155impl<O: PineOutput> Value<O> {
156 pub fn new_color(r: u8, g: u8, b: u8, t: u8) -> Value<O> {
157 Value::Color(Color::new(r, g, b, t))
158 }
159}
160
161impl<O: PineOutput> std::fmt::Debug for Value<O> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 match self {
165 Value::Int(n) => write!(f, "Int({:?})", n),
166 Value::Number(n) => write!(f, "Number({:?})", n),
167 Value::String(s) => write!(f, "String({:?})", s),
168 Value::Bool(b) => write!(f, "Bool({:?})", b),
169 Value::Na => write!(f, "Na"),
170 Value::Array(a) => write!(f, "Array({:?})", a),
171 Value::Series(s) => write!(f, "Series({:?})", s),
172 Value::Object {
173 type_name, fields, ..
174 } => write!(f, "Object({}:{:?})", type_name, fields),
175 Value::Function { params, .. } => write!(f, "Function({} params)", params.len()),
176 Value::BuiltinFunction(_) => write!(f, "BuiltinFunction"),
177 Value::Expr(_) => write!(f, "Expr"),
178 Value::Type { name, .. } => write!(f, "Type({})", name),
179 Value::Enum {
180 enum_name,
181 field_name,
182 ..
183 } => write!(f, "Enum({}::{})", enum_name, field_name),
184 Value::Color(color) => write!(
185 f,
186 "Color(rgba({}, {}, {}, {}))",
187 color.r, color.g, color.b, color.t
188 ),
189 Value::Matrix { element_type, data } => {
190 write!(f, "Matrix<{}>({:?})", element_type, data)
191 }
192 }
193 }
194}
195
196impl<O: PineOutput> PartialEq for Value<O> {
197 fn eq(&self, other: &Self) -> bool {
198 match (self, other) {
199 (Value::Int(a), Value::Int(b)) => a == b,
200 (Value::Int(a), Value::Number(b)) | (Value::Number(b), Value::Int(a)) => {
202 (*a as f64 - b).abs() < f64::EPSILON
203 }
204 (Value::Number(a), Value::Number(b)) => (a - b).abs() < f64::EPSILON,
205 (Value::String(a), Value::String(b)) => a == b,
206 (Value::Bool(a), Value::Bool(b)) => a == b,
207 (Value::Na, Value::Na) => true,
208 (Value::Array(a), Value::Array(b)) => Rc::ptr_eq(a, b),
210 (Value::Series(a), Value::Series(b)) => a.id == b.id && *a.current == *b.current,
212 (Value::Object { fields: a, .. }, Value::Object { fields: b, .. }) => Rc::ptr_eq(a, b),
213 (Value::Function { .. }, Value::Function { .. }) => false,
215 (Value::BuiltinFunction(_), Value::BuiltinFunction(_)) => false,
216 (Value::Type { name: a, .. }, Value::Type { name: b, .. }) => a == b,
218 (
220 Value::Enum {
221 enum_name: a_enum,
222 field_name: a_field,
223 ..
224 },
225 Value::Enum {
226 enum_name: b_enum,
227 field_name: b_field,
228 ..
229 },
230 ) => a_enum == b_enum && a_field == b_field,
231 (Value::Color(c1), Value::Color(c2)) => c1 == c2,
233 (Value::Matrix { data: a, .. }, Value::Matrix { data: b, .. }) => Rc::ptr_eq(a, b),
235 _ => false,
236 }
237 }
238}
239
240#[derive(Debug, Clone)]
242pub enum EvaluatedArg<O: PineOutput = DefaultPineOutput> {
243 Positional(Value<O>),
244 Named { name: String, value: Value<O> },
245}
246
247#[derive(Debug, Clone)]
249pub struct FunctionCallArgs<O: PineOutput = DefaultPineOutput> {
250 pub type_args: Vec<String>,
251 pub args: Vec<EvaluatedArg<O>>,
252 pub call_id: u32,
253}
254
255impl<O: PineOutput> FunctionCallArgs<O> {
256 pub fn new(type_args: Vec<String>, args: Vec<EvaluatedArg<O>>) -> Self {
257 Self {
258 type_args,
259 args,
260 call_id: 0,
261 }
262 }
263
264 pub fn without_types(args: Vec<EvaluatedArg<O>>) -> Self {
265 Self {
266 type_args: vec![],
267 args,
268 call_id: 0,
269 }
270 }
271
272 pub fn with_call_id(mut self, call_id: u32) -> Self {
273 self.call_id = call_id;
274 self
275 }
276}
277
278pub type BuiltinFn<O> =
280 Rc<dyn Fn(&mut Interpreter<O>, FunctionCallArgs<O>) -> Result<Value<O>, RuntimeError>>;
281
282#[derive(Clone)]
286pub struct Builtin<O: PineOutput> {
287 pub call: BuiltinFn<O>,
288 pub signature: BuiltinSignature,
289}
290
291impl<O: PineOutput> Builtin<O> {
292 pub fn untyped(call: BuiltinFn<O>) -> Self {
295 Self {
296 call,
297 signature: BuiltinSignature::default(),
298 }
299 }
300}
301
302impl<O: PineOutput> Value<O> {
303 pub fn as_number(&self) -> Result<f64, RuntimeError> {
306 self.to_number().map(|opt| opt.unwrap_or(f64::NAN))
307 }
308
309 pub fn as_bool(&self) -> Result<bool, RuntimeError> {
312 Ok(self.to_bool()?.unwrap_or(false))
313 }
314
315 pub fn as_num(&self) -> Option<Num> {
319 match self {
320 Value::Int(n) => Some(Num::Int(*n)),
321 Value::Number(n) => Some(Num::Float(*n)),
322 Value::Bool(b) => Some(Num::Int(if *b { 1 } else { 0 })),
323 Value::Series(series) => series.current.as_num(),
324 _ => None,
325 }
326 }
327
328 fn as_int(&self) -> Option<i64> {
330 match self.as_num() {
331 Some(Num::Int(n)) => Some(n),
332 _ => None,
333 }
334 }
335
336 pub fn to_number(&self) -> Result<Option<f64>, RuntimeError> {
339 match self {
340 Value::Int(n) => Ok(Some(*n as f64)),
341 Value::Number(n) => Ok(Some(*n)),
342 Value::Bool(b) => Ok(Some(if *b { 1.0 } else { 0.0 })),
343 Value::Series(series) => series.current.to_number(),
344 Value::Na => Ok(None),
345 _ => Err(RuntimeError::TypeError(format!(
346 "Expected number, got {:?}",
347 self
348 ))),
349 }
350 }
351
352 pub fn to_bool(&self) -> Result<Option<bool>, RuntimeError> {
354 match self {
355 Value::Bool(b) => Ok(Some(*b)),
356 Value::Int(n) => Ok(Some(*n != 0)),
357 Value::Number(n) => Ok(Some(*n != 0.0 && !n.is_nan())),
359 Value::Na => Ok(None),
360 _ => Err(RuntimeError::TypeError(format!(
361 "Expected bool, got {:?}",
362 self
363 ))),
364 }
365 }
366
367 pub fn truthy_for_condition(&self) -> Result<bool, RuntimeError> {
370 Ok(self.to_bool()?.unwrap_or(false))
371 }
372
373 pub fn as_string(&self) -> Result<String, RuntimeError> {
374 match self {
375 Value::String(s) => Ok(s.clone()),
376 Value::Int(n) => Ok(n.to_string()),
377 Value::Number(n) => Ok(n.to_string()),
378 Value::Bool(b) => Ok(b.to_string()),
379 Value::Na => Ok("na".to_string()),
380 _ => Err(RuntimeError::TypeError(format!(
381 "Cannot convert {:?} to string",
382 self
383 ))),
384 }
385 }
386
387 pub fn as_array(&self) -> Result<&Rc<RefCell<Vec<Value<O>>>>, RuntimeError> {
388 match self {
389 Value::Array(arr) => Ok(arr),
390 _ => Err(RuntimeError::TypeError(format!(
391 "Expected array, got {:?}",
392 self
393 ))),
394 }
395 }
396
397 pub fn as_color(&self) -> Result<Color, RuntimeError> {
398 match self {
399 Value::Color(color) => Ok(color.clone()),
400 _ => Err(RuntimeError::TypeError(format!(
401 "Expected color, got {:?}",
402 self
403 ))),
404 }
405 }
406}
407
408#[derive(Clone)]
410struct MethodDef {
411 type_name: String, params: Vec<pine_ast::MethodParam>,
413 body: Vec<Stmt>,
414}
415
416pub struct Interpreter<O: PineOutput> {
418 variables: HashMap<String, Variable<O>>,
420 user_types: HashMap<String, Value<O>>,
424 methods: HashMap<String, Vec<MethodDef>>,
426 library_loader: Option<Box<dyn LibraryLoader>>,
428 exports: HashMap<String, Value<O>>,
430 pub output: O,
432 pub user_series_history: HashMap<String, Vec<Value<O>>>,
436 function_local_state: HashMap<u32, HashMap<String, Variable<O>>>,
443 var_decls_initialized: HashMap<(u32, String), u64>,
454 current_call_id: u32,
457 bar_seq: u64,
460 pub broker: Option<Box<dyn pine_broker::Broker>>,
463 pub request_provider: Option<Rc<dyn pine_core::DataProvider>>,
468 pub chart_period: Option<i64>,
469}
470
471fn collect_assigned_names(body: &[Stmt], out: &mut std::collections::HashSet<String>) {
476 for s in body {
477 match s {
478 Stmt::VarDecl { name, .. } => {
479 out.insert(name.clone());
480 }
481 Stmt::Assignment {
482 target: Expr::Variable { name: n, .. },
483 ..
484 } => {
485 out.insert(n.clone());
486 }
487 Stmt::TupleAssignment { names, .. } => {
488 for n in names {
489 out.insert(n.clone());
490 }
491 }
492 Stmt::If {
493 then_branch,
494 else_if_branches,
495 else_branch,
496 ..
497 } => {
498 collect_assigned_names(then_branch, out);
499 for (_, b) in else_if_branches {
500 collect_assigned_names(b, out);
501 }
502 if let Some(b) = else_branch {
503 collect_assigned_names(b, out);
504 }
505 }
506 Stmt::For { var_name, body, .. } => {
507 out.insert(var_name.clone());
508 collect_assigned_names(body, out);
509 }
510 Stmt::While { body, .. } | Stmt::ForIn { body, .. } => {
511 collect_assigned_names(body, out)
512 }
513 _ => {}
514 }
515 }
516}
517
518fn builtin_namespace<O: PineOutput>(value: &Value<O>) -> Option<&'static str> {
521 match value {
522 Value::Array(_) => Some("array"),
523 Value::Matrix { .. } => Some("matrix"),
524 _ => None,
525 }
526}
527
528fn is_na_operand<O: PineOutput>(v: &Value<O>) -> bool {
532 matches!(v, Value::Na) || matches!(v, Value::Number(n) if n.is_nan())
533}
534
535impl<O: PineOutput> Interpreter<O> {
536 pub fn new() -> Self {
537 Self {
538 variables: HashMap::new(),
539 user_types: HashMap::new(),
540 methods: HashMap::new(),
541 library_loader: None,
542 exports: HashMap::new(),
543 output: O::default(),
544 user_series_history: HashMap::new(),
545 function_local_state: HashMap::new(),
546 var_decls_initialized: HashMap::new(),
547 current_call_id: 0,
548 bar_seq: 0,
549 broker: None,
550 request_provider: None,
551 chart_period: None,
552 }
553 }
554
555 pub fn bar_seq(&self) -> u64 {
558 self.bar_seq
559 }
560
561 pub fn set_library_loader(&mut self, library_loader: Box<dyn LibraryLoader>) {
563 self.library_loader = Some(library_loader);
564 }
565
566 pub fn snapshot(&self) -> HashMap<String, Value<O>> {
570 self.variables
571 .iter()
572 .map(|(name, var)| (name.clone(), var.value.clone()))
573 .collect()
574 }
575
576 pub fn exports(&self) -> &HashMap<String, Value<O>> {
578 &self.exports
579 }
580
581 pub fn execute(&mut self, program: &Program) -> Result<O, RuntimeError> {
583 self.output.clear();
585 self.bar_seq += 1;
587
588 for stmt in &program.statements {
589 self.execute_stmt(stmt)?;
590 }
591
592 Ok(self.output.clone())
594 }
595
596 pub fn get_variable(&self, name: &str) -> Option<&Value<O>> {
598 self.variables.get(name).map(|var| &var.value)
599 }
600
601 pub fn is_user_type(&self, name: &str) -> bool {
603 self.user_types.contains_key(name)
604 }
605
606 fn namespace_member(&self, namespace: &str, member: &str) -> Option<Value<O>> {
608 match self.variables.get(namespace).map(|var| &var.value) {
609 Some(Value::Object { fields, .. }) => fields.borrow().get(member).cloned(),
610 _ => None,
611 }
612 }
613
614 pub fn set_variable(&mut self, name: &str, value: Value<O>) {
616 self.variables.insert(
617 name.to_string(),
618 Variable {
619 value,
620 is_const: false,
621 is_var_persistent: false,
622 },
623 );
624 }
625
626 pub fn advance_series(&mut self, name: &str, value: Value<O>) {
633 if let Some(existing) = self.variables.get(name) {
634 let previous = match &existing.value {
637 Value::Series(series) => (*series.current).clone(),
638 other => other.clone(),
639 };
640 push_history(&mut self.user_series_history, name, previous);
641 }
642 self.set_variable(name, value);
643 }
644
645 pub fn set_object_field(&mut self, object: &str, field: &str, value: Value<O>) {
649 if let Some(Variable {
650 value: Value::Object { fields, .. },
651 ..
652 }) = self.variables.get(object)
653 {
654 fields.borrow_mut().insert(field.to_string(), value);
655 }
656 }
657
658 pub fn set_const_variable(&mut self, name: &str, value: Value<O>) {
660 self.variables.insert(
661 name.to_string(),
662 Variable {
663 value,
664 is_const: true,
665 is_var_persistent: false,
666 },
667 );
668 }
669
670 fn evaluate_arguments(
674 &mut self,
675 args: &[Argument],
676 signature: Option<&BuiltinSignature>,
677 ) -> Result<Vec<EvaluatedArg<O>>, RuntimeError> {
678 let mut evaluated_args = Vec::new();
679 let mut seen_named = false;
680 let mut positional_index = 0;
681
682 for arg in args {
683 match arg {
684 Argument::Positional(expr) => {
685 if seen_named {
686 return Err(RuntimeError::TypeError(
687 "Positional arguments cannot follow named arguments".to_string(),
688 ));
689 }
690 let lazy = signature.is_some_and(|s| s.positional_is_lazy(positional_index));
691 let value = self.eval_or_capture(expr, lazy)?;
692 evaluated_args.push(EvaluatedArg::Positional(value));
693 positional_index += 1;
694 }
695 Argument::Named { name, value: expr } => {
696 seen_named = true;
697 let lazy = signature.is_some_and(|s| s.named_is_lazy(name));
698 let value = self.eval_or_capture(expr, lazy)?;
699 evaluated_args.push(EvaluatedArg::Named {
700 name: name.clone(),
701 value,
702 });
703 }
704 }
705 }
706
707 Ok(evaluated_args)
708 }
709
710 fn eval_or_capture(&mut self, expr: &Expr, lazy: bool) -> Result<Value<O>, RuntimeError> {
713 if lazy {
714 Ok(Value::Expr(Rc::new(expr.clone())))
715 } else {
716 self.eval_expr(expr)
717 }
718 }
719
720 fn execute_stmt(&mut self, stmt: &Stmt) -> Result<Option<Value<O>>, RuntimeError> {
721 match stmt {
722 Stmt::VarDecl {
723 name,
724 type_qualifier,
725 type_annotation: _,
726 initializer,
727 var_kind,
730 ..
731 } => {
732 let is_var_persistent = var_kind.is_persistent();
733 if is_var_persistent {
740 let init_key = (self.current_call_id, name.clone());
741 if self.var_decls_initialized.contains_key(&init_key) {
742 return Ok(None);
743 }
744 self.var_decls_initialized.insert(init_key, self.bar_seq);
745 }
746 if !is_var_persistent {
750 if let Some(existing) = self.variables.get(name) {
751 push_history(&mut self.user_series_history, name, existing.value.clone());
752 }
753 }
754 let value = if let Some(init_expr) = initializer {
755 self.eval_expr(init_expr)?
756 } else {
757 Value::Na
758 };
759 let is_const = matches!(type_qualifier, Some(pine_ast::TypeQualifier::Const));
760 self.variables.insert(
761 name.clone(),
762 Variable {
763 value,
764 is_const,
765 is_var_persistent,
766 },
767 );
768 Ok(None)
769 }
770
771 Stmt::Assignment { target, value } => {
772 if let Expr::Variable { name, .. } = target {
781 if let Some(var) = self.variables.get(name) {
782 let born_this_bar = self
783 .var_decls_initialized
784 .get(&(self.current_call_id, name.clone()))
785 == Some(&self.bar_seq);
786 if var.is_var_persistent && !born_this_bar {
787 push_history(&mut self.user_series_history, name, var.value.clone());
788 }
789 }
790 }
791
792 let val = self.eval_expr(value)?;
793 match target {
794 Expr::Variable { name, .. } => {
795 let (is_const, is_var_persistent) =
797 if let Some(var) = self.variables.get(name) {
798 if var.is_const {
799 return Err(RuntimeError::ConstReassignment(name.clone()));
800 }
801 if !var.is_var_persistent {
802 push_history(
804 &mut self.user_series_history,
805 name,
806 var.value.clone(),
807 );
808 }
809 (false, var.is_var_persistent)
811 } else {
812 (false, false)
813 };
814
815 self.variables.insert(
816 name.clone(),
817 Variable {
818 value: val,
819 is_const,
820 is_var_persistent,
821 },
822 );
823 Ok(None)
824 }
825 Expr::MemberAccess { object, member, .. } => {
826 if let Expr::Variable { name: var_name, .. } = object.as_ref() {
828 if let Some(var) = self.variables.get(var_name) {
829 if var.is_const {
830 return Err(RuntimeError::ConstReassignment(format!(
831 "{}.{}",
832 var_name, member
833 )));
834 }
835 }
836 }
837
838 let obj_value = self.eval_expr(object)?;
840
841 if let Value::Object { fields, .. } = obj_value {
842 let mut obj = fields.borrow_mut();
843 obj.insert(member.clone(), val);
844 Ok(None)
845 } else {
846 Err(RuntimeError::TypeError(
847 "Cannot assign to member of non-object value".to_string(),
848 ))
849 }
850 }
851 _ => Err(RuntimeError::TypeError(
852 "Invalid assignment target".to_string(),
853 )),
854 }
855 }
856
857 Stmt::TupleAssignment { names, value, .. } => {
858 let val = self.eval_expr(value)?;
859 if let Value::Array(arr_ref) = val {
860 let arr = arr_ref.borrow();
861 for (i, name) in names.iter().enumerate() {
862 if let Some(var) = self.variables.get(name) {
864 push_history(&mut self.user_series_history, name, var.value.clone());
865 }
866 let element_val = arr.get(i).cloned().unwrap_or(Value::Na);
867 self.variables.insert(
868 name.clone(),
869 Variable {
870 value: element_val,
871 is_const: false,
872 is_var_persistent: false,
873 },
874 );
875 }
876 Ok(None)
877 } else {
878 Err(RuntimeError::TypeError(
879 "Expected array for tuple destructuring".to_string(),
880 ))
881 }
882 }
883
884 Stmt::Expression(expr) => {
885 self.eval_expr(expr)?;
886 Ok(None)
887 }
888
889 Stmt::If {
890 condition,
891 then_branch,
892 else_if_branches,
893 else_branch,
894 } => {
895 let cond_value = self.eval_expr(condition)?;
896 if cond_value.truthy_for_condition()? {
897 for stmt in then_branch {
898 self.execute_stmt(stmt)?;
899 }
900 } else {
901 let mut executed = false;
903 for (else_if_cond, else_if_body) in else_if_branches {
904 let else_if_value = self.eval_expr(else_if_cond)?;
905 if else_if_value.truthy_for_condition()? {
906 for stmt in else_if_body {
907 self.execute_stmt(stmt)?;
908 }
909 executed = true;
910 break;
911 }
912 }
913
914 if !executed {
916 if let Some(else_stmts) = else_branch {
917 for stmt in else_stmts {
918 self.execute_stmt(stmt)?;
919 }
920 }
921 }
922 }
923 Ok(None)
924 }
925
926 Stmt::For {
927 var_name,
928 from,
929 to,
930 body,
931 ..
932 } => {
933 let from_val = self.eval_expr(from)?.as_number()?;
934 let to_val = self.eval_expr(to)?.as_number()?;
935
936 if from_val > to_val {
937 return Err(RuntimeError::InvalidForLoop(from_val, to_val));
938 }
939
940 let mut i = from_val as i64;
941 let end = to_val as i64;
942
943 while i <= end {
944 self.variables.insert(
945 var_name.clone(),
946 Variable {
947 value: Value::Int(i),
948 is_const: false,
949 is_var_persistent: false,
950 },
951 );
952
953 let control = self.execute_loop_body(body)?;
954 if control == LoopControl::Break {
955 break;
956 }
957
958 i += 1;
959 }
960
961 Ok(None)
962 }
963
964 Stmt::ForIn {
965 index_var,
966 item_var,
967 collection,
968 body,
969 ..
970 } => {
971 let collection_value = self.eval_expr(collection)?;
972 let arr = collection_value.as_array()?;
973 let arr_borrowed = arr.borrow();
974
975 for (index, item) in arr_borrowed.iter().enumerate() {
976 if let Some(idx_var) = index_var {
978 self.variables.insert(
979 idx_var.clone(),
980 Variable {
981 value: Value::Int(index as i64),
982 is_const: false,
983 is_var_persistent: false,
984 },
985 );
986 }
987
988 self.variables.insert(
990 item_var.clone(),
991 Variable {
992 value: item.clone(),
993 is_const: false,
994 is_var_persistent: false,
995 },
996 );
997
998 let control = self.execute_loop_body(body)?;
999 if control == LoopControl::Break {
1000 break;
1001 }
1002 }
1003
1004 Ok(None)
1005 }
1006
1007 Stmt::While { condition, body } => {
1008 loop {
1009 let cond_value = self.eval_expr(condition)?;
1010 if !cond_value.truthy_for_condition()? {
1011 break;
1012 }
1013
1014 let control = self.execute_loop_body(body)?;
1015 if control == LoopControl::Break {
1016 break;
1017 }
1018 }
1019 Ok(None)
1020 }
1021
1022 Stmt::Break => Err(RuntimeError::BreakOutsideLoop),
1023 Stmt::Continue => Err(RuntimeError::ContinueOutsideLoop),
1024
1025 Stmt::TypeDecl {
1026 name,
1027 fields,
1028 export,
1029 ..
1030 } => {
1031 let type_value = Value::Type {
1033 name: name.clone(),
1034 fields: fields.clone(),
1035 };
1036 self.user_types.insert(name.clone(), type_value.clone());
1037 self.variables.insert(
1038 name.clone(),
1039 Variable {
1040 value: type_value.clone(),
1041 is_const: false,
1042 is_var_persistent: false,
1043 },
1044 );
1045
1046 if *export {
1048 self.exports.insert(name.clone(), type_value);
1049 }
1050 Ok(None)
1051 }
1052
1053 Stmt::EnumDecl {
1054 name,
1055 fields,
1056 export,
1057 ..
1058 } => {
1059 let mut enum_fields = HashMap::new();
1061
1062 for field in fields {
1063 let title = field.title.clone().unwrap_or_else(|| field.name.clone());
1064 let enum_value = Value::Enum {
1065 enum_name: name.clone(),
1066 field_name: field.name.clone(),
1067 title,
1068 };
1069 enum_fields.insert(field.name.clone(), enum_value);
1070 }
1071
1072 let enum_object = Value::Object {
1073 type_name: name.clone(),
1074 fields: Rc::new(RefCell::new(enum_fields)),
1075 call: None,
1076 };
1077 self.variables.insert(
1078 name.clone(),
1079 Variable {
1080 value: enum_object.clone(),
1081 is_const: false,
1082 is_var_persistent: false,
1083 },
1084 );
1085
1086 if *export {
1088 self.exports.insert(name.clone(), enum_object);
1089 }
1090 Ok(None)
1091 }
1092
1093 Stmt::Export { item } => {
1094 match item {
1096 pine_ast::ExportItem::Type(type_name) => {
1097 if let Some(var) = self.variables.get(type_name) {
1099 self.exports.insert(type_name.clone(), var.value.clone());
1100 }
1101 }
1102 pine_ast::ExportItem::Function(func_name) => {
1103 if let Some(var) = self.variables.get(func_name) {
1105 self.exports.insert(func_name.clone(), var.value.clone());
1106 }
1107 }
1108 }
1109 Ok(None)
1110 }
1111
1112 Stmt::Import { path, alias, .. } => {
1113 let source = match &self.library_loader {
1114 Some(loader) => loader.load_library(path),
1115 None => {
1116 return Err(RuntimeError::LibraryError(
1117 "Cannot import library: no library loader configured".to_string(),
1118 ))
1119 }
1120 }
1121 .map_err(|e| {
1122 RuntimeError::LibraryError(format!("Failed to load library '{}': {}", path, e))
1123 })?;
1124
1125 let library_program = pine_parser::Parser::parse_source(&source).map_err(|e| {
1126 RuntimeError::LibraryError(format!("Failed to parse library '{}': {}", path, e))
1127 })?;
1128
1129 let mut library_interp = Interpreter::new();
1130 library_interp.execute(&library_program)?;
1131 let library_exports = library_interp.exports();
1132
1133 for (method_name, method_defs) in &library_interp.methods {
1134 for method_def in method_defs {
1135 self.methods
1136 .entry(method_name.clone())
1137 .or_default()
1138 .push(method_def.clone());
1139 }
1140 }
1141
1142 let namespace: Value<O> = Value::Object {
1143 type_name: alias.clone(),
1144 fields: Rc::new(RefCell::new(library_exports.clone())),
1145 call: None,
1146 };
1147 self.variables.insert(
1148 alias.clone(),
1149 Variable {
1150 value: namespace,
1151 is_const: false,
1152 is_var_persistent: false,
1153 },
1154 );
1155 Ok(None)
1156 }
1157
1158 Stmt::MethodDecl {
1159 name,
1160 params,
1161 body,
1162 export,
1163 ..
1164 } => {
1165 let type_name = if let Some(first_param) = params.first() {
1167 first_param.type_annotation.clone().ok_or_else(|| {
1168 RuntimeError::TypeError(
1169 "Method's first parameter must have a type annotation".to_string(),
1170 )
1171 })?
1172 } else {
1173 return Err(RuntimeError::TypeError(
1174 "Method must have at least one parameter (this)".to_string(),
1175 ));
1176 };
1177
1178 let method_def = MethodDef {
1180 type_name,
1181 params: params.clone(),
1182 body: body.clone(),
1183 };
1184
1185 self.methods
1186 .entry(name.clone())
1187 .or_default()
1188 .push(method_def);
1189
1190 if *export {
1194 }
1196
1197 Ok(None)
1198 }
1199
1200 Stmt::FunctionDecl {
1201 name,
1202 params,
1203 body,
1204 export,
1205 ..
1206 } => {
1207 let func_value = Value::Function {
1209 params: params.clone(),
1210 body: body.clone(),
1211 };
1212 self.variables.insert(
1213 name.clone(),
1214 Variable {
1215 value: func_value.clone(),
1216 is_const: false,
1217 is_var_persistent: false,
1218 },
1219 );
1220
1221 if *export {
1223 self.exports.insert(name.clone(), func_value);
1224 }
1225
1226 Ok(None)
1227 }
1228 }
1229 }
1230
1231 fn execute_loop_body(&mut self, body: &[Stmt]) -> Result<LoopControl, RuntimeError> {
1233 for stmt in body {
1234 match stmt {
1235 Stmt::Break => return Ok(LoopControl::Break),
1236 Stmt::Continue => return Ok(LoopControl::Continue),
1237 Stmt::If {
1238 condition,
1239 then_branch,
1240 else_if_branches,
1241 else_branch,
1242 } => {
1243 let cond_value = self.eval_expr(condition)?;
1244 let branch = if cond_value.truthy_for_condition()? {
1245 then_branch
1246 } else {
1247 let mut matched_branch = None;
1249 for (else_if_cond, else_if_body) in else_if_branches {
1250 let else_if_value = self.eval_expr(else_if_cond)?;
1251 if else_if_value.truthy_for_condition()? {
1252 matched_branch = Some(else_if_body);
1253 break;
1254 }
1255 }
1256
1257 if let Some(branch) = matched_branch {
1258 branch
1259 } else if let Some(else_stmts) = else_branch {
1260 else_stmts
1261 } else {
1262 continue;
1263 }
1264 };
1265
1266 let control = self.execute_loop_body(branch)?;
1267 if control != LoopControl::None {
1268 return Ok(control);
1269 }
1270 }
1271 Stmt::For { .. } | Stmt::ForIn { .. } | Stmt::While { .. } => {
1272 self.execute_stmt(stmt)?;
1274 }
1275 _ => {
1276 self.execute_stmt(stmt)?;
1277 }
1278 }
1279 }
1280 Ok(LoopControl::None)
1281 }
1282
1283 fn eval_expr(&mut self, expr: &Expr) -> Result<Value<O>, RuntimeError> {
1284 match expr {
1285 Expr::Literal(lit) => Ok(self.eval_literal(lit)),
1286
1287 Expr::Variable { name, .. } => self
1288 .variables
1289 .get(name)
1290 .map(|var| var.value.clone())
1291 .ok_or_else(|| RuntimeError::UndefinedVariable(name.clone())),
1292
1293 Expr::Binary {
1294 left, op, right, ..
1295 } => {
1296 let left_val = self.eval_expr(left)?;
1297 if matches!(op, BinOp::And | BinOp::Or) {
1304 match (op, left_val.to_bool()?) {
1305 (BinOp::And, Some(false)) => return Ok(Value::Bool(false)),
1306 (BinOp::Or, Some(true)) => return Ok(Value::Bool(true)),
1307 _ => {}
1308 }
1309 }
1310 let right_val = self.eval_expr(right)?;
1311 self.eval_binary_op(&left_val, op, &right_val)
1312 }
1313
1314 Expr::Unary { op, expr } => {
1315 let val = self.eval_expr(expr)?;
1316 self.eval_unary_op(op, &val)
1317 }
1318
1319 Expr::Ternary {
1320 condition,
1321 then_expr,
1322 else_expr,
1323 } => {
1324 let cond_val = self.eval_expr(condition)?;
1325 if cond_val.truthy_for_condition()? {
1326 self.eval_expr(then_expr)
1327 } else {
1328 self.eval_expr(else_expr)
1329 }
1330 }
1331
1332 Expr::IfExpr {
1333 condition,
1334 then_expr,
1335 else_if_branches,
1336 else_expr,
1337 } => {
1338 let cond_val = self.eval_expr(condition)?;
1339 if cond_val.truthy_for_condition()? {
1340 self.eval_expr(then_expr)
1341 } else {
1342 for (else_if_cond, else_if_expr) in else_if_branches {
1344 let else_if_val = self.eval_expr(else_if_cond)?;
1345 if else_if_val.truthy_for_condition()? {
1346 return self.eval_expr(else_if_expr);
1347 }
1348 }
1349 if let Some(expr) = else_expr {
1351 self.eval_expr(expr)
1352 } else {
1353 Ok(Value::Na)
1354 }
1355 }
1356 }
1357
1358 Expr::Array(elements) => {
1359 let values: Result<Vec<_>, _> =
1360 elements.iter().map(|e| self.eval_expr(e)).collect();
1361 Ok(Value::Array(Rc::new(RefCell::new(values?))))
1362 }
1363
1364 Expr::Index { expr, index } => {
1365 let index_val = self.eval_expr(index)?.as_number()? as usize;
1366
1367 if index_val > 0 {
1374 if let Expr::Variable { name: var_name, .. } = expr.as_ref() {
1375 if let Some(h) = self.user_series_history.get(var_name) {
1376 return Ok(if h.len() >= index_val {
1377 h[h.len() - index_val].clone()
1378 } else {
1379 Value::Na
1380 });
1381 }
1382 if let Some(var) = self.variables.get(var_name) {
1386 if !matches!(var.value, Value::Series(_) | Value::Array(_)) {
1387 return Ok(Value::Na);
1388 }
1389 }
1390 }
1391 }
1392
1393 let val = self.eval_expr(expr)?;
1394
1395 match val {
1396 Value::Array(arr_ref) => {
1397 let arr = arr_ref.borrow();
1398 arr.get(index_val)
1399 .cloned()
1400 .ok_or(RuntimeError::IndexOutOfBounds(index_val))
1401 }
1402 Value::Series(series) => {
1403 if index_val == 0 {
1408 Ok((*series.current).clone())
1409 } else {
1410 Ok(Value::Na)
1411 }
1412 }
1413 ref v => Err(RuntimeError::TypeError(format!(
1414 "Cannot index non-array/non-series value: {:?}",
1415 v
1416 ))),
1417 }
1418 }
1419
1420 Expr::Switch { value, cases } => {
1421 let switch_val = self.eval_expr(value)?;
1422
1423 for (pattern, result) in cases {
1424 let pattern_val = self.eval_expr(pattern)?;
1426
1427 if pattern_val == Value::Bool(true)
1429 && matches!(pattern, Expr::Literal(Literal::Bool(true)))
1430 {
1431 return self.eval_expr(result);
1432 }
1433
1434 if self.values_equal(&switch_val, &pattern_val)? {
1436 return self.eval_expr(result);
1437 }
1438 }
1439
1440 Ok(Value::Na)
1442 }
1443
1444 Expr::Call {
1445 callee,
1446 type_args,
1447 args,
1448 id,
1449 ..
1450 } => {
1451 if let Expr::MemberAccess { object, member, .. } = callee.as_ref() {
1453 if let Some(method_defs) = self.methods.get(member).cloned() {
1455 let obj_value = self.eval_expr(object)?;
1457
1458 let obj_type = self.get_object_type_name(&obj_value)?;
1460
1461 if let Some(method_def) =
1462 method_defs.iter().find(|m| m.type_name == obj_type)
1463 {
1464 let mut evaluated_args: Vec<EvaluatedArg<O>> =
1466 vec![EvaluatedArg::Positional(obj_value)];
1467 evaluated_args.extend(self.evaluate_arguments(args, None)?);
1468
1469 return self.call_method(
1473 &method_def.params,
1474 &method_def.body,
1475 evaluated_args,
1476 *id,
1477 );
1478 }
1479 }
1480 }
1481
1482 if let Expr::MemberAccess { object, member, .. } = callee.as_ref() {
1487 if !matches!(object.as_ref(), Expr::Call { .. }) {
1488 let receiver = self.eval_expr(object)?;
1489 if let Some(namespace) = builtin_namespace(&receiver) {
1490 if let Some(Value::BuiltinFunction(builtin_fn)) =
1491 self.namespace_member(namespace, member)
1492 {
1493 let mut evaluated_args = vec![EvaluatedArg::Positional(receiver)];
1494 evaluated_args.extend(self.evaluate_arguments(args, None)?);
1495 let call_args =
1496 FunctionCallArgs::new(type_args.clone(), evaluated_args)
1497 .with_call_id(*id);
1498 return (builtin_fn.call)(self, call_args);
1499 }
1500 }
1501 }
1502 }
1503
1504 let callee_value = self.eval_expr(callee)?;
1508 let signature = match &callee_value {
1509 Value::BuiltinFunction(builtin) => Some(builtin.signature.clone()),
1510 _ => None,
1511 };
1512 let evaluated_args = self.evaluate_arguments(args, signature.as_ref())?;
1513
1514 match callee_value {
1516 Value::Function { params, body } => {
1517 self.call_user_function(¶ms, &body, args, evaluated_args, *id)
1520 }
1521 Value::BuiltinFunction(builtin_fn) => {
1522 let call_args = FunctionCallArgs::new(type_args.clone(), evaluated_args)
1525 .with_call_id(*id);
1526 (builtin_fn.call)(self, call_args)
1527 }
1528 Value::Object {
1531 call: Some(builtin_fn),
1532 ..
1533 } => {
1534 let call_args = FunctionCallArgs::new(type_args.clone(), evaluated_args)
1535 .with_call_id(*id);
1536 (builtin_fn)(self, call_args)
1537 }
1538 Value::Na => {
1540 let is_na = matches!(
1541 evaluated_args.first(),
1542 Some(EvaluatedArg::Positional(Value::Na)) | None
1543 );
1544 Ok(Value::Bool(is_na))
1545 }
1546 _ => Err(RuntimeError::TypeError(
1547 "Attempted to call a non-function value".to_string(),
1548 )),
1549 }
1550 }
1551
1552 Expr::MemberAccess { object, member, .. } => {
1553 let obj_value = match object.as_ref() {
1556 Expr::Variable { name, .. }
1557 if (member == "new" || member == "copy")
1558 && self.user_types.contains_key(name) =>
1559 {
1560 self.user_types[name].clone()
1561 }
1562 _ => self.eval_expr(object)?,
1563 };
1564 match obj_value {
1565 Value::Object { fields, .. } => {
1566 let obj = fields.borrow();
1567 obj.get(member).cloned().ok_or_else(|| {
1568 RuntimeError::TypeError(format!("Object has no member '{}'", member))
1569 })
1570 }
1571 Value::Type { name, fields } => {
1572 if member == "new" {
1574 Ok(Value::BuiltinFunction(Builtin::untyped(
1576 Self::create_constructor(name, fields),
1577 )))
1578 } else if member == "copy" {
1579 Ok(Value::BuiltinFunction(Builtin::untyped(
1581 Self::create_copy_function(),
1582 )))
1583 } else {
1584 Err(RuntimeError::TypeError(format!(
1585 "Type '{}' has no member '{}' (only 'new' and 'copy' are supported)",
1586 name, member
1587 )))
1588 }
1589 }
1590 _ => Err(RuntimeError::TypeError(format!(
1591 "Cannot access member '{}' on non-object value",
1592 member
1593 ))),
1594 }
1595 }
1596
1597 Expr::Function { params, body } => {
1598 Ok(Value::Function {
1600 params: params.clone(),
1601 body: body.clone(),
1602 })
1603 }
1604 }
1605 }
1606
1607 fn eval_literal(&self, lit: &Literal) -> Value<O> {
1608 match lit {
1609 Literal::Int(n) => Value::Int(*n),
1610 Literal::Number(n) => Value::Number(*n),
1611 Literal::String(s) => Value::String(s.clone()),
1612 Literal::Bool(b) => Value::Bool(*b),
1613 Literal::Na => Value::Na,
1614 Literal::HexColor(hex) => Value::String(hex.clone()),
1615 }
1616 }
1617
1618 fn eval_binary_op(
1619 &self,
1620 left: &Value<O>,
1621 op: &BinOp,
1622 right: &Value<O>,
1623 ) -> Result<Value<O>, RuntimeError> {
1624 match op {
1625 BinOp::Add => {
1626 if matches!(left, Value::String(_)) || matches!(right, Value::String(_)) {
1628 Ok(Value::String(format!(
1629 "{}{}",
1630 left.as_string()?,
1631 right.as_string()?
1632 )))
1633 } else {
1634 numeric_op(left, right, |a, b| Some(a + b))
1635 }
1636 }
1637
1638 BinOp::Sub => numeric_op(left, right, |a, b| Some(a - b)),
1639
1640 BinOp::Mul => numeric_op(left, right, |a, b| Some(a * b)),
1641
1642 BinOp::Div => numeric_op(left, right, Num::checked_div),
1645
1646 BinOp::Mod => numeric_op(left, right, Num::checked_rem),
1647
1648 BinOp::Eq => {
1656 if is_na_operand(left) || is_na_operand(right) {
1657 return Ok(Value::Na);
1658 }
1659 Ok(Value::Bool(self.values_equal(left, right)?))
1660 }
1661
1662 BinOp::NotEq => {
1663 if is_na_operand(left) || is_na_operand(right) {
1664 return Ok(Value::Na);
1665 }
1666 Ok(Value::Bool(!self.values_equal(left, right)?))
1667 }
1668
1669 BinOp::Less => {
1675 if is_na_operand(left) || is_na_operand(right) {
1676 return Ok(Value::Na);
1677 }
1678 match (left.to_number()?, right.to_number()?) {
1679 (Some(l), Some(r)) => Ok(Value::Bool(l < r)),
1680 _ => Ok(Value::Na),
1681 }
1682 }
1683
1684 BinOp::Greater => {
1685 if is_na_operand(left) || is_na_operand(right) {
1686 return Ok(Value::Na);
1687 }
1688 match (left.to_number()?, right.to_number()?) {
1689 (Some(l), Some(r)) => Ok(Value::Bool(l > r)),
1690 _ => Ok(Value::Na),
1691 }
1692 }
1693
1694 BinOp::LessEq => {
1695 if is_na_operand(left) || is_na_operand(right) {
1696 return Ok(Value::Na);
1697 }
1698 match (left.to_number()?, right.to_number()?) {
1699 (Some(l), Some(r)) => Ok(Value::Bool(l <= r)),
1700 _ => Ok(Value::Na),
1701 }
1702 }
1703
1704 BinOp::GreaterEq => {
1705 if is_na_operand(left) || is_na_operand(right) {
1706 return Ok(Value::Na);
1707 }
1708 match (left.to_number()?, right.to_number()?) {
1709 (Some(l), Some(r)) => Ok(Value::Bool(l >= r)),
1710 _ => Ok(Value::Na),
1711 }
1712 }
1713
1714 BinOp::And => match (left.to_bool()?, right.to_bool()?) {
1716 (Some(false), _) | (_, Some(false)) => Ok(Value::Bool(false)),
1717 (Some(true), Some(true)) => Ok(Value::Bool(true)),
1718 _ => Ok(Value::Na),
1719 },
1720
1721 BinOp::Or => match (left.to_bool()?, right.to_bool()?) {
1723 (Some(true), _) | (_, Some(true)) => Ok(Value::Bool(true)),
1724 (Some(false), Some(false)) => Ok(Value::Bool(false)),
1725 _ => Ok(Value::Na),
1726 },
1727 }
1728 }
1729
1730 fn eval_unary_op(&self, op: &UnOp, val: &Value<O>) -> Result<Value<O>, RuntimeError> {
1731 match op {
1732 UnOp::Neg => match val.as_int() {
1734 Some(n) => Ok(Value::Int(-n)),
1735 None => match val.to_number()? {
1736 Some(n) => Ok(Value::Number(-n)),
1737 None => Ok(Value::Na),
1738 },
1739 },
1740 UnOp::Not => match val.to_bool()? {
1741 Some(b) => Ok(Value::Bool(!b)),
1742 None => Ok(Value::Na),
1743 },
1744 }
1745 }
1746
1747 fn values_equal(&self, left: &Value<O>, right: &Value<O>) -> Result<bool, RuntimeError> {
1748 match (left, right) {
1749 (Value::Int(l), Value::Int(r)) => Ok(l == r),
1750 (Value::Int(l), Value::Number(r)) | (Value::Number(r), Value::Int(l)) => {
1752 Ok((*l as f64 - r).abs() < f64::EPSILON)
1753 }
1754 (Value::Number(l), Value::Number(r)) => Ok((l - r).abs() < f64::EPSILON),
1755 (Value::String(l), Value::String(r)) => Ok(l == r),
1756 (Value::Bool(l), Value::Bool(r)) => Ok(l == r),
1757 (Value::Na, Value::Na) => Ok(true),
1758 (
1759 Value::Enum {
1760 enum_name: a_enum,
1761 field_name: a_field,
1762 ..
1763 },
1764 Value::Enum {
1765 enum_name: b_enum,
1766 field_name: b_field,
1767 ..
1768 },
1769 ) => Ok(a_enum == b_enum && a_field == b_field),
1770 _ => Ok(false),
1771 }
1772 }
1773
1774 fn is_const_expr(&self, expr: &Expr) -> bool {
1776 match expr {
1777 Expr::Literal(_) => true,
1779 Expr::Variable { name, .. } => self
1781 .variables
1782 .get(name)
1783 .map(|var| var.is_const)
1784 .unwrap_or(false),
1785 Expr::MemberAccess { object, .. } => self.is_const_expr(object),
1787 _ => false,
1789 }
1790 }
1791
1792 fn call_user_function(
1793 &mut self,
1794 params: &[pine_ast::FunctionParam],
1795 body: &[Stmt],
1796 arg_exprs: &[Argument],
1797 args: Vec<EvaluatedArg<O>>,
1798 call_id: u32,
1799 ) -> Result<Value<O>, RuntimeError> {
1800 let mut positional_values = Vec::new();
1802 let mut positional_exprs = Vec::new();
1803
1804 for (i, arg) in args.iter().enumerate() {
1805 match arg {
1806 EvaluatedArg::Positional(value) => {
1807 positional_values.push(value.clone());
1808 if let Some(Argument::Positional(expr)) = arg_exprs.get(i) {
1809 positional_exprs.push(expr);
1810 }
1811 }
1812 EvaluatedArg::Named { .. } => {
1813 return Err(RuntimeError::TypeError(
1814 "User-defined functions do not support named arguments yet".to_string(),
1815 ))
1816 }
1817 }
1818 }
1819
1820 if positional_values.len() != params.len() {
1822 return Err(RuntimeError::TypeError(format!(
1823 "Expected {} arguments, got {}",
1824 params.len(),
1825 positional_values.len()
1826 )));
1827 }
1828
1829 for (i, param) in params.iter().enumerate() {
1831 if matches!(param.type_qualifier, Some(pine_ast::TypeQualifier::Const)) {
1832 if let Some(arg_expr) = positional_exprs.get(i) {
1833 if !self.is_const_expr(arg_expr) {
1834 return Err(RuntimeError::TypeError(format!(
1835 "Parameter '{}' requires a const argument, but received a non-const value",
1836 param.name
1837 )));
1838 }
1839 }
1840 }
1841 }
1842
1843 let param_bindings: Vec<(String, Variable<O>)> = params
1846 .iter()
1847 .zip(positional_values)
1848 .map(|(param, value)| {
1849 let is_const = matches!(param.type_qualifier, Some(pine_ast::TypeQualifier::Const));
1850 (
1851 param.name.clone(),
1852 Variable {
1853 value,
1854 is_const,
1855 is_var_persistent: false,
1856 },
1857 )
1858 })
1859 .collect();
1860
1861 self.run_call_site_body(call_id, param_bindings, body)
1862 }
1863
1864 fn run_call_site_body(
1872 &mut self,
1873 call_id: u32,
1874 param_bindings: Vec<(String, Variable<O>)>,
1875 body: &[Stmt],
1876 ) -> Result<Value<O>, RuntimeError> {
1877 let param_names: std::collections::HashSet<String> =
1878 param_bindings.iter().map(|(n, _)| n.clone()).collect();
1879
1880 let saved_vars = self.variables.clone();
1882
1883 if call_id != 0 {
1887 if let Some(local_state) = self.function_local_state.get(&call_id) {
1888 for (var_name, var) in local_state {
1889 if !param_names.contains(var_name) {
1890 self.variables.insert(var_name.clone(), var.clone());
1891 }
1892 }
1893 }
1894 }
1895
1896 for (name, var) in param_bindings {
1898 self.variables.insert(name, var);
1899 }
1900
1901 let prev_call_id = self.current_call_id;
1906 self.current_call_id = call_id;
1907 let mut result: Value<O> = Value::Na;
1908 for stmt in body {
1909 if let Some(return_value) = self.execute_stmt(stmt)? {
1910 result = return_value;
1911 } else if let Stmt::Expression(expr) = stmt {
1912 result = self.eval_expr(expr)?;
1914 }
1915 }
1916 self.current_call_id = prev_call_id;
1917
1918 let call_vars = std::mem::replace(&mut self.variables, saved_vars);
1922 if call_id != 0 {
1923 let mut assigned: std::collections::HashSet<String> = std::collections::HashSet::new();
1930 collect_assigned_names(body, &mut assigned);
1931 let local_state: HashMap<String, Variable<O>> = call_vars
1932 .into_iter()
1933 .filter(|(k, _)| !param_names.contains(k) && assigned.contains(k))
1934 .collect();
1935 self.function_local_state.insert(call_id, local_state);
1936 }
1937
1938 Ok(result)
1939 }
1940
1941 fn get_object_type_name(&self, value: &Value<O>) -> Result<String, RuntimeError> {
1943 match value {
1944 Value::Object { type_name, .. } => Ok(type_name.clone()),
1945 _ => Err(RuntimeError::TypeError(
1946 "Cannot determine type of non-object value".to_string(),
1947 )),
1948 }
1949 }
1950
1951 fn call_method(
1953 &mut self,
1954 params: &[MethodParam],
1955 body: &[Stmt],
1956 args: Vec<EvaluatedArg<O>>,
1957 call_id: u32,
1958 ) -> Result<Value<O>, RuntimeError> {
1959 let mut positional_idx = 0;
1963 let mut param_bindings: Vec<(String, Variable<O>)> = Vec::with_capacity(params.len());
1964
1965 for param in params {
1966 let param_value = if positional_idx < args.len() {
1967 match &args[positional_idx] {
1968 EvaluatedArg::Positional(value) => {
1969 positional_idx += 1;
1970 value.clone()
1971 }
1972 EvaluatedArg::Named { name, value } => {
1973 if name == ¶m.name {
1974 positional_idx += 1;
1975 value.clone()
1976 } else if let Some(default_expr) = ¶m.default_value {
1977 self.eval_expr(default_expr)?
1978 } else {
1979 Value::Na
1980 }
1981 }
1982 }
1983 } else if let Some(default_expr) = ¶m.default_value {
1984 self.eval_expr(default_expr)?
1985 } else {
1986 Value::Na
1987 };
1988
1989 param_bindings.push((
1990 param.name.clone(),
1991 Variable {
1992 value: param_value,
1993 is_const: false,
1994 is_var_persistent: false,
1995 },
1996 ));
1997 }
1998
1999 self.run_call_site_body(call_id, param_bindings, body)
2000 }
2001
2002 fn create_constructor(type_name: String, fields: Vec<TypeField>) -> BuiltinFn<O> {
2004 Rc::new(
2005 move |interp: &mut Interpreter<O>, call_args: FunctionCallArgs<O>| {
2006 let mut instance_fields = HashMap::new();
2007
2008 let mut positional_idx = 0;
2010
2011 for arg in &call_args.args {
2012 match arg {
2013 EvaluatedArg::Positional(value) => {
2014 if positional_idx < fields.len() {
2016 let field = &fields[positional_idx];
2017 instance_fields.insert(field.name.clone(), value.clone());
2018 positional_idx += 1;
2019 } else {
2020 return Err(RuntimeError::TypeError(format!(
2021 "Too many arguments for type '{}' (expected {} fields)",
2022 type_name,
2023 fields.len()
2024 )));
2025 }
2026 }
2027 EvaluatedArg::Named { name, value } => {
2028 if let Some(field) = fields.iter().find(|f| f.name == *name) {
2030 instance_fields.insert(field.name.clone(), value.clone());
2031 } else {
2032 return Err(RuntimeError::TypeError(format!(
2033 "Type '{}' has no field '{}'",
2034 type_name, name
2035 )));
2036 }
2037 }
2038 }
2039 }
2040
2041 for field in &fields {
2043 if !instance_fields.contains_key(&field.name) {
2044 if let Some(default_expr) = &field.default_value {
2045 let default_val = interp.eval_expr(default_expr)?;
2046 instance_fields.insert(field.name.clone(), default_val);
2047 } else {
2048 instance_fields.insert(field.name.clone(), Value::Na);
2050 }
2051 }
2052 }
2053
2054 Ok(Value::Object {
2055 type_name: type_name.clone(),
2056 fields: Rc::new(RefCell::new(instance_fields)),
2057 call: None,
2058 })
2059 },
2060 )
2061 }
2062
2063 fn create_copy_function() -> BuiltinFn<O> {
2065 Rc::new(
2066 |_interp: &mut Interpreter<O>, call_args: FunctionCallArgs<O>| {
2067 if call_args.args.len() != 1 {
2069 return Err(RuntimeError::TypeError(
2070 "copy() expects exactly one argument".to_string(),
2071 ));
2072 }
2073
2074 match &call_args.args[0] {
2075 EvaluatedArg::Positional(value) => {
2076 if let Value::Object {
2077 type_name,
2078 fields,
2079 call,
2080 } = value
2081 {
2082 let obj = fields.borrow();
2084 let copied_fields = obj.clone();
2085 Ok(Value::Object {
2086 type_name: type_name.clone(),
2087 fields: Rc::new(RefCell::new(copied_fields)),
2088 call: call.clone(),
2089 })
2090 } else {
2091 Err(RuntimeError::TypeError(
2092 "copy() expects an object argument".to_string(),
2093 ))
2094 }
2095 }
2096 EvaluatedArg::Named { .. } => Err(RuntimeError::TypeError(
2097 "copy() does not accept named arguments".to_string(),
2098 )),
2099 }
2100 },
2101 )
2102 }
2103}
2104
2105impl<O: PineOutput> Default for Interpreter<O> {
2106 fn default() -> Self {
2107 Self::new()
2108 }
2109}