userland: add archive tools and pinned GoAWK, esbuild, and QuickJS
This commit is contained in:
@@ -0,0 +1,800 @@
|
||||
// GoAWK parser - abstract syntax tree structs
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "github.com/benhoyt/goawk/lexer"
|
||||
)
|
||||
|
||||
// Program is a parsed AWK program.
|
||||
type Program struct {
|
||||
Begin []Stmts
|
||||
Actions []*Action
|
||||
End []Stmts
|
||||
Functions []*Function
|
||||
}
|
||||
|
||||
// String returns an indented, pretty-printed version of the parsed
|
||||
// program.
|
||||
func (p *Program) String() string {
|
||||
parts := []string{}
|
||||
for _, ss := range p.Begin {
|
||||
parts = append(parts, "BEGIN {\n"+ss.String()+"}")
|
||||
}
|
||||
for _, a := range p.Actions {
|
||||
parts = append(parts, a.String())
|
||||
}
|
||||
for _, ss := range p.End {
|
||||
parts = append(parts, "END {\n"+ss.String()+"}")
|
||||
}
|
||||
for _, function := range p.Functions {
|
||||
parts = append(parts, function.String())
|
||||
}
|
||||
return strings.Join(parts, "\n\n")
|
||||
}
|
||||
|
||||
// Stmts is a block containing multiple statements.
|
||||
type Stmts []Stmt
|
||||
|
||||
func (ss Stmts) String() string {
|
||||
lines := []string{}
|
||||
for _, s := range ss {
|
||||
subLines := strings.Split(s.String(), "\n")
|
||||
for _, sl := range subLines {
|
||||
lines = append(lines, " "+sl+"\n")
|
||||
}
|
||||
}
|
||||
return strings.Join(lines, "")
|
||||
}
|
||||
|
||||
// Action is pattern-action section of a program.
|
||||
type Action struct {
|
||||
Pattern []Expr
|
||||
Stmts Stmts
|
||||
}
|
||||
|
||||
func (a *Action) String() string {
|
||||
patterns := make([]string, len(a.Pattern))
|
||||
for i, p := range a.Pattern {
|
||||
patterns[i] = p.String()
|
||||
}
|
||||
sep := ""
|
||||
if len(patterns) > 0 && a.Stmts != nil {
|
||||
sep = " "
|
||||
}
|
||||
stmtsStr := ""
|
||||
if a.Stmts != nil {
|
||||
stmtsStr = "{\n" + a.Stmts.String() + "}"
|
||||
}
|
||||
return strings.Join(patterns, ", ") + sep + stmtsStr
|
||||
}
|
||||
|
||||
// Node is an interface to be satisfied by all AST elements.
|
||||
// We need it to be able to work with AST in a generic way, like in ast.Walk().
|
||||
type Node interface {
|
||||
node()
|
||||
}
|
||||
|
||||
// All these types implement the Node interface.
|
||||
func (p *Program) node() {}
|
||||
func (a *Action) node() {}
|
||||
func (f *Function) node() {}
|
||||
func (e *FieldExpr) node() {}
|
||||
func (e *NamedFieldExpr) node() {}
|
||||
func (e *UnaryExpr) node() {}
|
||||
func (e *BinaryExpr) node() {}
|
||||
func (e *InExpr) node() {}
|
||||
func (e *CondExpr) node() {}
|
||||
func (e *NumExpr) node() {}
|
||||
func (e *StrExpr) node() {}
|
||||
func (e *RegExpr) node() {}
|
||||
func (e *VarExpr) node() {}
|
||||
func (e *IndexExpr) node() {}
|
||||
func (e *AssignExpr) node() {}
|
||||
func (e *AugAssignExpr) node() {}
|
||||
func (e *IncrExpr) node() {}
|
||||
func (e *CallExpr) node() {}
|
||||
func (e *UserCallExpr) node() {}
|
||||
func (e *MultiExpr) node() {}
|
||||
func (e *GetlineExpr) node() {}
|
||||
func (e *GroupingExpr) node() {}
|
||||
func (s *PrintStmt) node() {}
|
||||
func (s *PrintfStmt) node() {}
|
||||
func (s *ExprStmt) node() {}
|
||||
func (s *IfStmt) node() {}
|
||||
func (s *ForStmt) node() {}
|
||||
func (s *ForInStmt) node() {}
|
||||
func (s *WhileStmt) node() {}
|
||||
func (s *DoWhileStmt) node() {}
|
||||
func (s *BreakStmt) node() {}
|
||||
func (s *ContinueStmt) node() {}
|
||||
func (s *NextStmt) node() {}
|
||||
func (s *NextfileStmt) node() {}
|
||||
func (s *ExitStmt) node() {}
|
||||
func (s *DeleteStmt) node() {}
|
||||
func (s *ReturnStmt) node() {}
|
||||
func (s *BlockStmt) node() {}
|
||||
|
||||
// Expr is the abstract syntax tree for any AWK expression.
|
||||
type Expr interface {
|
||||
Node
|
||||
precedence() int
|
||||
String() string
|
||||
}
|
||||
|
||||
// Table of operator precedence, lowest to highest
|
||||
const (
|
||||
precAssign = iota
|
||||
precCond
|
||||
precOr
|
||||
precAnd
|
||||
precIn
|
||||
precMatch
|
||||
precCompare
|
||||
precConcat
|
||||
precAdd
|
||||
precMul
|
||||
precUnary
|
||||
precPower
|
||||
precPreIncr
|
||||
precPostIncr
|
||||
precField
|
||||
precPrimary
|
||||
precGrouping
|
||||
)
|
||||
|
||||
// All these types implement the Expr interface.
|
||||
func (e *FieldExpr) precedence() int { return precField }
|
||||
func (e *NamedFieldExpr) precedence() int { return precField }
|
||||
func (e *UnaryExpr) precedence() int { return precUnary }
|
||||
func (e *InExpr) precedence() int { return precIn }
|
||||
func (e *CondExpr) precedence() int { return precCond }
|
||||
func (e *NumExpr) precedence() int { return precPrimary }
|
||||
func (e *StrExpr) precedence() int { return precPrimary }
|
||||
func (e *RegExpr) precedence() int { return precPrimary }
|
||||
func (e *VarExpr) precedence() int { return precPrimary }
|
||||
func (e *IndexExpr) precedence() int { return precPrimary }
|
||||
func (e *AssignExpr) precedence() int { return precAssign }
|
||||
func (e *AugAssignExpr) precedence() int { return precAssign }
|
||||
func (e *CallExpr) precedence() int { return precPrimary }
|
||||
func (e *UserCallExpr) precedence() int { return precPrimary }
|
||||
func (e *MultiExpr) precedence() int { return precPrimary }
|
||||
func (e *GetlineExpr) precedence() int { return precPrimary }
|
||||
func (e *GroupingExpr) precedence() int { return precGrouping }
|
||||
|
||||
func (e *IncrExpr) precedence() int {
|
||||
if e.Pre {
|
||||
return precPreIncr
|
||||
}
|
||||
return precPostIncr
|
||||
}
|
||||
|
||||
func (e *BinaryExpr) precedence() int {
|
||||
switch e.Op {
|
||||
case AND:
|
||||
return precAnd
|
||||
case OR:
|
||||
return precOr
|
||||
case CONCAT:
|
||||
return precConcat
|
||||
case ADD, SUB:
|
||||
return precAdd
|
||||
case MUL, DIV, MOD:
|
||||
return precMul
|
||||
case EQUALS, LESS, LTE, GREATER, GTE, NOT_EQUALS:
|
||||
return precCompare
|
||||
case MATCH, NOT_MATCH:
|
||||
return precMatch
|
||||
case POW:
|
||||
return precPower
|
||||
default:
|
||||
return precPrimary
|
||||
}
|
||||
}
|
||||
|
||||
// parenthesize returns the string version of e, surrounding it in
|
||||
// parentheses if e's precedence is lower than that of other.
|
||||
func parenthesize(e, other Expr) string {
|
||||
if e.precedence() < other.precedence() {
|
||||
return "(" + e.String() + ")"
|
||||
}
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// FieldExpr is an expression like $0.
|
||||
type FieldExpr struct {
|
||||
Index Expr
|
||||
}
|
||||
|
||||
func (e *FieldExpr) String() string {
|
||||
return "$" + parenthesize(e.Index, e)
|
||||
}
|
||||
|
||||
// NamedFieldExpr is an expression like @"name".
|
||||
type NamedFieldExpr struct {
|
||||
Field Expr
|
||||
}
|
||||
|
||||
func (e *NamedFieldExpr) String() string {
|
||||
return "@" + parenthesize(e.Field, e)
|
||||
}
|
||||
|
||||
// UnaryExpr is an expression like -1234.
|
||||
type UnaryExpr struct {
|
||||
Op Token
|
||||
Value Expr
|
||||
}
|
||||
|
||||
func (e *UnaryExpr) String() string {
|
||||
return e.Op.String() + parenthesize(e.Value, e)
|
||||
}
|
||||
|
||||
// BinaryExpr is an expression like 1 + 2.
|
||||
type BinaryExpr struct {
|
||||
Left Expr
|
||||
Op Token
|
||||
Right Expr
|
||||
}
|
||||
|
||||
func (e *BinaryExpr) String() string {
|
||||
var op string
|
||||
if e.Op == CONCAT {
|
||||
op = " "
|
||||
} else {
|
||||
op = " " + e.Op.String() + " "
|
||||
}
|
||||
return parenthesize(e.Left, e) + op + parenthesize(e.Right, e)
|
||||
}
|
||||
|
||||
// InExpr is an expression like (index in array).
|
||||
type InExpr struct {
|
||||
Index []Expr
|
||||
Array string
|
||||
ArrayPos Position
|
||||
}
|
||||
|
||||
func (e *InExpr) String() string {
|
||||
if len(e.Index) == 1 {
|
||||
return parenthesize(e.Index[0], e) + " in " + e.Array
|
||||
}
|
||||
indices := make([]string, len(e.Index))
|
||||
for i, index := range e.Index {
|
||||
indices[i] = index.String()
|
||||
}
|
||||
return "(" + strings.Join(indices, ", ") + ") in " + e.Array
|
||||
}
|
||||
|
||||
// CondExpr is an expression like cond ? 1 : 0.
|
||||
type CondExpr struct {
|
||||
Cond Expr
|
||||
True Expr
|
||||
False Expr
|
||||
}
|
||||
|
||||
func (e *CondExpr) String() string {
|
||||
return parenthesize(e.Cond, e) + " ? " + parenthesize(e.True, e) + " : " + parenthesize(e.False, e)
|
||||
}
|
||||
|
||||
// NumExpr is a literal number like 1234.
|
||||
type NumExpr struct {
|
||||
Value float64
|
||||
}
|
||||
|
||||
func (e *NumExpr) String() string {
|
||||
if e.Value == float64(int64(e.Value)) {
|
||||
return strconv.FormatInt(int64(e.Value), 10)
|
||||
} else {
|
||||
return fmt.Sprintf("%.6g", e.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// StrExpr is a literal string like "foo" or a regex constant like /foo/.
|
||||
type StrExpr struct {
|
||||
Value string
|
||||
Regex bool
|
||||
}
|
||||
|
||||
func (e *StrExpr) String() string {
|
||||
if e.Regex {
|
||||
return formatRegex(e.Value)
|
||||
}
|
||||
return strconv.Quote(e.Value)
|
||||
}
|
||||
|
||||
// RegExpr is a stand-alone regex expression, equivalent to:
|
||||
// $0 ~ /regex/.
|
||||
type RegExpr struct {
|
||||
Regex string
|
||||
}
|
||||
|
||||
func (e *RegExpr) String() string {
|
||||
return formatRegex(e.Regex)
|
||||
}
|
||||
|
||||
// VarExpr is a variable reference (special var, global, or local).
|
||||
type VarExpr struct {
|
||||
Name string
|
||||
Pos Position
|
||||
}
|
||||
|
||||
func (e *VarExpr) String() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
// IndexExpr is an expression like a[k] (rvalue or lvalue).
|
||||
type IndexExpr struct {
|
||||
Array string
|
||||
ArrayPos Position
|
||||
Index []Expr
|
||||
}
|
||||
|
||||
func (e *IndexExpr) String() string {
|
||||
indices := make([]string, len(e.Index))
|
||||
for i, index := range e.Index {
|
||||
indices[i] = index.String()
|
||||
}
|
||||
return e.Array + "[" + strings.Join(indices, ", ") + "]"
|
||||
}
|
||||
|
||||
// AssignExpr is an expression like x = 1234.
|
||||
type AssignExpr struct {
|
||||
Left Expr // can be one of: var, array[x], $n
|
||||
Right Expr
|
||||
}
|
||||
|
||||
func (e *AssignExpr) String() string {
|
||||
return parenthesize(e.Left, e) + " = " + parenthesize(e.Right, e)
|
||||
}
|
||||
|
||||
// AugAssignExpr is an assignment expression like x += 5.
|
||||
type AugAssignExpr struct {
|
||||
Left Expr // can be one of: var, array[x], $n
|
||||
Op Token
|
||||
Right Expr
|
||||
}
|
||||
|
||||
func (e *AugAssignExpr) String() string {
|
||||
return parenthesize(e.Left, e) + " " + e.Op.String() + "= " + parenthesize(e.Right, e)
|
||||
}
|
||||
|
||||
// IncrExpr is an increment or decrement expression like x++ or --y.
|
||||
type IncrExpr struct {
|
||||
Expr Expr
|
||||
Op Token
|
||||
Pre bool
|
||||
}
|
||||
|
||||
func (e *IncrExpr) String() string {
|
||||
if e.Pre {
|
||||
return e.Op.String() + parenthesize(e.Expr, e)
|
||||
} else {
|
||||
return parenthesize(e.Expr, e) + e.Op.String()
|
||||
}
|
||||
}
|
||||
|
||||
// CallExpr is a builtin function call like length($1).
|
||||
type CallExpr struct {
|
||||
Func Token
|
||||
Args []Expr
|
||||
}
|
||||
|
||||
func (e *CallExpr) String() string {
|
||||
args := make([]string, len(e.Args))
|
||||
for i, a := range e.Args {
|
||||
args[i] = a.String()
|
||||
}
|
||||
return e.Func.String() + "(" + strings.Join(args, ", ") + ")"
|
||||
}
|
||||
|
||||
// UserCallExpr is a user-defined function call like my_func(1, 2, 3),
|
||||
// where my_func is either AWK-defined or a native Go function.
|
||||
type UserCallExpr struct {
|
||||
Name string
|
||||
Args []Expr
|
||||
Pos Position
|
||||
}
|
||||
|
||||
func (e *UserCallExpr) String() string {
|
||||
args := make([]string, len(e.Args))
|
||||
for i, a := range e.Args {
|
||||
args[i] = a.String()
|
||||
}
|
||||
return e.Name + "(" + strings.Join(args, ", ") + ")"
|
||||
}
|
||||
|
||||
// MultiExpr isn't an interpretable expression, but it's used as a
|
||||
// pseudo-expression for print[f] parsing.
|
||||
type MultiExpr struct {
|
||||
Exprs []Expr
|
||||
}
|
||||
|
||||
func (e *MultiExpr) String() string {
|
||||
exprs := make([]string, len(e.Exprs))
|
||||
for i, e := range e.Exprs {
|
||||
exprs[i] = e.String()
|
||||
}
|
||||
return "(" + strings.Join(exprs, ", ") + ")"
|
||||
}
|
||||
|
||||
// GetlineExpr is an expression read from file or pipe input.
|
||||
type GetlineExpr struct {
|
||||
Command Expr
|
||||
Target Expr
|
||||
File Expr
|
||||
}
|
||||
|
||||
func (e *GetlineExpr) String() string {
|
||||
s := ""
|
||||
if e.Command != nil {
|
||||
s += parenthesize(e.Command, e) + " |"
|
||||
}
|
||||
s += "getline"
|
||||
if e.Target != nil {
|
||||
s += " " + e.Target.String()
|
||||
}
|
||||
if e.File != nil {
|
||||
s += " <" + parenthesize(e.File, e)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GroupingExpr is a parenthesized grouping expression.
|
||||
type GroupingExpr struct {
|
||||
Expr Expr
|
||||
}
|
||||
|
||||
func (e *GroupingExpr) String() string {
|
||||
return "(" + e.Expr.String() + ")"
|
||||
}
|
||||
|
||||
// IsLValue returns true if the given expression can be used as an
|
||||
// lvalue (on the left-hand side of an assignment, in a ++ or --
|
||||
// operation, or as the third argument to sub or gsub).
|
||||
func IsLValue(expr Expr) bool {
|
||||
switch expr.(type) {
|
||||
case *VarExpr, *IndexExpr, *FieldExpr:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// formatRegex formats the regex string r.
|
||||
func formatRegex(r string) string {
|
||||
escaped := strings.ReplaceAll(r, "/", `\/`)
|
||||
return "/" + escaped + "/"
|
||||
}
|
||||
|
||||
// Stmt is the abstract syntax tree for any AWK statement.
|
||||
type Stmt interface {
|
||||
Node
|
||||
StartPos() Position // position of first character belonging to the node
|
||||
EndPos() Position // position of first character immediately after the node
|
||||
stmt()
|
||||
String() string
|
||||
}
|
||||
|
||||
// All these types implement the Stmt interface.
|
||||
func (s *PrintStmt) stmt() {}
|
||||
func (s *PrintfStmt) stmt() {}
|
||||
func (s *ExprStmt) stmt() {}
|
||||
func (s *IfStmt) stmt() {}
|
||||
func (s *ForStmt) stmt() {}
|
||||
func (s *ForInStmt) stmt() {}
|
||||
func (s *WhileStmt) stmt() {}
|
||||
func (s *DoWhileStmt) stmt() {}
|
||||
func (s *BreakStmt) stmt() {}
|
||||
func (s *ContinueStmt) stmt() {}
|
||||
func (s *NextStmt) stmt() {}
|
||||
func (s *NextfileStmt) stmt() {}
|
||||
func (s *ExitStmt) stmt() {}
|
||||
func (s *DeleteStmt) stmt() {}
|
||||
func (s *ReturnStmt) stmt() {}
|
||||
func (s *BlockStmt) stmt() {}
|
||||
|
||||
func (s *PrintStmt) StartPos() Position { return s.Start }
|
||||
func (s *PrintfStmt) StartPos() Position { return s.Start }
|
||||
func (s *ExprStmt) StartPos() Position { return s.Start }
|
||||
func (s *IfStmt) StartPos() Position { return s.Start }
|
||||
func (s *ForStmt) StartPos() Position { return s.Start }
|
||||
func (s *ForInStmt) StartPos() Position { return s.Start }
|
||||
func (s *WhileStmt) StartPos() Position { return s.Start }
|
||||
func (s *DoWhileStmt) StartPos() Position { return s.Start }
|
||||
func (s *BreakStmt) StartPos() Position { return s.Start }
|
||||
func (s *ContinueStmt) StartPos() Position { return s.Start }
|
||||
func (s *NextStmt) StartPos() Position { return s.Start }
|
||||
func (s *NextfileStmt) StartPos() Position { return s.Start }
|
||||
func (s *ExitStmt) StartPos() Position { return s.Start }
|
||||
func (s *DeleteStmt) StartPos() Position { return s.Start }
|
||||
func (s *ReturnStmt) StartPos() Position { return s.Start }
|
||||
func (s *BlockStmt) StartPos() Position { return s.Start }
|
||||
|
||||
func (s *PrintStmt) EndPos() Position { return s.End }
|
||||
func (s *PrintfStmt) EndPos() Position { return s.End }
|
||||
func (s *ExprStmt) EndPos() Position { return s.End }
|
||||
func (s *IfStmt) EndPos() Position { return s.End }
|
||||
func (s *ForStmt) EndPos() Position { return s.End }
|
||||
func (s *ForInStmt) EndPos() Position { return s.End }
|
||||
func (s *WhileStmt) EndPos() Position { return s.End }
|
||||
func (s *DoWhileStmt) EndPos() Position { return s.End }
|
||||
func (s *BreakStmt) EndPos() Position { return s.End }
|
||||
func (s *ContinueStmt) EndPos() Position { return s.End }
|
||||
func (s *NextStmt) EndPos() Position { return s.End }
|
||||
func (s *NextfileStmt) EndPos() Position { return s.End }
|
||||
func (s *ExitStmt) EndPos() Position { return s.End }
|
||||
func (s *DeleteStmt) EndPos() Position { return s.End }
|
||||
func (s *ReturnStmt) EndPos() Position { return s.End }
|
||||
func (s *BlockStmt) EndPos() Position { return s.End }
|
||||
|
||||
// PrintStmt is a statement like print $1, $3.
|
||||
type PrintStmt struct {
|
||||
Args []Expr
|
||||
Redirect Token
|
||||
Dest Expr
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *PrintStmt) String() string {
|
||||
return printString("print", s.Args, s.Redirect, s.Dest)
|
||||
}
|
||||
|
||||
func printString(f string, args []Expr, redirect Token, dest Expr) string {
|
||||
parts := make([]string, len(args))
|
||||
for i, a := range args {
|
||||
parts[i] = a.String()
|
||||
}
|
||||
str := f + " " + strings.Join(parts, ", ")
|
||||
if dest != nil {
|
||||
str += " " + redirect.String() + dest.String()
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// PrintfStmt is a statement like printf "%3d", 1234.
|
||||
type PrintfStmt struct {
|
||||
Args []Expr
|
||||
Redirect Token
|
||||
Dest Expr
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *PrintfStmt) String() string {
|
||||
return printString("printf", s.Args, s.Redirect, s.Dest)
|
||||
}
|
||||
|
||||
// ExprStmt is statement like a bare function call: my_func(x).
|
||||
type ExprStmt struct {
|
||||
Expr Expr
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *ExprStmt) String() string {
|
||||
return s.Expr.String()
|
||||
}
|
||||
|
||||
// IfStmt is an if or if-else statement.
|
||||
type IfStmt struct {
|
||||
Cond Expr
|
||||
BodyStart Position
|
||||
Body Stmts
|
||||
Else Stmts
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *IfStmt) String() string {
|
||||
str := "if (" + s.Cond.String() + ") {\n" + s.Body.String() + "}"
|
||||
if len(s.Else) > 0 {
|
||||
str += " else {\n" + s.Else.String() + "}"
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// ForStmt is a C-like for loop: for (i=0; i<10; i++) print i.
|
||||
type ForStmt struct {
|
||||
Pre Stmt
|
||||
Cond Expr
|
||||
Post Stmt
|
||||
BodyStart Position
|
||||
Body Stmts
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *ForStmt) String() string {
|
||||
preStr := ""
|
||||
if s.Pre != nil {
|
||||
preStr = s.Pre.String()
|
||||
}
|
||||
condStr := ""
|
||||
if s.Cond != nil {
|
||||
condStr = " " + s.Cond.String()
|
||||
}
|
||||
postStr := ""
|
||||
if s.Post != nil {
|
||||
postStr = " " + s.Post.String()
|
||||
}
|
||||
return "for (" + preStr + ";" + condStr + ";" + postStr + ") {\n" + s.Body.String() + "}"
|
||||
}
|
||||
|
||||
// ForInStmt is a for loop like for (k in a) print k, a[k].
|
||||
type ForInStmt struct {
|
||||
Var string
|
||||
VarPos Position
|
||||
Array string
|
||||
ArrayPos Position
|
||||
BodyStart Position
|
||||
Body Stmts
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *ForInStmt) String() string {
|
||||
return "for (" + s.Var + " in " + s.Array + ") {\n" + s.Body.String() + "}"
|
||||
}
|
||||
|
||||
// WhileStmt is a while loop.
|
||||
type WhileStmt struct {
|
||||
Cond Expr
|
||||
BodyStart Position
|
||||
Body Stmts
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *WhileStmt) String() string {
|
||||
return "while (" + s.Cond.String() + ") {\n" + s.Body.String() + "}"
|
||||
}
|
||||
|
||||
// DoWhileStmt is a do-while loop.
|
||||
type DoWhileStmt struct {
|
||||
Body Stmts
|
||||
Cond Expr
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *DoWhileStmt) String() string {
|
||||
return "do {\n" + s.Body.String() + "} while (" + s.Cond.String() + ")"
|
||||
}
|
||||
|
||||
// BreakStmt is a break statement.
|
||||
type BreakStmt struct {
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *BreakStmt) String() string {
|
||||
return "break"
|
||||
}
|
||||
|
||||
// ContinueStmt is a continue statement.
|
||||
type ContinueStmt struct {
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *ContinueStmt) String() string {
|
||||
return "continue"
|
||||
}
|
||||
|
||||
// NextStmt is a next statement.
|
||||
type NextStmt struct {
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *NextStmt) String() string {
|
||||
return "next"
|
||||
}
|
||||
|
||||
// NextfileStmt is a nextfile statement.
|
||||
type NextfileStmt struct {
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *NextfileStmt) String() string {
|
||||
return "nextfile"
|
||||
}
|
||||
|
||||
// ExitStmt is an exit statement.
|
||||
type ExitStmt struct {
|
||||
Status Expr
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *ExitStmt) String() string {
|
||||
var statusStr string
|
||||
if s.Status != nil {
|
||||
statusStr = " " + s.Status.String()
|
||||
}
|
||||
return "exit" + statusStr
|
||||
}
|
||||
|
||||
// DeleteStmt is a statement like delete a[k].
|
||||
type DeleteStmt struct {
|
||||
Array string
|
||||
ArrayPos Position
|
||||
Index []Expr
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *DeleteStmt) String() string {
|
||||
if len(s.Index) == 0 {
|
||||
return "delete " + s.Array
|
||||
}
|
||||
indices := make([]string, len(s.Index))
|
||||
for i, index := range s.Index {
|
||||
indices[i] = index.String()
|
||||
}
|
||||
return "delete " + s.Array + "[" + strings.Join(indices, ", ") + "]"
|
||||
}
|
||||
|
||||
// ReturnStmt is a return statement.
|
||||
type ReturnStmt struct {
|
||||
Value Expr
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *ReturnStmt) String() string {
|
||||
var valueStr string
|
||||
if s.Value != nil {
|
||||
valueStr = " " + s.Value.String()
|
||||
}
|
||||
return "return" + valueStr
|
||||
}
|
||||
|
||||
// BlockStmt is a stand-alone block like { print "x" }.
|
||||
type BlockStmt struct {
|
||||
Body Stmts
|
||||
Start Position
|
||||
End Position
|
||||
}
|
||||
|
||||
func (s *BlockStmt) String() string {
|
||||
return "{\n" + s.Body.String() + "}"
|
||||
}
|
||||
|
||||
// Function is the AST for a user-defined function.
|
||||
type Function struct {
|
||||
Name string
|
||||
Params []string
|
||||
Body Stmts
|
||||
Pos Position
|
||||
}
|
||||
|
||||
func (f *Function) String() string {
|
||||
return "function " + f.Name + "(" + strings.Join(f.Params, ", ") + ") {\n" +
|
||||
f.Body.String() + "}"
|
||||
}
|
||||
|
||||
// PositionError represents an error bound to specific position in source.
|
||||
type PositionError struct {
|
||||
// Source line/column position where the error occurred.
|
||||
Position Position
|
||||
// Error message.
|
||||
Message string
|
||||
}
|
||||
|
||||
// PosErrorf like fmt.Errorf, but with an explicit position.
|
||||
func PosErrorf(pos Position, format string, args ...any) error {
|
||||
message := fmt.Sprintf(format, args...)
|
||||
return &PositionError{pos, message}
|
||||
}
|
||||
|
||||
// Error returns a formatted version of the error, including the line
|
||||
// and column numbers.
|
||||
func (e *PositionError) Error() string {
|
||||
return fmt.Sprintf("parse error at %d:%d: %s", e.Position.Line, e.Position.Column, e.Message)
|
||||
}
|
||||
Reference in New Issue
Block a user