Appearance
| 1 | namespace Syntax is | |
| 2 | use System.Text.StringBuilder; | |
| 3 | ||
| 4 | use Trees; | |
| 5 | ||
| 6 | // Walks a parsed file's declaration syntax tree nodes — every node except | |
| 7 | // the code inside method, function and accessor bodies — and builds a | |
| 8 | // signature string from them. Two parses of a file with the same | |
| 9 | // signature have the same interface: anything another file could resolve | |
| 10 | // against (a name, a type, a modifier, the declaration structure) is | |
| 11 | // folded in, while body code is skipped. The analyser compares signatures | |
| 12 | // across an EDIT to tell whether the edit could affect any other file. | |
| 13 | class INTERFACE_SIGNATURE: StrictVisitor is | |
| 14 | _builder: StringBuilder; | |
| 15 | ||
| 16 | init() is | |
| 17 | super.init(); | |
| 18 | ||
| 19 | _builder = StringBuilder(); | |
| 20 | si | |
| 21 | ||
| 22 | signature: string => _builder.to_string(); | |
| 23 | ||
| 24 | // StrictVisitor routes every node it has no specific handler for to | |
| 25 | // throw_not_implemented; nothing here overrides a visit, so every node | |
| 26 | // arrives here. Fold the node kind, plus the salient text of the | |
| 27 | // content-bearing leaves (identifiers, modifiers, literals) — node | |
| 28 | // kind alone would miss a rename or a changed literal. | |
| 29 | throw_not_implemented(name: string, node: Node) is | |
| 30 | _builder.append(name); | |
| 31 | ||
| 32 | let identifier = cast Identifiers.Identifier?(node); | |
| 33 | let modifier = cast Modifiers.Modifier?(node); | |
| 34 | let literal = cast Expressions.Literals.Literal?(node); | |
| 35 | ||
| 36 | if identifier? then | |
| 37 | for part in identifier.names do | |
| 38 | _builder.append(':').append(part); | |
| 39 | od | |
| 40 | elif modifier? then | |
| 41 | _builder.append(':').append(modifier.name); | |
| 42 | elif literal? then | |
| 43 | _builder.append(':').append(literal.value_string); | |
| 44 | fi | |
| 45 | ||
| 46 | _builder.append(';'); | |
| 47 | si | |
| 48 | ||
| 49 | // Reached only by a node type with no visit handler at all — fold its | |
| 50 | // kind so structure is still captured rather than throwing. | |
| 51 | visit(node: Node) is | |
| 52 | _builder.append("node;"); | |
| 53 | si | |
| 54 | ||
| 55 | // Skip the code inside method, function and accessor bodies: an edit | |
| 56 | // confined to a body must not change the signature. The pre/accept | |
| 57 | // split means the body node itself is still visited (its kind | |
| 58 | // folded), so making a method abstract vs concrete is still caught — | |
| 59 | // only the body interior is excluded. | |
| 60 | pre(block: Bodies.BLOCK) -> bool => true; | |
| 61 | pre(expression: Bodies.EXPRESSION) -> bool => true; | |
| 62 | pre(innate_body: Bodies.INNATE) -> bool => true; | |
| 63 | pre(null_body: Bodies.NULL) -> bool => true; | |
| 64 | si | |
| 65 | si |