@@ -3,9 +3,6 @@ package vibes
33import (
44 "context"
55 "errors"
6- "fmt"
7- "regexp"
8- "strings"
96)
107
118type ScriptFunction struct {
@@ -78,185 +75,6 @@ type callFrame struct {
7875 Pos Position
7976}
8077
81- type StackFrame struct {
82- Function string
83- Pos Position
84- }
85-
86- type RuntimeError struct {
87- Type string
88- Message string
89- CodeFrame string
90- Frames []StackFrame
91- }
92-
93- type assertionFailureError struct {
94- message string
95- }
96-
97- func (e * assertionFailureError ) Error () string {
98- return e .message
99- }
100-
101- const (
102- runtimeErrorTypeBase = "RuntimeError"
103- runtimeErrorTypeAssertion = "AssertionError"
104- runtimeErrorFrameHead = 8
105- runtimeErrorFrameTail = 8
106- )
107-
108- var (
109- errLoopBreak = errors .New ("loop break" )
110- errLoopNext = errors .New ("loop next" )
111- errStepQuotaExceeded = errors .New ("step quota exceeded" )
112- errMemoryQuotaExceeded = errors .New ("memory quota exceeded" )
113- stringTemplatePattern = regexp .MustCompile (`\{\{\s*([A-Za-z_][A-Za-z0-9_.-]*)\s*\}\}` )
114- )
115-
116- func (re * RuntimeError ) Error () string {
117- var b strings.Builder
118- b .WriteString (re .Message )
119- if re .CodeFrame != "" {
120- b .WriteString ("\n " )
121- b .WriteString (re .CodeFrame )
122- }
123- renderFrame := func (frame StackFrame ) {
124- if frame .Pos .Line > 0 && frame .Pos .Column > 0 {
125- fmt .Fprintf (& b , "\n at %s (%d:%d)" , frame .Function , frame .Pos .Line , frame .Pos .Column )
126- } else if frame .Pos .Line > 0 {
127- fmt .Fprintf (& b , "\n at %s (line %d)" , frame .Function , frame .Pos .Line )
128- } else {
129- fmt .Fprintf (& b , "\n at %s" , frame .Function )
130- }
131- }
132-
133- if len (re .Frames ) <= runtimeErrorFrameHead + runtimeErrorFrameTail {
134- for _ , frame := range re .Frames {
135- renderFrame (frame )
136- }
137- return b .String ()
138- }
139-
140- for _ , frame := range re .Frames [:runtimeErrorFrameHead ] {
141- renderFrame (frame )
142- }
143- omitted := len (re .Frames ) - (runtimeErrorFrameHead + runtimeErrorFrameTail )
144- fmt .Fprintf (& b , "\n ... %d frames omitted ..." , omitted )
145- for _ , frame := range re .Frames [len (re .Frames )- runtimeErrorFrameTail :] {
146- renderFrame (frame )
147- }
148-
149- return b .String ()
150- }
151-
152- // Unwrap returns nil to satisfy the error unwrapping interface.
153- // RuntimeError is a terminal error that wraps the original error message but not the error itself.
154- func (re * RuntimeError ) Unwrap () error {
155- return nil
156- }
157-
158- func canonicalRuntimeErrorType (name string ) (string , bool ) {
159- switch {
160- case strings .EqualFold (name , runtimeErrorTypeBase ), strings .EqualFold (name , "Error" ):
161- return runtimeErrorTypeBase , true
162- case strings .EqualFold (name , runtimeErrorTypeAssertion ):
163- return runtimeErrorTypeAssertion , true
164- default :
165- return "" , false
166- }
167- }
168-
169- func classifyRuntimeErrorType (err error ) string {
170- if err == nil {
171- return runtimeErrorTypeBase
172- }
173- var assertionErr * assertionFailureError
174- if errors .As (err , & assertionErr ) {
175- return runtimeErrorTypeAssertion
176- }
177- if runtimeErr , ok := err .(* RuntimeError ); ok {
178- if kind , known := canonicalRuntimeErrorType (runtimeErr .Type ); known {
179- return kind
180- }
181- }
182- return runtimeErrorTypeBase
183- }
184-
185- func newAssertionFailureError (message string ) error {
186- return & assertionFailureError {message : message }
187- }
188-
189- func (exec * Execution ) step () error {
190- exec .steps ++
191- if exec .quota > 0 && exec .steps > exec .quota {
192- return fmt .Errorf ("%w (%d)" , errStepQuotaExceeded , exec .quota )
193- }
194- if exec .memoryQuota > 0 && (exec .steps & 15 ) == 0 {
195- if err := exec .checkMemory (); err != nil {
196- return err
197- }
198- }
199- if exec .ctx != nil {
200- select {
201- case <- exec .ctx .Done ():
202- return exec .ctx .Err ()
203- default :
204- }
205- }
206- return nil
207- }
208-
209- func (exec * Execution ) errorAt (pos Position , format string , args ... any ) error {
210- return exec .newRuntimeError (fmt .Sprintf (format , args ... ), pos )
211- }
212-
213- func (exec * Execution ) newRuntimeError (message string , pos Position ) error {
214- return exec .newRuntimeErrorWithType (runtimeErrorTypeBase , message , pos )
215- }
216-
217- func (exec * Execution ) newRuntimeErrorWithType (kind string , message string , pos Position ) error {
218- if canonical , ok := canonicalRuntimeErrorType (kind ); ok {
219- kind = canonical
220- } else {
221- kind = runtimeErrorTypeBase
222- }
223-
224- frames := make ([]StackFrame , 0 , len (exec .callStack )+ 1 )
225-
226- if len (exec .callStack ) > 0 {
227- // First frame: where the error occurred (within the current function)
228- current := exec .callStack [len (exec .callStack )- 1 ]
229- frames = append (frames , StackFrame {Function : current .Function , Pos : pos })
230-
231- // Remaining frames: the call stack (where each function was called from)
232- for i := len (exec .callStack ) - 1 ; i >= 0 ; i -- {
233- cf := exec .callStack [i ]
234- frames = append (frames , StackFrame (cf ))
235- }
236- } else {
237- // No call stack means error at script top level
238- frames = append (frames , StackFrame {Function : "<script>" , Pos : pos })
239- }
240- codeFrame := ""
241- if exec .script != nil {
242- codeFrame = formatCodeFrame (exec .script .source , pos )
243- }
244- return & RuntimeError {Type : kind , Message : message , CodeFrame : codeFrame , Frames : frames }
245- }
246-
247- func (exec * Execution ) wrapError (err error , pos Position ) error {
248- if err == nil {
249- return nil
250- }
251- if isHostControlSignal (err ) {
252- return err
253- }
254- if _ , ok := err .(* RuntimeError ); ok {
255- return err
256- }
257- return exec .newRuntimeErrorWithType (classifyRuntimeErrorType (err ), err .Error (), pos )
258- }
259-
26078func (exec * Execution ) evalStatements (stmts []Statement , env * Env ) (Value , bool , error ) {
26179 exec .pushEnv (env )
26280 defer exec .popEnv ()
0 commit comments