Go Template Toolkit, Сергей Свистунов, Lazada

41
Go template toolkit github.com/go-qbit/template

Transcript of Go Template Toolkit, Сергей Свистунов, Lazada

Page 1: Go Template Toolkit, Сергей Свистунов, Lazada

Go template toolkit

github.com/go-qbit/template

Page 2: Go Template Toolkit, Сергей Свистунов, Lazada
Page 3: Go Template Toolkit, Сергей Свистунов, Lazada

Пример шаблона

[% WRAPPER page(caption string) %]

<!DOCTYPE html>

<html>

<head>

<title>[% caption | html %]</title>

</head>

<body>

[% CONTENT %]

</body>

</html>

[% END %]

Page 4: Go Template Toolkit, Сергей Свистунов, Lazada

[% TEMPLATE Test(header string, users []User) USE WRAPPER page(header) %]

<h1>[% header | html %]</h1>

<hr>

[% FOR user IN users %]

<p>

[% PROCESS UserName(user) %]:

[% user.Age %] ([% IF user.IsMan %]Man[% ELSE %]Woman[% END %])

</p>

[% END %]

[% END %]

[% TEMPLATE UserName(user User) %]

[% user.Name | html +%] [% user.Lastname | html %]

[% END %]

Page 5: Go Template Toolkit, Сергей Свистунов, Lazada

package templates

import (

"context"

"github.com/go-qbit/template/filter"

"io"

)

