github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/structs/while.go (about)

     1  package structs
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/lmorg/murex/builtins/pipes/streams"
     8  	"github.com/lmorg/murex/lang"
     9  	"github.com/lmorg/murex/lang/stdio"
    10  	"github.com/lmorg/murex/lang/types"
    11  )
    12  
    13  func init() {
    14  	lang.DefineFunction("while", cmdWhile, types.Null)
    15  	lang.DefineFunction("!while", cmdWhile, types.Null)
    16  }
    17  
    18  const (
    19  	whileConditional int = iota + 1
    20  	whileCheckStdout
    21  )
    22  
    23  func cmdWhile(p *lang.Process) error {
    24  	p.Stdout.SetDataType(types.Generic)
    25  
    26  	var state, iteration int
    27  
    28  	switch p.Parameters.Len() {
    29  	case 2:
    30  		state = whileConditional
    31  
    32  	case 1:
    33  		state = whileCheckStdout
    34  
    35  	default:
    36  		return fmt.Errorf("invalid usage. Please check docs at https://murex.rocks or `murex-docs %s`", p.Name.String())
    37  	}
    38  
    39  	switch state {
    40  	case whileCheckStdout:
    41  		// Condition is taken from the while loop.
    42  		block, err := p.Parameters.Block(0)
    43  		if err != nil {
    44  			return err
    45  		}
    46  
    47  		for {
    48  			if p.HasCancelled() {
    49  				return errors.New(errCancelled)
    50  			}
    51  
    52  			iteration++
    53  			if !setMetaValues(p, iteration) {
    54  				return fmt.Errorf("cancelled")
    55  			}
    56  
    57  			fork := p.Fork(lang.F_PARENT_VARTABLE | lang.F_NO_STDIN)
    58  			var stdout stdio.Io
    59  			fork.Stdout, stdout = streams.NewTee(p.Stdout)
    60  
    61  			i, err := fork.Execute(block)
    62  			if err != nil {
    63  				return err
    64  			}
    65  			b, err := stdout.ReadAll()
    66  			if err != nil {
    67  				return err
    68  			}
    69  
    70  			conditional := types.IsTrue(b, i)
    71  
    72  			if (!p.IsNot && !conditional) ||
    73  				(p.IsNot && conditional) {
    74  				return nil
    75  			}
    76  
    77  		}
    78  
    79  	case whileConditional:
    80  		// Condition is first parameter, while loop is second.
    81  		ifBlock, err := p.Parameters.Block(0)
    82  		if err != nil {
    83  			return err
    84  		}
    85  
    86  		whileBlock, err := p.Parameters.Block(1)
    87  		if err != nil {
    88  			return err
    89  		}
    90  
    91  		for {
    92  			if p.HasTerminated() {
    93  				return nil
    94  			}
    95  
    96  			iteration++
    97  			if !setMetaValues(p, iteration) {
    98  				return fmt.Errorf("cancelled")
    99  			}
   100  
   101  			fork := p.Fork(lang.F_NO_STDIN | lang.F_CREATE_STDOUT | lang.F_NO_STDERR)
   102  			i, err := fork.Execute(ifBlock)
   103  			if err != nil {
   104  				return err
   105  			}
   106  			b, err := fork.Stdout.ReadAll()
   107  			if err != nil {
   108  				return err
   109  			}
   110  			conditional := types.IsTrue(b, i)
   111  
   112  			if (!p.IsNot && !conditional) ||
   113  				(p.IsNot && conditional) {
   114  				return nil
   115  			}
   116  
   117  			fork = p.Fork(lang.F_NO_STDIN)
   118  			_, err = fork.Execute(whileBlock)
   119  			if err != nil {
   120  				return err
   121  			}
   122  		}
   123  
   124  	default:
   125  		return errors.New("this condition should never be reached. Please file a bug at https://github.com/lmorg/murex/issues")
   126  
   127  	}
   128  }