userland: add archive tools and pinned GoAWK, esbuild, and QuickJS

This commit is contained in:
2026-11-07 10:56:29 +00:00
parent 7e7e49bfa2
commit d79b2bc2e1
1798 changed files with 562969 additions and 1 deletions
+800
View File
@@ -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)
}
@@ -0,0 +1,100 @@
// Special variable constants
package ast
import (
"fmt"
)
const (
V_ILLEGAL = iota
V_ARGC
V_CONVFMT
V_FILENAME
V_FNR
V_FS
V_INPUTMODE
V_NF
V_NR
V_OFMT
V_OFS
V_ORS
V_OUTPUTMODE
V_RLENGTH
V_RS
V_RSTART
V_RT
V_SUBSEP
V_LAST = V_SUBSEP
)
var specialVars = map[string]int{
"ARGC": V_ARGC,
"CONVFMT": V_CONVFMT,
"FILENAME": V_FILENAME,
"FNR": V_FNR,
"FS": V_FS,
"INPUTMODE": V_INPUTMODE,
"NF": V_NF,
"NR": V_NR,
"OFMT": V_OFMT,
"OFS": V_OFS,
"ORS": V_ORS,
"OUTPUTMODE": V_OUTPUTMODE,
"RLENGTH": V_RLENGTH,
"RS": V_RS,
"RSTART": V_RSTART,
"RT": V_RT,
"SUBSEP": V_SUBSEP,
}
// SpecialVarIndex returns the "index" of the special variable, or 0
// if it's not a special variable.
func SpecialVarIndex(name string) int {
return specialVars[name]
}
// SpecialVarName returns the name of the special variable by index.
func SpecialVarName(index int) string {
switch index {
case V_ILLEGAL:
return "ILLEGAL"
case V_ARGC:
return "ARGC"
case V_CONVFMT:
return "CONVFMT"
case V_FILENAME:
return "FILENAME"
case V_FNR:
return "FNR"
case V_FS:
return "FS"
case V_INPUTMODE:
return "INPUTMODE"
case V_NF:
return "NF"
case V_NR:
return "NR"
case V_OFMT:
return "OFMT"
case V_OFS:
return "OFS"
case V_ORS:
return "ORS"
case V_OUTPUTMODE:
return "OUTPUTMODE"
case V_RLENGTH:
return "RLENGTH"
case V_RS:
return "RS"
case V_RSTART:
return "RSTART"
case V_RT:
return "RT"
case V_SUBSEP:
return "SUBSEP"
default:
return fmt.Sprintf("<unknown special var %d>", index)
}
}
@@ -0,0 +1,46 @@
package ast
import (
"testing"
)
func TestNameIndex(t *testing.T) {
tests := []struct {
name string
index int
}{
{"ILLEGAL", V_ILLEGAL},
{"ARGC", V_ARGC},
{"CONVFMT", V_CONVFMT},
{"FILENAME", V_FILENAME},
{"FNR", V_FNR},
{"FS", V_FS},
{"INPUTMODE", V_INPUTMODE},
{"NF", V_NF},
{"NR", V_NR},
{"OFMT", V_OFMT},
{"OFS", V_OFS},
{"ORS", V_ORS},
{"OUTPUTMODE", V_OUTPUTMODE},
{"RLENGTH", V_RLENGTH},
{"RS", V_RS},
{"RSTART", V_RSTART},
{"RT", V_RT},
{"SUBSEP", V_SUBSEP},
{"<unknown special var 42>", 42},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
name := SpecialVarName(test.index)
if name != test.name {
t.Errorf("got %q, want %q", name, test.name)
}
if test.index <= V_LAST {
index := SpecialVarIndex(test.name)
if index != test.index {
t.Errorf("got %d, want %d", index, test.index)
}
}
})
}
}
+177
View File
@@ -0,0 +1,177 @@
package ast
import "fmt"
// Visitor has a Visit method which is invoked for each node encountered by Walk.
// If the result visitor w is not nil, Walk visits each of the children
// of node with the visitor w, followed by a call of w.Visit(nil).
type Visitor interface {
Visit(node Node) (w Visitor)
}
// WalkExprList walks a visitor over a list of expression AST nodes
func WalkExprList(v Visitor, exprs []Expr) {
for _, expr := range exprs {
Walk(v, expr)
}
}
// WalkStmtList walks a visitor over a list of statement AST nodes
func WalkStmtList(v Visitor, stmts []Stmt) {
for _, stmt := range stmts {
Walk(v, stmt)
}
}
// Walk traverses an AST in depth-first order: It starts by calling
// v.Visit(node); if node is nil, it does nothing. If the visitor w returned by
// v.Visit(node) is not nil, Walk is invoked recursively with visitor
// w for each of the non-nil children of node, followed by a call of
// w.Visit(nil).
func Walk(v Visitor, node Node) {
if node == nil {
return
}
if v = v.Visit(node); v == nil {
return
}
// walk children
// (the order of the cases matches the order
// of the corresponding node types in ast.go)
switch n := node.(type) {
// expressions
case *FieldExpr:
Walk(v, n.Index)
case *NamedFieldExpr:
Walk(v, n.Field)
case *UnaryExpr:
Walk(v, n.Value)
case *BinaryExpr:
Walk(v, n.Left)
Walk(v, n.Right)
case *InExpr:
WalkExprList(v, n.Index)
case *CondExpr:
Walk(v, n.Cond)
Walk(v, n.True)
Walk(v, n.False)
case *NumExpr: // leaf
case *StrExpr: // leaf
case *RegExpr: // leaf
case *VarExpr: // leaf
case *IndexExpr:
WalkExprList(v, n.Index)
case *AssignExpr:
Walk(v, n.Left)
Walk(v, n.Right)
case *AugAssignExpr:
Walk(v, n.Left)
Walk(v, n.Right)
case *IncrExpr:
Walk(v, n.Expr)
case *CallExpr:
WalkExprList(v, n.Args)
case *UserCallExpr:
WalkExprList(v, n.Args)
case *MultiExpr:
WalkExprList(v, n.Exprs)
case *GetlineExpr:
Walk(v, n.Command)
Walk(v, n.Target)
Walk(v, n.File)
case *GroupingExpr:
Walk(v, n.Expr)
// statements
case *PrintStmt:
WalkExprList(v, n.Args)
Walk(v, n.Dest)
case *PrintfStmt:
WalkExprList(v, n.Args)
Walk(v, n.Dest)
case *ExprStmt:
Walk(v, n.Expr)
case *IfStmt:
Walk(v, n.Cond)
WalkStmtList(v, n.Body)
WalkStmtList(v, n.Else)
case *ForStmt:
Walk(v, n.Pre)
Walk(v, n.Cond)
Walk(v, n.Post)
WalkStmtList(v, n.Body)
case *ForInStmt:
WalkStmtList(v, n.Body)
case *WhileStmt:
Walk(v, n.Cond)
WalkStmtList(v, n.Body)
case *DoWhileStmt:
WalkStmtList(v, n.Body)
Walk(v, n.Cond)
case *BreakStmt: // leaf
case *ContinueStmt: // leaf
case *NextStmt: // leaf
case *NextfileStmt: // leaf
case *ExitStmt:
Walk(v, n.Status)
case *DeleteStmt:
WalkExprList(v, n.Index)
case *ReturnStmt:
Walk(v, n.Value)
case *BlockStmt:
WalkStmtList(v, n.Body)
case *Program:
for _, stmts := range n.Begin {
WalkStmtList(v, stmts)
}
for _, action := range n.Actions {
Walk(v, action)
}
for _, function := range n.Functions {
Walk(v, function)
}
for _, stmts := range n.End {
WalkStmtList(v, stmts)
}
case *Action:
WalkExprList(v, n.Pattern)
WalkStmtList(v, n.Stmts)
case *Function:
WalkStmtList(v, n.Body)
default:
panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
}
v.Visit(nil)
}