CSS-like selectors for Go AST?

282 views
Skip to first unread message

cpu...@gmail.com

unread,
Oct 13, 2020, 3:37:21 AM10/13/20
to golang-nuts
Good morning.

I've recently found myself writing a code generator for dynamic interface composition at runtime. It became ugly and I had to pass in quite a number of parameters that should have been available from reading the source code.

In a second attempt I've reimplemented the generator using stringer-like AST handling. It worked well but was cumbersome to code.

Now an idea surfaced: wouldn't it make sense to implement a selector interface on top of the golang AST, something like CSS or JQ selectors but targeted at the go language constructs? I've stumbled across Guru but it does seem to target a slightly different use case.

Is anybody aware of such a selector lib/module or does the idea even sound interesting?

Cheers,
Andi

David Skinner

unread,
Oct 13, 2020, 11:29:28 AM10/13/20
to golang-nuts
We have something like that on the drawing board, but right now it is very much vaporware.

The basic concept that we are using, is that you create a new instance of a type of input and specify a CSS style of class. The actual implementation of the class may vary depending upon whether the DI says we are using WebAsm or ConIo, or Batch, or Gui. In the case of the web, the class may be implemented in SCSS by the UX designer simply by @extend of the functional classes. Otherwise, the implementation is defined by JSON files which are created by the UX designer and optionally bound to the program. Our UX designer does not like programming and our programmers are not good stylists, so I am trying to keep them separated.

I hope to have an Alpha version working in a few weeks. I am currently working on staffing the project. If you would like to be a Beta tester or assist with the project, let me know.

Max

unread,
Oct 14, 2020, 12:09:08 PM10/14/20
to golang-nuts
I can only agree, traversing or navigating Go AST is cumbersome for at least two reasons:
1. each type has its own fields - no uniform API is available
2. you often need type assertions on fields because they have interface type instead of concrete type

I developed a wrapper around them to mitigate these problems for my Go interpreter https://github.com/cosmos72/gomacro
If you are interested, it is the package https://github.com/cosmos72/gomacro/tree/master/ast2

You may find it helps by providing an uniform API and removing the need for type assertions, but it still requires compile-time code,
not CSS-style strings that can be created at runtime.

A quick example:  this uses plain go/ast
```
package main
import (
    "fmt"
    "go/ast"
    "go/parser"
)
func main() {
    x, _ := parser.ParseExpr("foo[1 + 2 * 3]")
    // we want to extract the "3"
    var three string = x.(*ast.IndexExpr).Index.(*ast.BinaryExpr).Y.(*ast.BinaryExpr).Y.(*ast.BasicLit).Value
    fmt.Printf("%s\n", three) // prints 3
}
```
while this is the equivalent using github.com/cosmos72/gomacro/ast2
```
package main
import (
    "fmt"
    "go/ast"
    "go/parser"
func main() {
    x, _ := parser.ParseExpr("foo[1 + 2 * 3]")
    y := ast2.AnyToAst(x, nil) // wrap the existing AST. does not allocate memory.
   
    ythree := y.Get(1).Get(1).Get(1) // extract the "3" as ast2.Node

    // now unwrap it, getting the *ast.BasicLit and the Value inside it
    var three string = ast2.ToBasicLit(ythree).Value
    fmt.Printf("%s\n", three) // prints 3
}
```

cpu...@gmail.com

unread,
Oct 14, 2020, 2:06:57 PM10/14/20
to golang-nuts
Hi Max,

Great answer, thank you! Exactly what I was looking for in terms of a fluid api, no need for parsing the selectors.

The API is not quite suitable for public consumption. I feel this could be a great success if extracted into its own module.
Could you kindly clarify what the meaning of the int parameters is, i.e. is there a general rule or does it depend on the function?

I‘ll play a bit with it and will comment on GH if ok.

Much appreciated,
Andi

Max

unread,
Oct 21, 2020, 2:13:33 PM10/21/20
to golang-nuts
Yes, it could be extracted to a separate module, but package  github.com/cosmos72/gomacro/ast2 already has very few dependencies.
In particular, it does *not* depend on the Go interpreter github.com/cosmos72/gomacro/fast

The integer arguments are simple:
`y.Get(0)` means "get the first (index = 0 ) child node of y"
`y.Get(1)` means "get the second (index = 1) child node of y"
and so on.

The interface `ast2.Ast` also has methods to query the number of children, and to modify the AST:
```
type Ast interface {
        Interface() interface{}
        Op() token.Token
        Size() int
        Get(i int) Ast
        Set(i int, child Ast)
        New() Ast // returns a copy of Ast. the children are not copied
    }
```
Reply all
Reply to author
Forward
0 new messages