From b6e659649092b7762ae1428065d280836a032220 Mon Sep 17 00:00:00 2001 From: wow20220809-lab Date: Tue, 4 Aug 2026 14:26:24 +0800 Subject: [PATCH] fix: parse adjacent set_by_lua_block opening brace Treat an unquoted opening brace as the end of the output-variable argument. Preserve ${variable} expansion handling and add regression coverage for spaced and adjacent block openings. Refs #177. --- lex_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lua.go | 33 ++++++++++++++++++----------- 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/lex_test.go b/lex_test.go index cb3c5148..3cf8d1cd 100644 --- a/lex_test.go +++ b/lex_test.go @@ -9,6 +9,7 @@ package crossplane import ( "os" + "reflect" "strings" "testing" ) @@ -446,6 +447,65 @@ func TestLex(t *testing.T) { } } +func TestLex_setByLuaBlockOpeningBraceDelimiter(t *testing.T) { + t.Parallel() + + testcases := []struct { + name string + config string + expectedArg string + }{ + { + name: "whitespace before opening brace", + config: `set_by_lua_block $pass { return { ok = true } }`, + expectedArg: "$pass", + }, + { + name: "opening brace adjacent to output variable", + config: `set_by_lua_block $pass{ return { ok = true } }`, + expectedArg: "$pass", + }, + { + name: "whitespace after braced variable", + config: `set_by_lua_block ${pass} { return { ok = true } }`, + expectedArg: "${pass}", + }, + { + name: "opening brace adjacent to braced variable", + config: `set_by_lua_block ${pass}{ return { ok = true } }`, + expectedArg: "${pass}", + }, + } + + for _, testcase := range testcases { + testcase := testcase + t.Run(testcase.name, func(t *testing.T) { + t.Parallel() + + var actual []string + options := LexOptions{ + Lexers: []RegisterLexer{lua.RegisterLexer()}, + } + for token := range LexWithOptions(strings.NewReader(testcase.config), options) { + if token.Error != nil { + t.Fatal(token.Error) + } + actual = append(actual, token.Value) + } + + expected := []string{ + setByLuaBlock, + testcase.expectedArg, + " return { ok = true } ", + ";", + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected tokens %#v, got %#v", expected, actual) + } + }) + } +} + func TestLex_unhappy(t *testing.T) { t.Parallel() diff --git a/lua.go b/lua.go index dd4a45ba..9fd58eba 100644 --- a/lua.go +++ b/lua.go @@ -72,6 +72,13 @@ func (l *Lua) Lex(s *SubScanner, matchedToken string) <-chan NgxToken { return } next := s.Text() + // NGINX treats an opening brace as the end of the current argument, + // except when it starts a parameter expansion such as ${variable}. + if next == "{" && arg != "" && !strings.HasSuffix(arg, "$") { + tokenCh <- NgxToken{Value: arg, Line: s.Line(), IsQuoted: false} + tokenDepth++ + break + } if isSpace(next) { if arg != "" { tokenCh <- NgxToken{Value: arg, Line: s.Line(), IsQuoted: false} @@ -90,20 +97,22 @@ func (l *Lua) Lex(s *SubScanner, matchedToken string) <-chan NgxToken { } // check that Lua block starts correctly - for { - if !s.Scan() { - return - } - next := s.Text() - - if !isSpace(next) { - if next != "{" { - lineno := s.Line() - tokenCh <- NgxToken{Error: &ParseError{File: &lexerFile, What: `expected "{" to start lua block`, Line: &lineno}} + if tokenDepth == 0 { + for { + if !s.Scan() { return } - tokenDepth++ - break + next := s.Text() + + if !isSpace(next) { + if next != "{" { + lineno := s.Line() + tokenCh <- NgxToken{Error: &ParseError{File: &lexerFile, What: `expected "{" to start lua block`, Line: &lineno}} + return + } + tokenDepth++ + break + } } }