func (d *desc) Visit(node ast.Node) ast.Visitor { switch v := node.(type) { case *ast.RuleSpec: for i := range v.Values { ast.Walk(d, v.Values[i]) } case *ast.GenDecl: for _, spec := range v.Specs { ast.Walk(d, spec) } return nil case *ast.CallExpr: d.c.name = v.Fun.(*ast.Ident).Name for _, arg := range v.Args { switch v := arg.(type) { case *ast.KeyValueExpr: d.c.params = append(d.c.params, v) case *ast.Ident: d.c.params = append(d.c.params, &ast.KeyValueExpr{ Key: v, }) default: panic(fmt.Errorf("%s failed to parse arg % #v\n", d.c.name, v)) } } return nil case nil: return nil default: panic(fmt.Errorf("illegal walk % #v\n", v)) } return d }
func (ctx *Context) run(path string, src interface{}) ([]byte, error) { ctx.fset = token.NewFileSet() // ctx.mode = parser.Trace pf, err := parser.ParseFile(ctx.fset, path, src, ctx.mode) if err != nil { return nil, err } ast.Walk(ctx, pf) lr, _ := utf8.DecodeLastRune(ctx.buf.Bytes()) _ = lr if ctx.buf.Len() > 0 && lr != '\n' { ctx.out("\n") } // ctx.printSels(pf.Decls) return ctx.buf.Bytes(), nil }
func register(s string, ch builtin.CallFunc, h builtin.CallHandle) { fset := token.NewFileSet() pf, err := ParseFile(fset, "", s, FuncOnly) if err != nil { if !strings.HasSuffix(err.Error(), "expected ';', found 'EOF'") { log.Fatal(err) } } d := &desc{c: call{ ch: ch, handle: h, }} ast.Walk(d, pf.Decls[0]) if d.err != nil { log.Fatal("failed to parse func description", d.err) } if _, ok := builtins[d.c.name]; ok { log.Println("already registered", d.c.name) } builtins[d.c.name] = d.c }
// Visit is an internal compiler method. It is exported to allow ast.Walk // to walk through the parser AST tree. func (ctx *Context) Visit(node ast.Node) ast.Visitor { if ctx.err != nil { fmt.Println(ctx.err) return nil } var key ast.Node switch v := node.(type) { case *ast.BlockStmt: if (ctx.scope.RuleLen() > 0 || ctx.activeMedia != nil) && !ctx.hiddenBlock { ctx.level = ctx.level + 1 if !ctx.firstRule { fmt.Fprintf(ctx.buf, " }\n") } } ctx.scope = NewScope(ctx.scope) if !ctx.hiddenBlock { ctx.firstRule = true } for _, node := range v.List { ast.Walk(ctx, node) } if ctx.level > 0 { ctx.level = ctx.level - 1 } ctx.scope = CloseScope(ctx.scope) if !ctx.hiddenBlock { ctx.blockOutro() ctx.firstRule = true } ctx.hiddenBlock = false // ast.Walk(ctx, v.List) // fmt.Fprintf(ctx.buf, "}") return nil case *ast.SelDecl: case *ast.File, *ast.GenDecl, *ast.Value: // Nothing to print for these case *ast.Ident: // The first IDENT is always the filename, just preserve // it somewhere key = ident case *ast.PropValueSpec: key = propSpec case *ast.DeclStmt: key = declStmt case *ast.IncludeSpec: // panic("not supported") case *ast.ValueSpec: key = valueSpec case *ast.RuleSpec: key = ruleSpec case *ast.SelStmt: // We will need to combine parent selectors // while printing these key = selStmt // Nothing to do case *ast.CommStmt: case *ast.CommentGroup: case *ast.Comment: key = comment case *ast.FuncDecl: ctx.printers[funcDecl](ctx, node) // Do not traverse mixins in the regular context return nil case *ast.BasicLit: return ctx case *ast.CallExpr: case nil: return ctx case *ast.MediaStmt: fmt.Println("mediastmt") key = mediaStmt case *ast.EmptyStmt: case *ast.AssignStmt: key = assignStmt case *ast.EachStmt: key = eachStmt case *ast.ListLit: case *ast.ImportSpec: case *ast.IfDecl: case *ast.IfStmt: key = ifStmt default: fmt.Printf("add printer for: %T\n", v) fmt.Printf("% #v\n", v) } ctx.printers[key](ctx, node) return ctx }
func printInclude(ctx *Context, n ast.Node) { panic("dont call this") spec := n.(*ast.IncludeSpec) name := spec.Name.String() var params []*ast.Field if spec.Params != nil { params = spec.Params.List } numargs := spec.Params.NumFields() mix, err := ctx.scope.Mixin(name, numargs) if err != nil { log.Fatal(err) } // Add new scope, register args ctx.scope = NewScope(ctx.scope) mixargs := mix.fn.Type.Params.List for i := range mixargs { // Param passed by include var param *ast.Field if len(params) > i { param = params[i] } var ( key *ast.BasicLit val *ast.Ident ) _, _ = key, val switch v := mixargs[i].Type.(type) { case *ast.BasicLit: key = v case *ast.KeyValueExpr: key = v.Key.(*ast.BasicLit) val = ast.ToIdent(v.Value) } if param != nil { switch v := param.Type.(type) { case *ast.KeyValueExpr: // Key args specify their argument, so use their key // instead of the mixins argument for this position // Params with defaults key = v.Key.(*ast.BasicLit) val = ast.ToIdent(v.Value) case *ast.Ident: val = v case *ast.BasicLit: val = ast.ToIdent(v) default: fmt.Printf("dropped param: % #v\n", v) } } // if key != nil && val != nil { // ctx.scope.Insert(key.Value, val.Name) // } } if len(params) > len(mixargs) { fmt.Printf("dropped extra params: % #v\n", params[len(mixargs):]) } for _, stmt := range mix.fn.Body.List { ast.Walk(ctx, stmt) } // Exit new scope, removing args ctx.scope = CloseScope(ctx.scope) }