Okay, but even in Lisp, I'm not sure that's a good way to go about it. What do you want to do when you manipulate code, generally? Oftentimes you seek to find all instances of a specific pattern and modify part of that pattern. So why not do something like this:

    transform = Replace(pattern="$x + $y", replacement="($x + 1) + $y")
    transform("1 + 1")       # "(1 + 1) + 1
    transform("3 + 4 + 5")   # "(((3 + 1) + 4) + 1) + 5
    transform("10 * 1 + 1")  # no change, because the expression is actually (10 + 1) + 1
If you give me a primitive like that in Lisp, I'm going to use it. It's a lot more trustworthy than "replace item in list by index". Because it's nice and all that everything is a list in Lisp, but manipulating code as lists (or maps or whatever) like you describe isn't going to get you very far. If you want to do something as simple as replacing all calls to (f x) by (f x 1), you better hope you don't come across code like (let ((f 123)) ...), for example. Outside of toy cases and examples, you need robust primitives that understand the code's semantic structure.

There's actually a library called ast-grep which does something very similar to what you're describing. They have an example in their introduction which performs a find and replace operation on a JS AST using a pattern:

  ast-grep --pattern 'var code = $PAT' --rewrite 'let code = $PAT' --lang js

https://ast-grep.github.io/guide/introduction.html