var (

s1ef997d5dd161505e230e6dbbfcaaa48 = []byte{0x3C, 0x21, 0x44, 0x4F, 0x43, 0x54, 0x59, 0x50, 0x45,

sf3847a1d6d502a855dd746c6ccc81aae = []byte{0x3C, 0x2F, 0x62, 0x6F, 0x64, 0x79, 0x3E, 0xA, 0x3C, 0x2F

s89efc060410cc373c7da167f1451d1f5 = []byte{0x3C, 0x2F, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x3E, 0xA, 0x20

)

func Wrapperpage(ctx context.Context, w io.Writer, tplClbF func(), caption string) {

w.Write(s1ef997d5dd161505e230e6dbbfcaaa48)

io.WriteString(w, filter.Filterhtml(caption))

w.Write(s89efc060410cc373c7da167f1451d1f5)

tplClbF()

w.Write(sf3847a1d6d502a855dd746c6ccc81aae)

}

Page 6: Go Template Toolkit, Сергей Свистунов, Lazada

...

func ProcessTest(ctx context.Context, w io.Writer, header string, users []User) {

Wrapperpage(ctx, w, func() {

w.Write(sedbe56ac7404d66602a627bd637ef2c1)

io.WriteString(w, filter.Filterhtml(header))

w.Write(s0f9baed348e2ded156c71ed2555fb1da)

for _, user := range users {

w.Write(sf715636fb0391a8fdd43473a43b1ea72)

ProcessUserName(ctx, w, user)

w.Write(sf8de4bf55e29f51642893aea2c376d30)

io.WriteString(w, utils.ToString(user.Age))

w.Write(sd2c38510206af487d1a4984a8af4ca49)

if user.IsMan {

w.Write(s9444e86b936ba53fdc56632949cb3038)

} else {

w.Write(s9ed6dfeeb854d5f5e292f977637a729b)

}

w.Write(s9e23173c90723882045e02a5a6c79a9b)

}

}, header)

}

...

Page 7: Go Template Toolkit, Сергей Свистунов, Lazada

Отличия

• Perl TT MACRO -> TEMPLATE / WRAPPER

• Perl TT USE -> IMPORT

• Явная передача данных, нет stash’а

• Нет виртуальных методов для переменных

• Код генерируется до компиляции (ttgen)

Page 8: Go Template Toolkit, Сергей Свистунов, Lazada

Benchmark

BenchmarkGoCoreTemplate-4 3000 5306959 ns/op 1041697 B/op 31035 allocs/op

BenchmarkQBitTemplate-4 30000 407632 ns/op 36096 B/op 4006 allocs/op

Page 9: Go Template Toolkit, Сергей Свистунов, Lazada

Синтаксис

• Директивы должны быть внутри [% %]

• Несколько директив должны разделяться ;

• Пробельные символы вокруг [% %] не печатаются, чтобы их сохранить надо использовать [%+ для сохранения слева и +%]для сохранения справа.

• Поддерживаются фильтры: [% expr | filter1 | filter2 %]

Page 10: Go Template Toolkit, Сергей Свистунов, Lazada

Директивы и операции

• IMPORT

• TEMPLATE

• WRAPPER

• PROCESS

• CONTENT

• IF / ELSE

• FOR … IN slice/map

• FOR ; ; ;

• Сравнение• >, GT

• <, LT

• >=, GE

• <=, LE

• ==, EQ

• Математические• +

• -

• *

• /

• %

• Boolean• &&, AND

• ||, OR

• Унарные• !, NOT

• ++

• --

• Присвоение• :=

• =

Page 11: Go Template Toolkit, Сергей Свистунов, Lazada

Инструменты

Page 12: Go Template Toolkit, Сергей Свистунов, Lazada

Средства разработки анализаторов• ANTLR — генератор парсеров

• Bison — генератор парсеров

• Coco/R — генератор сканера и парсера

• GOLD — парсер

• JavaCC — генератор парсеров для языка Java

• Lemon Parser — генератор парсеров

• Lex — генератор сканеров

• Ragel — генератор встраиваемых парсеров

• Spirit Parser Framework — генератор парсеров

• SYNTAX

• Syntax Definition Formalism

• UltraGram

• VivaCore

• Yacc — генератор парсеров

Page 13: Go Template Toolkit, Сергей Свистунов, Lazada

Средства разработки анализаторов• ANTLR — генератор парсеров

• Bison — генератор парсеров

• Coco/R — генератор сканера и парсера

• GOLD — парсер

• JavaCC — генератор парсеров для языка Java

• Lemon Parser — генератор парсеров

• Lex — генератор сканеров

• Ragel — генератор встраиваемых парсеров

• Spirit Parser Framework — генератор парсеров

• SYNTAX

• Syntax Definition Formalism

• UltraGram

• VivaCore

• Yacc — генератор парсеров

Page 14: Go Template Toolkit, Сергей Свистунов, Lazada

Препроцессинг

Page 15: Go Template Toolkit, Сергей Свистунов, Lazada

[% WRAPPER page(caption string) %]

<!DOCTYPE html>

<html>

<head>

<title>[% caption | html %]</title>

</head>

<body>

[% CONTENT %]

</body>

</html>

[% END %]

Page 16: Go Template Toolkit, Сергей Свистунов, Lazada

[% WRAPPER page(caption string) %]

<!DOCTYPE html>

<html>

<head>

<title>[% caption | html %]</title>

</head>

<body>

[% CONTENT %]

</body>

</html>

[% END %]

Page 17: Go Template Toolkit, Сергей Свистунов, Lazada

WRAPPER page(caption string);

"<!DOCTYPE html>

<html>

<head>

<title>"; caption | html; "</title>

</head>

<body>";

CONTENT;

"</body>

</html>";

END;

Page 18: Go Template Toolkit, Сергей Свистунов, Lazada

Построение ASTgo tool yacc

Page 19: Go Template Toolkit, Сергей Свистунов, Lazada

Грамматика для YACC

%{

package template;

import (

__yyfmt__ "fmt"

)

%}

%token IDENTIFIER STRING NUMBER FOR IN IF ELSE END VARS TEMPLATE IMPORT WRAPPER USE

%token CONTENT_MARKER PROCESS EQ NE GE LE OR AND NOT ASSIGNMENT INC DEC

Page 20: Go Template Toolkit, Сергей Свистунов, Lazada

%union {

string string

iAstNode iAstNode

astList *astList

...

}

%type <string> STRING IDENTIFIER NUMBER var_type var_value

%type <iAstNode> top file header macros_stmt var body_stmt expr loop

%type <astList> imports import_list macroses param_list var_list body

...

%left '+' '-' '*' '/' '%' '>' '<' EQ NE GE LE OR AND

%left NOT

%%

Page 21: Go Template Toolkit, Сергей Свистунов, Lazada

top: file { yylex.(*exprLex).result = $$ }

file: header macroses { $$ = &astFile{$1, $2} }

...

macroses: macros_stmt { $$ = &astList{[]iAstNode{$1}} }

| macroses ';' macros_stmt { $$.Add($3) }

macros_stmt: { $$ = nil }

| TEMPLATE IDENTIFIER '(' var_list ')' use_wrapper ';' body ';' END

{ $$ = &astTemplate{$2, $4, $6, $8} }

| WRAPPER IDENTIFIER '(' var_list ')' ';' body ';' END

{ $$ = &astWrapper{$2, $4, $7} }

| STRING { $$ = nil }

...

Page 22: Go Template Toolkit, Сергей Свистунов, Lazada

expr: expr '>' expr { $$ = &astExpr{">", $1, $3} }

| expr '<' expr { $$ = &astExpr{"<", $1, $3} }

| expr '+' expr { $$ = &astExpr{"+", $1, $3} }

| expr '-' expr { $$ = &astExpr{"-", $1, $3} }

| expr '*' expr { $$ = &astExpr{"*", $1, $3} }

...

| NOT expr { $$ = &astExpr{"!", nil, $2} }

| '(' expr ')' { $$ = &astParenthesis{$2} }

| var_value INC { $$ = &astExpr{"++", &astValue{$1}, nil} }

| var_value DEC { $$ = &astExpr{"--", &astValue{$1}, nil} }

| var_value '(' param_list ')'{ $$ = &astFunc{$1, $3} }

| var_value { $$ = &astValue{$1} }

| STRING { $$ = &astString{$1} }

| NUMBER { $$ = &astValue{$1} }

...

%%

Page 23: Go Template Toolkit, Сергей Свистунов, Lazada

y.go...

type yySymType struct {

yys int

string string

iAstNode iAstNode

...

}

const IDENTIFIER = 57346

const STRING = 57347

...

type yyLexer interface {

Lex(lval *yySymType) int

Error(s string)

}

...

func yyParse(yylex yyLexer) int {

return yyNewParser().Parse(yylex)

}

Page 24: Go Template Toolkit, Сергей Свистунов, Lazada

Lexer

Простые токеныvar simpleTokens = []simpleToken{

{"TEMPLATE", TEMPLATE},

{"WRAPPER", WRAPPER},

...

{"&&", AND},

{"||", OR},

{"++", INC},

{"--", DEC},

{"!", NOT},

}

Регулярные выраженияvar reTokens = []reToken{

{`^(?:\")(?:[^\\\"]*(?:\\.[^\\\"]*)*)(?:\")`, STRING},

{`^-?\d+(?:[.,]\d+)?`, NUMBER},

{`^[a-zA-Z_][a-zA-Z0-9\_]*`, IDENTIFIER},

}

Page 25: Go Template Toolkit, Сергей Свистунов, Lazada

Начало

Начинается с простого токена

Начинается с RegExp’а

Длина текста равна 0

Пробельный символ

Нет

Нет

Нет

Вернуть 0

Сдвинуть текст на 1 символ

Да

Да Вернуть код символа

Нет

• Заполнить yyval• Сдвинуть текст на

длину токена

Да

Да

Вернуть код токена

Page 26: Go Template Toolkit, Сергей Свистунов, Lazada

AST

Page 27: Go Template Toolkit, Сергей Свистунов, Lazada

iAstNode

type iAstNode interface {

GetImports() []string

GetStrings() []string

WriteGo(io.Writer, *GenGoOpts)

}

Page 28: Go Template Toolkit, Сергей Свистунов, Lazada

astFunctype astFunc struct {

name string

params *astList

}

func (n *astFunc) GetImports() []string {

if n.params != nil {

return n.params.GetImports()

}

return []string{}

}

func (n *astFunc) GetStrings() []string {

if n.params != nil {

return n.params.GetStrings()

}

return []string{}

}

Page 29: Go Template Toolkit, Сергей Свистунов, Lazada

astFunc

func (n *astFunc) WriteGo(w io.Writer, opts *GenGoOpts) {

io.WriteString(w, n.name+"(")

if n.params != nil {

for i, param := range n.params.children {

if i > 0 {

io.WriteString(w, ", ")

}

param.WriteGo(w, opts)

}

}

io.WriteString(w, ")")

}

Page 30: Go Template Toolkit, Сергей Свистунов, Lazada

ОптимизацииBenchmark & pprof

Page 31: Go Template Toolkit, Сергей Свистунов, Lazada

Версия №1 func ProcessTest(ctx context.Context, w io.Writer, header string, users []User) {

Wrapperpage(ctx, w, func() {

io.WriteString(w, "<h1>")

io.WriteString(w, filter.Filterhtml(header))

io.WriteString(w, "</h1>\n <hr>")

for _, user := range users {

io.WriteString(w, "<p>")

ProcessUserName(ctx, w, user)

io.WriteString(w, ":")

io.WriteString(w, fmt.Sprint(user.Age))

io.WriteString(w, "(")

if user.IsMan {

io.WriteString(w, "Man")

} else {

io.WriteString(w, "Woman")

}

io.WriteString(w, ")\n </p>")

}

}, header)

} 728133 ns/op 48149 B/op 6008 allocs/op

Page 32: Go Template Toolkit, Сергей Свистунов, Lazada

CPU profile

Page 33: Go Template Toolkit, Сергей Свистунов, Lazada

CPU profile

Page 34: Go Template Toolkit, Сергей Свистунов, Lazada

Версия №2func ProcessTest(ctx context.Context, w io.Writer, header string, users []User) {

Wrapperpage(ctx, w, func() {

io.WriteString(w, "<h1>")

io.WriteString(w, filter.Filterhtml(header))

io.WriteString(w, "</h1>\n <hr>")

for _, user := range users {

io.WriteString(w, "<p>")

ProcessUserName(ctx, w, user)

io.WriteString(w, ":")

io.WriteString(w, utils.ToString(user.Age))

io.WriteString(w, "(")

if user.IsMan {

io.WriteString(w, "Man")

} else {

io.WriteString(w, "Woman")

}

io.WriteString(w, ")\n </p>")

}

}, header)

} 490204 ns/op 36096 B/op 4006 allocs/op

Page 35: Go Template Toolkit, Сергей Свистунов, Lazada

fmt.Sprint -> utils.ToString

func ToString(v interface{}) string {

switch v := v.(type) {

case string:

return v

case int:

return strconv.FormatInt(int64(v), 10)

...

case bool:

if v {

return "true"

}

return "false"

default:

return fmt.Sprint(v)

}

}

Page 36: Go Template Toolkit, Сергей Свистунов, Lazada

CPU profile

Page 37: Go Template Toolkit, Сергей Свистунов, Lazada

src/io/io.go

// WriteString writes the contents of the string s to w, which accepts a slice of bytes.

// If w implements a WriteString method, it is invoked directly.

// Otherwise, w.Write is called exactly once.

func WriteString(w Writer, s string) (n int, err error) {

if sw, ok := w.(stringWriter); ok {

return sw.WriteString(s)

}

return w.Write([]byte(s))

}

Page 38: Go Template Toolkit, Сергей Свистунов, Lazada

Версия №3

var (

s3c00a1c100edee938dfcd27113fd1f93 = []byte{0x20}

sd2c38510206af487d1a4984a8af4ca49 = []byte{0x28}

s9e23173c90723882045e02a5a6c79a9b = []byte{0x29, 0xA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x70, 0x3E}

sf8de4bf55e29f51642893aea2c376d30 = []byte{0x3A}

s0f9baed348e2ded156c71ed2555fb1da = []byte{0x3C, 0x2F, 0x68, 0x31, 0x3E, 0xA, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x68, 0x72, 0x3E}

sedbe56ac7404d66602a627bd637ef2c1 = []byte{0x3C, 0x68, 0x31, 0x3E}

sf715636fb0391a8fdd43473a43b1ea72 = []byte{0x3C, 0x70, 0x3E}

s9444e86b936ba53fdc56632949cb3038 = []byte{0x4D, 0x61, 0x6E}

s9ed6dfeeb854d5f5e292f977637a729b = []byte{0x57, 0x6F, 0x6D, 0x61, 0x6E}

)

Page 39: Go Template Toolkit, Сергей Свистунов, Lazada

Версия №3func ProcessTest(ctx context.Context, w io.Writer, header string, users []User) {

Wrapperpage(ctx, w, func() {

w.Write(sedbe56ac7404d66602a627bd637ef2c1)

io.WriteString(w, filter.Filterhtml(header))

w.Write(s0f9baed348e2ded156c71ed2555fb1da)

for _, user := range users {

w.Write(sf715636fb0391a8fdd43473a43b1ea72)

ProcessUserName(ctx, w, user)

w.Write(sf8de4bf55e29f51642893aea2c376d30)

io.WriteString(w, utils.ToString(user.Age))

w.Write(sd2c38510206af487d1a4984a8af4ca49)

if user.IsMan {

w.Write(s9444e86b936ba53fdc56632949cb3038)

} else {

w.Write(s9ed6dfeeb854d5f5e292f977637a729b)

}

w.Write(s9e23173c90723882045e02a5a6c79a9b)

}

}, header)

} 390227 ns/op 36096 B/op 4006 allocs/op

Page 40: Go Template Toolkit, Сергей Свистунов, Lazada

Результат оптимизаций

0

100000

200000

300000

400000

500000

600000

700000

800000

Версия 1 Версия 2 Версия 3

ns/op

0

1000

2000

3000

4000

5000

6000

7000

Версия 1 Версия 2 Версия 3

allocs/op

Page 41: Go Template Toolkit, Сергей Свистунов, Lazada

Вопросы?