@@ -20,6 +20,11 @@ type RadValue struct {
2020 // nulls are RadNull
2121 // errors are *RadError
2222 Val interface {}
23+
24+ // Span tracks where this value originated in source code.
25+ // Used for "assigned here" context in error messages.
26+ // nil for intermediate computations (e.g., a + b).
27+ Span * Span
2328}
2429
2530func (v RadValue ) Type () rl.RadType {
@@ -51,6 +56,22 @@ func (v RadValue) IsNull() bool {
5156 return v .Val == RAD_NULL
5257}
5358
59+ // HasSpan returns true if this value has source location information.
60+ func (v RadValue ) HasSpan () bool {
61+ return v .Span != nil
62+ }
63+
64+ // GetSpan returns the source location span, or nil if not available.
65+ func (v RadValue ) GetSpan () * Span {
66+ return v .Span
67+ }
68+
69+ // WithSpan returns a copy of this value with the given span attached.
70+ func (v RadValue ) WithSpan (span * Span ) RadValue {
71+ v .Span = span
72+ return v
73+ }
74+
5475func (v RadValue ) Index (i * Interpreter , idxNode * ts.Node ) RadValue {
5576 switch coerced := v .Val .(type ) {
5677 case RadString :
@@ -453,48 +474,67 @@ func (v RadValue) ToCompatSubject(i *Interpreter) (out rl.TypingCompatVal) {
453474 return
454475}
455476
477+ // spanFromNode creates a Span from a tree-sitter node for value tracking.
478+ // Returns nil if the node is nil (intermediate computations don't have spans).
479+ func spanFromNode (i * Interpreter , node * ts.Node ) * Span {
480+ if node == nil {
481+ return nil
482+ }
483+ file := ""
484+ if i != nil {
485+ file = i .sd .ScriptName
486+ }
487+ span := NewSpanFromNode (node , file )
488+ return & span
489+ }
490+
456491func newRadValue (i * Interpreter , node * ts.Node , value interface {}) RadValue {
492+ span := spanFromNode (i , node )
457493 switch coerced := value .(type ) {
458494 case RadValue :
495+ // Preserve existing span if the value already has one
496+ if coerced .Span == nil {
497+ coerced .Span = span
498+ }
459499 return coerced
460500 case []RadValue : // todo risky to have this? might cover up issues
461501 list := NewRadList ()
462502 list .Values = coerced
463503 return newRadValue (i , node , list )
464504 case RadString :
465- return RadValue {Val : coerced }
505+ return RadValue {Val : coerced , Span : span }
466506 case string :
467- return RadValue {Val : NewRadString (coerced )}
507+ return RadValue {Val : NewRadString (coerced ), Span : span }
468508 case int :
469- return RadValue {Val : int64 (coerced )}
509+ return RadValue {Val : int64 (coerced ), Span : span }
470510 case int64 , float64 , bool :
471- return RadValue {Val : coerced }
511+ return RadValue {Val : coerced , Span : span }
472512 case * RadList :
473- return RadValue {Val : coerced }
513+ return RadValue {Val : coerced , Span : span }
474514 case RadList :
475- return RadValue {Val : & coerced }
515+ return RadValue {Val : & coerced , Span : span }
476516 case * RadMap :
477- return RadValue {Val : coerced }
517+ return RadValue {Val : coerced , Span : span }
478518 case RadMap :
479- return RadValue {Val : & coerced }
519+ return RadValue {Val : & coerced , Span : span }
480520 case RadFn :
481- return RadValue {Val : coerced }
521+ return RadValue {Val : coerced , Span : span }
482522 case * RadError :
483- return RadValue {Val : coerced }
523+ return RadValue {Val : coerced , Span : span }
484524 case map [string ]interface {}:
485525 radMap := NewRadMap ()
486526 for key , val := range coerced {
487527 radMap .Set (newRadValue (i , node , key ), newRadValue (i , node , val ))
488528 }
489- return RadValue {Val : radMap }
529+ return RadValue {Val : radMap , Span : span }
490530 case []interface {}:
491531 list := NewRadListFromGeneric (i , node , coerced )
492- return RadValue {Val : list }
532+ return RadValue {Val : list , Span : span }
493533 case []string :
494534 list := NewRadListFromGeneric (i , node , coerced )
495- return RadValue {Val : list }
535+ return RadValue {Val : list , Span : span }
496536 case RadNull , nil :
497- return RadValue {Val : RAD_NULL }
537+ return RadValue {Val : RAD_NULL , Span : span }
498538 default :
499539 if i != nil && node != nil {
500540 i .errorf (node , "Unsupported value type: %s" , TypeAsString (coerced ))
0 commit comments