Really don't analyze computed jumps; fix IF...GOTO parsing.
catseye
12 years ago
53 | 53 | existing line number. This includes any jumps that may occur in immediate |
54 | 54 | mode commands (i.e. commands with no line number) given in the text |
55 | 55 | file. If this check fails, further transformations may not be performed on |
56 | the program. To suppress this check, pass the -L option to `yucca`. | |
56 | the program. To suppress this check, pass the `-L` option to `yucca`. | |
57 | 57 | |
58 | The -o option may be given to dump a copy of the program to the standard | |
58 | `yucca` cannot analyze the validity of any computed line number in a BASIC | |
59 | program which contains computed `GOTO`s or `GOSUB`s. To reduce the chance | |
60 | of an computed line number going unnoticed and unanalyzed, `yucca`'s | |
61 | default behavior is to report an error if it finds any computed `GOTO`s | |
62 | or `GOSUB`s in the input program. To acknowledge that you are aware that | |
63 | the program contains computed jumps that `yucca` will not be able to | |
64 | analyze, pass the `-C` option to have `yucca` suppress these errors. | |
65 | ||
66 | The `-o` option may be given to dump a copy of the program to the standard | |
59 | 67 | output. This option is implied by the following two options. |
60 | 68 | |
61 | The -I option strips all immediate mode commands from the program before | |
69 | The `-I` option strips all immediate mode commands from the program before | |
62 | 70 | analyzing and outputting it. |
63 | 71 | |
64 | The -R option strips all remarks (REM statements) from the program | |
72 | The `-R` option strips all remarks (`REM` statements) from the program | |
65 | 73 | before analyzing and outputting it. Note that this happens before |
66 | analysis, so that any jumps to lines which contain only a REM will be | |
74 | analysis, so that any jumps to lines which contain only a `REM` will be | |
67 | 75 | found and reported. |
68 | 76 | |
69 | The -p option causes all program transformations to act only on program | |
70 | lines, not on immediate mode lines. Thus, in combination with -R, REMs | |
71 | on immediate mode lines are not removed. It does not affect -I at all. | |
77 | The `-p` option causes all program transformations to act only on program | |
78 | lines, not on immediate mode lines. Thus, in combination with `-R`, `REM`s | |
79 | on immediate mode lines are not removed. It does not affect `-I` at all. | |
72 | 80 | |
73 | The -t option runs `yucca` through its internal test suite and exits | |
81 | The `-t` option runs `yucca` through its internal test suite and exits | |
74 | 82 | immediately. |
75 | 83 | |
76 | 84 | TODO |
77 | 85 | ---- |
78 | 86 | |
79 | * Show a warning message if the program contains computed jumps. | |
80 | 87 | * Show errors in the order they occur in the program. |
81 | 88 | * Handle duplicate and deleted lines (line number then nothing.) |
82 | 89 |
82 | 82 | match = re.match(r'^(\s*gosub)(.*?)$', text, re.I) |
83 | 83 | if match: |
84 | 84 | return Gosub(match.group(1), LineNumber(match.group(2))) |
85 | match = re.match(r'^(\s*if(.*?)goto)(.*?)$', text, re.I) | |
86 | if match: | |
87 | return IfThenLine(match.group(1), LineNumber(match.group(3))) | |
88 | 85 | # I doubt many BASICs allow a computed goto right after a 'THEN'... |
89 | 86 | match = re.match(r'^(\s*if(.*?)then)(\s*\d+\s*)$', text, re.I) |
90 | 87 | if match: |
92 | 89 | match = re.match(r'^(\s*if(.*?)then)(.*?)$', text, re.I) |
93 | 90 | if match: |
94 | 91 | return IfThen(match.group(1), BasicCommand.create(match.group(3))) |
92 | # We do this check *after* the above two, so as to not accidentally | |
93 | # match something like IF A THEN PRINT "HI":GOTO 50... | |
94 | match = re.match(r'^(\s*if(.*?)goto)(.*?)$', text, re.I) | |
95 | if match: | |
96 | return IfThenLine(match.group(1), LineNumber(match.group(3))) | |
95 | 97 | match = re.match(r'^(\s*on(.*?)go(to|sub))(.*?)$', text, re.I) |
96 | 98 | if match: |
97 | 99 | line_numbers = [LineNumber(x) for x in match.group(4).split(',')] |
376 | 378 | ... '35 GOTO 35.0\n' |
377 | 379 | ... '40 IFATHENP*40\n' |
378 | 380 | ... '50 IFAGOTOP*40\n' |
381 | ... '60 ONAGOTO10,20,70\n' | |
379 | 382 | ... 'GOSUB -10*-1\n') |
380 | 383 | >>> for e in b.check_computed_jumps(): print e |
381 | 384 | ?COMPUTED JUMP TO "A * 4" IN: 10 GOTO A * 4 |
382 | 385 | ?COMPUTED JUMP TO "6+7" IN: 20 EARTH:AIR:WATER:FIRE:GOSUB 6+7 |
383 | 386 | ?COMPUTED JUMP TO "35.0" IN: 35 GOTO 35.0 |
384 | 387 | ?COMPUTED JUMP TO "P*40" IN: 50 IFAGOTOP*40 |
385 | ?COMPUTED JUMP TO "-10*-1" IN: GOSUB -10*-1 (immediate mode, text file line 7) | |
388 | ?COMPUTED JUMP TO "-10*-1" IN: GOSUB -10*-1 (immediate mode, text file line 8) | |
389 | ||
390 | Computed GOTOs/GOSUBs are not analyzed for validity as jump targets. | |
391 | ||
392 | >>> for e in b.check_line_numbers(): print e | |
393 | ?UNDEFINED STATEMENT "70" IN: 60 ONAGOTO10,20,70 | |
394 | ||
395 | >>> b = BasicProgram('418 IF IR%>=92 THEN ON IR%-91 GOTO 361,311,321,331') | |
396 | >>> print b.lines[0].commands[0].__class__.__name__ | |
397 | IfThen | |
398 | >>> len([e for e in b.check_computed_jumps()]) | |
399 | 0 | |
386 | 400 | |
387 | 401 | """ |
388 | 402 | def __init__(self, text=None): |
411 | 425 | text_file_line += 1 |
412 | 426 | for (location, referenced_line_numbers) in referenced.iteritems(): |
413 | 427 | for referenced_line_number in referenced_line_numbers: |
428 | if referenced_line_number.is_computed(): | |
429 | continue | |
414 | 430 | if referenced_line_number.number not in defined: |
415 | 431 | errors.append(UndefinedStatement(defined[location], |
416 | 432 | referenced_line_number)) |