| 1 | // Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module ast |
| 5 | |
| 6 | import strings |
| 7 | |
| 8 | // signature returns a canonical, content-addressed string representation of |
| 9 | // the flat AST. Two flat ASTs produced from the same legacy AST must produce |
| 10 | // identical signatures — this is the round-trip lossless invariant. |
| 11 | // |
| 12 | // The signature includes per-node fields (kind, flags, aux, extra, pos) and |
| 13 | // resolves all interned strings to their text content, so signatures are |
| 14 | // independent of the order in which the intern table was populated. |
| 15 | pub fn (flat &FlatAst) signature() string { |
| 16 | mut sb := strings.new_builder(flat.nodes.len * 16) |
| 17 | for ff in flat.files { |
| 18 | sb.write_string('FILE name=${flat.string_at(ff.name_idx)} mod=${flat.string_at(ff.mod_idx)} root=') |
| 19 | flat.write_node_sig(mut sb, ff.file_id, 0) |
| 20 | sb.write_string('\n') |
| 21 | } |
| 22 | return sb.str() |
| 23 | } |
| 24 | |
| 25 | // subtree_signature returns the same canonical, content-addressed string as |
| 26 | // `signature()` but rooted at a single node id rather than the file roots. |
| 27 | // Useful for tests that want to pin the shape of a specific subtree (e.g. |
| 28 | // the `FlatBuilder.prepend_to_fn_body` bit-equality test) without managing |
| 29 | // a file root or worrying about intern-order leakage in the `.file` node's |
| 30 | // `extra` slot. |
| 31 | pub fn (flat &FlatAst) subtree_signature(node_id FlatNodeId) string { |
| 32 | mut sb := strings.new_builder(128) |
| 33 | flat.write_node_sig(mut sb, node_id, 0) |
| 34 | return sb.str() |
| 35 | } |
| 36 | |
| 37 | fn (flat &FlatAst) write_node_sig(mut sb strings.Builder, id FlatNodeId, depth int) { |
| 38 | if id < 0 || id >= flat.nodes.len { |
| 39 | sb.write_string('<nil>') |
| 40 | return |
| 41 | } |
| 42 | n := flat.nodes[id] |
| 43 | sb.write_string('(') |
| 44 | sb.write_string(n.kind.str()) |
| 45 | sb.write_string(' flags=${n.flags:x} aux=${n.aux:x} extra=${n.extra} pos=${n.pos.offset}:${n.pos.id}') |
| 46 | if n.name_id >= 0 { |
| 47 | sb.write_string(' name="') |
| 48 | sb.write_string(flat.string_at(n.name_id)) |
| 49 | sb.write_string('"') |
| 50 | } |
| 51 | for i in 0 .. n.edge_count { |
| 52 | sb.write_string(' ') |
| 53 | flat.write_node_sig(mut sb, flat.edges[n.first_edge + i].child_id, depth + 1) |
| 54 | } |
| 55 | sb.write_string(')') |
| 56 | } |
| 57 | |