Appearance
| 1 | namespace Syntax is | |
| 2 | use Collections.LIST; | |
| 3 | ||
| 4 | use Trees; | |
| 5 | use Trees.Definitions.FUNCTION; | |
| 6 | ||
| 7 | // Collects a parsed file's `Definitions.FUNCTION` declarations in | |
| 8 | // declaration order. After `add_accessors_for_properties` (run as part of | |
| 9 | // rewrite-syntax-trees) this is every method plus the accessor functions | |
| 10 | // synthesised for properties and indexers. `pre` returns true for a | |
| 11 | // function, so bodies are not descended into and nested lambdas are not | |
| 12 | // collected. | |
| 13 | class FUNCTION_COLLECTOR: Visitor is | |
| 14 | functions: LIST[FUNCTION] public; | |
| 15 | ||
| 16 | init() is | |
| 17 | super.init(); | |
| 18 | functions = LIST[FUNCTION](); | |
| 19 | si | |
| 20 | ||
| 21 | pre(function: FUNCTION) -> bool is | |
| 22 | functions.add(function); | |
| 23 | return true; | |
| 24 | si | |
| 25 | si | |
| 26 | ||
| 27 | // One retained-to-donor function pairing for the incremental body re-walk. | |
| 28 | struct FUNCTION_PAIR is | |
| 29 | retained: FUNCTION public; | |
| 30 | donor: FUNCTION public; | |
| 31 | ||
| 32 | init(retained: FUNCTION, donor: FUNCTION) is | |
| 33 | self.retained = retained; | |
| 34 | self.donor = donor; | |
| 35 | si | |
| 36 | si | |
| 37 | ||
| 38 | // Pairs the function declarations of two parses of the same file — the | |
| 39 | // retained AST and a freshly parsed donor — for the incremental body | |
| 40 | // re-walk. Only interface-preserving edits reach this, so the two parses | |
| 41 | // have an identical declaration sequence and the pairing is positional; | |
| 42 | // name and argument count are checked per pair purely as a desync guard. | |
| 43 | // | |
| 44 | // `pairs` is null when the two parses do not pair cleanly: the caller must | |
| 45 | // then fall back to a full rebuild rather than splice. | |
| 46 | class FUNCTION_PAIRING is | |
| 47 | pairs: LIST[FUNCTION_PAIR]? public; | |
| 48 | ||
| 49 | init(retained_root: Node, donor_root: Node) is | |
| 50 | let retained = collect(retained_root); | |
| 51 | let donor = collect(donor_root); | |
| 52 | ||
| 53 | if retained.count != donor.count then | |
| 54 | pairs = null; | |
| 55 | return; | |
| 56 | fi | |
| 57 | ||
| 58 | let result = LIST[FUNCTION_PAIR](); | |
| 59 | ||
| 60 | for i in 0..retained.count do | |
| 61 | let r = retained[i]; | |
| 62 | let d = donor[i]; | |
| 63 | ||
| 64 | if !signatures_match(r, d) then | |
| 65 | pairs = null; | |
| 66 | return; | |
| 67 | fi | |
| 68 | ||
| 69 | result.add(FUNCTION_PAIR(r, d)); | |
| 70 | od | |
| 71 | ||
| 72 | pairs = result; | |
| 73 | si | |
| 74 | ||
| 75 | collect(root: Node) -> LIST[FUNCTION] static is | |
| 76 | let collector = FUNCTION_COLLECTOR(); | |
| 77 | root.walk(collector); | |
| 78 | return collector.functions; | |
| 79 | si | |
| 80 | ||
| 81 | signatures_match(a: FUNCTION, b: FUNCTION) -> bool static => | |
| 82 | name_of(a) =~ name_of(b) /\ | |
| 83 | a.arguments.variables.count == b.arguments.variables.count; | |
| 84 | ||
| 85 | name_of(function: FUNCTION) -> string static => | |
| 86 | if function.name? then function.name.name else "" fi; | |
| 87 | si | |
| 88 | si |