Skip to content

Commit

Permalink
Split package render->parser
Browse files Browse the repository at this point in the history
  • Loading branch information
osteele committed Jul 7, 2017
1 parent 18e2540 commit 903acb8
Show file tree
Hide file tree
Showing 26 changed files with 174 additions and 126 deletions.
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# Go Liquid Template Parser

[![Build Status](https://travis-ci.org/osteele/liquid.svg?branch=master)](https://travis-ci.org/osteele/liquid)
[![Go Report Card](https://goreportcard.com/badge/github.com/osteele/liquid)](https://goreportcard.com/report/github.com/osteele/liquid)
[![GoDoc](https://godoc.org/github.com/osteele/liquid?status.svg)](http://godoc.org/github.com/osteele/liquid)
[![Coverage Status](https://coveralls.io/repos/github/osteele/liquid/badge.svg?branch=master)](https://coveralls.io/github/osteele/liquid?branch=master)
[![][travis-svg]][travis-url] [![][coveralls-svg]][coveralls-url] [![][go-report-card-svg]][go-report-card-url] [![][godoc-svg]][godoc-url] [![][license-svg]][license-url]

> “Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.” – Philip Greenspun
Expand Down Expand Up @@ -127,3 +124,18 @@ The [original Liquid engine](https://shopify.github.io/liquid), of course, for t
## License

MIT License

[coveralls-url]: https://coveralls.io/r/osteele/liquid?branch=master
[coveralls-svg]: https://img.shields.io/coveralls/osteele/liquid.svg?branch=master

[godoc-url]: https://godoc.org/github.com/osteele/liquid
[godoc-svg]: https://godoc.org/github.com/osteele/liquid?status.svg

[license-url]: https://github.com/osteele/liquid/blob/master/LICENSE
[license-svg]: https://img.shields.io/badge/license-MIT-blue.svg

[go-report-card-url]: https://goreportcard.com/report/github.com/osteele/liquid
[go-report-card-svg]: https://goreportcard.com/badge/github.com/osteele/liquid

[travis-url]: https://travis-ci.org/osteele/liquid
[travis-svg]: https://img.shields.io/travis/osteele/liquid.svg?branch=master
4 changes: 2 additions & 2 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type engine struct{ settings render.Config }
// NewEngine returns a new template engine.
func NewEngine() Engine {
e := engine{render.NewConfig()}
filters.AddStandardFilters(e.settings.Config)
filters.AddStandardFilters(&e.settings.Config.Config)
tags.AddStandardTags(e.settings)
return e
}
Expand Down Expand Up @@ -57,7 +57,7 @@ func (e engine) ParseTemplate(text []byte) (Template, error) {
if err != nil {
return nil, err
}
return &template{ast, e.settings}, nil
return &template{ast, &e.settings}, nil
}

// ParseAndRender is in the Engine interface.
Expand Down
2 changes: 0 additions & 2 deletions evaluator/call.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package evaluator

import (
"fmt"
"reflect"
)

Expand All @@ -15,7 +14,6 @@ func Call(fn reflect.Value, args []interface{}) (interface{}, error) {
if len(outs) > 1 && outs[1].Interface() != nil {
switch e := outs[1].Interface().(type) {
case error:
fmt.Println("error")
return nil, e
default:
panic(e)
Expand Down
3 changes: 3 additions & 0 deletions expression/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ func (d *filterDictionary) AddFilter(name string, fn interface{}) {
// case rf.Type().Out(1).Implements(…):
// panic(typeError("a filter's second output must be type error"))
}
if len(d.filters) == 0 {
d.filters = make(map[string]interface{})
}
d.filters[name] = fn
}

Expand Down
4 changes: 3 additions & 1 deletion expression/filters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

func TestContext_runFilter(t *testing.T) {
cfg := NewConfig()
ctx := NewContext(map[string]interface{}{"x": 10}, cfg)
constant := func(value interface{}) valueFn {
return func(Context) interface{} { return value }
}
Expand All @@ -19,13 +18,15 @@ func TestContext_runFilter(t *testing.T) {
cfg.AddFilter("f1", func(s string) string {
return "<" + s + ">"
})
ctx := NewContext(map[string]interface{}{"x": 10}, cfg)
out := ctx.ApplyFilter("f1", receiver, []valueFn{})
require.Equal(t, "<self>", out)

// filter argument
cfg.AddFilter("with_arg", func(a, b string) string {
return fmt.Sprintf("(%s, %s)", a, b)
})
ctx = NewContext(map[string]interface{}{"x": 10}, cfg)
out = ctx.ApplyFilter("with_arg", receiver, []valueFn{constant("arg")})
require.Equal(t, "(self, arg)", out)

Expand All @@ -43,6 +44,7 @@ func TestContext_runFilter(t *testing.T) {
}
return fmt.Sprintf("(%v, %v)", a, value), nil
})
ctx = NewContext(map[string]interface{}{"x": 10}, cfg)
out = ctx.ApplyFilter("closure", receiver, []valueFn{constant("x |add: y")})
require.Equal(t, "(self, 11)", out)
}
2 changes: 1 addition & 1 deletion filters/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ var filterTestBindings = map[string]interface{}{

func TestFilters(t *testing.T) {
settings := expression.NewConfig()
AddStandardFilters(settings)
AddStandardFilters(&settings)
context := expression.NewContext(filterTestBindings, settings)

for i, test := range filterTests {
Expand Down
84 changes: 42 additions & 42 deletions filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ import (
)

// AddStandardFilters defines the standard Liquid filters.
func AddStandardFilters(settings expression.Config) { // nolint: gocyclo
func AddStandardFilters(cfg *expression.Config) { // nolint: gocyclo
// values
settings.AddFilter("default", func(value, defaultValue interface{}) interface{} {
cfg.AddFilter("default", func(value, defaultValue interface{}) interface{} {
if value == nil || value == false || evaluator.IsEmpty(value) {
value = defaultValue
}
return value
})

// dates
settings.AddFilter("date", func(t time.Time, format interface{}) (string, error) {
cfg.AddFilter("date", func(t time.Time, format interface{}) (string, error) {
form, ok := format.(string)
if !ok {
form = "%a, %b %d, %y"
Expand All @@ -40,7 +40,7 @@ func AddStandardFilters(settings expression.Config) { // nolint: gocyclo
})

// arrays
settings.AddFilter("compact", func(array []interface{}) interface{} {
cfg.AddFilter("compact", func(array []interface{}) interface{} {
out := []interface{}{}
for _, item := range array {
if item != nil {
Expand All @@ -49,46 +49,46 @@ func AddStandardFilters(settings expression.Config) { // nolint: gocyclo
}
return out
})
settings.AddFilter("join", joinFilter)
settings.AddFilter("map", func(array []map[string]interface{}, key string) interface{} {
cfg.AddFilter("join", joinFilter)
cfg.AddFilter("map", func(array []map[string]interface{}, key string) interface{} {
out := []interface{}{}
for _, obj := range array {
out = append(out, obj[key])
}
return out
})
settings.AddFilter("reverse", reverseFilter)
settings.AddFilter("sort", sortFilter)
cfg.AddFilter("reverse", reverseFilter)
cfg.AddFilter("sort", sortFilter)
// https://shopify.github.io/liquid/ does not demonstrate first and last as filters,
// but https://help.shopify.com/themes/liquid/filters/array-filters does
settings.AddFilter("first", func(array []interface{}) interface{} {
cfg.AddFilter("first", func(array []interface{}) interface{} {
if len(array) == 0 {
return nil
}
return array[0]
})
settings.AddFilter("last", func(array []interface{}) interface{} {
cfg.AddFilter("last", func(array []interface{}) interface{} {
if len(array) == 0 {
return nil
}
return array[len(array)-1]
})

// numbers
settings.AddFilter("abs", math.Abs)
settings.AddFilter("ceil", math.Ceil)
settings.AddFilter("floor", math.Floor)
settings.AddFilter("modulo", math.Mod)
settings.AddFilter("minus", func(a, b float64) float64 {
cfg.AddFilter("abs", math.Abs)
cfg.AddFilter("ceil", math.Ceil)
cfg.AddFilter("floor", math.Floor)
cfg.AddFilter("modulo", math.Mod)
cfg.AddFilter("minus", func(a, b float64) float64 {
return a - b
})
settings.AddFilter("plus", func(a, b float64) float64 {
cfg.AddFilter("plus", func(a, b float64) float64 {
return a + b
})
settings.AddFilter("times", func(a, b float64) float64 {
cfg.AddFilter("times", func(a, b float64) float64 {
return a * b
})
settings.AddFilter("divided_by", func(a float64, b interface{}) interface{} {
cfg.AddFilter("divided_by", func(a float64, b interface{}) interface{} {
switch bt := b.(type) {
case int, int16, int32, int64:
return int(a) / bt.(int)
Expand All @@ -98,7 +98,7 @@ func AddStandardFilters(settings expression.Config) { // nolint: gocyclo
return nil
}
})
settings.AddFilter("round", func(n float64, places interface{}) float64 {
cfg.AddFilter("round", func(n float64, places interface{}) float64 {
pl, ok := places.(int)
if !ok {
pl = 0
Expand All @@ -108,45 +108,45 @@ func AddStandardFilters(settings expression.Config) { // nolint: gocyclo
})

// sequences
settings.AddFilter("size", evaluator.Length)
cfg.AddFilter("size", evaluator.Length)

// strings
settings.AddFilter("append", func(s, suffix string) string {
cfg.AddFilter("append", func(s, suffix string) string {
return s + suffix
})
settings.AddFilter("capitalize", func(s, suffix string) string {
cfg.AddFilter("capitalize", func(s, suffix string) string {
if len(s) < 1 {
return s
}
return strings.ToUpper(s[:1]) + s[1:]
})
settings.AddFilter("downcase", func(s, suffix string) string {
cfg.AddFilter("downcase", func(s, suffix string) string {
return strings.ToLower(s)
})
settings.AddFilter("escape", html.EscapeString)
settings.AddFilter("escape_once", func(s, suffix string) string {
cfg.AddFilter("escape", html.EscapeString)
cfg.AddFilter("escape_once", func(s, suffix string) string {
return html.EscapeString(html.UnescapeString(s))
})
// TODO test case for this
settings.AddFilter("newline_to_br", func(s string) string {
cfg.AddFilter("newline_to_br", func(s string) string {
return strings.Replace(s, "\n", "<br />", -1)
})
settings.AddFilter("prepend", func(s, prefix string) string {
cfg.AddFilter("prepend", func(s, prefix string) string {
return prefix + s
})
settings.AddFilter("remove", func(s, old string) string {
cfg.AddFilter("remove", func(s, old string) string {
return strings.Replace(s, old, "", -1)
})
settings.AddFilter("remove_first", func(s, old string) string {
cfg.AddFilter("remove_first", func(s, old string) string {
return strings.Replace(s, old, "", 1)
})
settings.AddFilter("replace", func(s, old, new string) string {
cfg.AddFilter("replace", func(s, old, new string) string {
return strings.Replace(s, old, new, -1)
})
settings.AddFilter("replace_first", func(s, old, new string) string {
cfg.AddFilter("replace_first", func(s, old, new string) string {
return strings.Replace(s, old, new, 1)
})
settings.AddFilter("slice", func(s string, start int, length interface{}) string {
cfg.AddFilter("slice", func(s string, start int, length interface{}) string {
// runes aren't bytes; don't use slice
n, ok := length.(int)
if !ok {
Expand All @@ -158,23 +158,23 @@ func AddStandardFilters(settings expression.Config) { // nolint: gocyclo
p := regexp.MustCompile(fmt.Sprintf(`^.{%d}(.{0,%d}).*$`, start, n))
return p.ReplaceAllString(s, "$1")
})
settings.AddFilter("split", splitFilter)
settings.AddFilter("strip_html", func(s string) string {
cfg.AddFilter("split", splitFilter)
cfg.AddFilter("strip_html", func(s string) string {
// TODO this probably isn't sufficient
return regexp.MustCompile(`<.*?>`).ReplaceAllString(s, "")
})
// TODO test case for this
settings.AddFilter("strip_newlines", func(s string) string {
cfg.AddFilter("strip_newlines", func(s string) string {
return strings.Replace(s, "\n", "", -1)
})
settings.AddFilter("strip", strings.TrimSpace)
settings.AddFilter("lstrip", func(s string) string {
cfg.AddFilter("strip", strings.TrimSpace)
cfg.AddFilter("lstrip", func(s string) string {
return strings.TrimLeftFunc(s, unicode.IsSpace)
})
settings.AddFilter("rstrip", func(s string) string {
cfg.AddFilter("rstrip", func(s string) string {
return strings.TrimRightFunc(s, unicode.IsSpace)
})
settings.AddFilter("truncate", func(s string, n int, ellipsis interface{}) string {
cfg.AddFilter("truncate", func(s string, n int, ellipsis interface{}) string {
// runes aren't bytes; don't use slice
el, ok := ellipsis.(string)
if !ok {
Expand All @@ -183,20 +183,20 @@ func AddStandardFilters(settings expression.Config) { // nolint: gocyclo
p := regexp.MustCompile(fmt.Sprintf(`^(.{%d})..{%d,}`, n-len(el), len(el)))
return p.ReplaceAllString(s, `$1`+el)
})
settings.AddFilter("upcase", func(s, suffix string) string {
cfg.AddFilter("upcase", func(s, suffix string) string {
return strings.ToUpper(s)
})

// debugging extensions
// inspect is from Jekyll
settings.AddFilter("inspect", func(value interface{}) string {
cfg.AddFilter("inspect", func(value interface{}) string {
s, err := json.Marshal(value)
if err != nil {
return fmt.Sprintf("%#v", value)
}
return string(s)
})
settings.AddFilter("type", func(value interface{}) string {
cfg.AddFilter("type", func(value interface{}) string {
return reflect.TypeOf(value).String()
})
}
Expand Down
7 changes: 4 additions & 3 deletions liquid.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ Package liquid is a pure Go implementation of Shopify Liquid templates, for use
See the project README https://github.com/osteele/liquid for additional information and implementation status.
Note that the API for this package is not frozen. It is *especiallY* likely that subpackage APIs will
Note that the API for this package is not frozen. It is *especially* likely that subpackage APIs will
change drastically. Don't use anything except from a subpackage except render.Context.
*/
package liquid

import (
"github.com/osteele/liquid/evaluator"
"github.com/osteele/liquid/expression"
"github.com/osteele/liquid/parser"
"github.com/osteele/liquid/render"
)

Expand Down Expand Up @@ -71,9 +72,9 @@ func IsTemplateError(err error) bool {
return true
case expression.ParseError:
return true
case render.CompilationError:
case parser.ParseError:
return true
case render.ParseError:
case render.CompilationError:
return true
case render.Error:
return true
Expand Down
2 changes: 0 additions & 2 deletions liquid_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package liquid

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

func TestIsTemplateError(t *testing.T) {
_, err := NewEngine().ParseAndRenderString("{{ syntax error }}", emptyBindings)
fmt.Printf("%T", err)
require.True(t, IsTemplateError(err))
_, err = NewEngine().ParseAndRenderString("{% if %}", emptyBindings)
require.True(t, IsTemplateError(err))
Expand Down
6 changes: 3 additions & 3 deletions render/ast.go → parser/ast.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package render
package parser

import (
"github.com/osteele/liquid/expression"
Expand All @@ -17,7 +17,7 @@ type ASTBlock struct {

// ASTRaw holds the text between the start and end of a raw tag.
type ASTRaw struct {
slices []string
Slices []string
}

// ASTTag is a tag.
Expand All @@ -33,7 +33,7 @@ type ASTText struct {
// ASTObject is an {{ object }} object.
type ASTObject struct {
Chunk
expr expression.Expression
Expr expression.Expression
}

// ASTSeq is a sequence of nodes.
Expand Down
Loading

0 comments on commit 903acb8

Please sign in to comment.