github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/interpreter.go (about) 1 package lang 2 3 import ( 4 "context" 5 "strings" 6 7 "github.com/lmorg/murex/builtins/pipes/streams" 8 "github.com/lmorg/murex/lang/expressions/functions" 9 "github.com/lmorg/murex/lang/ref" 10 "github.com/lmorg/murex/lang/runmode" 11 "github.com/lmorg/murex/lang/state" 12 ) 13 14 var ParseBlock func(block []rune) (*[]functions.FunctionT, error) 15 var ParseExpression func([]rune, int, bool) (int, error) 16 var ParseStatementParameters func([]rune, *Process) (string, []string, error) 17 18 const ExpressionFunctionName = "expr" 19 20 func compile(tree *[]functions.FunctionT, parent *Process) (*[]Process, int) { 21 if parent == nil { 22 panic("nil parent") 23 } 24 25 if tree == nil { 26 panic("nil tree") 27 } 28 29 rm := parent.RunMode 30 if len(*tree) > 0 && (string((*tree)[0].Command) == "runmode" || 31 string((*tree)[0].Command) == "runmode:") { 32 _, params, err := ParseStatementParameters((*tree)[0].Raw, parent) 33 if err != nil { 34 return nil, ErrUnableToParseParametersInRunmode 35 } 36 37 switch strings.Join(params, " ") { 38 39 // function wide scopes 40 41 case "unsafe function": 42 rm = runmode.FunctionUnsafe 43 44 case "try function": 45 rm = runmode.FunctionTry 46 47 case "trypipe function": 48 rm = runmode.FunctionTryPipe 49 50 case "tryerr function": 51 rm = runmode.FunctionTryErr 52 53 case "trypipeerr function": 54 rm = runmode.FunctionTryPipeErr 55 56 // module wide scopes 57 58 case "unsafe module": 59 rm = runmode.ModuleUnsafe 60 ModuleRunModes[parent.FileRef.Source.Module] = rm 61 62 case "try module": 63 rm = runmode.ModuleTry 64 ModuleRunModes[parent.FileRef.Source.Module] = rm 65 66 case "trypipe module": 67 rm = runmode.ModuleTryPipe 68 ModuleRunModes[parent.FileRef.Source.Module] = rm 69 70 case "tryerr module": 71 rm = runmode.ModuleTryErr 72 ModuleRunModes[parent.FileRef.Source.Module] = rm 73 74 case "trypipeerr module": 75 rm = runmode.ModuleTryPipeErr 76 ModuleRunModes[parent.FileRef.Source.Module] = rm 77 78 default: 79 return nil, ErrInvalidParametersInRunmode 80 } 81 82 parent.Scope.RunMode = rm 83 *tree = (*tree)[1:] 84 } 85 86 procs := make([]Process, len(*tree)) 87 88 for i := range *tree { 89 procs[i].State.Set(state.MemAllocated) 90 procs[i].raw = (*tree)[i].Raw 91 procs[i].Name.SetRune((*tree)[i].CommandName()) 92 procs[i].Parameters.PreParsed = (*tree)[i].Parameters 93 procs[i].namedPipes = (*tree)[i].NamedPipes 94 procs[i].IsMethod = (*tree)[i].Properties.Method() 95 procs[i].OperatorLogicAnd = (*tree)[i].Properties.LogicAnd() 96 procs[i].OperatorLogicOr = (*tree)[i].Properties.LogicOr() 97 procs[i].Background.Set(parent.Background.Get()) 98 procs[i].Parent = parent 99 procs[i].Scope = parent.Scope 100 procs[i].WaitForTermination = make(chan bool) 101 procs[i].WaitForStopped = make(chan bool) 102 procs[i].HasStopped = make(chan bool) 103 procs[i].RunMode = rm //parent.RunMode 104 procs[i].Config = parent.Config 105 procs[i].Tests = parent.Tests 106 procs[i].Variables = parent.Variables 107 procs[i].CCEvent = parent.CCEvent 108 procs[i].CCExists = parent.CCExists 109 procs[i].FileRef = &ref.File{Source: parent.FileRef.Source} 110 procs[i].Forks = NewForkManagement() 111 112 procs[i].FileRef.Column = parent.FileRef.Column + (*tree)[i].ColumnN 113 procs[i].FileRef.Line = (*tree)[i].LineN 114 115 // Define previous and next processes: 116 switch { 117 case i == 0: 118 // first 119 procs[0].Previous = parent 120 if i == len(*tree)-1 { 121 procs[0].Next = parent 122 } else { 123 procs[0].Next = &procs[1] 124 } 125 126 case i == len(*tree)-1: 127 // last 128 procs[i].Previous = &procs[i-1] 129 procs[i].Next = parent 130 131 case i > 0: 132 // everything in the middle 133 procs[i].Previous = &procs[i-1] 134 procs[i].Next = &procs[i+1] 135 136 default: 137 // This condition should never happen, 138 // but lets but a default catch and stack trace in just in case. 139 panic("Failed in an unexpected way: Compile()->switch{default}") 140 } 141 142 // Define stdin interface: 143 switch { 144 case i == 0: 145 // first 146 procs[0].Stdin = parent.Stdin 147 148 case (*tree)[i].Properties.NewChain(): 149 // new chain 150 procs[i].Stdin = streams.NewStdin() 151 //procs[i].Stdin = new(null.Null) 152 } 153 154 // Define stdout / stderr interfaces: 155 switch { 156 case (*tree)[i].Properties.PipeOut(): 157 if i+1 == len(procs) { 158 return nil, ErrPipingToNothing 159 } 160 procs[i+1].Stdin = streams.NewStdin() 161 procs[i].Stdout = procs[i].Next.Stdin 162 procs[i].Stderr = procs[i].Parent.Stderr 163 164 case (*tree)[i].Properties.PipeErr(): 165 if i+1 == len(procs) { 166 return nil, ErrPipingToNothing 167 } 168 procs[i+1].Stdin = streams.NewStdin() 169 procs[i].Stdout = procs[i].Parent.Stderr //Stdout 170 procs[i].Stderr = procs[i].Next.Stdin 171 172 default: 173 procs[i].Stdout = procs[i].Parent.Stdout 174 procs[i].Stderr = procs[i].Parent.Stderr 175 } 176 177 procs[i].Context, procs[i].Done = context.WithCancel(context.Background()) 178 procs[i].Kill = func() { 179 procs[i].Stdin.ForceClose() 180 procs[i].Stdout.ForceClose() 181 procs[i].Stderr.ForceClose() 182 procs[i].Done() 183 } 184 185 if len((*tree)[i].Cast) != 0 { 186 procs[i].Stdin.SetDataType(string((*tree)[i].Cast)) 187 } 188 } 189 190 for i := range *tree { 191 createProcess(&procs[i], !(*tree)[i].Properties.NewChain()) 192 } 193 194 return &procs, 0 195 } 196 197 func checkTryErr(p *Process, exitNum *int) { 198 outSize, _ := p.Stdout.Stats() 199 errSize, _ := p.Stderr.Stats() 200 201 if *exitNum < 1 && errSize > outSize { 202 *exitNum = 1 203 } 204 }