loop.go (1543B)
1 package engine 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "io" 8 "strings" 9 ) 10 11 // Loop starts an engine execution loop with the given symbol as the starting node. 12 // 13 // The root reads inputs from the provided reader, one line at a time. 14 // 15 // It will execute until running out of bytecode in the buffer. 16 // 17 // Any error not handled by the engine will terminate the oop and return an error. 18 // 19 // Rendered output is written to the provided writer. 20 // 21 // If initial is set, the value will be used for the first (initializing) execution 22 // If nil, an empty byte value will be used. 23 func Loop(ctx context.Context, en Engine, reader io.Reader, writer io.Writer, initial []byte) error { 24 defer en.Finish() 25 if initial == nil { 26 initial = []byte{} 27 } 28 cont, err := en.Exec(ctx, initial) 29 if err != nil { 30 return err 31 } 32 l, err := en.Flush(ctx, writer) 33 if err != nil { 34 if err != ErrFlushNoExec { 35 return err 36 } 37 } 38 if l > 0 { 39 writer.Write([]byte{0x0a}) 40 } 41 if !cont { 42 return nil 43 } 44 45 running := true 46 bufReader := bufio.NewReader(reader) 47 for running { 48 in, err := bufReader.ReadString('\n') 49 if err == io.EOF { 50 logg.DebugCtxf(ctx, "EOF found, that's all folks") 51 return nil 52 } 53 if err != nil { 54 return fmt.Errorf("cannot read input: %v\n", err) 55 } 56 in = strings.TrimSpace(in) 57 running, err = en.Exec(ctx, []byte(in)) 58 if err != nil { 59 return fmt.Errorf("unexpected termination: %v\n", err) 60 } 61 l, err := en.Flush(ctx, writer) 62 if err != nil { 63 return err 64 } 65 if l > 0 { 66 writer.Write([]byte{0x0a}) 67 } 68 } 69 return nil 70 }