@@ -2,6 +2,7 @@ package internal
22
33import (
44 "bufio"
5+ "errors"
56 "fmt"
67 "os"
78 "regexp"
@@ -17,6 +18,11 @@ type CodeProcessor struct {
1718var (
1819 assignmentPattern = regexp .MustCompile (`^\s*(var\s+\w+(\s+[\w.\[\]]+)?\s*=|[\w.,\s]+\s*:=|[\w.]+\s*=)\s*` )
1920 assignmentSplitPattern = regexp .MustCompile (`^(\s*(?:var\s+\w+(?:\s+[\w.\[\]]+)?\s*=|[\w.,\s]+\s*:=|[\w.]+\s*=)\s*)(.*)$` )
21+ stringLiteralPattern = regexp .MustCompile (`""` )
22+ numericZeroPattern = regexp .MustCompile (`\b0\b` )
23+ floatZeroPattern = regexp .MustCompile (`\b0\.0\b` )
24+ boolFalsePattern = regexp .MustCompile (`\bfalse\b` )
25+ errNoReplacement = errors .New ("no replacement performed" )
2026)
2127
2228func NewCodeProcessor (ctx * ProcessorContext , executor * FunctionExecutor ) * CodeProcessor {
@@ -95,59 +101,80 @@ func (cp *CodeProcessor) processCodeLine(line, funcName, argsStr, filePath strin
95101 return line , false
96102 }
97103
98- typeHint := "other"
99- if userFunc , ok := cp .ctx .Functions [funcName ]; ok {
100- typeHint = mapOutputType (userFunc .OutputType )
104+ typeHint := cp .typeHintFor (funcName , result )
105+ formattedResult := formatResultForReplacement (result , typeHint )
106+
107+ leadingWhitespace , _ := splitLeadingWhitespace (line )
108+ newLine , replaced , buildErr := cp .buildReplacementLine (line , leadingWhitespace , funcName , argsStr , formattedResult , typeHint )
109+ if buildErr != nil {
110+ if errors .Is (buildErr , errNoReplacement ) {
111+ _ , _ = fmt .Fprintf (os .Stderr , "Warning: Could not replace function call for '%s' in line: %s\n " , funcName , strings .TrimSpace (line ))
112+ }
113+ return line , false
101114 }
102115
103- inferred := inferResultKind (result )
104- if typeHint == "other" {
105- typeHint = inferred
116+ if replaced {
117+ _ , _ = fmt .Fprintf (os .Stderr , "[goahead] Replaced in %s: %s(%s) -> %s\n " , filePath , funcName , argsStr , result )
118+ if verbose {
119+ _ , _ = fmt .Fprintf (os .Stderr , " Original: '%s'\n New: '%s'\n " , strings .TrimSpace (line ), strings .TrimSpace (newLine ))
120+ }
106121 }
107122
108- formattedResult := formatResultForReplacement (result , typeHint )
123+ return newLine , replaced
124+ }
109125
110- trimmedLine := strings . TrimSpace (line )
111- leadingWhitespace := ""
112- if len (line ) > len (trimmedLine ) {
113- leadingWhitespace = line [: len ( line ) - len ( trimmedLine )]
126+ func splitLeadingWhitespace (line string ) ( string , string ) {
127+ trimmed := strings . TrimSpace ( line )
128+ if len (line ) == len (trimmed ) {
129+ return "" , line
114130 }
131+ return line [:len (line )- len (trimmed )], trimmed
132+ }
115133
116- isAssignment := assignmentPattern .MatchString (line )
134+ func (cp * CodeProcessor ) buildReplacementLine (originalLine , leadingWhitespace , funcName , argsStr , formattedResult , typeHint string ) (string , bool , error ) {
135+ if assignmentPattern .MatchString (originalLine ) {
136+ return cp .replaceInAssignment (originalLine , funcName , argsStr , formattedResult , typeHint )
137+ }
117138
118- var newLine string
119- if isAssignment {
120- matches := assignmentSplitPattern . FindStringSubmatch ( line )
139+ if replacedLine , ok := cp . replaceFunctionCall ( originalLine , funcName , argsStr , formattedResult ); ok {
140+ return replacedLine , true , nil
141+ }
121142
122- if len (matches ) >= 3 {
123- varAssignPart := matches [1 ]
124- expressionPart := matches [2 ]
125- replacedExpression := cp .replaceFirstPlaceholder (expressionPart , formattedResult , typeHint )
126- if replacedExpression != expressionPart {
127- newLine = varAssignPart + replacedExpression
128- } else {
129- newLine = varAssignPart + formattedResult
130- }
131- } else {
132- replacement := cp .replaceFunctionCall (line , funcName , argsStr , formattedResult )
133- if replacement == line {
134- _ , _ = fmt .Fprintf (os .Stderr , "Warning: Could not replace function call for '%s' in line: %s\n " , funcName , strings .TrimSpace (line ))
135- return line , false
136- }
137- newLine = replacement
143+ newLine := leadingWhitespace + formattedResult
144+ return newLine , newLine != originalLine , nil
145+ }
146+
147+ func (cp * CodeProcessor ) replaceInAssignment (originalLine , funcName , argsStr , formattedResult , typeHint string ) (string , bool , error ) {
148+ matches := assignmentSplitPattern .FindStringSubmatch (originalLine )
149+ if len (matches ) < 3 {
150+ replacedLine , ok := cp .replaceFunctionCall (originalLine , funcName , argsStr , formattedResult )
151+ if ! ok {
152+ return "" , false , errNoReplacement
138153 }
139- } else {
140- newLine = leadingWhitespace + formattedResult
154+ return replacedLine , true , nil
141155 }
142156
143- replaced := newLine != line
157+ varAssignPart := matches [1 ]
158+ expressionPart := matches [2 ]
159+
160+ replacedExpression , replaced := cp .replaceFirstPlaceholder (expressionPart , formattedResult , typeHint )
144161 if replaced {
145- _ , _ = fmt .Fprintf (os .Stderr , "[goahead] Replaced in %s: %s(%s) -> %s\n " , filePath , funcName , argsStr , result )
146- if verbose {
147- _ , _ = fmt .Fprintf (os .Stderr , " Original: '%s'\n New: '%s'\n " , strings .TrimSpace (line ), strings .TrimSpace (newLine ))
162+ newLine := varAssignPart + replacedExpression
163+ return newLine , newLine != originalLine , nil
164+ }
165+
166+ newLine := varAssignPart + formattedResult
167+ return newLine , newLine != originalLine , nil
168+ }
169+
170+ func (cp * CodeProcessor ) typeHintFor (funcName , result string ) string {
171+ if userFunc , ok := cp .ctx .Functions [funcName ]; ok {
172+ hint := mapOutputType (userFunc .OutputType )
173+ if hint != "other" {
174+ return hint
148175 }
149176 }
150- return newLine , replaced
177+ return inferResultKind ( result )
151178}
152179
153180func (cp * CodeProcessor ) writeFile (filePath string , lines []string ) error {
@@ -183,98 +210,49 @@ func escapeString(s string) string {
183210 return `"` + strings .ReplaceAll (s , `"` , `\"` ) + `"`
184211}
185212
186- func (cp * CodeProcessor ) replaceFirstPlaceholder (expression , replacement , typeHint string ) string {
213+ func (cp * CodeProcessor ) replaceFirstPlaceholder (expression , replacement , typeHint string ) ( string , bool ) {
187214 switch typeHint {
188215 case "string" :
189- re := regexp .MustCompile (`""` )
190- if re .MatchString (expression ) {
191- replaced := false
192- return re .ReplaceAllStringFunc (expression , func (match string ) string {
193- if ! replaced {
194- replaced = true
195- return replacement
196- }
197- return match
198- })
199- }
200-
201- case "int" :
202- re := regexp .MustCompile (`\b0\b` )
203- if re .MatchString (expression ) {
204- replaced := false
205- return re .ReplaceAllStringFunc (expression , func (match string ) string {
206- if ! replaced {
207- replaced = true
208- return replacement
209- }
210- return match
211- })
212- }
213-
214- case "uint" :
215- re := regexp .MustCompile (`\b0\b` )
216- if re .MatchString (expression ) {
217- replaced := false
218- return re .ReplaceAllStringFunc (expression , func (match string ) string {
219- if ! replaced {
220- replaced = true
221- return replacement
222- }
223- return match
224- })
225- }
226-
216+ return replaceFirstMatch (stringLiteralPattern , expression , replacement )
217+ case "int" , "uint" :
218+ return replaceFirstMatch (numericZeroPattern , expression , replacement )
227219 case "float" :
228- re := regexp .MustCompile (`\b0\.0\b` )
229- if re .MatchString (expression ) {
230- replaced := false
231- return re .ReplaceAllStringFunc (expression , func (match string ) string {
232- if ! replaced {
233- replaced = true
234- return replacement
235- }
236- return match
237- })
238- }
239- reZero := regexp .MustCompile (`\b0\b` )
240- if reZero .MatchString (expression ) {
241- replaced := false
242- return reZero .ReplaceAllStringFunc (expression , func (match string ) string {
243- if ! replaced {
244- replaced = true
245- return replacement
246- }
247- return match
248- })
220+ if updated , ok := replaceFirstMatch (floatZeroPattern , expression , replacement ); ok {
221+ return updated , true
249222 }
250-
223+ return replaceFirstMatch ( numericZeroPattern , expression , replacement )
251224 case "bool" :
252- re := regexp .MustCompile (`\bfalse\b` )
253- if re .MatchString (expression ) {
254- replaced := false
255- return re .ReplaceAllStringFunc (expression , func (match string ) string {
256- if ! replaced {
257- replaced = true
258- return replacement
259- }
260- return match
261- })
262- }
225+ return replaceFirstMatch (boolFalsePattern , expression , replacement )
226+ default :
227+ return expression , false
263228 }
264- return expression
265229}
266230
267- func (cp * CodeProcessor ) replaceFunctionCall (line , funcName , argsStr , replacement string ) string {
268- updated := line
231+ func replaceFirstMatch (re * regexp.Regexp , expression , replacement string ) (string , bool ) {
232+ replaced := false
233+ updated := re .ReplaceAllStringFunc (expression , func (match string ) string {
234+ if replaced {
235+ return match
236+ }
237+ replaced = true
238+ return replacement
239+ })
240+ return updated , replaced
241+ }
242+
243+ func (cp * CodeProcessor ) replaceFunctionCall (line , funcName , argsStr , replacement string ) (string , bool ) {
269244 if argsStr != "" {
270245 re := regexp .MustCompile (fmt .Sprintf (`%s\(\s*%s\s*\)` , regexp .QuoteMeta (funcName ), regexp .QuoteMeta (argsStr )))
271- updated = re .ReplaceAllString (updated , replacement )
246+ if re .MatchString (line ) {
247+ return re .ReplaceAllString (line , replacement ), true
248+ }
272249 }
273- if updated == line {
274- re := regexp .MustCompile (fmt .Sprintf (`%s\([^)]*\)` , regexp .QuoteMeta (funcName )))
275- updated = re .ReplaceAllString (updated , replacement )
250+
251+ re := regexp .MustCompile (fmt .Sprintf (`%s\([^)]*\)` , regexp .QuoteMeta (funcName )))
252+ if re .MatchString (line ) {
253+ return re .ReplaceAllString (line , replacement ), true
276254 }
277- return updated
255+ return line , false
278256}
279257
280258func mapOutputType (outputType string ) string {
0 commit comments