miller/pkg/terminals/repl/dsl.go
Adam Lesperance 085e831668
The package version must match the major tag version (#1654)
* Update package version

* Update makefile targets

* Update readme packages

* Remaining old packages via rg/sd
2024-09-20 12:10:11 -04:00

108 lines
3.6 KiB
Go

// ================================================================
// This is the handler for taking DSL statements typed in interactively by the
// user, parsing them to an AST, building a CST from the AST, and executing the
// CST. It also handles DSL statements invoked using ':load' or multi-line '<<'
// ... '>>' wherein statements are built into the AST without being executed
// right away.
//
// Specifically:
//
// * begin/end statements are parsed and stored into the AST regardless.
//
// * func/subr definitions are parsed and stored into the AST regardless.
//
// * For anything else in an interactive command besides begin/end/func/subr
// blocks, the statement(s) is/are executed immediately for interactive mode,
// else populated into the CST's main-statements block for load-from-file
// multi-line mode.
// ================================================================
package repl
import (
"fmt"
"strings"
"github.com/johnkerl/miller/v6/pkg/dsl"
"github.com/johnkerl/miller/v6/pkg/dsl/cst"
"github.com/johnkerl/miller/v6/pkg/mlrval"
)
// ----------------------------------------------------------------
func (repl *Repl) handleDSLStringImmediate(dslString string, doWarnings bool) error {
return repl.handleDSLStringAux(dslString, true, doWarnings)
}
func (repl *Repl) handleDSLStringBulk(dslString string, doWarnings bool) error {
return repl.handleDSLStringAux(dslString, false, doWarnings)
}
func (repl *Repl) handleDSLStringAux(
dslString string,
isReplImmediate bool, // False for load-from-file or non-immediate multi-line; true otherwise
doWarnings bool,
) error {
if strings.TrimSpace(dslString) == "" {
return nil
}
repl.cstRootNode.ResetForREPL()
// Disregard the first hadWarnings return value since it's only used for fatal-on-warnings,
// which the REPL doesn't use.
_, err := repl.cstRootNode.Build(
[]string{dslString},
cst.DSLInstanceTypeREPL,
isReplImmediate,
doWarnings,
func(dslString string, astNode *dsl.AST) {
if repl.astPrintMode == ASTPrintParex {
astNode.PrintParex()
} else if repl.astPrintMode == ASTPrintParexOneLine {
astNode.PrintParexOneLine()
} else if repl.astPrintMode == ASTPrintIndent {
astNode.Print()
}
},
)
if err != nil {
return err
}
// For load-from-file / non-immediate multi-line, each statement not in
// begin/end, and not a user-defined function/subroutine, is recorded in
// the "main block" to be executed later when the user asks to do so. For
// single-line/interactive mode, begin/end statements and UDF/UDS are
// recorded, but any other statements are executed immediately.
if isReplImmediate {
outrec, err := repl.cstRootNode.ExecuteREPLImmediate(repl.runtimeState)
if err != nil {
return err
}
repl.runtimeState.Inrec = outrec
// The filter expression for the main Miller DSL is any non-assignment
// statement like 'true' or '$x > 0.5' etc. For the REPL, we re-use this for
// interactive expressions to be printed to the terminal. For the main DSL,
// the default is mlrval.FromTrue(); for the REPL, the default is
// mlrval.NULL.
filterExpression := repl.runtimeState.FilterExpression
if filterExpression.IsNull() {
// nothing to print
} else if filterExpression.IsError() {
_, err := filterExpression.GetError()
if err == nil { // No supporting information
fmt.Printf("\"%s\"\n", filterExpression.String())
} else {
fmt.Printf("%v\n", err)
}
} else if filterExpression.IsStringOrVoid() {
fmt.Printf("\"%s\"\n", filterExpression.String())
} else {
fmt.Println(filterExpression.String())
}
repl.runtimeState.FilterExpression = mlrval.NULL
}
return nil
}