|
1 | 1 | package extgen |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "bufio" |
5 | 4 | "fmt" |
6 | 5 | "go/ast" |
7 | 6 | "go/parser" |
@@ -204,91 +203,88 @@ func (cp *classParser) goTypeToPHPType(goType string) phpType { |
204 | 203 | return phpMixed |
205 | 204 | } |
206 | 205 |
|
207 | | -func (cp *classParser) parseMethods(filename string) (methods []phpClassMethod, err error) { |
208 | | - file, err := os.Open(filename) |
| 206 | +func (cp *classParser) parseMethods(filename string) ([]phpClassMethod, error) { |
| 207 | + src, err := os.ReadFile(filename) |
209 | 208 | if err != nil { |
210 | 209 | return nil, err |
211 | 210 | } |
212 | 211 |
|
213 | | - defer func() { |
214 | | - e := file.Close() |
215 | | - if err != nil { |
216 | | - err = e |
217 | | - } |
218 | | - }() |
219 | | - |
220 | | - scanner := bufio.NewScanner(file) |
221 | | - var currentMethod *phpClassMethod |
| 212 | + fset := token.NewFileSet() |
| 213 | + file, err := parser.ParseFile(fset, filename, src, parser.ParseComments) |
| 214 | + if err != nil { |
| 215 | + return nil, fmt.Errorf("parsing file: %w", err) |
| 216 | + } |
222 | 217 |
|
223 | | - lineNumber := 0 |
224 | | - for scanner.Scan() { |
225 | | - lineNumber++ |
226 | | - line := strings.TrimSpace(scanner.Text()) |
| 218 | + validator := Validator{} |
| 219 | + var methods []phpClassMethod |
| 220 | + consumed := make(map[int]bool) |
227 | 221 |
|
228 | | - if matches := phpMethodRegex.FindStringSubmatch(line); matches != nil { |
229 | | - className := strings.TrimSpace(matches[1]) |
230 | | - signature := strings.TrimSpace(matches[2]) |
| 222 | + for _, decl := range file.Decls { |
| 223 | + funcDecl, ok := decl.(*ast.FuncDecl) |
| 224 | + if !ok { |
| 225 | + continue |
| 226 | + } |
231 | 227 |
|
232 | | - method, err := cp.parseMethodSignature(className, signature) |
233 | | - if err != nil { |
234 | | - fmt.Printf("Warning: Error parsing method signature %q: %v\n", signature, err) |
| 228 | + directive, directiveLine := findDirective(funcDecl.Doc, fset, phpMethodRegex) |
| 229 | + if directive == "" { |
| 230 | + continue |
| 231 | + } |
| 232 | + rawMatch := phpMethodRegex.FindStringSubmatch(findMatchingComment(funcDecl.Doc, phpMethodRegex)) |
| 233 | + if len(rawMatch) != 3 { |
| 234 | + continue |
| 235 | + } |
| 236 | + className := strings.TrimSpace(rawMatch[1]) |
| 237 | + signature := strings.TrimSpace(rawMatch[2]) |
| 238 | + consumed[directiveLine] = true |
235 | 239 |
|
236 | | - continue |
237 | | - } |
| 240 | + method, err := cp.parseMethodSignature(className, signature) |
| 241 | + if err != nil { |
| 242 | + fmt.Printf("Warning: Error parsing method signature %q: %v\n", signature, err) |
| 243 | + continue |
| 244 | + } |
238 | 245 |
|
239 | | - validator := Validator{} |
240 | | - phpFunc := phpFunction{ |
241 | | - Name: method.Name, |
242 | | - Signature: method.Signature, |
243 | | - Params: method.Params, |
244 | | - ReturnType: method.ReturnType, |
245 | | - IsReturnNullable: method.isReturnNullable, |
246 | | - } |
| 246 | + phpFunc := phpFunction{ |
| 247 | + Name: method.Name, |
| 248 | + Signature: method.Signature, |
| 249 | + Params: method.Params, |
| 250 | + ReturnType: method.ReturnType, |
| 251 | + IsReturnNullable: method.isReturnNullable, |
| 252 | + } |
| 253 | + if err := validator.validateTypes(phpFunc); err != nil { |
| 254 | + fmt.Printf("Warning: Method \"%s::%s\" uses unsupported types: %v\n", className, method.Name, err) |
| 255 | + continue |
| 256 | + } |
247 | 257 |
|
248 | | - if err := validator.validateTypes(phpFunc); err != nil { |
249 | | - fmt.Printf("Warning: Method \"%s::%s\" uses unsupported types: %v\n", className, method.Name, err) |
| 258 | + method.lineNumber = directiveLine |
| 259 | + method.GoFunction = extractNodeSource(src, fset, funcDecl) |
250 | 260 |
|
251 | | - continue |
252 | | - } |
253 | | - |
254 | | - method.lineNumber = lineNumber |
255 | | - currentMethod = method |
| 261 | + phpFunc.GoFunction = method.GoFunction |
| 262 | + if err := validator.validateGoFunctionSignatureWithOptions(phpFunc, true); err != nil { |
| 263 | + fmt.Printf("Warning: Go method signature mismatch for '%s::%s': %v\n", method.ClassName, method.Name, err) |
| 264 | + continue |
256 | 265 | } |
257 | 266 |
|
258 | | - if currentMethod != nil && strings.HasPrefix(line, "func ") { |
259 | | - goFunc, err := cp.extractGoMethodFunction(scanner, line) |
260 | | - if err != nil { |
261 | | - return nil, fmt.Errorf("extracting Go method function: %w", err) |
262 | | - } |
263 | | - |
264 | | - currentMethod.GoFunction = goFunc |
| 267 | + methods = append(methods, *method) |
| 268 | + } |
265 | 269 |
|
266 | | - validator := Validator{} |
267 | | - phpFunc := phpFunction{ |
268 | | - Name: currentMethod.Name, |
269 | | - Signature: currentMethod.Signature, |
270 | | - GoFunction: currentMethod.GoFunction, |
271 | | - Params: currentMethod.Params, |
272 | | - ReturnType: currentMethod.ReturnType, |
273 | | - IsReturnNullable: currentMethod.isReturnNullable, |
274 | | - } |
| 270 | + if err := checkOrphanDirectives(file, fset, phpMethodRegex, consumed, "//export_php:method"); err != nil { |
| 271 | + return nil, err |
| 272 | + } |
275 | 273 |
|
276 | | - if err := validator.validateGoFunctionSignatureWithOptions(phpFunc, true); err != nil { |
277 | | - fmt.Printf("Warning: Go method signature mismatch for '%s::%s': %v\n", currentMethod.ClassName, currentMethod.Name, err) |
278 | | - currentMethod = nil |
279 | | - continue |
280 | | - } |
| 274 | + return methods, nil |
| 275 | +} |
281 | 276 |
|
282 | | - methods = append(methods, *currentMethod) |
283 | | - currentMethod = nil |
284 | | - } |
| 277 | +// findMatchingComment returns the raw comment text whose line matches re. |
| 278 | +func findMatchingComment(group *ast.CommentGroup, re *regexp.Regexp) string { |
| 279 | + if group == nil { |
| 280 | + return "" |
285 | 281 | } |
286 | | - |
287 | | - if currentMethod != nil { |
288 | | - return nil, fmt.Errorf("//export_php:method directive at line %d is not followed by a function declaration", currentMethod.lineNumber) |
| 282 | + for _, comment := range group.List { |
| 283 | + if re.MatchString(comment.Text) { |
| 284 | + return comment.Text |
| 285 | + } |
289 | 286 | } |
290 | | - |
291 | | - return methods, scanner.Err() |
| 287 | + return "" |
292 | 288 | } |
293 | 289 |
|
294 | 290 | func (cp *classParser) parseMethodSignature(className, signature string) (*phpClassMethod, error) { |
@@ -365,27 +361,3 @@ func (cp *classParser) sanitizeDefaultValue(value string) string { |
365 | 361 | return strings.Trim(value, `'"`) |
366 | 362 | } |
367 | 363 |
|
368 | | -func (cp *classParser) extractGoMethodFunction(scanner *bufio.Scanner, firstLine string) (string, error) { |
369 | | - goFunc := firstLine + "\n" |
370 | | - braceCount := 1 |
371 | | - |
372 | | - for scanner.Scan() { |
373 | | - line := scanner.Text() |
374 | | - goFunc += line + "\n" |
375 | | - |
376 | | - for _, char := range line { |
377 | | - switch char { |
378 | | - case '{': |
379 | | - braceCount++ |
380 | | - case '}': |
381 | | - braceCount-- |
382 | | - } |
383 | | - } |
384 | | - |
385 | | - if braceCount == 0 { |
386 | | - break |
387 | | - } |
388 | | - } |
389 | | - |
390 | | - return goFunc, nil |
391 | | -} |
0 commit comments