github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_block.go (about)

     1  package expressions
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/lmorg/murex/lang"
    10  	fn "github.com/lmorg/murex/lang/expressions/functions"
    11  	"github.com/lmorg/murex/lang/types"
    12  	"github.com/lmorg/murex/utils/consts"
    13  )
    14  
    15  func init() {
    16  	lang.ParseExpression = ExpressionParser
    17  	lang.ParseStatementParameters = StatementParametersParser
    18  }
    19  
    20  // ExpressionParser is intended to be called from other parsers as a way of
    21  // embedding this expressions library into other language syntaxes. This
    22  // function just parses the expression and returns the end of the expression.
    23  func ExpressionParser(expression []rune, offset int, exec bool) (int, error) {
    24  	tree := NewParser(nil, expression, offset)
    25  
    26  	err := tree.parseExpression(exec, false)
    27  	if err != nil {
    28  		return 0, err
    29  	}
    30  
    31  	err = tree.validateExpression(exec)
    32  	if err != nil {
    33  		return 0, err
    34  	}
    35  
    36  	return tree.charPos, nil
    37  }
    38  
    39  // StatementParametersParser is intended to be called from other parsers as a
    40  // way of parsing function parameters
    41  func StatementParametersParser(expression []rune, p *lang.Process) (string, []string, error) {
    42  	if p.Name.String() == lang.ExpressionFunctionName {
    43  		return p.Name.String(), []string{string(p.Parameters.PreParsed[0])}, nil
    44  	}
    45  
    46  	tree := NewParser(nil, expression, 0)
    47  	tree.p = p
    48  	err := tree.ParseStatement(true)
    49  	if err != nil {
    50  		return "", nil, err
    51  	}
    52  
    53  	return tree.statement.String(), tree.statement.Parameters(), nil
    54  }
    55  
    56  func NewParser(p *lang.Process, expression []rune, offset int) *ParserT {
    57  	tree := new(ParserT)
    58  	tree.expression = expression
    59  	tree.p = p
    60  	tree.charOffset = offset
    61  	return tree
    62  }
    63  
    64  func (tree *ParserT) preParser() (int, error) {
    65  	expErr := tree.parseExpression(false, false)
    66  	if expErr == nil {
    67  		// if successful parse, then also validate.
    68  		// no point validating if the parser has already failed
    69  		expErr = tree.validateExpression(false)
    70  	}
    71  
    72  	if expErr == nil {
    73  		return tree.charPos, nil
    74  	}
    75  
    76  	stErr := tree.ParseStatement(false)
    77  	if stErr != nil {
    78  		return 0, stErr
    79  	}
    80  
    81  	stErr = tree.statement.validate()
    82  
    83  	if len(tree.statement.command) == 0 {
    84  		return 0, errors.New("you cannot have zero length commands")
    85  	}
    86  
    87  	if stErr == nil && !tree.statement.asStatement &&
    88  		len(tree.statement.parameters) > 0 && len(tree.statement.parameters[0]) > 0 && tree.statement.parameters[0][0] == '=' {
    89  		// i _still_ think this is probably an expression
    90  		return 0, expErr
    91  	}
    92  
    93  	return tree.charPos, nil
    94  
    95  }
    96  
    97  var expressionFunctionName = []rune(lang.ExpressionFunctionName)
    98  
    99  func (blk *BlockT) append(tree *ParserT, this fn.Property, next fn.Property) error {
   100  	switch {
   101  
   102  	case tree == nil && blk.nextProperty.FollowOnFn():
   103  		exprRune, exprPos := cropCodeInErrMsg(blk.expression, blk.charPos)
   104  		return fmt.Errorf("invalid syntax at %d. Unexpected pipeline continuation token:\n> %s\n> %s\n> these tokens:     %s\n> shouldn't follow: %s",
   105  			blk.charPos,
   106  			string(exprRune), strings.Repeat(" ", exprPos)+"^",
   107  			next.Decompose(),
   108  			fn.Property(fn.P_PIPE_OUT|fn.P_PIPE_ERR|fn.P_LOGIC_AND|fn.P_LOGIC_OR).Decompose())
   109  
   110  	case len(blk.Functions) > 0 && tree == nil && next.FollowOnFn():
   111  		exprRune, exprPos := cropCodeInErrMsg(blk.expression, blk.charPos)
   112  		return fmt.Errorf("invalid syntax at %d. Semi-colon or line break preceding a pipeline continuation token:\n> %s\n> %s\n> these tokens:     %s\n> shouldn't follow: %s",
   113  			blk.charPos,
   114  			string(exprRune), strings.Repeat(" ", exprPos)+"^",
   115  			this.Decompose(),
   116  			fn.Property(fn.P_NEW_CHAIN|fn.P_LOGIC_AND|fn.P_LOGIC_OR).Decompose())
   117  
   118  	case tree == nil:
   119  		// do nothing
   120  
   121  	case tree.statement == nil:
   122  		if tree.charPos+1 >= len(tree.expression) {
   123  			tree.charPos = len(tree.expression) - 1
   124  		}
   125  		blk.Functions = append(blk.Functions, fn.FunctionT{
   126  			Raw:        tree.expression[:tree.charPos+1],
   127  			Command:    expressionFunctionName,
   128  			Parameters: [][]rune{tree.expression[:tree.charPos+1]},
   129  			Properties: blk.nextProperty | this,
   130  			LineN:      blk.lineN + tree.GetLineN(),
   131  			ColumnN:    tree.GetColumnN(),
   132  		})
   133  
   134  	default:
   135  		blk.Functions = append(blk.Functions, fn.FunctionT{
   136  			Raw:        tree.expression[:tree.charPos+1],
   137  			Command:    tree.statement.command,
   138  			Parameters: tree.statement.parameters,
   139  			NamedPipes: tree.statement.namedPipes,
   140  			Cast:       tree.statement.cast,
   141  			Properties: blk.nextProperty | this,
   142  			LineN:      blk.lineN + tree.GetLineN(),
   143  			ColumnN:    tree.GetColumnN(),
   144  		})
   145  
   146  	}
   147  
   148  	blk.nextProperty = next
   149  	return nil
   150  }
   151  
   152  var formatGeneric = []rune("format " + types.Generic)
   153  
   154  func (blk *BlockT) ParseBlock() error {
   155  	var tree *ParserT
   156  
   157  	for ; blk.charPos < len(blk.expression); blk.charPos++ {
   158  		r := blk.expression[blk.charPos]
   159  
   160  		switch r {
   161  		case ' ', '\t', '\r':
   162  			continue
   163  
   164  		case '\n':
   165  			if err := blk.append(tree, 0, fn.P_NEW_CHAIN); err != nil {
   166  				return err
   167  			}
   168  			tree = nil
   169  			blk.lineN++
   170  			blk.offset = blk.charPos
   171  			continue
   172  
   173  		case '#':
   174  			comment := NewParser(nil, blk.expression[blk.charPos:], 0)
   175  			comment.parseComment()
   176  			blk.charPos += comment.charPos
   177  
   178  		case '/':
   179  			switch {
   180  			case blk.nextChar() == '#':
   181  				comment := NewParser(nil, blk.expression[blk.charPos:], 0)
   182  				if err := comment.parseCommentMultiLine(); err != nil {
   183  					return err
   184  				}
   185  				blk.charPos += comment.charPos
   186  			default:
   187  				tree = NewParser(nil, blk.expression[blk.charPos:], blk.charPos-1)
   188  				newPos, err := tree.preParser()
   189  				if err != nil {
   190  					return err
   191  				}
   192  				blk.charPos += newPos
   193  			}
   194  
   195  		case ';':
   196  			if err := blk.append(tree, 0, fn.P_NEW_CHAIN); err != nil {
   197  				return err
   198  			}
   199  			tree = nil
   200  
   201  		case '&':
   202  			switch {
   203  			case blk.nextChar() == '&':
   204  				blk.charPos++
   205  				if err := blk.append(tree, 0, fn.P_NEW_CHAIN|fn.P_FOLLOW_ON|fn.P_LOGIC_AND); err != nil {
   206  					return err
   207  				}
   208  				tree = nil
   209  			case tree == nil:
   210  				tree = NewParser(nil, blk.expression[blk.charPos:], 0)
   211  				newPos, err := tree.preParser()
   212  				if err != nil {
   213  					return err
   214  				}
   215  				blk.charPos += newPos
   216  			default:
   217  				blk.panic('&', '&')
   218  			}
   219  
   220  		case '|':
   221  			if blk.nextChar() == '|' {
   222  				blk.charPos++
   223  				if err := blk.append(tree, 0, fn.P_NEW_CHAIN|fn.P_FOLLOW_ON|fn.P_LOGIC_OR); err != nil {
   224  					return err
   225  				}
   226  				tree = nil
   227  
   228  			} else {
   229  				if err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil {
   230  					return err
   231  				}
   232  				tree = nil
   233  			}
   234  
   235  		case '-':
   236  			switch {
   237  			case blk.nextChar() == '>':
   238  				blk.charPos++
   239  				if err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil {
   240  					return err
   241  				}
   242  				tree = nil
   243  			case tree == nil:
   244  				tree = NewParser(nil, blk.expression[blk.charPos:], 0)
   245  				newPos, err := tree.preParser()
   246  				if err != nil {
   247  					return err
   248  				}
   249  				blk.charPos += newPos
   250  			default:
   251  				blk.panic('-', '>')
   252  			}
   253  
   254  		case '?':
   255  			message := fmt.Sprintf("!!! WARNING: The operator `?` has been deprecated and will be removed in the next release\n!!!        : Line:   %d\n!!!        : Column: %d\n",
   256  				blk.lineN, blk.charPos)
   257  			os.Stderr.WriteString(message)
   258  
   259  			if err := blk.append(tree, fn.P_PIPE_ERR, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil {
   260  				return err
   261  			}
   262  			tree = nil
   263  
   264  		case '=':
   265  			switch {
   266  			case blk.nextChar() == '>':
   267  				blk.charPos++
   268  				if err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil {
   269  					return err
   270  				}
   271  				tree = nil
   272  				format := NewParser(nil, formatGeneric, 0)
   273  				_, _ = format.preParser()
   274  				if err := blk.append(format, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD); err != nil {
   275  					return err
   276  				}
   277  
   278  			case tree == nil:
   279  				tree = NewParser(nil, blk.expression[blk.charPos:], 0)
   280  				newPos, err := tree.preParser()
   281  				if err != nil {
   282  					return err
   283  				}
   284  				blk.charPos += newPos
   285  			default:
   286  				blk.panic('=', '>')
   287  			}
   288  
   289  		case '>':
   290  			switch {
   291  			case blk.nextChar() == '>':
   292  				/*if len(blk.Functions) > 0 &&
   293  					len(blk.Functions[len(blk.Functions)-1].Raw) == 0 &&
   294  					!blk.Functions[len(blk.Functions)-1].Properties.Method() {
   295  					panic("ugh")
   296  				}*/
   297  
   298  				err := blk.append(tree, fn.P_PIPE_OUT, fn.P_FOLLOW_ON|fn.P_METHOD)
   299  				if err != nil {
   300  					return err
   301  				}
   302  				tree, err = blk.parseStatementWithKnownCommand('>', '>')
   303  				if err != nil {
   304  					return err
   305  				}
   306  			default:
   307  				tree = NewParser(nil, blk.expression[blk.charPos:], blk.charPos-1)
   308  				newPos, err := tree.preParser()
   309  				if err != nil {
   310  					return err
   311  				}
   312  				blk.charPos += newPos
   313  			}
   314  
   315  		default:
   316  			tree = NewParser(nil, blk.expression[blk.charPos:], blk.charPos-1)
   317  			newPos, err := tree.preParser()
   318  			if err != nil {
   319  				return err
   320  			}
   321  			blk.charPos += newPos
   322  
   323  		}
   324  
   325  	}
   326  
   327  	if blk.charPos >= len(blk.expression) {
   328  		if err := blk.append(tree, 0, 0); err != nil {
   329  			return err
   330  		}
   331  	}
   332  
   333  	return nil
   334  }
   335  
   336  func (blk *BlockT) parseStatementWithKnownCommand(command ...rune) (*ParserT, error) {
   337  	tree := NewParser(nil, blk.expression[blk.charPos:], 0)
   338  	tree.statement = new(StatementT)
   339  	tree.statement.command = command
   340  	tree.charPos = len(command)
   341  	err := tree.parseStatement(false)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	blk.charPos += tree.charPos
   346  	return tree, nil
   347  }
   348  
   349  func (blk *BlockT) panic(found rune, follows rune) {
   350  	msg := "unexpected parser error: '%s' found"
   351  	if follows == 0 {
   352  		panic(fmt.Sprintf(msg+". "+consts.IssueTrackerURL, string(found)))
   353  	}
   354  
   355  	msg += " but no '%s' follows"
   356  	panic(fmt.Sprintf(msg+". "+consts.IssueTrackerURL, string(found), string(follows)))
   357  }