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 pub 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 broker_factory: Option<Box<dyn pine_broker::BrokerFactory>>,
465 pub request_provider: Option<Rc<dyn pine_core::DataProvider>>,
467 pub chart_period: Option<i64>,
468}
469
470fn collect_assigned_names(body: &[Stmt], out: &mut std::collections::HashSet<String>) {
475 for s in body {
476 match s {
477 Stmt::VarDecl { name, .. } => {
478 out.insert(name.clone());
479 }
480 Stmt::Assignment {
481 target: Expr::Variable { name: n, .. },
482 ..
483 } => {
484 out.insert(n.clone());
485 }
486 Stmt::TupleAssignment { names, .. } => {
487 for n in names {
488 out.insert(n.clone());
489 }
490 }
491 Stmt::If {
492 then_branch,
493 else_if_branches,
494 else_branch,
495 ..
496 } => {
497 collect_assigned_names(then_branch, out);
498 for (_, b) in else_if_branches {
499 collect_assigned_names(b, out);
500 }
501 if let Some(b) = else_branch {
502 collect_assigned_names(b, out);
503 }
504 }
505 Stmt::For { var_name, body, .. } => {
506 out.insert(var_name.clone());
507 collect_assigned_names(body, out);
508 }
509 Stmt::While { body, .. } | Stmt::ForIn { body, .. } => {
510 collect_assigned_names(body, out)
511 }
512 _ => {}
513 }
514 }
515}
516
517fn builtin_namespace<O: PineOutput>(value: &Value<O>) -> Option<&'static str> {
520 match value {
521 Value::Array(_) => Some("array"),
522 Value::Matrix { .. } => Some("matrix"),
523 _ => None,
524 }
525}
526
527fn is_na_operand<O: PineOutput>(v: &Value<O>) -> bool {
531 matches!(v, Value::Na) || matches!(v, Value::Number(n) if n.is_nan())
532}
533
534impl<O: PineOutput> Interpreter<O> {
535 pub fn new() -> Self {
536 Self {
537 variables: HashMap::new(),
538 user_types: HashMap::new(),
539 methods: HashMap::new(),
540 library_loader: None,
541 exports: HashMap::new(),
542 output: O::default(),
543 user_series_history: HashMap::new(),
544 function_local_state: HashMap::new(),
545 var_decls_initialized: HashMap::new(),
546 current_call_id: 0,
547 bar_seq: 0,
548 broker: None,
549 broker_factory: Some(Box::new(pine_broker::DefaultBrokerFactory)),
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 pub fn set_const_variables(&mut self, variables: HashMap<String, Value<O>>) {
673 for (name, value) in variables {
674 self.set_const_variable(&name, value);
675 }
676 }
677
678 fn evaluate_arguments(
682 &mut self,
683 args: &[Argument],
684 signature: Option<&BuiltinSignature>,
685 ) -> Result<Vec<EvaluatedArg<O>>, RuntimeError> {
686 let mut evaluated_args = Vec::new();
687 let mut seen_named = false;
688 let mut positional_index = 0;
689
690 for arg in args {
691 match arg {
692 Argument::Positional(expr) => {
693 if seen_named {
694 return Err(RuntimeError::TypeError(
695 "Positional arguments cannot follow named arguments".to_string(),
696 ));
697 }
698 let lazy = signature.is_some_and(|s| s.positional_is_lazy(positional_index));
699 let value = self.eval_or_capture(expr, lazy)?;
700 evaluated_args.push(EvaluatedArg::Positional(value));
701 positional_index += 1;
702 }
703 Argument::Named { name, value: expr } => {
704 seen_named = true;
705 let lazy = signature.is_some_and(|s| s.named_is_lazy(name));
706 let value = self.eval_or_capture(expr, lazy)?;
707 evaluated_args.push(EvaluatedArg::Named {
708 name: name.clone(),
709 value,
710 });
711 }
712 }
713 }
714
715 Ok(evaluated_args)
716 }
717
718 fn eval_or_capture(&mut self, expr: &Expr, lazy: bool) -> Result<Value<O>, RuntimeError> {
721 if lazy {
722 Ok(Value::Expr(Rc::new(expr.clone())))
723 } else {
724 self.eval_expr(expr)
725 }
726 }
727
728 fn execute_stmt(&mut self, stmt: &Stmt) -> Result<Option<Value<O>>, RuntimeError> {
729 match stmt {
730 Stmt::VarDecl {
731 name,
732 type_qualifier,
733 type_annotation: _,
734 initializer,
735 var_kind,
738 ..
739 } => {
740 let is_var_persistent = var_kind.is_persistent();
741 if is_var_persistent {
748 let init_key = (self.current_call_id, name.clone());
749 if self.var_decls_initialized.contains_key(&init_key) {
750 return Ok(None);
751 }
752 self.var_decls_initialized.insert(init_key, self.bar_seq);
753 }
754 if !is_var_persistent {
758 if let Some(existing) = self.variables.get(name) {
759 push_history(&mut self.user_series_history, name, existing.value.clone());
760 }
761 }
762 let value = if let Some(init_expr) = initializer {
763 self.eval_expr(init_expr)?
764 } else {
765 Value::Na
766 };
767 let is_const = matches!(type_qualifier, Some(pine_ast::TypeQualifier::Const));
768 self.variables.insert(
769 name.clone(),
770 Variable {
771 value,
772 is_const,
773 is_var_persistent,
774 },
775 );
776 Ok(None)
777 }
778
779 Stmt::Assignment { target, value } => {
780 if let Expr::Variable { name, .. } = target {
789 if let Some(var) = self.variables.get(name) {
790 let born_this_bar = self
791 .var_decls_initialized
792 .get(&(self.current_call_id, name.clone()))
793 == Some(&self.bar_seq);
794 if var.is_var_persistent && !born_this_bar {
795 push_history(&mut self.user_series_history, name, var.value.clone());
796 }
797 }
798 }
799
800 let val = self.eval_expr(value)?;
801 match target {
802 Expr::Variable { name, .. } => {
803 let (is_const, is_var_persistent) =
805 if let Some(var) = self.variables.get(name) {
806 if var.is_const {
807 return Err(RuntimeError::ConstReassignment(name.clone()));
808 }
809 if !var.is_var_persistent {
810 push_history(
812 &mut self.user_series_history,
813 name,
814 var.value.clone(),
815 );
816 }
817 (false, var.is_var_persistent)
819 } else {
820 (false, false)
821 };
822
823 self.variables.insert(
824 name.clone(),
825 Variable {
826 value: val,
827 is_const,
828 is_var_persistent,
829 },
830 );
831 Ok(None)
832 }
833 Expr::MemberAccess { object, member, .. } => {
834 if let Expr::Variable { name: var_name, .. } = object.as_ref() {
836 if let Some(var) = self.variables.get(var_name) {
837 if var.is_const {
838 return Err(RuntimeError::ConstReassignment(format!(
839 "{}.{}",
840 var_name, member
841 )));
842 }
843 }
844 }
845
846 let obj_value = self.eval_expr(object)?;
848
849 if let Value::Object { fields, .. } = obj_value {
850 let mut obj = fields.borrow_mut();
851 obj.insert(member.clone(), val);
852 Ok(None)
853 } else {
854 Err(RuntimeError::TypeError(
855 "Cannot assign to member of non-object value".to_string(),
856 ))
857 }
858 }
859 _ => Err(RuntimeError::TypeError(
860 "Invalid assignment target".to_string(),
861 )),
862 }
863 }
864
865 Stmt::TupleAssignment { names, value, .. } => {
866 let val = self.eval_expr(value)?;
867 if let Value::Array(arr_ref) = val {
868 let arr = arr_ref.borrow();
869 for (i, name) in names.iter().enumerate() {
870 if let Some(var) = self.variables.get(name) {
872 push_history(&mut self.user_series_history, name, var.value.clone());
873 }
874 let element_val = arr.get(i).cloned().unwrap_or(Value::Na);
875 self.variables.insert(
876 name.clone(),
877 Variable {
878 value: element_val,
879 is_const: false,
880 is_var_persistent: false,
881 },
882 );
883 }
884 Ok(None)
885 } else {
886 Err(RuntimeError::TypeError(
887 "Expected array for tuple destructuring".to_string(),
888 ))
889 }
890 }
891
892 Stmt::Expression(expr) => {
893 self.eval_expr(expr)?;
894 Ok(None)
895 }
896
897 Stmt::If {
898 condition,
899 then_branch,
900 else_if_branches,
901 else_branch,
902 } => {
903 let cond_value = self.eval_expr(condition)?;
904 if cond_value.truthy_for_condition()? {
905 for stmt in then_branch {
906 self.execute_stmt(stmt)?;
907 }
908 } else {
909 let mut executed = false;
911 for (else_if_cond, else_if_body) in else_if_branches {
912 let else_if_value = self.eval_expr(else_if_cond)?;
913 if else_if_value.truthy_for_condition()? {
914 for stmt in else_if_body {
915 self.execute_stmt(stmt)?;
916 }
917 executed = true;
918 break;
919 }
920 }
921
922 if !executed {
924 if let Some(else_stmts) = else_branch {
925 for stmt in else_stmts {
926 self.execute_stmt(stmt)?;
927 }
928 }
929 }
930 }
931 Ok(None)
932 }
933
934 Stmt::For {
935 var_name,
936 from,
937 to,
938 step,
939 body,
940 ..
941 } => {
942 let from_val = self.eval_expr(from)?.as_number()?;
943 let to_val = self.eval_expr(to)?.as_number()?;
944
945 let step_val = match step {
948 Some(expr) => self.eval_expr(expr)?.as_number()?.abs(),
949 None => 1.0,
950 };
951 if step_val == 0.0 {
952 return Err(RuntimeError::InvalidForLoop(from_val, to_val));
953 }
954 let down = from_val > to_val;
955
956 let mut i = from_val as i64;
957 let end = to_val as i64;
958 let step = step_val as i64;
959
960 while if down { i >= end } else { i <= end } {
961 self.variables.insert(
962 var_name.clone(),
963 Variable {
964 value: Value::Int(i),
965 is_const: false,
966 is_var_persistent: false,
967 },
968 );
969
970 let control = self.execute_loop_body(body)?;
971 if control == LoopControl::Break {
972 break;
973 }
974
975 if down {
976 i -= step;
977 } else {
978 i += step;
979 }
980 }
981
982 Ok(None)
983 }
984
985 Stmt::ForIn {
986 index_var,
987 item_var,
988 collection,
989 body,
990 ..
991 } => {
992 let collection_value = self.eval_expr(collection)?;
993 let arr = collection_value.as_array()?;
994 let arr_borrowed = arr.borrow();
995
996 for (index, item) in arr_borrowed.iter().enumerate() {
997 if let Some(idx_var) = index_var {
999 self.variables.insert(
1000 idx_var.clone(),
1001 Variable {
1002 value: Value::Int(index as i64),
1003 is_const: false,
1004 is_var_persistent: false,
1005 },
1006 );
1007 }
1008
1009 self.variables.insert(
1011 item_var.clone(),
1012 Variable {
1013 value: item.clone(),
1014 is_const: false,
1015 is_var_persistent: false,
1016 },
1017 );
1018
1019 let control = self.execute_loop_body(body)?;
1020 if control == LoopControl::Break {
1021 break;
1022 }
1023 }
1024
1025 Ok(None)
1026 }
1027
1028 Stmt::While { condition, body } => {
1029 loop {
1030 let cond_value = self.eval_expr(condition)?;
1031 if !cond_value.truthy_for_condition()? {
1032 break;
1033 }
1034
1035 let control = self.execute_loop_body(body)?;
1036 if control == LoopControl::Break {
1037 break;
1038 }
1039 }
1040 Ok(None)
1041 }
1042
1043 Stmt::Break { .. } => Err(RuntimeError::BreakOutsideLoop),
1044 Stmt::Continue { .. } => Err(RuntimeError::ContinueOutsideLoop),
1045
1046 Stmt::TypeDecl {
1047 name,
1048 fields,
1049 export,
1050 ..
1051 } => {
1052 let type_value = Value::Type {
1054 name: name.clone(),
1055 fields: fields.clone(),
1056 };
1057 self.user_types.insert(name.clone(), type_value.clone());
1058 self.variables.insert(
1059 name.clone(),
1060 Variable {
1061 value: type_value.clone(),
1062 is_const: false,
1063 is_var_persistent: false,
1064 },
1065 );
1066
1067 if *export {
1069 self.exports.insert(name.clone(), type_value);
1070 }
1071 Ok(None)
1072 }
1073
1074 Stmt::EnumDecl {
1075 name,
1076 fields,
1077 export,
1078 ..
1079 } => {
1080 let mut enum_fields = HashMap::new();
1082
1083 for field in fields {
1084 let title = field.title.clone().unwrap_or_else(|| field.name.clone());
1085 let enum_value = Value::Enum {
1086 enum_name: name.clone(),
1087 field_name: field.name.clone(),
1088 title,
1089 };
1090 enum_fields.insert(field.name.clone(), enum_value);
1091 }
1092
1093 let enum_object = Value::Object {
1094 type_name: name.clone(),
1095 fields: Rc::new(RefCell::new(enum_fields)),
1096 call: None,
1097 };
1098 self.variables.insert(
1099 name.clone(),
1100 Variable {
1101 value: enum_object.clone(),
1102 is_const: false,
1103 is_var_persistent: false,
1104 },
1105 );
1106
1107 if *export {
1109 self.exports.insert(name.clone(), enum_object);
1110 }
1111 Ok(None)
1112 }
1113
1114 Stmt::Export { item } => {
1115 match item {
1117 pine_ast::ExportItem::Type(type_name) => {
1118 if let Some(var) = self.variables.get(type_name) {
1120 self.exports.insert(type_name.clone(), var.value.clone());
1121 }
1122 }
1123 pine_ast::ExportItem::Function(func_name) => {
1124 if let Some(var) = self.variables.get(func_name) {
1126 self.exports.insert(func_name.clone(), var.value.clone());
1127 }
1128 }
1129 }
1130 Ok(None)
1131 }
1132
1133 Stmt::Import { path, alias, .. } => {
1134 let source = match &self.library_loader {
1135 Some(loader) => loader.load_library(path),
1136 None => {
1137 return Err(RuntimeError::LibraryError(
1138 "Cannot import library: no library loader configured".to_string(),
1139 ))
1140 }
1141 }
1142 .map_err(|e| {
1143 RuntimeError::LibraryError(format!("Failed to load library '{}': {}", path, e))
1144 })?;
1145
1146 let library_program = pine_parser::Parser::parse_source(&source).map_err(|e| {
1147 RuntimeError::LibraryError(format!("Failed to parse library '{}': {}", path, e))
1148 })?;
1149
1150 let mut library_interp = Interpreter::new();
1153 for (name, value) in self.snapshot() {
1154 library_interp.set_variable(&name, value);
1155 }
1156 library_interp.execute(&library_program)?;
1157 let library_exports = library_interp.exports();
1158
1159 for (method_name, method_defs) in &library_interp.methods {
1160 for method_def in method_defs {
1161 self.methods
1162 .entry(method_name.clone())
1163 .or_default()
1164 .push(method_def.clone());
1165 }
1166 }
1167
1168 let namespace: Value<O> = Value::Object {
1169 type_name: alias.clone(),
1170 fields: Rc::new(RefCell::new(library_exports.clone())),
1171 call: None,
1172 };
1173 self.variables.insert(
1174 alias.clone(),
1175 Variable {
1176 value: namespace,
1177 is_const: false,
1178 is_var_persistent: false,
1179 },
1180 );
1181 Ok(None)
1182 }
1183
1184 Stmt::MethodDecl {
1185 name,
1186 params,
1187 body,
1188 export,
1189 ..
1190 } => {
1191 let type_name = if let Some(first_param) = params.first() {
1193 first_param.type_annotation.clone().ok_or_else(|| {
1194 RuntimeError::TypeError(
1195 "Method's first parameter must have a type annotation".to_string(),
1196 )
1197 })?
1198 } else {
1199 return Err(RuntimeError::TypeError(
1200 "Method must have at least one parameter (this)".to_string(),
1201 ));
1202 };
1203
1204 let method_def = MethodDef {
1206 type_name,
1207 params: params.clone(),
1208 body: body.clone(),
1209 };
1210
1211 self.methods
1212 .entry(name.clone())
1213 .or_default()
1214 .push(method_def);
1215
1216 if *export {
1220 }
1222
1223 Ok(None)
1224 }
1225
1226 Stmt::FunctionDecl {
1227 name,
1228 params,
1229 body,
1230 export,
1231 ..
1232 } => {
1233 let func_value = Value::Function {
1235 params: params.clone(),
1236 body: body.clone(),
1237 };
1238 self.variables.insert(
1239 name.clone(),
1240 Variable {
1241 value: func_value.clone(),
1242 is_const: false,
1243 is_var_persistent: false,
1244 },
1245 );
1246
1247 if *export {
1249 self.exports.insert(name.clone(), func_value);
1250 }
1251
1252 Ok(None)
1253 }
1254 }
1255 }
1256
1257 fn execute_loop_body(&mut self, body: &[Stmt]) -> Result<LoopControl, RuntimeError> {
1259 for stmt in body {
1260 match stmt {
1261 Stmt::Break { .. } => return Ok(LoopControl::Break),
1262 Stmt::Continue { .. } => return Ok(LoopControl::Continue),
1263 Stmt::If {
1264 condition,
1265 then_branch,
1266 else_if_branches,
1267 else_branch,
1268 } => {
1269 let cond_value = self.eval_expr(condition)?;
1270 let branch = if cond_value.truthy_for_condition()? {
1271 then_branch
1272 } else {
1273 let mut matched_branch = None;
1275 for (else_if_cond, else_if_body) in else_if_branches {
1276 let else_if_value = self.eval_expr(else_if_cond)?;
1277 if else_if_value.truthy_for_condition()? {
1278 matched_branch = Some(else_if_body);
1279 break;
1280 }
1281 }
1282
1283 if let Some(branch) = matched_branch {
1284 branch
1285 } else if let Some(else_stmts) = else_branch {
1286 else_stmts
1287 } else {
1288 continue;
1289 }
1290 };
1291
1292 let control = self.execute_loop_body(branch)?;
1293 if control != LoopControl::None {
1294 return Ok(control);
1295 }
1296 }
1297 Stmt::For { .. } | Stmt::ForIn { .. } | Stmt::While { .. } => {
1298 self.execute_stmt(stmt)?;
1300 }
1301 _ => {
1302 self.execute_stmt(stmt)?;
1303 }
1304 }
1305 }
1306 Ok(LoopControl::None)
1307 }
1308
1309 fn eval_expr(&mut self, expr: &Expr) -> Result<Value<O>, RuntimeError> {
1310 match expr {
1311 Expr::Literal(lit) => Ok(self.eval_literal(lit)),
1312
1313 Expr::Variable { name, .. } => self
1314 .variables
1315 .get(name)
1316 .map(|var| var.value.clone())
1317 .ok_or_else(|| RuntimeError::UndefinedVariable(name.clone())),
1318
1319 Expr::Binary {
1320 left, op, right, ..
1321 } => {
1322 let left_val = self.eval_expr(left)?;
1323 if matches!(op, BinOp::And | BinOp::Or) {
1330 match (op, left_val.to_bool()?) {
1331 (BinOp::And, Some(false)) => return Ok(Value::Bool(false)),
1332 (BinOp::Or, Some(true)) => return Ok(Value::Bool(true)),
1333 _ => {}
1334 }
1335 }
1336 let right_val = self.eval_expr(right)?;
1337 self.eval_binary_op(&left_val, op, &right_val)
1338 }
1339
1340 Expr::Unary { op, expr } => {
1341 let val = self.eval_expr(expr)?;
1342 self.eval_unary_op(op, &val)
1343 }
1344
1345 Expr::Ternary {
1346 condition,
1347 then_expr,
1348 else_expr,
1349 } => {
1350 let cond_val = self.eval_expr(condition)?;
1351 if cond_val.truthy_for_condition()? {
1352 self.eval_expr(then_expr)
1353 } else {
1354 self.eval_expr(else_expr)
1355 }
1356 }
1357
1358 Expr::IfExpr {
1359 condition,
1360 then_expr,
1361 else_if_branches,
1362 else_expr,
1363 } => {
1364 let cond_val = self.eval_expr(condition)?;
1365 if cond_val.truthy_for_condition()? {
1366 self.eval_expr(then_expr)
1367 } else {
1368 for (else_if_cond, else_if_expr) in else_if_branches {
1370 let else_if_val = self.eval_expr(else_if_cond)?;
1371 if else_if_val.truthy_for_condition()? {
1372 return self.eval_expr(else_if_expr);
1373 }
1374 }
1375 if let Some(expr) = else_expr {
1377 self.eval_expr(expr)
1378 } else {
1379 Ok(Value::Na)
1380 }
1381 }
1382 }
1383
1384 Expr::Array(elements) => {
1385 let values: Result<Vec<_>, _> =
1386 elements.iter().map(|e| self.eval_expr(e)).collect();
1387 Ok(Value::Array(Rc::new(RefCell::new(values?))))
1388 }
1389
1390 Expr::Index { expr, index } => {
1391 let index_val = self.eval_expr(index)?.as_number()? as usize;
1392
1393 if index_val > 0 {
1400 if let Expr::Variable { name: var_name, .. } = expr.as_ref() {
1401 if let Some(h) = self.user_series_history.get(var_name) {
1402 return Ok(if h.len() >= index_val {
1403 h[h.len() - index_val].clone()
1404 } else {
1405 Value::Na
1406 });
1407 }
1408 if let Some(var) = self.variables.get(var_name) {
1412 if !matches!(var.value, Value::Series(_) | Value::Array(_)) {
1413 return Ok(Value::Na);
1414 }
1415 }
1416 }
1417 }
1418
1419 let val = self.eval_expr(expr)?;
1420
1421 match val {
1422 Value::Array(arr_ref) => {
1423 let arr = arr_ref.borrow();
1424 arr.get(index_val)
1425 .cloned()
1426 .ok_or(RuntimeError::IndexOutOfBounds(index_val))
1427 }
1428 Value::Series(series) => {
1429 if index_val == 0 {
1434 Ok((*series.current).clone())
1435 } else {
1436 Ok(Value::Na)
1437 }
1438 }
1439 ref v => Err(RuntimeError::TypeError(format!(
1440 "Cannot index non-array/non-series value: {:?}",
1441 v
1442 ))),
1443 }
1444 }
1445
1446 Expr::Switch { value, cases } => {
1447 let switch_val = self.eval_expr(value)?;
1448
1449 for (pattern, result) in cases {
1450 let pattern_val = self.eval_expr(pattern)?;
1452
1453 if pattern_val == Value::Bool(true)
1455 && matches!(pattern, Expr::Literal(Literal::Bool(true)))
1456 {
1457 return self.eval_expr(result);
1458 }
1459
1460 if self.values_equal(&switch_val, &pattern_val)? {
1462 return self.eval_expr(result);
1463 }
1464 }
1465
1466 Ok(Value::Na)
1468 }
1469
1470 Expr::Call {
1471 callee,
1472 type_args,
1473 args,
1474 id,
1475 ..
1476 } => {
1477 if let Expr::MemberAccess { object, member, .. } = callee.as_ref() {
1479 if let Some(method_defs) = self.methods.get(member).cloned() {
1481 let obj_value = self.eval_expr(object)?;
1483
1484 let obj_type = self.get_object_type_name(&obj_value)?;
1486
1487 if let Some(method_def) =
1488 method_defs.iter().find(|m| m.type_name == obj_type)
1489 {
1490 let mut evaluated_args: Vec<EvaluatedArg<O>> =
1492 vec![EvaluatedArg::Positional(obj_value)];
1493 evaluated_args.extend(self.evaluate_arguments(args, None)?);
1494
1495 return self.call_method(
1499 &method_def.params,
1500 &method_def.body,
1501 evaluated_args,
1502 *id,
1503 );
1504 }
1505 }
1506 }
1507
1508 if let Expr::MemberAccess { object, member, .. } = callee.as_ref() {
1513 if !matches!(object.as_ref(), Expr::Call { .. }) {
1514 let receiver = self.eval_expr(object)?;
1515 if let Some(namespace) = builtin_namespace(&receiver) {
1516 if let Some(Value::BuiltinFunction(builtin_fn)) =
1517 self.namespace_member(namespace, member)
1518 {
1519 let mut evaluated_args = vec![EvaluatedArg::Positional(receiver)];
1520 evaluated_args.extend(self.evaluate_arguments(args, None)?);
1521 let call_args =
1522 FunctionCallArgs::new(type_args.clone(), evaluated_args)
1523 .with_call_id(*id);
1524 return (builtin_fn.call)(self, call_args);
1525 }
1526 }
1527 }
1528 }
1529
1530 let callee_value = self.eval_expr(callee)?;
1534 let signature = match &callee_value {
1535 Value::BuiltinFunction(builtin) => Some(builtin.signature.clone()),
1536 _ => None,
1537 };
1538 let evaluated_args = self.evaluate_arguments(args, signature.as_ref())?;
1539
1540 match callee_value {
1542 Value::Function { params, body } => {
1543 self.call_user_function(¶ms, &body, args, evaluated_args, *id)
1546 }
1547 Value::BuiltinFunction(builtin_fn) => {
1548 let call_args = FunctionCallArgs::new(type_args.clone(), evaluated_args)
1551 .with_call_id(*id);
1552 (builtin_fn.call)(self, call_args)
1553 }
1554 Value::Object {
1557 call: Some(builtin_fn),
1558 ..
1559 } => {
1560 let call_args = FunctionCallArgs::new(type_args.clone(), evaluated_args)
1561 .with_call_id(*id);
1562 (builtin_fn)(self, call_args)
1563 }
1564 Value::Na => {
1566 let is_na = matches!(
1567 evaluated_args.first(),
1568 Some(EvaluatedArg::Positional(Value::Na)) | None
1569 );
1570 Ok(Value::Bool(is_na))
1571 }
1572 _ => Err(RuntimeError::TypeError(
1573 "Attempted to call a non-function value".to_string(),
1574 )),
1575 }
1576 }
1577
1578 Expr::MemberAccess { object, member, .. } => {
1579 let obj_value = match object.as_ref() {
1582 Expr::Variable { name, .. }
1583 if (member == "new" || member == "copy")
1584 && self.user_types.contains_key(name) =>
1585 {
1586 self.user_types[name].clone()
1587 }
1588 _ => self.eval_expr(object)?,
1589 };
1590 match obj_value {
1591 Value::Object { fields, .. } => {
1592 let obj = fields.borrow();
1593 obj.get(member).cloned().ok_or_else(|| {
1594 RuntimeError::TypeError(format!("Object has no member '{}'", member))
1595 })
1596 }
1597 Value::Type { name, fields } => {
1598 if member == "new" {
1600 Ok(Value::BuiltinFunction(Builtin::untyped(
1602 Self::create_constructor(name, fields),
1603 )))
1604 } else if member == "copy" {
1605 Ok(Value::BuiltinFunction(Builtin::untyped(
1607 Self::create_copy_function(),
1608 )))
1609 } else {
1610 Err(RuntimeError::TypeError(format!(
1611 "Type '{}' has no member '{}' (only 'new' and 'copy' are supported)",
1612 name, member
1613 )))
1614 }
1615 }
1616 _ => Err(RuntimeError::TypeError(format!(
1617 "Cannot access member '{}' on non-object value",
1618 member
1619 ))),
1620 }
1621 }
1622
1623 Expr::Function { params, body } => {
1624 Ok(Value::Function {
1626 params: params.clone(),
1627 body: body.clone(),
1628 })
1629 }
1630 }
1631 }
1632
1633 fn eval_literal(&self, lit: &Literal) -> Value<O> {
1634 match lit {
1635 Literal::Int(n) => Value::Int(*n),
1636 Literal::Number(n) => Value::Number(*n),
1637 Literal::String(s) => Value::String(s.clone()),
1638 Literal::Bool(b) => Value::Bool(*b),
1639 Literal::Na => Value::Na,
1640 Literal::HexColor(hex) => Value::String(hex.clone()),
1641 }
1642 }
1643
1644 fn eval_binary_op(
1645 &self,
1646 left: &Value<O>,
1647 op: &BinOp,
1648 right: &Value<O>,
1649 ) -> Result<Value<O>, RuntimeError> {
1650 match op {
1651 BinOp::Add => {
1652 if matches!(left, Value::String(_)) || matches!(right, Value::String(_)) {
1654 Ok(Value::String(format!(
1655 "{}{}",
1656 left.as_string()?,
1657 right.as_string()?
1658 )))
1659 } else {
1660 numeric_op(left, right, |a, b| Some(a + b))
1661 }
1662 }
1663
1664 BinOp::Sub => numeric_op(left, right, |a, b| Some(a - b)),
1665
1666 BinOp::Mul => numeric_op(left, right, |a, b| Some(a * b)),
1667
1668 BinOp::Div => numeric_op(left, right, Num::checked_div),
1671
1672 BinOp::Mod => numeric_op(left, right, Num::checked_rem),
1673
1674 BinOp::Eq => {
1682 if is_na_operand(left) || is_na_operand(right) {
1683 return Ok(Value::Na);
1684 }
1685 Ok(Value::Bool(self.values_equal(left, right)?))
1686 }
1687
1688 BinOp::NotEq => {
1689 if is_na_operand(left) || is_na_operand(right) {
1690 return Ok(Value::Na);
1691 }
1692 Ok(Value::Bool(!self.values_equal(left, right)?))
1693 }
1694
1695 BinOp::Less => {
1701 if is_na_operand(left) || is_na_operand(right) {
1702 return Ok(Value::Na);
1703 }
1704 match (left.to_number()?, right.to_number()?) {
1705 (Some(l), Some(r)) => Ok(Value::Bool(l < r)),
1706 _ => Ok(Value::Na),
1707 }
1708 }
1709
1710 BinOp::Greater => {
1711 if is_na_operand(left) || is_na_operand(right) {
1712 return Ok(Value::Na);
1713 }
1714 match (left.to_number()?, right.to_number()?) {
1715 (Some(l), Some(r)) => Ok(Value::Bool(l > r)),
1716 _ => Ok(Value::Na),
1717 }
1718 }
1719
1720 BinOp::LessEq => {
1721 if is_na_operand(left) || is_na_operand(right) {
1722 return Ok(Value::Na);
1723 }
1724 match (left.to_number()?, right.to_number()?) {
1725 (Some(l), Some(r)) => Ok(Value::Bool(l <= r)),
1726 _ => Ok(Value::Na),
1727 }
1728 }
1729
1730 BinOp::GreaterEq => {
1731 if is_na_operand(left) || is_na_operand(right) {
1732 return Ok(Value::Na);
1733 }
1734 match (left.to_number()?, right.to_number()?) {
1735 (Some(l), Some(r)) => Ok(Value::Bool(l >= r)),
1736 _ => Ok(Value::Na),
1737 }
1738 }
1739
1740 BinOp::And => match (left.to_bool()?, right.to_bool()?) {
1742 (Some(false), _) | (_, Some(false)) => Ok(Value::Bool(false)),
1743 (Some(true), Some(true)) => Ok(Value::Bool(true)),
1744 _ => Ok(Value::Na),
1745 },
1746
1747 BinOp::Or => match (left.to_bool()?, right.to_bool()?) {
1749 (Some(true), _) | (_, Some(true)) => Ok(Value::Bool(true)),
1750 (Some(false), Some(false)) => Ok(Value::Bool(false)),
1751 _ => Ok(Value::Na),
1752 },
1753 }
1754 }
1755
1756 fn eval_unary_op(&self, op: &UnOp, val: &Value<O>) -> Result<Value<O>, RuntimeError> {
1757 match op {
1758 UnOp::Neg => match val.as_int() {
1760 Some(n) => Ok(Value::Int(-n)),
1761 None => match val.to_number()? {
1762 Some(n) => Ok(Value::Number(-n)),
1763 None => Ok(Value::Na),
1764 },
1765 },
1766 UnOp::Not => match val.to_bool()? {
1767 Some(b) => Ok(Value::Bool(!b)),
1768 None => Ok(Value::Na),
1769 },
1770 }
1771 }
1772
1773 fn values_equal(&self, left: &Value<O>, right: &Value<O>) -> Result<bool, RuntimeError> {
1774 match (left, right) {
1775 (Value::Int(l), Value::Int(r)) => Ok(l == r),
1776 (Value::Int(l), Value::Number(r)) | (Value::Number(r), Value::Int(l)) => {
1778 Ok((*l as f64 - r).abs() < f64::EPSILON)
1779 }
1780 (Value::Number(l), Value::Number(r)) => Ok((l - r).abs() < f64::EPSILON),
1781 (Value::String(l), Value::String(r)) => Ok(l == r),
1782 (Value::Bool(l), Value::Bool(r)) => Ok(l == r),
1783 (Value::Na, Value::Na) => Ok(true),
1784 (
1785 Value::Enum {
1786 enum_name: a_enum,
1787 field_name: a_field,
1788 ..
1789 },
1790 Value::Enum {
1791 enum_name: b_enum,
1792 field_name: b_field,
1793 ..
1794 },
1795 ) => Ok(a_enum == b_enum && a_field == b_field),
1796 _ => Ok(false),
1797 }
1798 }
1799
1800 fn is_const_expr(&self, expr: &Expr) -> bool {
1802 match expr {
1803 Expr::Literal(_) => true,
1805 Expr::Variable { name, .. } => self
1807 .variables
1808 .get(name)
1809 .map(|var| var.is_const)
1810 .unwrap_or(false),
1811 Expr::MemberAccess { object, .. } => self.is_const_expr(object),
1813 _ => false,
1815 }
1816 }
1817
1818 fn call_user_function(
1819 &mut self,
1820 params: &[pine_ast::FunctionParam],
1821 body: &[Stmt],
1822 arg_exprs: &[Argument],
1823 args: Vec<EvaluatedArg<O>>,
1824 call_id: u32,
1825 ) -> Result<Value<O>, RuntimeError> {
1826 let mut positional_values = Vec::new();
1828 let mut positional_exprs = Vec::new();
1829
1830 for (i, arg) in args.iter().enumerate() {
1831 match arg {
1832 EvaluatedArg::Positional(value) => {
1833 positional_values.push(value.clone());
1834 if let Some(Argument::Positional(expr)) = arg_exprs.get(i) {
1835 positional_exprs.push(expr);
1836 }
1837 }
1838 EvaluatedArg::Named { .. } => {
1839 return Err(RuntimeError::TypeError(
1840 "User-defined functions do not support named arguments yet".to_string(),
1841 ))
1842 }
1843 }
1844 }
1845
1846 if positional_values.len() != params.len() {
1848 return Err(RuntimeError::TypeError(format!(
1849 "Expected {} arguments, got {}",
1850 params.len(),
1851 positional_values.len()
1852 )));
1853 }
1854
1855 for (i, param) in params.iter().enumerate() {
1857 if matches!(param.type_qualifier, Some(pine_ast::TypeQualifier::Const)) {
1858 if let Some(arg_expr) = positional_exprs.get(i) {
1859 if !self.is_const_expr(arg_expr) {
1860 return Err(RuntimeError::TypeError(format!(
1861 "Parameter '{}' requires a const argument, but received a non-const value",
1862 param.name
1863 )));
1864 }
1865 }
1866 }
1867 }
1868
1869 let param_bindings: Vec<(String, Variable<O>)> = params
1872 .iter()
1873 .zip(positional_values)
1874 .map(|(param, value)| {
1875 let is_const = matches!(param.type_qualifier, Some(pine_ast::TypeQualifier::Const));
1876 (
1877 param.name.clone(),
1878 Variable {
1879 value,
1880 is_const,
1881 is_var_persistent: false,
1882 },
1883 )
1884 })
1885 .collect();
1886
1887 self.run_call_site_body(call_id, param_bindings, body)
1888 }
1889
1890 fn run_call_site_body(
1898 &mut self,
1899 call_id: u32,
1900 param_bindings: Vec<(String, Variable<O>)>,
1901 body: &[Stmt],
1902 ) -> Result<Value<O>, RuntimeError> {
1903 let param_names: std::collections::HashSet<String> =
1904 param_bindings.iter().map(|(n, _)| n.clone()).collect();
1905
1906 let saved_vars = self.variables.clone();
1908
1909 if call_id != 0 {
1913 if let Some(local_state) = self.function_local_state.get(&call_id) {
1914 for (var_name, var) in local_state {
1915 if !param_names.contains(var_name) {
1916 self.variables.insert(var_name.clone(), var.clone());
1917 }
1918 }
1919 }
1920 }
1921
1922 for (name, var) in param_bindings {
1924 self.variables.insert(name, var);
1925 }
1926
1927 let prev_call_id = self.current_call_id;
1932 self.current_call_id = call_id;
1933 let mut result: Value<O> = Value::Na;
1934 for stmt in body {
1935 if let Some(return_value) = self.execute_stmt(stmt)? {
1936 result = return_value;
1937 } else if let Stmt::Expression(expr) = stmt {
1938 result = self.eval_expr(expr)?;
1940 }
1941 }
1942 self.current_call_id = prev_call_id;
1943
1944 let call_vars = std::mem::replace(&mut self.variables, saved_vars);
1948 if call_id != 0 {
1949 let mut assigned: std::collections::HashSet<String> = std::collections::HashSet::new();
1956 collect_assigned_names(body, &mut assigned);
1957 let local_state: HashMap<String, Variable<O>> = call_vars
1958 .into_iter()
1959 .filter(|(k, _)| !param_names.contains(k) && assigned.contains(k))
1960 .collect();
1961 self.function_local_state.insert(call_id, local_state);
1962 }
1963
1964 Ok(result)
1965 }
1966
1967 fn get_object_type_name(&self, value: &Value<O>) -> Result<String, RuntimeError> {
1969 match value {
1970 Value::Object { type_name, .. } => Ok(type_name.clone()),
1971 _ => Err(RuntimeError::TypeError(
1972 "Cannot determine type of non-object value".to_string(),
1973 )),
1974 }
1975 }
1976
1977 fn call_method(
1979 &mut self,
1980 params: &[MethodParam],
1981 body: &[Stmt],
1982 args: Vec<EvaluatedArg<O>>,
1983 call_id: u32,
1984 ) -> Result<Value<O>, RuntimeError> {
1985 let mut positional_idx = 0;
1989 let mut param_bindings: Vec<(String, Variable<O>)> = Vec::with_capacity(params.len());
1990
1991 for param in params {
1992 let param_value = if positional_idx < args.len() {
1993 match &args[positional_idx] {
1994 EvaluatedArg::Positional(value) => {
1995 positional_idx += 1;
1996 value.clone()
1997 }
1998 EvaluatedArg::Named { name, value } => {
1999 if name == ¶m.name {
2000 positional_idx += 1;
2001 value.clone()
2002 } else if let Some(default_expr) = ¶m.default_value {
2003 self.eval_expr(default_expr)?
2004 } else {
2005 Value::Na
2006 }
2007 }
2008 }
2009 } else if let Some(default_expr) = ¶m.default_value {
2010 self.eval_expr(default_expr)?
2011 } else {
2012 Value::Na
2013 };
2014
2015 param_bindings.push((
2016 param.name.clone(),
2017 Variable {
2018 value: param_value,
2019 is_const: false,
2020 is_var_persistent: false,
2021 },
2022 ));
2023 }
2024
2025 self.run_call_site_body(call_id, param_bindings, body)
2026 }
2027
2028 fn create_constructor(type_name: String, fields: Vec<TypeField>) -> BuiltinFn<O> {
2030 Rc::new(
2031 move |interp: &mut Interpreter<O>, call_args: FunctionCallArgs<O>| {
2032 let mut instance_fields = HashMap::new();
2033
2034 let mut positional_idx = 0;
2036
2037 for arg in &call_args.args {
2038 match arg {
2039 EvaluatedArg::Positional(value) => {
2040 if positional_idx < fields.len() {
2042 let field = &fields[positional_idx];
2043 instance_fields.insert(field.name.clone(), value.clone());
2044 positional_idx += 1;
2045 } else {
2046 return Err(RuntimeError::TypeError(format!(
2047 "Too many arguments for type '{}' (expected {} fields)",
2048 type_name,
2049 fields.len()
2050 )));
2051 }
2052 }
2053 EvaluatedArg::Named { name, value } => {
2054 if let Some(field) = fields.iter().find(|f| f.name == *name) {
2056 instance_fields.insert(field.name.clone(), value.clone());
2057 } else {
2058 return Err(RuntimeError::TypeError(format!(
2059 "Type '{}' has no field '{}'",
2060 type_name, name
2061 )));
2062 }
2063 }
2064 }
2065 }
2066
2067 for field in &fields {
2069 if !instance_fields.contains_key(&field.name) {
2070 if let Some(default_expr) = &field.default_value {
2071 let default_val = interp.eval_expr(default_expr)?;
2072 instance_fields.insert(field.name.clone(), default_val);
2073 } else {
2074 instance_fields.insert(field.name.clone(), Value::Na);
2076 }
2077 }
2078 }
2079
2080 Ok(Value::Object {
2081 type_name: type_name.clone(),
2082 fields: Rc::new(RefCell::new(instance_fields)),
2083 call: None,
2084 })
2085 },
2086 )
2087 }
2088
2089 fn create_copy_function() -> BuiltinFn<O> {
2091 Rc::new(
2092 |_interp: &mut Interpreter<O>, call_args: FunctionCallArgs<O>| {
2093 if call_args.args.len() != 1 {
2095 return Err(RuntimeError::TypeError(
2096 "copy() expects exactly one argument".to_string(),
2097 ));
2098 }
2099
2100 match &call_args.args[0] {
2101 EvaluatedArg::Positional(value) => {
2102 if let Value::Object {
2103 type_name,
2104 fields,
2105 call,
2106 } = value
2107 {
2108 let obj = fields.borrow();
2110 let copied_fields = obj.clone();
2111 Ok(Value::Object {
2112 type_name: type_name.clone(),
2113 fields: Rc::new(RefCell::new(copied_fields)),
2114 call: call.clone(),
2115 })
2116 } else {
2117 Err(RuntimeError::TypeError(
2118 "copy() expects an object argument".to_string(),
2119 ))
2120 }
2121 }
2122 EvaluatedArg::Named { .. } => Err(RuntimeError::TypeError(
2123 "copy() does not accept named arguments".to_string(),
2124 )),
2125 }
2126 },
2127 )
2128 }
2129}
2130
2131impl<O: PineOutput> Default for Interpreter<O> {
2132 fn default() -> Self {
2133 Self::new()
2134 }
2135}