This is a well-trodden road. It starts with "wouldn't it be awesome if we could manipulate everything as ASTs", then usability intervenes and we fall back to "well, we need to be able to selectively edit as text", which means you need to be able to convert everything to bags of characters and back. And now you've built that conversion, you might as well represent the "source of truth" as bags of characters like everyone else does.
(And heaven forbid you should want to make a checkpoint commit that doesn't parse...)
- Using Merkle trees, we can assign every node a hash-based ID. So now we can refer to other nodes by ID. This lets us store a graph whose vertices are all the tree nodes.
- With the graph, we can now reference bindings not by string literal, but by ID. This eliminates shadowing problems and missing imports.
- There is now one source of truth for the names of variables and functions. As a result, in source control, a commit that renames something is a one-line change.