From 93dcd76db0344bbc3d37c92e2c57eb90edc7f6d6 Mon Sep 17 00:00:00 2001 From: Ivan Shalganov Date: Mon, 6 Apr 2020 10:20:20 +0300 Subject: [PATCH] Optimize code space; fix docs --- docs/api.md | 23 +++++++++ rockspec/aspect-1.6-1.rockspec | 49 ------------------- ...t-1.6-0.rockspec => aspect-1.9-0.rockspec} | 12 +++-- rockspec/aspect-git-1.rockspec | 22 +++++---- src/aspect/compiler.lua | 21 +++----- src/aspect/output.lua | 4 ++ src/aspect/tags.lua | 2 +- src/aspect/template.lua | 2 +- src/aspect/utils/range.lua | 44 +++++++++++------ 9 files changed, 83 insertions(+), 96 deletions(-) delete mode 100644 rockspec/aspect-1.6-1.rockspec rename rockspec/{aspect-1.6-0.rockspec => aspect-1.9-0.rockspec} (85%) diff --git a/docs/api.md b/docs/api.md index 23dd6ac..dc18207 100644 --- a/docs/api.md +++ b/docs/api.md @@ -54,6 +54,22 @@ aspect:display("dashboard.tpl", { }) ``` +## Render result + +```lua +local output, err = aspect:display(template, vars) +``` + +- `output` is `aspect.output` object and contains rendering information, even if the rendering failed. + If you use `aspect:render` method `output` contains rendered string: + ```lua + local output, err = aspect:render(template, vars) + if not err then + io.write(tostring(output)) + end + ``` +- `err` is `aspect.error` object and contains error information. `nil` if no errors. + Rendering Templates ------------------- @@ -442,3 +458,10 @@ end ```twig {{ data.raw|e("csv") }} ``` + +## Iterator + +The `Aspect` implements custom iterators as in Lua 5.2 - through the metatable and `__pairs()` function. +Works for all lua/luajit versions. + +For example see [range iterator](../src/aspect/utils/range.lua). \ No newline at end of file diff --git a/rockspec/aspect-1.6-1.rockspec b/rockspec/aspect-1.6-1.rockspec deleted file mode 100644 index 533876b..0000000 --- a/rockspec/aspect-1.6-1.rockspec +++ /dev/null @@ -1,49 +0,0 @@ -package = "aspect" -version = "1.6-1" -source = { - url = "https://github.com/unifire-app/aspect/archive/1.6.zip", - dir = "aspect-1.6" -} -description = { - summary = "Aspect is a compiling templating engine for Lua and OpenResty with syntax Twig/Django/Jinja.", - detailed = [[ -* Well known: The most popular Django-like syntax is used - Twig compatible and Jinja like. -* Fast: Aspect compiles templates down to plain optimized Lua code. - Moreover, Lua code compiles into bytecode - the fastest representation of a template. -* Secure: Aspect has a sandbox mode to evaluate all template code. - This allows Aspect to be used as a template language for applications where users may modify the template design. -* Flexible: Aspect is powered by a flexible lexer and parser. - This allows the developer to define their own custom tags, filters, functions and operators, and to create their own DSL. -* Comfortable: Aspect allows you to process userdata data. - More intuitive behavior with special values such as a empty string, number zero and so on. -* Memory-safe: The template is built in such a way as to save maximum memory when it is executed, even if the iterator provides a lot of data. - ]], - license = "BSD-3-Clause", -} -dependencies = { - "penlight", - "date", - "lua-cjson" -} -build = { - type = "builtin", - modules = { - ["aspect"] = "src/aspect/init.lua", - ["aspect.config"] = "src/aspect/config.lua", - ["aspect.template"] = "src/aspect/template.lua", - ["aspect.compiler"] = "src/aspect/compiler.lua", - ["aspect.output"] = "src/aspect/output.lua", - ["aspect.tags"] = "src/aspect/tags.lua", - ["aspect.err"] = "src/aspect/err.lua", - ["aspect.filters"] = "src/aspect/filters.lua", - ["aspect.funcs"] = "src/aspect/funcs.lua", - ["aspect.tests"] = "src/aspect/tests.lua", - ["aspect.tokenizer"] = "src/aspect/tokenizer.lua", - ["aspect.utils"] = "src/aspect/utils.lua", - ["aspect.ast"] = "src/aspect/ast.lua", - ["aspect.ast.ops"] = "src/aspect/ast/ops.lua", - ["aspect.utils.batch"] = "src/aspect/utils/batch.lua", - ["aspect.utils.range"] = "src/aspect/utils/range.lua", - ["aspect.loader.array"] = "src/aspect/loader/array.lua", - } -} \ No newline at end of file diff --git a/rockspec/aspect-1.6-0.rockspec b/rockspec/aspect-1.9-0.rockspec similarity index 85% rename from rockspec/aspect-1.6-0.rockspec rename to rockspec/aspect-1.9-0.rockspec index 4104014..25b9351 100644 --- a/rockspec/aspect-1.6-0.rockspec +++ b/rockspec/aspect-1.9-0.rockspec @@ -1,13 +1,13 @@ package = "aspect" -version = "1.6-0" +version = "1.9-0" source = { - url = "https://github.com/unifire-app/aspect/archive/1.6.zip", - dir = "aspect-1.6" + url = "https://github.com/unifire-app/aspect/archive/1.9.zip", + dir = "aspect-1.9" } description = { - summary = "Aspect is a compiling templating engine for Lua and OpenResty with syntax Twig/Django/Jinja.", + summary = "Aspect is a compiling templating engine for Lua and OpenResty with syntax Twig/Jinja/Liquid.", detailed = [[ -* Well known: The most popular Django-like syntax is used - Twig compatible and Jinja like. +* Well known: The most popular Liquid-like syntax is used - Twig compatible and Jinja like. * Fast: Aspect compiles templates down to plain optimized Lua code. Moreover, Lua code compiles into bytecode - the fastest representation of a template. * Secure: Aspect has a sandbox mode to evaluate all template code. @@ -45,5 +45,7 @@ build = { ["aspect.utils.batch"] = "src/aspect/utils/batch.lua", ["aspect.utils.range"] = "src/aspect/utils/range.lua", ["aspect.loader.array"] = "src/aspect/loader/array.lua", + ["aspect.loader.array"] = "src/aspect/loader/filesystem.lua", + ["aspect.loader.array"] = "src/aspect/loader/resty.lua", } } \ No newline at end of file diff --git a/rockspec/aspect-git-1.rockspec b/rockspec/aspect-git-1.rockspec index d60efdc..9f9a821 100644 --- a/rockspec/aspect-git-1.rockspec +++ b/rockspec/aspect-git-1.rockspec @@ -6,16 +6,16 @@ source = { description = { summary = "Aspect is a compiling templating engine for Lua and OpenResty with syntax Twig/Django/Jinja.", detailed = [[ -* Popular Django syntax compatible with Twig2 and Jinja2 -* Bytecode and lua code caching of compiled templates are available. -* Safe launch of templates in the sandbox. -* Automatic casting of data types when used. -* Designed for highload and big input and output data. -* Detailed error messages make it easy to debug templates. -* Easily extensible: add your own tags, filters, functions, handlers, operators. -* Intuitive behavior. For example, unlike lua, zero, an empty string, csjon.null, and so on, will be false in if-conditions. All of this is extensible. -* Custom behavior with userdata. -* Supports work with OpenResty. +* Well known: The most popular Liquid-like syntax is used - Twig compatible and Jinja like. +* Fast: Aspect compiles templates down to plain optimized Lua code. + Moreover, Lua code compiles into bytecode - the fastest representation of a template. +* Secure: Aspect has a sandbox mode to evaluate all template code. + This allows Aspect to be used as a template language for applications where users may modify the template design. +* Flexible: Aspect is powered by a flexible lexer and parser. + This allows the developer to define their own custom tags, filters, functions and operators, and to create their own DSL. +* Comfortable: Aspect allows you to process userdata data. + More intuitive behavior with special values such as a empty string, number zero and so on. +* Memory-safe: The template is built in such a way as to save maximum memory when it is executed, even if the iterator provides a lot of data. ]], license = "BSD-3-Clause", } @@ -44,5 +44,7 @@ build = { ["aspect.utils.batch"] = "src/aspect/utils/batch.lua", ["aspect.utils.range"] = "src/aspect/utils/range.lua", ["aspect.loader.array"] = "src/aspect/loader/array.lua", + ["aspect.loader.array"] = "src/aspect/loader/filesystem.lua", + ["aspect.loader.array"] = "src/aspect/loader/resty.lua", } } \ No newline at end of file diff --git a/src/aspect/compiler.lua b/src/aspect/compiler.lua index 2ff9092..958aa31 100644 --- a/src/aspect/compiler.lua +++ b/src/aspect/compiler.lua @@ -895,28 +895,19 @@ end --- @param name string the tag name --- @param code_space table|nil for lua code --- @return aspect.tag -function compiler:push_tag(name, code_space, code_space_name, var_space, push_state) +function compiler:push_tag(name, code_space, code_space_name, var_space) self.idx = self.idx + 1 - if push_state == nil then - push_state = true - end --- @type aspect.tag local tag = { id = self.idx, name = name, - line = self.line, - push_state = push_state + line = self.line } if code_space then insert(self.code, code_space) - if not code_space_name then - code_space_name = "nil" - else - code_space_name = quote_string(code_space_name) - end - tag.code_space_name = code_space_name - if push_state then - code_space[#code_space + 1] = "__:push_state(_self, " .. self.line .. ", " .. code_space_name .. ")" + if code_space_name then + tag.code_space_name = code_space_name + code_space[#code_space + 1] = "__:push_state(_self, " .. self.line .. ", " .. quote_string(code_space_name) .. ")" end self.prev_line = self.line tag.code_space_no = #self.code @@ -973,7 +964,7 @@ function compiler:pop_tag(name) compiler_error(nil, "compiler", "invalid code space layer in the tag " .. name) else local prev = remove(self.code) - if tag.push_state then + if tag.code_space_name then prev[#prev + 1] = "__:pop_state()" end end diff --git a/src/aspect/output.lua b/src/aspect/output.lua index 1a38cf3..207f235 100644 --- a/src/aspect/output.lua +++ b/src/aspect/output.lua @@ -160,6 +160,10 @@ function output:set_print(p, size) self.size = size end +--- Push new state to call stack +--- @param view aspect.view +--- @param line number +--- @param scope_name string function output:push_state(view, line, scope_name) if #self.stack > self.opts.stack_size then runtime_error(self, "Call stack overflow (maximum " .. self.opts.stack_size .. ")") diff --git a/src/aspect/tags.lua b/src/aspect/tags.lua index ee1a5eb..e1298d3 100644 --- a/src/aspect/tags.lua +++ b/src/aspect/tags.lua @@ -380,7 +380,7 @@ function tags.tag_for(compiler, tok) compiler_error(tok, "syntax", "expecting variable name") end end - local tag = compiler:push_tag('for', {}, nil, nil, false) + local tag = compiler:push_tag('for', {}) from = compiler:parse_expression(tok:require("in"):next()) tag.has_loop = false tag.from = from diff --git a/src/aspect/template.lua b/src/aspect/template.lua index 8ea8f40..ac55733 100644 --- a/src/aspect/template.lua +++ b/src/aspect/template.lua @@ -55,7 +55,7 @@ local _ = {} --- @field bytecode_load fun(name: string, tpl: aspect.template):string --- @field bytecode_save fun(name: string, bytecode: string, tpl: aspect.template) local template = { - _VERSION = "1.8", + _VERSION = "1.9", _NAME = "aspect", } local mt = { __index = template } diff --git a/src/aspect/utils/range.lua b/src/aspect/utils/range.lua index ed12d4e..796c9e6 100644 --- a/src/aspect/utils/range.lua +++ b/src/aspect/utils/range.lua @@ -2,7 +2,26 @@ local setmetatable = setmetatable local floor = math.floor local abs = math.abs -local function iterator(self, k) +--- Range iterator +--- @class aspect.utils.range +--- @field from number +--- @field to number +--- @field step number +--- @field incr boolean +local range = {} + +--- Magic function for {{ for }} tag +--- @return fun iterator (see range.__iterate) +--- @return table context object +--- @return number initial key value +function range:__pairs() + return self.__iterate, self, 0 +end + +--- Iterator +--- @return number the key +--- @return number the value +function range:__iterate(k) local i = self.from + k * self.step if self.incr then if i > self.to then @@ -19,11 +38,9 @@ local function iterator(self, k) end end -local function __pairs(self) - return iterator, self, 0 -end - -local function __count(self) +--- Magic function for calculating the number of iterations (elements) +--- @return number count of iterations/elements +function range:__count() if self.incr then return floor(abs((self.to - self.from) / self.step)) + 1 else @@ -31,18 +48,16 @@ local function __count(self) end end ---- @class aspect.utils.range ---- @field from number ---- @field to number ---- @field step number ---- @field incr boolean -local range = {} local mt = { __index = range, - __pairs = __pairs, - __count = __count + __pairs = range.__pairs, + __count = range.__count } +--- Range constructor +--- @param from number +--- @param to number +--- @param step number function range.new(from, to, step) return setmetatable({ incr = from < to, @@ -52,5 +67,4 @@ function range.new(from, to, step) }, mt) end - return range \ No newline at end of file