miller/pkg/dsl/ast_print.go
John Kerl b4ff26a7d0
Static-check fixes from @lespea #1657, batch 6/n (#1708)
* Static-check fixes from @lespea #1657, batch 2/n

* Static-check fixes from @lespea #1657, batch 3/n

* Static-check fixes from @lespea #1657, batch 4/n

* Static-check fixes from @lespea #1657, batch 5/n

* Static-check fixes from @lespea #1657, batch 6/n
2024-10-27 12:06:17 -04:00

325 lines
7.4 KiB
Go

// ================================================================
// Print routines for AST and ASTNode
// ================================================================
package dsl
import (
"fmt"
"strings"
)
// ================================================================
// Print is indent-style multiline print.
// Example, given parse of '$y = 2 * $x + 1':
//
// * statement block
// * assignment "="
// * direct field value "y"
// * operator "+"
// * operator "*"
// * int literal "2"
// * direct field value "x"
// * int literal "1"
func (ast *AST) Print() {
ast.RootNode.Print()
}
// PrintParex is parenthesized-expression print.
// Example, given parse of '$y = 2 * $x + 1':
//
// (statement-block
// (=
// $y
// (+
// (* 2 $x)
// 1
// )
// )
// )
func (ast *AST) PrintParex() {
ast.RootNode.PrintParex()
}
// PrintParexOneLine is parenthesized-expression print, all on one line.
// Example, given parse of '$y = 2 * $x + 1':
//
// (statement-block (= $y (+ (* 2 $x) 1)))
func (ast *AST) PrintParexOneLine() {
ast.RootNode.PrintParexOneLine()
}
// ================================================================
// Print is indent-style multiline print.
func (node *ASTNode) Print() {
node.printAux(0)
}
// printAux is a recursion-helper for Print.
func (node *ASTNode) printAux(depth int) {
// Indent
for i := 0; i < depth; i++ {
fmt.Print(" ")
}
// Token text (if non-nil) and token type
tok := node.Token
fmt.Print("* " + node.Type)
if tok != nil {
fmt.Printf(" \"%s\"", string(tok.Lit))
}
fmt.Println()
// Children, indented one level further
for _, child := range node.Children {
child.printAux(depth + 1)
}
}
// ----------------------------------------------------------------
// PrintParex is parenthesized-expression print.
func (node *ASTNode) PrintParex() {
node.printParexAux(0)
}
// printParexAux is a recursion-helper for PrintParex.
func (node *ASTNode) printParexAux(depth int) {
if node.IsLeaf() {
for i := 0; i < depth; i++ {
fmt.Print(" ")
}
fmt.Println(node.Text())
} else if node.ChildrenAreAllLeaves() {
// E.g. (= sum 0) or (+ 1 2)
for i := 0; i < depth; i++ {
fmt.Print(" ")
}
fmt.Print("(")
fmt.Print(node.Text())
for _, child := range node.Children {
fmt.Print(" ")
fmt.Print(child.Text())
}
fmt.Println(")")
} else {
// Parent and opening parenthesis on first line
for i := 0; i < depth; i++ {
fmt.Print(" ")
}
fmt.Print("(")
fmt.Println(node.Text())
// Children on their own lines
for _, child := range node.Children {
child.printParexAux(depth + 1)
}
// Closing parenthesis on last line
for i := 0; i < depth; i++ {
fmt.Print(" ")
}
fmt.Println(")")
}
}
// ----------------------------------------------------------------
// PrintParexOneLine is parenthesized-expression print, all on one line.
func (node *ASTNode) PrintParexOneLine() {
node.printParexOneLineAux()
fmt.Println()
}
// printParexOneLineAux is a recursion-helper for PrintParexOneLine.
func (node *ASTNode) printParexOneLineAux() {
if node.IsLeaf() {
fmt.Print(node.Text())
} else {
fmt.Print("(")
fmt.Print(node.Text())
for _, child := range node.Children {
fmt.Print(" ")
child.printParexOneLineAux()
}
fmt.Print(")")
}
}
// ----------------------------------------------------------------
// IsLeaf determines if an AST node is a leaf node.
func (node *ASTNode) IsLeaf() bool {
return len(node.Children) == 0
}
// ChildrenAreAllLeaves determines if an AST node's children are all leaf nodes.
func (node *ASTNode) ChildrenAreAllLeaves() bool {
for _, child := range node.Children {
if !child.IsLeaf() {
return false
}
}
return true
}
// ----------------------------------------------------------------
// Text makes a human-readable, whitespace-free name for an AST node. Some
// nodes have non-nil tokens; other, nil. And token-types can have spaces in
// them. In this method we use custom mappings to always get a whitespace-free
// representation of the content of a single AST node.
func (node *ASTNode) Text() string {
tokenText := ""
if node.Token != nil {
tokenText = string(node.Token.Lit)
}
switch node.Type {
case NodeTypeStringLiteral:
return "\"" + strings.ReplaceAll(tokenText, "\"", "\\\"") + "\""
case NodeTypeIntLiteral:
return tokenText
case NodeTypeFloatLiteral:
return tokenText
case NodeTypeBoolLiteral:
return tokenText
case NodeTypeNullLiteral:
return tokenText
case NodeTypeArrayLiteral:
return tokenText
case NodeTypeMapLiteral:
return tokenText
case NodeTypeMapLiteralKeyValuePair:
return tokenText
case NodeTypeArrayOrMapIndexAccess:
return "[]"
case NodeTypeArraySliceAccess:
return "[:]"
case NodeTypeArraySliceEmptyLowerIndex:
return "array-slice-empty-lower-index"
case NodeTypeArraySliceEmptyUpperIndex:
return "array-slice-empty-upper-index"
case NodeTypeContextVariable:
return tokenText
case NodeTypeConstant:
return tokenText
case NodeTypeEnvironmentVariable:
return "ENV[\"" + tokenText + "\"]"
case NodeTypeDirectFieldValue:
return "$" + tokenText
case NodeTypeIndirectFieldValue:
return "$[" + tokenText + "]"
case NodeTypeFullSrec:
return tokenText
case NodeTypeDirectOosvarValue:
return "@" + tokenText
case NodeTypeIndirectOosvarValue:
return "@[" + tokenText + "]"
case NodeTypeFullOosvar:
return tokenText
case NodeTypeLocalVariable:
return tokenText
case NodeTypeTypedecl:
return tokenText
case NodeTypeStatementBlock:
return "statement-block"
case NodeTypeAssignment:
return tokenText
case NodeTypeUnset:
return tokenText
case NodeTypeBareBoolean:
return "bare-boolean"
case NodeTypeFilterStatement:
return tokenText
case NodeTypeEmit1Statement:
return tokenText
case NodeTypeEmitStatement:
return tokenText
case NodeTypeEmitPStatement:
return tokenText
case NodeTypeEmitFStatement:
return tokenText
case NodeTypeDumpStatement:
return tokenText
case NodeTypeEdumpStatement:
return tokenText
case NodeTypePrintStatement:
return tokenText
case NodeTypeEprintStatement:
return tokenText
case NodeTypePrintnStatement:
return tokenText
case NodeTypeEprintnStatement:
return tokenText
case NodeTypeNoOp:
return "no-op"
case NodeTypeOperator:
return tokenText
case NodeTypeDotOperator:
return tokenText
case NodeTypeFunctionCallsite:
return tokenText
case NodeTypeBeginBlock:
return "begin"
case NodeTypeEndBlock:
return "end"
case NodeTypeIfChain:
return "if-chain"
case NodeTypeIfItem:
return tokenText
case NodeTypeCondBlock:
return "cond"
case NodeTypeWhileLoop:
return tokenText
case NodeTypeDoWhileLoop:
return tokenText
case NodeTypeForLoopOneVariable:
return tokenText
case NodeTypeForLoopTwoVariable:
return tokenText
case NodeTypeForLoopMultivariable:
return tokenText
case NodeTypeTripleForLoop:
return tokenText
case NodeTypeBreak:
return tokenText
case NodeTypeContinue:
return tokenText
case NodeTypeNamedFunctionDefinition:
return "named-udf"
case NodeTypeUnnamedFunctionDefinition:
return "unnamed-udf"
case NodeTypeSubroutineDefinition:
return "subr"
case NodeTypeParameterList:
return "parameters"
case NodeTypeParameter:
return "parameter"
case NodeTypeParameterName:
return tokenText
case NodeTypeReturn:
return tokenText
case NodeTypePanic:
return tokenText
}
return "[ERROR]"
}