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

     1  package mkarray
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/lmorg/murex/lang"
     7  	"github.com/lmorg/murex/lang/stdio"
     8  	"github.com/lmorg/murex/lang/types"
     9  )
    10  
    11  func init() {
    12  	lang.DefineFunction("a", cmdA, types.String)
    13  	lang.DefineFunction("ja", cmdJa, types.Json)
    14  	lang.DefineFunction("ta", cmdTa, types.WriteArray)
    15  }
    16  
    17  const (
    18  	astTypeString = iota
    19  	astTypeOpen
    20  	astTypeClose
    21  	astTypeSeparator
    22  	astTypeRange
    23  )
    24  
    25  type ast struct {
    26  	Data []byte
    27  	Type int
    28  }
    29  
    30  type arrayT struct {
    31  	p          *lang.Process
    32  	expression []byte
    33  	dataType   string
    34  	writer     stdio.ArrayWriter
    35  	groups     [][]ast
    36  }
    37  
    38  func cmdA(p *lang.Process) error {
    39  	return mkArray(p, types.String)
    40  }
    41  
    42  func cmdJa(p *lang.Process) error {
    43  	return mkArray(p, types.Json)
    44  }
    45  
    46  func cmdTa(p *lang.Process) error {
    47  	dataType, err := p.Parameters.String(0)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	params := p.Parameters.StringArray()[1:]
    53  	p.Parameters.DefineParsed(params)
    54  
    55  	return mkArray(p, dataType)
    56  }
    57  
    58  // echo %[1..3]
    59  // a: [1..10] -> ...
    60  // ja: [1..10] -> ...
    61  // ta: dataType [1..10] -> ...
    62  func mkArray(p *lang.Process, dataType string) error {
    63  	p.Stdout.SetDataType(dataType)
    64  
    65  	a := new(arrayT)
    66  	a.p = p
    67  	a.expression = p.Parameters.ByteAll()
    68  	a.dataType = dataType
    69  
    70  	ok, err := a.isNumberArray()
    71  	if ok || err != nil {
    72  		return err
    73  	}
    74  
    75  	return a.isStringArray()
    76  }
    77  
    78  func (a *arrayT) parseExpression() error {
    79  	var (
    80  		escaped, open, dots bool
    81  		nodes               = make([]ast, 1)
    82  		node                = &nodes[0]
    83  	)
    84  
    85  	for i, b := range a.expression {
    86  		switch b {
    87  		case '\\':
    88  			dots = false
    89  			if escaped {
    90  				node.Data = append(node.Data, b)
    91  			}
    92  			escaped = !escaped
    93  
    94  		case ',':
    95  			dots = false
    96  			if escaped {
    97  				node.Data = append(node.Data, b)
    98  				escaped = !escaped
    99  				continue
   100  			}
   101  			nodes = append(nodes,
   102  				ast{
   103  					Data: []byte{','},
   104  					Type: astTypeSeparator,
   105  				},
   106  				ast{},
   107  			)
   108  			node = &nodes[len(nodes)-1]
   109  
   110  		case '[':
   111  			dots = false
   112  			if escaped {
   113  				node.Data = append(node.Data, b)
   114  				escaped = !escaped
   115  				continue
   116  			}
   117  			if open {
   118  				return fmt.Errorf("cannot open bracket (char %d) inside of open bracket.\nIf you wanted to print the bracket then please escape it: `\\[``", i)
   119  			}
   120  			open = true
   121  			nodes = append(nodes,
   122  				ast{
   123  					Data: []byte{'['},
   124  					Type: astTypeOpen,
   125  				},
   126  				ast{},
   127  			)
   128  			node = &nodes[len(nodes)-1]
   129  
   130  		case ']':
   131  			dots = false
   132  			if escaped {
   133  				node.Data = append(node.Data, b)
   134  				escaped = !escaped
   135  				continue
   136  			}
   137  			if !open {
   138  				return fmt.Errorf("cannot close bracket (char %d) with an open bracket.\nIf you wanted to print the bracket then please escape it: `\\]``", i)
   139  			}
   140  			open = false
   141  			nodes = append(nodes,
   142  				ast{
   143  					Data: []byte{']'},
   144  					Type: astTypeClose,
   145  				},
   146  				ast{},
   147  			)
   148  			node = &nodes[len(nodes)-1]
   149  
   150  		case '.':
   151  			if open {
   152  				if dots {
   153  					node.Type = astTypeRange
   154  				}
   155  				dots = !dots
   156  			}
   157  			node.Data = append(node.Data, b)
   158  
   159  		default:
   160  			dots = false
   161  			escaped = false
   162  			node.Data = append(node.Data, b)
   163  		}
   164  	}
   165  
   166  	if open {
   167  		return fmt.Errorf("missing closing square bracket, ']', in: %s", string(a.expression))
   168  	}
   169  
   170  	// Group the parameters to handle recursive matching
   171  	a.groups = make([][]ast, 1)
   172  	for i := range nodes {
   173  		switch nodes[i].Type {
   174  		case astTypeOpen:
   175  			open = true
   176  			a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i])
   177  
   178  		case astTypeClose:
   179  			open = false
   180  			a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i])
   181  
   182  		case astTypeSeparator:
   183  			if open {
   184  				a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i])
   185  			} else {
   186  				a.groups = append(a.groups, []ast{})
   187  			}
   188  
   189  		default:
   190  			a.groups[len(a.groups)-1] = append(a.groups[len(a.groups)-1], nodes[i])
   191  		}
   192  	}
   193  
   194  	return nil
   195  }