From 9963df409089b14d6730b6a59c7c4182a8d0e8c3 Mon Sep 17 00:00:00 2001 From: John Kerl Date: Wed, 5 Mar 2025 08:19:15 -0500 Subject: [PATCH] Switch to generics (#1763) * gradually replace list.List with slices * gradually replace list.List with slices * more * more * more --- pkg/dsl/cst/root.go | 34 +++++++++------------ pkg/dsl/cst/types.go | 9 +++--- pkg/runtime/stack.go | 18 +++++------ pkg/runtime/state.go | 17 +++++------ pkg/terminals/regtest/regtester.go | 49 ++++++++++++++---------------- 5 files changed, 57 insertions(+), 70 deletions(-) diff --git a/pkg/dsl/cst/root.go b/pkg/dsl/cst/root.go index e6d2de59a..099301bac 100644 --- a/pkg/dsl/cst/root.go +++ b/pkg/dsl/cst/root.go @@ -6,7 +6,6 @@ package cst import ( - "container/list" "errors" "fmt" "os" @@ -37,9 +36,9 @@ func NewEmptyRoot( udfManager: NewUDFManager(), udsManager: NewUDSManager(), allowUDFUDSRedefinitions: false, - unresolvedFunctionCallsites: list.New(), - unresolvedSubroutineCallsites: list.New(), - outputHandlerManagers: list.New(), + unresolvedFunctionCallsites: make([]*UDFCallsite, 0), + unresolvedSubroutineCallsites: make([]*UDSCallsite, 0), + outputHandlerManagers: make([]output.OutputHandlerManager, 0), recordWriterOptions: recordWriterOptions, dslInstanceType: dslInstanceType, } @@ -364,11 +363,11 @@ func (root *RootNode) buildMainPass(ast *dsl.AST, isReplImmediate bool) error { // This is invoked within the buildMainPass call tree whenever a function is // called before it's defined. func (root *RootNode) rememberUnresolvedFunctionCallsite(udfCallsite *UDFCallsite) { - root.unresolvedFunctionCallsites.PushBack(udfCallsite) + root.unresolvedFunctionCallsites = append(root.unresolvedFunctionCallsites, udfCallsite) } func (root *RootNode) rememberUnresolvedSubroutineCallsite(udsCallsite *UDSCallsite) { - root.unresolvedSubroutineCallsites.PushBack(udsCallsite) + root.unresolvedSubroutineCallsites = append(root.unresolvedSubroutineCallsites, udsCallsite) } // After-pass after buildMainPass returns, in case a function was called before @@ -381,10 +380,9 @@ func (root *RootNode) rememberUnresolvedSubroutineCallsite(udsCallsite *UDSCalls // So, our error message should reflect all those options. func (root *RootNode) resolveFunctionCallsites() error { - for root.unresolvedFunctionCallsites.Len() > 0 { - unresolvedFunctionCallsite := root.unresolvedFunctionCallsites.Remove( - root.unresolvedFunctionCallsites.Front(), - ).(*UDFCallsite) + for len(root.unresolvedFunctionCallsites) > 0 { + unresolvedFunctionCallsite := root.unresolvedFunctionCallsites[0] + root.unresolvedFunctionCallsites = root.unresolvedFunctionCallsites[1:] functionName := unresolvedFunctionCallsite.udf.signature.funcOrSubrName callsiteArity := unresolvedFunctionCallsite.udf.signature.arity @@ -405,10 +403,9 @@ func (root *RootNode) resolveFunctionCallsites() error { } func (root *RootNode) resolveSubroutineCallsites() error { - for root.unresolvedSubroutineCallsites.Len() > 0 { - unresolvedSubroutineCallsite := root.unresolvedSubroutineCallsites.Remove( - root.unresolvedSubroutineCallsites.Front(), - ).(*UDSCallsite) + for len(root.unresolvedSubroutineCallsites) > 0 { + unresolvedSubroutineCallsite := root.unresolvedSubroutineCallsites[0] + root.unresolvedSubroutineCallsites = root.unresolvedSubroutineCallsites[1:] subroutineName := unresolvedSubroutineCallsite.uds.signature.funcOrSubrName callsiteArity := unresolvedSubroutineCallsite.uds.signature.arity @@ -438,12 +435,11 @@ func (root *RootNode) resolveSubroutineCallsites() error { func (root *RootNode) RegisterOutputHandlerManager( outputHandlerManager output.OutputHandlerManager, ) { - root.outputHandlerManagers.PushBack(outputHandlerManager) + root.outputHandlerManagers = append(root.outputHandlerManagers, outputHandlerManager) } func (root *RootNode) ProcessEndOfStream() { - for entry := root.outputHandlerManagers.Front(); entry != nil; entry = entry.Next() { - outputHandlerManager := entry.Value.(output.OutputHandlerManager) + for _, outputHandlerManager := range root.outputHandlerManagers { errs := outputHandlerManager.Close() if len(errs) != 0 { for _, err := range errs { @@ -501,8 +497,8 @@ func (root *RootNode) ExecuteREPLImmediate(state *runtime.State) (outrec *mlrval // This is the 'and then discarded' part of that. func (root *RootNode) ResetForREPL() { root.replImmediateBlock = NewStatementBlockNode() - root.unresolvedFunctionCallsites = list.New() - root.unresolvedSubroutineCallsites = list.New() + root.unresolvedFunctionCallsites = make([]*UDFCallsite, 0) + root.unresolvedSubroutineCallsites = make([]*UDSCallsite, 0) } // This is for the REPL's context-printer command. diff --git a/pkg/dsl/cst/types.go b/pkg/dsl/cst/types.go index bc1b2768a..dea4861a6 100644 --- a/pkg/dsl/cst/types.go +++ b/pkg/dsl/cst/types.go @@ -5,11 +5,10 @@ package cst import ( - "container/list" - "github.com/johnkerl/miller/v6/pkg/cli" "github.com/johnkerl/miller/v6/pkg/dsl" "github.com/johnkerl/miller/v6/pkg/mlrval" + "github.com/johnkerl/miller/v6/pkg/output" "github.com/johnkerl/miller/v6/pkg/runtime" ) @@ -44,9 +43,9 @@ type RootNode struct { udfManager *UDFManager udsManager *UDSManager allowUDFUDSRedefinitions bool - unresolvedFunctionCallsites *list.List - unresolvedSubroutineCallsites *list.List - outputHandlerManagers *list.List + unresolvedFunctionCallsites []*UDFCallsite + unresolvedSubroutineCallsites []*UDSCallsite + outputHandlerManagers []output.OutputHandlerManager recordWriterOptions *cli.TWriterOptions dslInstanceType DSLInstanceType // put, filter, repl strictMode bool diff --git a/pkg/runtime/stack.go b/pkg/runtime/stack.go index 6424ebf19..a71f83379 100644 --- a/pkg/runtime/stack.go +++ b/pkg/runtime/stack.go @@ -26,7 +26,6 @@ package runtime import ( - "container/list" "fmt" "github.com/johnkerl/miller/v6/pkg/lib" @@ -68,7 +67,7 @@ func (sv *StackVariable) GetName() string { type Stack struct { // list of *StackFrameSet - stackFrameSets *list.List + stackFrameSets []*StackFrameSet // Invariant: equal to the head of the stackFrameSets list. This is cached // since all sets/gets in between frameset-push and frameset-pop will all @@ -77,9 +76,9 @@ type Stack struct { } func NewStack() *Stack { - stackFrameSets := list.New() + stackFrameSets := make([]*StackFrameSet, 1) head := newStackFrameSet() - stackFrameSets.PushFront(head) + stackFrameSets[0] = head return &Stack{ stackFrameSets: stackFrameSets, head: head, @@ -89,13 +88,13 @@ func NewStack() *Stack { // For when a user-defined function/subroutine is being entered func (stack *Stack) PushStackFrameSet() { stack.head = newStackFrameSet() - stack.stackFrameSets.PushFront(stack.head) + stack.stackFrameSets = append([]*StackFrameSet{stack.head}, stack.stackFrameSets...) } // For when a user-defined function/subroutine is being exited func (stack *Stack) PopStackFrameSet() { - stack.stackFrameSets.Remove(stack.stackFrameSets.Front()) - stack.head = stack.stackFrameSets.Front().Value.(*StackFrameSet) + stack.stackFrameSets = stack.stackFrameSets[1:] + stack.head = stack.stackFrameSets[0] } // ---------------------------------------------------------------- @@ -180,9 +179,8 @@ func (stack *Stack) UnsetIndexed( } func (stack *Stack) Dump() { - fmt.Printf("STACK FRAMESETS (count %d):\n", stack.stackFrameSets.Len()) - for entry := stack.stackFrameSets.Front(); entry != nil; entry = entry.Next() { - stackFrameSet := entry.Value.(*StackFrameSet) + fmt.Printf("STACK FRAMESETS (count %d):\n", len(stack.stackFrameSets)) + for _, stackFrameSet := range stack.stackFrameSets { stackFrameSet.dump() } } diff --git a/pkg/runtime/state.go b/pkg/runtime/state.go index 6b8b5d29c..3fe93aa18 100644 --- a/pkg/runtime/state.go +++ b/pkg/runtime/state.go @@ -35,7 +35,7 @@ type State struct { // This is necessary for the stateful semantics of `=~` and "\1", "\2", etc. // Those are avoided when the user calls `matchx`, which is newer, and // stateless. However, `=~` exists in the Miller DSL and we must support it. - regexCapturesByFrame *list.List // list of []string + regexCapturesByFrame [][]string Options *cli.TOptions @@ -46,8 +46,8 @@ type State struct { func NewEmptyState(options *cli.TOptions, strictMode bool) *State { // See lib.MakeEmptyCaptures for context. - regexCapturesByFrame := list.New() - regexCapturesByFrame.PushFront(lib.MakeEmptyCaptures()) + regexCapturesByFrame := make([][]string, 1) + regexCapturesByFrame[0] = lib.MakeEmptyCaptures() oosvars := mlrval.NewMlrmap() return &State{ @@ -72,25 +72,24 @@ func (state *State) Update( ) { state.Inrec = inrec state.Context = context - state.regexCapturesByFrame.Front().Value = lib.MakeEmptyCaptures() + state.regexCapturesByFrame[0] = lib.MakeEmptyCaptures() } func (state *State) SetRegexCaptures( captures []string, ) { - state.regexCapturesByFrame.Front().Value = lib.CopyStringArray(captures) + state.regexCapturesByFrame[0] = lib.CopyStringArray(captures) } func (state *State) GetRegexCaptures() []string { - regexCaptures := state.regexCapturesByFrame.Front().Value.([]string) + regexCaptures := state.regexCapturesByFrame[0] return lib.CopyStringArray(regexCaptures) } func (state *State) PushRegexCapturesFrame() { - state.regexCapturesByFrame.PushFront(lib.MakeEmptyCaptures()) + state.regexCapturesByFrame = append([][]string{lib.MakeEmptyCaptures()}, state.regexCapturesByFrame...) } func (state *State) PopRegexCapturesFrame() { - // There is no PopFront - state.regexCapturesByFrame.Remove(state.regexCapturesByFrame.Front()) + state.regexCapturesByFrame = state.regexCapturesByFrame[1:] } diff --git a/pkg/terminals/regtest/regtester.go b/pkg/terminals/regtest/regtester.go index 029ace13b..749002b89 100644 --- a/pkg/terminals/regtest/regtester.go +++ b/pkg/terminals/regtest/regtester.go @@ -56,7 +56,6 @@ package regtest import ( - "container/list" "fmt" "os" "path/filepath" @@ -110,8 +109,8 @@ type RegTester struct { casePassCount int caseFailCount int - failDirNames *list.List - failCaseNames *list.List + failDirNames []string + failCaseNames []string firstNFailsToShow int } @@ -132,8 +131,8 @@ func NewRegTester( directoryFailCount: 0, casePassCount: 0, caseFailCount: 0, - failDirNames: list.New(), - failCaseNames: list.New(), + failDirNames: make([]string, 0), + failCaseNames: make([]string, 0), firstNFailsToShow: firstNFailsToShow, } } @@ -182,13 +181,13 @@ func (regtester *RegTester) Execute( regtester.executeSinglePath(path) } - if regtester.failCaseNames.Len() > 0 && regtester.firstNFailsToShow > 0 { + if len(regtester.failCaseNames) > 0 && regtester.firstNFailsToShow > 0 { fmt.Println() fmt.Println("RERUNS OF FIRST FAILED CASE FILES:") verbosityLevel := 3 i := 0 - for e := regtester.failCaseNames.Front(); e != nil; e = e.Next() { - regtester.executeSingleCmdFile(e.Value.(string), verbosityLevel) + for _, e := range regtester.failCaseNames { + regtester.executeSingleCmdFile(e, verbosityLevel) i++ if i >= regtester.firstNFailsToShow { break @@ -196,11 +195,11 @@ func (regtester *RegTester) Execute( } } - if !regtester.plainMode && regtester.failDirNames.Len() > 0 { + if !regtester.plainMode && len(regtester.failDirNames) > 0 { fmt.Println() fmt.Println("FAILED CASE DIRECTORIES:") - for e := regtester.failDirNames.Front(); e != nil; e = e.Next() { - fmt.Printf(" %s/\n", e.Value.(string)) + for _, e := range regtester.failDirNames { + fmt.Printf(" %s/\n", e) } } @@ -248,7 +247,7 @@ func (regtester *RegTester) executeSinglePath( regtester.directoryPassCount++ } else { regtester.directoryFailCount++ - regtester.failDirNames.PushBack(path) + regtester.failDirNames = append(regtester.failDirNames, path) } } return passed @@ -260,7 +259,7 @@ func (regtester *RegTester) executeSinglePath( regtester.casePassCount++ } else { regtester.caseFailCount++ - regtester.failCaseNames.PushBack(path) + regtester.failCaseNames = append(regtester.failCaseNames, path) } return passed } @@ -478,8 +477,7 @@ func (regtester *RegTester) executeSingleCmdFile( // Copy any files requested by the test. (Most don't; some do, e.g. those // which test the write-in-place logic of mlr -I.) - for pe := preCopySrcDestPairs.Front(); pe != nil; pe = pe.Next() { - pair := pe.Value.(stringPair) + for _, pair := range preCopySrcDestPairs { src := pair.first dst := pair.second if verbosityLevel >= 3 { @@ -564,8 +562,7 @@ func (regtester *RegTester) executeSingleCmdFile( } } - for pe := postCompareExpectedActualPairs.Front(); pe != nil; pe = pe.Next() { - pair := pe.Value.(stringPair) + for _, pair := range postCompareExpectedActualPairs { expectedFileName := pair.first actualFileName := pair.second @@ -686,8 +683,7 @@ func (regtester *RegTester) executeSingleCmdFile( // Compare any additional output files. Most test cases don't have // these (just stdout/stderr), but some do: for example, those which // test the tee verb/function. - for pe := postCompareExpectedActualPairs.Front(); pe != nil; pe = pe.Next() { - pair := pe.Value.(stringPair) + for _, pair := range postCompareExpectedActualPairs { expectedFileName := pair.first actualFileName := pair.second ok, expectedContents, actualContents, err := regtester.compareFiles(expectedFileName, actualFileName, caseDir) @@ -725,8 +721,7 @@ func (regtester *RegTester) executeSingleCmdFile( } // Clean up any requested file-copies so that we're git-clean after the regression-test run. - for pe := preCopySrcDestPairs.Front(); pe != nil; pe = pe.Next() { - pair := pe.Value.(stringPair) + for _, pair := range preCopySrcDestPairs { dst := pair.second os.Remove(dst) if verbosityLevel >= 3 { @@ -735,8 +730,7 @@ func (regtester *RegTester) executeSingleCmdFile( } // Clean up any extra output files so that we're git-clean after the regression-test run. - for pe := postCompareExpectedActualPairs.Front(); pe != nil; pe = pe.Next() { - pair := pe.Value.(stringPair) + for _, pair := range postCompareExpectedActualPairs { actualFileName := pair.second os.Remove(actualFileName) if verbosityLevel >= 3 { @@ -868,12 +862,13 @@ func (regtester *RegTester) loadEnvFile( func (regtester *RegTester) loadStringPairFile( filename string, caseDir string, -) (*list.List, error) { +) ([]stringPair, error) { + pairs := make([]stringPair, 0) // If the file doesn't exist that's the normal case -- most cases do not // have a .precopy or .postcmp file. _, err := os.Stat(filename) if os.IsNotExist(err) { - return list.New(), nil + return pairs, nil } // If the file does exist and isn't loadable, that's an error. @@ -882,7 +877,7 @@ func (regtester *RegTester) loadStringPairFile( return nil, err } - pairs := list.New() + pairs = make([]stringPair, 0) lines := strings.Split(contents, "\n") for _, line := range lines { line = strings.TrimSuffix(line, "\r") @@ -897,7 +892,7 @@ func (regtester *RegTester) loadStringPairFile( ) } pair := stringPair{first: fields[0], second: fields[1]} - pairs.PushBack(pair) + pairs = append(pairs, pair) } return pairs, nil }