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

     1  package structs
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/lmorg/murex/lang"
     7  	"github.com/lmorg/murex/lang/types"
     8  )
     9  
    10  func init() {
    11  	lang.DefineMethod("if", cmdIf, types.Any, types.Generic)
    12  	lang.DefineMethod("!if", cmdIf, types.Any, types.Generic)
    13  }
    14  
    15  const (
    16  	fIf = iota
    17  	fThen
    18  	fElse
    19  	fDone
    20  )
    21  
    22  func cmdIf(p *lang.Process) error {
    23  	p.Stdout.SetDataType(types.Generic)
    24  
    25  	if p.Parameters.Len() == 0 {
    26  		return errors.New("no arguments made. `if` requires parameters")
    27  	}
    28  
    29  	var (
    30  		blocks [3][]rune
    31  		flag   int
    32  	)
    33  
    34  	if p.IsMethod {
    35  		// We derive the conditional state from stdin
    36  		flag++
    37  	}
    38  
    39  	for i := 0; i < p.Parameters.Len(); i++ {
    40  		switch {
    41  		case i == 0 && !p.IsMethod:
    42  			r, err := p.Parameters.Block(0)
    43  			if err != nil {
    44  				return err
    45  			}
    46  
    47  			blocks[0] = r
    48  			flag++
    49  
    50  		default:
    51  			if flag == fDone {
    52  				return errors.New("parameters past end of `then` block")
    53  			}
    54  
    55  			s, err := p.Parameters.String(i)
    56  			if err != nil {
    57  				return err
    58  			}
    59  
    60  			matched, err := setFlag(&s, &flag)
    61  			if err != nil {
    62  				return err
    63  			}
    64  
    65  			if matched {
    66  				continue
    67  			}
    68  
    69  			r, err := p.Parameters.Block(i)
    70  			if err != nil {
    71  				return err
    72  			}
    73  
    74  			blocks[flag] = r
    75  			flag++
    76  		}
    77  	}
    78  
    79  	var conditional bool
    80  
    81  	if len(blocks[fIf]) > 0 {
    82  		// --- IF --- (function)
    83  		fork := p.Fork(lang.F_PARENT_VARTABLE | lang.F_NO_STDIN | lang.F_CREATE_STDOUT | lang.F_NO_STDERR)
    84  		i, err := fork.Execute(blocks[fIf])
    85  		if err != nil {
    86  			return err
    87  		}
    88  
    89  		b, err := fork.Stdout.ReadAll()
    90  		if err != nil {
    91  			return err
    92  		}
    93  		conditional = types.IsTrue(b, i)
    94  
    95  	} else {
    96  		// --- IF --- (method)
    97  		b, err := p.Stdin.ReadAll()
    98  		if err != nil {
    99  			return err
   100  		}
   101  		conditional = types.IsTrue(b, p.Previous.ExitNum)
   102  	}
   103  
   104  	if (conditional && !p.IsNot) || (!conditional && p.IsNot) {
   105  		// --- THEN ---
   106  		if len(blocks[fThen]) > 0 {
   107  			_, err := p.Fork(lang.F_PARENT_VARTABLE | lang.F_NO_STDIN).Execute(blocks[fThen])
   108  			if err != nil {
   109  				return err
   110  			}
   111  		}
   112  
   113  	} else {
   114  		// --- ELSE ---
   115  		if len(blocks[fElse]) > 0 {
   116  			_, err := p.Fork(lang.F_PARENT_VARTABLE | lang.F_NO_STDIN).Execute(blocks[fElse])
   117  			if err != nil {
   118  				return err
   119  			}
   120  		}
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func setFlag(s *string, flag *int) (bool, error) {
   127  	switch *s {
   128  	case "then":
   129  		if *flag > fThen {
   130  			return false, errors.New("`then` appears too late in parameters")
   131  		}
   132  		*flag = fThen
   133  		return true, nil
   134  
   135  	case "else":
   136  		if *flag > fElse {
   137  			return false, errors.New("`else` appears too late in parameters")
   138  		}
   139  		*flag = fElse
   140  		return true, nil
   141  
   142  	default:
   143  		return false, nil
   144  
   145  	}
   146  }