Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Collections.LIST; | |
| 3 | ||
| 4 | use Source; | |
| 5 | use Trees; | |
| 6 | ||
| 7 | // Reconciles the retained AST of an interface-preserving incremental | |
| 8 | // EDIT against the freshly-parsed donor. An interface-preserving EDIT | |
| 9 | // keeps the retained interface nodes (their symbols stay valid) but | |
| 10 | // those nodes carry pre-edit locations — a body edit, or reformatting | |
| 11 | // around it, moves them. This visitor copies the donor parse's correct | |
| 12 | // locations onto the retained nodes, and records the pre-edit -> | |
| 13 | // post-edit correspondence of every node it touches so the analyser's | |
| 14 | // location side-tables can be reconciled the same way. | |
| 15 | // | |
| 16 | // It harvests from the donor in walk order, then applies to the | |
| 17 | // retained tree in the same walk order. Body interiors are not | |
| 18 | // descended (`pre` returns true for the body node types): after the | |
| 19 | // splice the retained AST's bodies *are* the donor's body nodes, | |
| 20 | // already current, and skipping them keeps the harvest and apply walks | |
| 21 | // counting the same nodes. `is_consistent` is false if the two walks | |
| 22 | // disagreed — on node count, or on a node's type — and the caller must | |
| 23 | // then fall back to a full rebuild. | |
| 24 | ||
| 25 | // One harvested node: its location, plus its runtime type (held as | |
| 26 | // `object` — the result of `get_type()`, compared by identity). The | |
| 27 | // type is checked against the retained node on apply, so a structural | |
| 28 | // divergence between the two parses outside an edited body — meaning | |
| 29 | // the interface classification was wrong — is caught at the first | |
| 30 | // mismatching node, not just by an end-of-walk count. | |
| 31 | struct TRANSFER_POINT is | |
| 32 | location: LOCATION public; | |
| 33 | kind: object public; | |
| 34 | ||
| 35 | init(location: LOCATION, kind: object) is | |
| 36 | self.location = location; | |
| 37 | self.kind = kind; | |
| 38 | si | |
| 39 | si | |
| 40 | ||
| 41 | class LOCATION_TRANSFER: Visitor is | |
| 42 | _harvesting: bool; | |
| 43 | _points: LIST[TRANSFER_POINT]; | |
| 44 | _index: int; | |
| 45 | _structurally_consistent: bool; | |
| 46 | _correspondence: LOCATION_CORRESPONDENCE; | |
| 47 | ||
| 48 | init() is | |
| 49 | super.init(); | |
| 50 | _points = LIST[TRANSFER_POINT](); | |
| 51 | _correspondence = LOCATION_CORRESPONDENCE(); | |
| 52 | si | |
| 53 | ||
| 54 | // The pre-edit -> post-edit location of every retained interface | |
| 55 | // node, built during apply_to. Valid only when is_consistent. | |
| 56 | correspondence: LOCATION_CORRESPONDENCE => _correspondence; | |
| 57 | ||
| 58 | harvest(root: Node) is | |
| 59 | _harvesting = true; | |
| 60 | _points = LIST[TRANSFER_POINT](); | |
| 61 | root.walk(self); | |
| 62 | si | |
| 63 | ||
| 64 | apply_to(root: Node) is | |
| 65 | _harvesting = false; | |
| 66 | _index = 0; | |
| 67 | _structurally_consistent = true; | |
| 68 | _correspondence = LOCATION_CORRESPONDENCE(); | |
| 69 | root.walk(self); | |
| 70 | si | |
| 71 | ||
| 72 | is_consistent: bool => | |
| 73 | !_harvesting /\ _index == _points.count /\ _structurally_consistent; | |
| 74 | ||
| 75 | transfer(node: Node) is | |
| 76 | if _harvesting then | |
| 77 | _points.add(TRANSFER_POINT(node.location, node.get_type())); | |
| 78 | return; | |
| 79 | fi | |
| 80 | ||
| 81 | if _index >= _points.count then | |
| 82 | _structurally_consistent = false; | |
| 83 | _index = _index + 1; | |
| 84 | return; | |
| 85 | fi | |
| 86 | ||
| 87 | let point = _points[_index]; | |
| 88 | _index = _index + 1; | |
| 89 | ||
| 90 | if node.get_type() != point.kind then | |
| 91 | _structurally_consistent = false; | |
| 92 | fi | |
| 93 | ||
| 94 | // record old -> new before overwriting the location | |
| 95 | _correspondence.add(node.location, point.location); | |
| 96 | ||
| 97 | node.location = point.location; | |
| 98 | si | |
| 99 | ||
| 100 | // Body interiors are donor nodes already in current coordinates — | |
| 101 | // skip them so harvest and apply walk the same node set. | |
| 102 | pre(block: Bodies.BLOCK) -> bool => true; | |
| 103 | pre(expression: Bodies.EXPRESSION) -> bool => true; | |
| 104 | pre(innate_body: Bodies.INNATE) -> bool => true; | |
| 105 | pre(null_body: Bodies.NULL) -> bool => true; | |
| 106 | ||
| 107 | visit(identifier: Identifiers.Identifier) is transfer(identifier); si | |
| 108 | visit(identifier: Identifiers.QUALIFIED) is transfer(identifier); si | |
| 109 | ||
| 110 | visit(modifier: Modifiers.Modifier) is transfer(modifier); si | |
| 111 | visit(modifiers: Modifiers.LIST) is transfer(modifiers); si | |
| 112 | visit(pragma: Pragmas.PRAGMA) is transfer(pragma); si | |
| 113 | ||
| 114 | visit(definition: Definitions.Definition) is transfer(definition); si | |
| 115 | visit(definitions: Definitions.LIST) is transfer(definitions); si | |
| 116 | visit(pragma: Definitions.PRAGMA) is transfer(pragma); si | |
| 117 | visit(`namespace: Definitions.NAMESPACE) is transfer(`namespace); si | |
| 118 | visit(`use: Definitions.USE) is transfer(`use); si | |
| 119 | visit(`class: Definitions.CLASS) is transfer(`class); si | |
| 120 | visit(`trait: Definitions.TRAIT) is transfer(`trait); si | |
| 121 | visit(`struct: Definitions.STRUCT) is transfer(`struct); si | |
| 122 | visit(`union: Definitions.UNION) is transfer(`union); si | |
| 123 | visit(variant: Definitions.VARIANT) is transfer(variant); si | |
| 124 | visit(`enum: Definitions.ENUM) is transfer(`enum); si | |
| 125 | visit(enum_member: Definitions.ENUM_MEMBER) is transfer(enum_member); si | |
| 126 | visit(function: Definitions.FUNCTION) is transfer(function); si | |
| 127 | visit(functions: Definitions.FUNCTION_GROUP) is transfer(functions); si | |
| 128 | visit(property: Definitions.PROPERTY) is transfer(property); si | |
| 129 | visit(indexer: Definitions.INDEXER) is transfer(indexer); si | |
| 130 | ||
| 131 | visit(variable: Variables.VARIABLE) is transfer(variable); si | |
| 132 | visit(variables: Variables.LIST) is transfer(variables); si | |
| 133 | visit(left: Trees.Variables.SIMPLE_VARIABLE_LEFT) is transfer(left); si | |
| 134 | visit(destructure_left: Trees.Variables.DESTRUCTURING_VARIABLE_LEFT) is transfer(destructure_left); si | |
| 135 | visit(literal_leaf: Trees.Variables.LITERAL_VARIABLE_LEFT) is transfer(literal_leaf); si | |
| 136 | ||
| 137 | visit(type_expression: TypeExpressions.TypeExpression) is transfer(type_expression); si | |
| 138 | visit(type_expression: TypeExpressions.INFER) is transfer(type_expression); si | |
| 139 | visit(structured: TypeExpressions.Structured) is transfer(structured); si | |
| 140 | visit(array: TypeExpressions.ARRAY_) is transfer(array); si | |
| 141 | visit(pointer: TypeExpressions.POINTER) is transfer(pointer); si | |
| 142 | visit(optional: TypeExpressions.OPTIONAL) is transfer(optional); si | |
| 143 | visit(reference: TypeExpressions.REFERENCE) is transfer(reference); si | |
| 144 | visit(member: TypeExpressions.MEMBER) is transfer(member); si | |
| 145 | visit(named: TypeExpressions.NAMED) is transfer(named); si | |
| 146 | visit(types: TypeExpressions.LIST) is transfer(types); si | |
| 147 | visit(generic: TypeExpressions.GENERIC) is transfer(generic); si | |
| 148 | visit(function: TypeExpressions.FUNCTION) is transfer(function); si | |
| 149 | visit(functions: TypeExpressions.FUNCTION_GROUP) is transfer(functions); si | |
| 150 | visit(tuple: TypeExpressions.TUPLE) is transfer(tuple); si | |
| 151 | visit(element: TypeExpressions.NAMED_TUPLE_ELEMENT) is transfer(element); si | |
| 152 | visit(constraint: TypeExpressions.TYPE_PARAMETER_CONSTRAINT) is transfer(constraint); si | |
| 153 | visit(element: TypeExpressions.UNDEFINED) is transfer(element); si | |
| 154 | ||
| 155 | visit(expression: Expressions.Expression) is transfer(expression); si | |
| 156 | visit(identifier: Expressions.IDENTIFIER) is transfer(identifier); si | |
| 157 | visit(literal: Expressions.Literals.Literal) is transfer(literal); si | |
| 158 | visit(`string: Expressions.Literals.STRING) is transfer(`string); si | |
| 159 | visit(interpolation: Expressions.STRING_INTERPOLATION) is transfer(interpolation); si | |
| 160 | visit(integer: Expressions.Literals.INTEGER) is transfer(integer); si | |
| 161 | visit(float: Expressions.Literals.FLOAT) is transfer(float); si | |
| 162 | visit(character: Expressions.Literals.CHARACTER) is transfer(character); si | |
| 163 | visit(boolean: Expressions.Literals.BOOLEAN) is transfer(boolean); si | |
| 164 | visit(variable: Expressions.VARIABLE) is transfer(variable); si | |
| 165 | visit(variable: Expressions.TUPLE_ELEMENT) is transfer(variable); si | |
| 166 | visit(none: Expressions.Literals.NONE) is transfer(none); si | |
| 167 | visit(`null: Expressions.NULL) is transfer(`null); si | |
| 168 | visit(`self: Expressions.SELF) is transfer(`self); si | |
| 169 | visit(`super: Expressions.SUPER) is transfer(`super); si | |
| 170 | visit(`new: Expressions.NEW) is transfer(`new); si | |
| 171 | visit(`cast: Expressions.CAST) is transfer(`cast); si | |
| 172 | visit(`isa: Expressions.ISA) is transfer(`isa); si | |
| 173 | visit(`isa: Expressions.TYPEOF) is transfer(`isa); si | |
| 174 | visit(`default: Expressions.DEFAULT) is transfer(`default); si | |
| 175 | visit(function: Expressions.FUNCTION) is transfer(function); si | |
| 176 | visit(recurse: Expressions.RECURSE) is transfer(recurse); si | |
| 177 | visit(tuple: Expressions.TUPLE) is transfer(tuple); si | |
| 178 | visit(sequence: Expressions.SEQUENCE) is transfer(sequence); si | |
| 179 | visit(list: Expressions.LIST) is transfer(list); si | |
| 180 | visit(call: Expressions.CALL) is transfer(call); si | |
| 181 | visit(member: Expressions.MEMBER) is transfer(member); si | |
| 182 | visit(member: Expressions.EXPLICIT_SPECIALIZATION) is transfer(member); si | |
| 183 | visit(ambiguous_expression: Expressions.AMBIGUOUS_EXPRESSION) is transfer(ambiguous_expression); si | |
| 184 | visit(ambiguous_expression: Expressions.GENERIC_APPLICATION) is transfer(ambiguous_expression); si | |
| 185 | visit(index: Expressions.INDEX) is transfer(index); si | |
| 186 | visit(has_value: Expressions.HAS_VALUE) is transfer(has_value); si | |
| 187 | visit(unwrap: Expressions.UNWRAP) is transfer(unwrap); si | |
| 188 | visit(reference: Expressions.REFERENCE) is transfer(reference); si | |
| 189 | visit(unary: Expressions.UNARY) is transfer(unary); si | |
| 190 | visit(binary: Expressions.BINARY) is transfer(binary); si | |
| 191 | visit(statement: Expressions.STATEMENT) is transfer(statement); si | |
| 192 | visit(statement: Expressions.LET_IN) is transfer(statement); si | |
| 193 | visit(block: Expressions.VAL_BLOCK) is transfer(block); si | |
| 194 | visit(assert_in: Expressions.ASSERT_IN) is transfer(assert_in); si | |
| 195 | ||
| 196 | visit(left: Trees.Expressions.SIMPLE_LEFT_EXPRESSION) is transfer(left); si | |
| 197 | visit(destructure_left: Trees.Expressions.DESTRUCTURING_LEFT_EXPRESSION) is transfer(destructure_left); si | |
| 198 | ||
| 199 | visit(statement: Statements.Statement) is transfer(statement); si | |
| 200 | visit(statements: Statements.LIST) is transfer(statements); si | |
| 201 | visit(l: Statements.LET) is transfer(l); si | |
| 202 | visit(assign: Statements.ASSIGNMENT) is transfer(assign); si | |
| 203 | visit(expression: Statements.EXPRESSION) is transfer(expression); si | |
| 204 | visit(`return: Statements.RETURN) is transfer(`return); si | |
| 205 | visit(`throw: Statements.THROW) is transfer(`throw); si | |
| 206 | visit(assert__: Statements.ASSERT) is transfer(assert__); si | |
| 207 | visit(`if: Statements.IF) is transfer(`if); si | |
| 208 | visit(if_branch: Statements.IF_BRANCH) is transfer(if_branch); si | |
| 209 | visit(`case: Statements.CASE) is transfer(`case); si | |
| 210 | visit(case_match: Statements.CASE_MATCH) is transfer(case_match); si | |
| 211 | visit(`try: Statements.TRY) is transfer(`try); si | |
| 212 | visit(`catch: Statements.CATCH) is transfer(`catch); si | |
| 213 | visit(`do: Statements.DO) is transfer(`do); si | |
| 214 | visit(`for: Statements.FOR) is transfer(`for); si | |
| 215 | visit(labelled: Statements.LABELLED) is transfer(labelled); si | |
| 216 | visit(`break: Statements.BREAK) is transfer(`break); si | |
| 217 | visit(`continue: Statements.CONTINUE) is transfer(`continue); si | |
| 218 | visit(pragma: Statements.PRAGMA) is transfer(pragma); si | |
| 219 | ||
| 220 | visit(expression: Bodies.EXPRESSION) is transfer(expression); si | |
| 221 | visit(block: Bodies.BLOCK) is transfer(block); si | |
| 222 | visit(block: Bodies.NULL) is transfer(block); si | |
| 223 | visit(block: Bodies.INNATE) is transfer(block); si | |
| 224 | si | |
| 225 | ||
| 226 | // Copies the donor parse's locations onto the structurally-identical | |
| 227 | // retained AST and returns the pre-edit -> post-edit correspondence of | |
| 228 | // every retained interface node. Returns null if the two walks | |
| 229 | // disagreed — on node count or node type — and the caller must then | |
| 230 | // fall back to a full rebuild. | |
| 231 | class LOCATION_REFRESH is | |
| 232 | apply(retained_root: Trees.Node, donor_root: Trees.Node) -> Source.LOCATION_CORRESPONDENCE? static is | |
| 233 | let transfer = LOCATION_TRANSFER(); | |
| 234 | ||
| 235 | transfer.harvest(donor_root); | |
| 236 | transfer.apply_to(retained_root); | |
| 237 | ||
| 238 | if !transfer.is_consistent then | |
| 239 | return null; | |
| 240 | fi | |
| 241 | ||
| 242 | return transfer.correspondence; | |
| 243 | si | |
| 244 | si | |
| 245 | si |