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  }