Skip to content

Commit

Permalink
Var
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Dec 9, 2019
1 parent d7566c8 commit dec08f7
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 35 deletions.
19 changes: 17 additions & 2 deletions common/hreflect/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func NewInvoker(funcs func(name string) interface{}) *Invoker {

func (i *Invoker) InvokeFunction(path []string, args ...interface{}) (interface{}, error) {
name := path[0]
f := i.funcs(name)
f := i.funcs(name) // TODO1 store them as reflect.Value
if f == nil {
return err("function with name %s not found", name)
}
Expand All @@ -53,11 +53,22 @@ func (i *Invoker) InvokeFunction(path []string, args ...interface{}) (interface{
return nil, nil
}

if result.Type() == reflectValueType {
result = result.Interface().(reflect.Value)
}

return result.Interface(), nil
}

func (i *Invoker) InvokeMethod(receiver interface{}, path []string, args ...interface{}) (interface{}, error) {
v := reflect.ValueOf(receiver)
var v reflect.Value

if rv, ok := receiver.(reflect.Value); ok {
v = rv
} else {
v = reflect.ValueOf(receiver)
}

result, err := i.invoke(v, path, args)
if err != nil {
return nil, err
Expand All @@ -67,6 +78,10 @@ func (i *Invoker) InvokeMethod(receiver interface{}, path []string, args ...inte
return nil, nil
}

if result.Type() == reflectValueType {
result = result.Interface().(reflect.Value)
}

return result.Interface(), nil

}
Expand Down
18 changes: 12 additions & 6 deletions hugolib/content_render_hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ package hugolib

import "testing"

func TestTempT(t *testing.T) {
func TestAbba(t *testing.T) {
config := `
baseURL="https://example.org"
defaultContentLanguageInSubDir=true
[params]
[params.COLORS]
BLUE="nice"
Yellow="bright"
[languages]
[languages.en]
Expand All @@ -34,18 +35,23 @@ weight=2
b.WithTemplates(
"index.html", `
{{ $params := .Site.Params }}
{{ $colors := $params.Colors }}
{{ $blue := $colors.Blue }}
Len: {{ len $params.Colors }}
Upper: {{ $params.Colors.Blue | upper }}
{{ range $k, $v := $params.Colors }}
Range: {{ $k }}: {{ $v }}
{{ end }}
Params: {{ $colors }}
Site en: {{ site.Language.Lang }}|{{ .Site.Language.Lang }}|Blue: {{ $blue }}`,
Site en: {{ site.Language.Lang }}|{{ .Site.Language.Lang }}|Blue: `,
"index.nn.html", `Site nn: {{ site.Language.Lang }}|{{ .Site.Language.Lang }}`)
b.WithContent("p1.md", "asdf")
b.Build(BuildCfg{})

b.AssertFileContent("public/nn/index.html", "Site nn: nn|nn")
b.AssertFileContent("public/en/index.html", "Blue: nice")
b.AssertFileContent("public/en/index.html", "Len: 2sds")

}
func TestRenderHooks(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions tpl/internal/go_text_template_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ get to the internal template funcs in Go.
The file below is imported as-is from
https://github.com/golang/go/blob/94e9a5e19b831504eca2b7202b78d1a48c4be547/src/text/template/funcs.go
And removed one func, and exported the builtinFuncs var.
And removed one func, and exported the Builtins var.
*/

Expand All @@ -41,7 +41,7 @@ And removed one func, and exported the builtinFuncs var.
// type can return interface{} or reflect.Value.
type FuncMap map[string]interface{}

var builtins = FuncMap{
var Builtins = FuncMap{
"and": and,
"call": call,
"html": HTMLEscaper,
Expand All @@ -65,7 +65,7 @@ var builtins = FuncMap{
"ne": ne, // !=
}

var BuiltinFuncs = createValueFuncs(builtins)
var builtinFuncs = createValueFuncs(Builtins)

// createValueFuncs turns a FuncMap into a map[string]reflect.Value
func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
Expand Down
80 changes: 56 additions & 24 deletions tpl/tplimpl/template_ast_transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package tplimpl

import (
"fmt"
"html/template"
"regexp"
"strings"
Expand Down Expand Up @@ -186,11 +185,13 @@ func applyTemplateTransformers(
const (
partialReturnWrapperTempl = `{{ $_hugo_dot := $ }}{{ $ := .Arg }}{{ with .Arg }}{{ $_hugo_dot.Set ("PLACEHOLDER") }}{{ end }}`
dotContextWrapperTempl = `{{ invokeDot . "NAME" "ARGS" }}`
pipeWrapperTempl = `{{ ("ARGS") }}`
)

var (
partialReturnWrapper *parse.ListNode
dotContextWrapper *parse.CommandNode
pipeWrapper *parse.PipeNode
)

func init() {
Expand All @@ -210,6 +211,12 @@ func init() {
}
action := templ.Tree.Root.Nodes[0].(*parse.ActionNode)
dotContextWrapper = action.Pipe.Cmds[0]

templ, err = tt.Parse(pipeWrapperTempl)
if err != nil {
panic(err)
}
pipeWrapper = templ.Tree.Root.Nodes[0].(*parse.ActionNode).Pipe
}

func (c *templateContext) wrapInPartialReturnWrapper(n *parse.ListNode) *parse.ListNode {
Expand All @@ -228,58 +235,80 @@ func (c *templateContext) wrapInPartialReturnWrapper(n *parse.ListNode) *parse.L
}

var (
ignoreFuncsRe = regexp.MustCompile("invokeDot|html")
goBuiltInFuncs = regexp.MustCompile("len")
ignoreFuncsRe = regexp.MustCompile("invokeDot")
)

func (c *templateContext) wrapInPipe(n parse.Node) *parse.PipeNode {
pipe := pipeWrapper.Copy().(*parse.PipeNode)
pipe.Cmds[0].Args = []parse.Node{n}
return pipe
}

func (c *templateContext) wrapDotIfNeeded(n parse.Node) parse.Node {
switch node := n.(type) {
case *parse.ChainNode:
if pipe, ok := node.Node.(*parse.PipeNode); ok {
for _, cmd := range pipe.Cmds {
c.wrapDot(cmd)
}
}
case *parse.IdentifierNode:
// A function.
if ignoreFuncsRe.MatchString(node.Ident) {
return n
}

case *parse.PipeNode:
for _, cmd := range node.Cmds {
c.wrapDot(cmd)
}
return n
case *parse.VariableNode:
if len(node.Ident) < 2 {
return n
}
return c.wrapInPipe(n)
default:
}

return n
}

// TODO1 somehow inject (wrap dot?) a receiver object that can be
// set in .Execute. To avoid global funcs.
func (c *templateContext) wrapDot(d bool, cmd *parse.CommandNode) {
func (c *templateContext) wrapDot(cmd *parse.CommandNode) {
var dotNode parse.Node
doDebug := d || strings.Contains(cmd.String(), "blue")
var fields string

firstWord := cmd.Args[0]

c.wrapDotIfNeeded(firstWord)

switch a := firstWord.(type) {
case *parse.FieldNode:
fields = a.String()
//return s.evalFieldNode(dot, n, cmd.Args, final)
case *parse.ChainNode:
if pipe, ok := a.Node.(*parse.PipeNode); ok {
for _, cmd := range pipe.Cmds {
c.wrapDot(doDebug, cmd)
}

}
return // TODO1
// fields = a.String()
//return s.evalChainNode(dot, n, cmd.Args, final)
case *parse.IdentifierNode:
// Must be a function.
if ignoreFuncsRe.MatchString(a.Ident) {
return
}
if goBuiltInFuncs.MatchString(a.Ident) {
fmt.Println(a.Ident, "==>", cmd.Args[1:])
return
}
fields = a.Ident
case *parse.PipeNode:
for _, cmd := range a.Cmds {
c.wrapDot(doDebug, cmd)
}
//s.notAFunction(cmd.Args, final)
//return s.evalPipeline(dot, n)
return
case *parse.VariableNode:
if len(a.Ident) < 2 {
return
}

// $x.Field has $x as the first ident, Field as the second.
fields = "." + strings.Join(a.Ident[1:], ".")
a.Ident = a.Ident[:1]
dotNode = a

default:
//fmt.Printf("UNKNOWN: %T\n", firstWord)
return
}

Expand All @@ -295,6 +324,9 @@ func (c *templateContext) wrapDot(d bool, cmd *parse.CommandNode) {

args := wrapper.Args[:3]
if len(cmd.Args) > 1 {
for i, n := range cmd.Args[1:] {
cmd.Args[i+1] = c.wrapDotIfNeeded(n)
}
args = append(args, cmd.Args[1:]...)
}

Expand Down Expand Up @@ -372,7 +404,7 @@ func (c *templateContext) applyTransformations(n parse.Node) (bool, error) {
}

case *parse.CommandNode:
c.wrapDot(false, x)
c.wrapDot(x)
if false || len(x.Args) > 1 {
first := x.Args[0]
var id string
Expand Down
6 changes: 6 additions & 0 deletions tpl/tplimpl/template_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ func (*TemplateProvider) CreateFuncMap(in interface{}) map[string]interface{} {
}

}
}

// Add the Go internal funcs that's not overloaded above.
for k, v := range internal.Builtins {
if _, exists := funcMap[k]; !exists {
funcMap[k] = v
}
}

return funcMap
Expand Down

0 comments on commit dec08f7

Please sign in to comment.