mirror of
https://github.com/danog/liquid.git
synced 2024-11-26 21:14:45 +01:00
Add error line number
This commit is contained in:
parent
69d26a21ad
commit
4472b1594c
@ -5,10 +5,12 @@ import "github.com/osteele/liquid/expression"
|
||||
// A Config holds configuration information for parsing and rendering.
|
||||
type Config struct {
|
||||
expression.Config
|
||||
Grammar Grammar
|
||||
Grammar Grammar
|
||||
Filename string
|
||||
LineNo int
|
||||
}
|
||||
|
||||
// NewConfig creates a parser Config.
|
||||
func NewConfig() Config {
|
||||
return Config{}
|
||||
return Config{LineNo: 1}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func parseErrorf(format string, a ...interface{}) ParseError {
|
||||
|
||||
// Parse parses a source template. It returns an AST root, that can be compiled and evaluated.
|
||||
func (c Config) Parse(source string) (ASTNode, error) {
|
||||
tokens := Scan(source, "")
|
||||
tokens := Scan(source, c.Filename, c.LineNo)
|
||||
return c.parseTokens(tokens)
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,12 @@ import (
|
||||
var tokenMatcher = regexp.MustCompile(`{{\s*(.+?)\s*}}|{%\s*(\w+)(?:\s+((?:[^%]|%[^}])+?))?\s*%}`)
|
||||
|
||||
// Scan breaks a string into a sequence of Tokens.
|
||||
func Scan(data string, pathname string) []Token {
|
||||
func Scan(data string, pathname string, firstLine int) []Token {
|
||||
// TODO error on unterminated {{ and {%
|
||||
// TODO probably an error when a tag contains a {{ or {%, at least outside of a string
|
||||
var (
|
||||
p, pe = 0, len(data)
|
||||
si = SourceInfo{pathname, 1}
|
||||
si = SourceInfo{pathname, firstLine}
|
||||
out = make([]Token, 0)
|
||||
)
|
||||
for _, m := range tokenMatcher.FindAllStringSubmatchIndex(data, -1) {
|
||||
|
@ -22,45 +22,46 @@ var scannerCountTests = []struct {
|
||||
{`{{ expr arg }}{{ expr arg }}`, 2},
|
||||
}
|
||||
|
||||
func TestChunkScanner(t *testing.T) {
|
||||
tokens := Scan("12", "")
|
||||
func TestScan(t *testing.T) {
|
||||
scan := func(src string) []Token { return Scan(src, "", 1) }
|
||||
tokens := scan("12")
|
||||
require.NotNil(t, tokens)
|
||||
require.Len(t, tokens, 1)
|
||||
require.Equal(t, TextTokenType, tokens[0].Type)
|
||||
require.Equal(t, "12", tokens[0].Source)
|
||||
|
||||
tokens = Scan("{{obj}}", "")
|
||||
tokens = scan("{{obj}}")
|
||||
require.NotNil(t, tokens)
|
||||
require.Len(t, tokens, 1)
|
||||
require.Equal(t, ObjTokenType, tokens[0].Type)
|
||||
require.Equal(t, "obj", tokens[0].Args)
|
||||
|
||||
tokens = Scan("{{ obj }}", "")
|
||||
tokens = scan("{{ obj }}")
|
||||
require.NotNil(t, tokens)
|
||||
require.Len(t, tokens, 1)
|
||||
require.Equal(t, ObjTokenType, tokens[0].Type)
|
||||
require.Equal(t, "obj", tokens[0].Args)
|
||||
|
||||
tokens = Scan("{%tag args%}", "")
|
||||
tokens = scan("{%tag args%}")
|
||||
require.NotNil(t, tokens)
|
||||
require.Len(t, tokens, 1)
|
||||
require.Equal(t, TagTokenType, tokens[0].Type)
|
||||
require.Equal(t, "tag", tokens[0].Name)
|
||||
require.Equal(t, "args", tokens[0].Args)
|
||||
|
||||
tokens = Scan("{% tag args %}", "")
|
||||
tokens = scan("{% tag args %}")
|
||||
require.NotNil(t, tokens)
|
||||
require.Len(t, tokens, 1)
|
||||
require.Equal(t, TagTokenType, tokens[0].Type)
|
||||
require.Equal(t, "tag", tokens[0].Name)
|
||||
require.Equal(t, "args", tokens[0].Args)
|
||||
|
||||
tokens = Scan("pre{% tag args %}mid{{ object }}post", "")
|
||||
tokens = scan("pre{% tag args %}mid{{ object }}post")
|
||||
require.Equal(t, `[TextTokenType{"pre"} TagTokenType{Tag:"tag", Args:"args"} TextTokenType{"mid"} ObjTokenType{"object"} TextTokenType{"post"}]`, fmt.Sprint(tokens))
|
||||
|
||||
for i, test := range scannerCountTests {
|
||||
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
|
||||
tokens := Scan(test.in, "")
|
||||
tokens := scan(test.in)
|
||||
require.Len(t, tokens, test.len)
|
||||
})
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
// Config holds configuration information for parsing and rendering.
|
||||
type Config struct {
|
||||
parser.Config
|
||||
Filename string
|
||||
tags map[string]TagCompiler
|
||||
blockDefs map[string]*blockSyntax
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ import (
|
||||
)
|
||||
|
||||
// nodeContext provides the evaluation context for rendering the AST.
|
||||
//
|
||||
// This type has a clumsy name so that render.Context, in the public API, can
|
||||
// have a clean name that doesn't stutter.
|
||||
type nodeContext struct {
|
||||
bindings map[string]interface{}
|
||||
config Config
|
||||
@ -18,7 +21,6 @@ func newNodeContext(scope map[string]interface{}, c Config) nodeContext {
|
||||
for k, v := range scope {
|
||||
vars[k] = v
|
||||
}
|
||||
// fmt.Println("new", c.Config)
|
||||
return nodeContext{vars, c}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ type Template interface {
|
||||
// RenderString is a convenience wrapper for Render, that has string input and output.
|
||||
RenderString(Bindings) (string, error)
|
||||
SetSourcePath(string)
|
||||
SetSourceLocation(string, int)
|
||||
}
|
||||
|
||||
type template struct {
|
||||
@ -42,3 +43,8 @@ func (t *template) RenderString(b Bindings) (string, error) {
|
||||
func (t *template) SetSourcePath(filename string) {
|
||||
t.config.Filename = filename
|
||||
}
|
||||
|
||||
func (t *template) SetSourceLocation(filename string, lineNo int) {
|
||||
t.config.Filename = filename
|
||||
t.config.LineNo = lineNo
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user