// newIdentCmd produces a command containing a single identifier node. func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode { return &parse.CommandNode{ NodeType: parse.NodeCommand, Args: []parse.Node{parse.NewIdentifier(identifier).SetPos(pos)}, } }
// newIdentCmd produces a command containing a single identifier node. func newIdentCmd(identifier string) *parse.CommandNode { return &parse.CommandNode{ NodeType: parse.NodeCommand, Args: []parse.Node{parse.NewIdentifier(identifier)}, } }
// ReplaceTranslatableBlocks replaces begintrans/endtrans blocks // with an equivalent action using the translation function named // by fn. func ReplaceTranslatableBlocks(tr *parse.Tree, fn string) error { var err error WalkTree(tr, func(n, p parse.Node) { if err != nil { return } if IsPseudoFunction(n, BeginTranslatableBlock) { list, ok := p.(*parse.ListNode) if !ok { loc, ctx := tr.ErrorContext(n) err = fmt.Errorf("%s:%s:%s not in ListNode (%T)", loc, ctx, BeginTranslatableBlock, p) return } cmd := &parse.CommandNode{ NodeType: parse.NodeCommand, Pos: n.Position(), } pipe := &parse.PipeNode{ NodeType: parse.NodePipe, Pos: n.Position(), Cmds: []*parse.CommandNode{cmd}, } repl := &parse.ActionNode{ NodeType: parse.NodeAction, Pos: n.Position(), Pipe: pipe, } if repl != nil { } pos := -1 for ii, v := range list.Nodes { if v == n { pos = ii break } } var pipes []parse.Node var buf bytes.Buffer endPos := -1 Nodes: for ii, v := range list.Nodes[pos+1:] { switch x := v.(type) { case *parse.TextNode: buf.Write(x.Text) case *parse.ActionNode: if IsPseudoFunction(x, EndTranslatableBlock) { endPos = ii break Nodes } if len(x.Pipe.Decl) > 0 { loc, ctx := tr.ErrorContext(n) err = fmt.Errorf("%s:%s:%s translatable block can't contain a pipe with declaractions", loc, ctx, v) } buf.WriteString("%v") pipes = append(pipes, x.Pipe) default: loc, ctx := tr.ErrorContext(n) err = fmt.Errorf("%s translatable block can't contain %T %s", loc, v, ctx) return } } if buf.Len() > 0 { text := buf.String() quoted := strconv.Quote(text) innerPipe := &parse.PipeNode{ NodeType: parse.NodePipe, Pos: n.Position(), Cmds: []*parse.CommandNode{ &parse.CommandNode{ NodeType: parse.NodeCommand, Pos: n.Position(), Args: []parse.Node{parse.NewIdentifier(fn), &parse.StringNode{ NodeType: parse.NodeString, Pos: n.Position(), Quoted: quoted, Text: text, }}, }, }, } cmd.Args = append(cmd.Args, parse.NewIdentifier("printf"), innerPipe) cmd.Args = append(cmd.Args, pipes...) } nodes := list.Nodes[:pos] nodes = append(nodes, repl) nodes = append(nodes, list.Nodes[endPos+pos+1:]...) list.Nodes = nodes } }) return err }
/** hackNode 对模板中 template/import 的目标模板重新命名. 规则: 没有扩展名当作内嵌模板, 反之当作文件模板. 目标名称变更为绝对路径名. 所有 template 用 import 替换. 格式为: import "from" "target" args... 参数: t 模板. names 所有使用的模板需要检查是否已经载入. from 来源模板名. 绝对路径. node 待 hack 的原始 parse.Node. 返回: 是否有错误发生的错误. */ func hackNode(t *Template, names map[string]bool, from string, list *parse.ListNode, i int, node parse.Node) error { var ( pipe *parse.PipeNode args []parse.Node target *parse.StringNode ) rootdir := t.RootDir() switch n := node.(type) { default: return nil case *parse.ListNode: for i, node := range n.Nodes { err := hackNode(t, names, from, n, i, node) if err != nil { return err } } return nil case *parse.TemplateNode: args = make([]parse.Node, 3) args[0] = parse.NewIdentifier("import").SetPos(n.Pos) // from, 保存调用者 args[1] = &parse.StringNode{ NodeType: parse.NodeString, Pos: n.Position(), // 伪造 Quoted: strconv.Quote(from), Text: from, } // target, 重建目标 args[2] = &parse.StringNode{ NodeType: parse.NodeString, Pos: n.Position(), // 伪造 Quoted: strconv.Quote(n.Name), Text: n.Name, } // 复制其它参数 pipe = n.Pipe if pipe != nil && len(pipe.Cmds) != 0 && pipe.Cmds[0].NodeType == parse.NodeCommand { for _, arg := range pipe.Cmds[0].Args { args = append(args, arg) } } else { if pipe == nil { pipe = &parse.PipeNode{ NodeType: parse.NodePipe, Pos: n.Position(), // 伪造 Line: n.Line, Cmds: []*parse.CommandNode{ &parse.CommandNode{ NodeType: parse.NodeCommand, Pos: n.Position(), }, }, } } } pipe.Cmds[0].Args = args // 改成 ActionNode list.Nodes[i] = &parse.ActionNode{ NodeType: parse.NodeAction, Pos: n.Pos, Line: n.Line, Pipe: pipe, } case *parse.ActionNode: pipe = n.Pipe if pipe == nil || len(pipe.Decl) != 0 || len(pipe.Cmds) == 0 || pipe.Cmds[0].NodeType != parse.NodeCommand || len(pipe.Cmds[0].Args) == 0 || pipe.Cmds[0].Args[0].Type() != parse.NodeIdentifier || pipe.Cmds[0].Args[0].String() != "import" { return nil } args = make([]parse.Node, len(pipe.Cmds[0].Args)+1) args[0] = pipe.Cmds[0].Args[0] // from, 增加调用者来源 args[1] = &parse.StringNode{ NodeType: parse.NodeString, Pos: args[0].Position(), // 伪造 Quoted: strconv.Quote(from), Text: from, } // 复制其它参数 for i, arg := range pipe.Cmds[0].Args { if i != 0 { args[i+1] = arg } } pipe.Cmds[0].Args = args } // 处理目标模板 args[2], 有可能是变量. target, _ = args[2].(*parse.StringNode) if target == nil { return nil } // 计算目标路径 name := relToURI(rootdir, absPath(from), target.Text) if name == "" { return fmt.Errorf( "template: is invalid on define %q", target.Text) } // 判断文件模板是否载入 if path.Ext(name) != "" && !t.base.Lookup(name).IsValid() { names[name] = true } target.Text = name target.Quoted = strconv.Quote(name) return nil }