Preserve regex captures across stack frames (#1447)

* privatize state.RegexCaptures

* stack frame for regex captures

* merge

* unit-test case

* docs re stack frames for regex captures

* more
This commit is contained in:
John Kerl 2023-12-18 10:21:09 -05:00 committed by GitHub
parent 1ae670fd4a
commit 4053d7684c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 183 additions and 77 deletions

View file

@ -25,27 +25,42 @@ type State struct {
// For holding "\0".."\9" between where they are set via things like
// '$x =~ "(..)_(...)"', and interpolated via things like '$y = "\2:\1"'.
RegexCaptures []string
Options *cli.TOptions
//
// Each top-level block and user-defined function has its own captures.
//
// For example, in function `f()`, one can do `somevar =~ someregex`, then
// call some function `g()` which also uses `=~`, and then when `g()` returns,
// `f()` will have its "\1", "\2", etc intact.
//
// 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
Options *cli.TOptions
// StrictMode allows for runtime handling of absent-reads and untyped assignments.
StrictMode bool
}
func NewEmptyState(options *cli.TOptions, strictMode bool) *State {
// See lib.MakeEmptyCaptures for context.
regexCapturesByFrame := list.New()
regexCapturesByFrame.PushFront(lib.MakeEmptyCaptures())
oosvars := mlrval.NewMlrmap()
return &State{
Inrec: nil,
Context: nil,
Oosvars: oosvars,
FilterExpression: mlrval.TRUE,
Stack: NewStack(),
Inrec: nil,
Context: nil,
Oosvars: oosvars,
FilterExpression: mlrval.TRUE,
Stack: NewStack(),
regexCapturesByFrame: regexCapturesByFrame,
// OutputRecordsAndContexts is assigned after construction
// See lib.MakeEmptyCaptures for context.
RegexCaptures: lib.MakeEmptyCaptures(),
Options: options,
Options: options,
StrictMode: strictMode,
}
@ -57,5 +72,25 @@ func (state *State) Update(
) {
state.Inrec = inrec
state.Context = context
state.RegexCaptures = lib.MakeEmptyCaptures()
state.regexCapturesByFrame.Front().Value = lib.MakeEmptyCaptures()
}
func (state *State) SetRegexCaptures(
captures []string,
) {
state.regexCapturesByFrame.Front().Value = lib.CopyStringArray(captures)
}
func (state *State) GetRegexCaptures() []string {
regexCaptures := state.regexCapturesByFrame.Front().Value.([]string)
return lib.CopyStringArray(regexCaptures)
}
func (state *State) PushRegexCapturesFrame() {
state.regexCapturesByFrame.PushFront(lib.MakeEmptyCaptures())
}
func (state *State) PopRegexCapturesFrame() {
// There is no PopFront
state.regexCapturesByFrame.Remove(state.regexCapturesByFrame.Front())
}