From 922db2487a2b57ecd0c2d220ff5d65bae69fec71 Mon Sep 17 00:00:00 2001 From: sshniro Date: Mon, 16 Mar 2020 08:44:00 +0100 Subject: [PATCH 1/4] Refactoring code generation to a table structure --- lib/jsonschema.lua | 85 ++++++++++------------------------------------ 1 file changed, 18 insertions(+), 67 deletions(-) diff --git a/lib/jsonschema.lua b/lib/jsonschema.lua index 7d2bb58..c50b17f 100644 --- a/lib/jsonschema.lua +++ b/lib/jsonschema.lua @@ -6,8 +6,6 @@ local ipairs = ipairs local unpack = unpack or table.unpack local sformat = string.format local mmax, mmodf = math.max, math.modf -local coro_wrap = coroutine.wrap -local coro_yield = coroutine.yield local DEBUG = os and os.getenv and os.getenv('DEBUG') == '1' local tab_concat = table.concat local tab_insert = table.insert @@ -98,47 +96,10 @@ end -- Returns an expression that will result in passed value. -- Currently user vlaues are stored in an array to avoid consuming a lot of local -- and upvalue slots. Array accesses are still decently fast. -function codectx_mt:uservalue(val) - local slot = #self._root._uservalues + 1 - self._root._uservalues[slot] = val - return sformat('uservalues[%d]', slot) -end - -local function q(s) return sformat('%q', s) end - -function codectx_mt:validator(path, schema) - local ref = self._schema:child(path) - local resolved = ref:resolve() - local root = self._root - local var = root._validators[resolved] - if not var then - var = root:localvar('nil') - root._validators[resolved] = var - root:stmt(sformat('%s = ', var), generate_validator(root:child(ref), resolved)) - end - return var -end - -function codectx_mt:preface(...) - assert(self._preface, 'preface is only available for root contexts') - for i=1, select('#', ...) do - tab_insert(self._preface, (select(i, ...))) - end - tab_insert(self._preface, '\n') -end - -function codectx_mt:stmt(...) - for i=1, select('#', ...) do - tab_insert(self._body, (select(i, ...))) - end - tab_insert(self._body, '\n') -end - --- load doesn't like at all empty string, but sometimes it is easier to add --- some in the chunk buffer -local function yield_chunk(chunk) +local codeTable = {} +local function insertCode(chunk) if chunk and chunk ~= '' then - coro_yield(chunk) + tab_insert(codeTable, chunk) end end @@ -146,60 +107,50 @@ function codectx_mt:_generate() local indent = '' if self._root == self then for _, stmt in ipairs(self._preface) do - yield_chunk(indent) + insertCode(indent) if getmetatable(stmt) == codectx_mt then stmt:_generate() else - yield_chunk(stmt) + insertCode(stmt) end end else - coro_yield('function(') + insertCode('function(') for i=1, self._nparams do - yield_chunk('p_' .. i) - if i ~= self._nparams then yield_chunk(', ') end + insertCode('p_' .. i) + if i ~= self._nparams then insertCode(', ') end end - yield_chunk(')\n') + insertCode(')\n') indent = string.rep(' ', self._idx) end for _, stmt in ipairs(self._body) do - yield_chunk(indent) + insertCode(indent) if getmetatable(stmt) == codectx_mt then stmt:_generate() else - yield_chunk(stmt) + insertCode(stmt) end end if self._root ~= self then - yield_chunk('end') + insertCode('end') end end function codectx_mt:_get_loader() - return coro_wrap(function() - self:_generate() - end) + codeTable = {} + self:_generate() end function codectx_mt:as_string() - local buf, n = {}, 0 - for chunk in self:_get_loader() do - n = n+1 - buf[n] = chunk - end - return tab_concat(buf) + self:_get_loader() + return tab_concat(codeTable) end function codectx_mt:as_func(name, ...) - local buf, n = {}, 0 - for chunk in self:_get_loader() do - n = n + 1 - buf[n] = chunk - end - - local loader, err = loadstring(tab_concat(buf, ""), 'jsonschema:' .. (name or 'anonymous')) + self:_get_loader() + local loader, err = loadstring(tab_concat(codeTable, ""), 'jsonschema:' .. (name or 'anonymous')) if loader then local validator validator, err = loader(self._uservalues, ...) From 0a9f499f4d0df1f58b63d8aa549609bbfe190a17 Mon Sep 17 00:00:00 2001 From: sshniro Date: Wed, 18 Mar 2020 13:22:11 +0100 Subject: [PATCH 2/4] Reverting deleted function --- lib/jsonschema.lua | 60 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/lib/jsonschema.lua b/lib/jsonschema.lua index c50b17f..5acc34e 100644 --- a/lib/jsonschema.lua +++ b/lib/jsonschema.lua @@ -96,8 +96,46 @@ end -- Returns an expression that will result in passed value. -- Currently user vlaues are stored in an array to avoid consuming a lot of local -- and upvalue slots. Array accesses are still decently fast. +function codectx_mt:uservalue(val) + local slot = #self._root._uservalues + 1 + self._root._uservalues[slot] = val + return sformat('uservalues[%d]', slot) +end + +local function q(s) return sformat('%q', s) end + +function codectx_mt:validator(path, schema) + local ref = self._schema:child(path) + local resolved = ref:resolve() + local root = self._root + local var = root._validators[resolved] + if not var then + var = root:localvar('nil') + root._validators[resolved] = var + root:stmt(sformat('%s = ', var), generate_validator(root:child(ref), resolved)) + end + return var +end + +function codectx_mt:preface(...) + assert(self._preface, 'preface is only available for root contexts') + for i=1, select('#', ...) do + tab_insert(self._preface, (select(i, ...))) + end + tab_insert(self._preface, '\n') +end + +function codectx_mt:stmt(...) + for i=1, select('#', ...) do + tab_insert(self._body, (select(i, ...))) + end + tab_insert(self._body, '\n') +end + +-- load doesn't like at all empty string, but sometimes it is easier to add +-- some in the chunk buffer local codeTable = {} -local function insertCode(chunk) +local function insert_code(chunk) if chunk and chunk ~= '' then tab_insert(codeTable, chunk) end @@ -107,44 +145,44 @@ function codectx_mt:_generate() local indent = '' if self._root == self then for _, stmt in ipairs(self._preface) do - insertCode(indent) + insert_code(indent) if getmetatable(stmt) == codectx_mt then stmt:_generate() else - insertCode(stmt) + insert_code(stmt) end end else - insertCode('function(') + insert_code('function(') for i=1, self._nparams do - insertCode('p_' .. i) - if i ~= self._nparams then insertCode(', ') end + insert_code('p_' .. i) + if i ~= self._nparams then insert_code(', ') end end - insertCode(')\n') + insert_code(')\n') indent = string.rep(' ', self._idx) end for _, stmt in ipairs(self._body) do - insertCode(indent) + insert_code(indent) if getmetatable(stmt) == codectx_mt then stmt:_generate() else - insertCode(stmt) + insert_code(stmt) end end if self._root ~= self then - insertCode('end') + insert_code('end') end end function codectx_mt:_get_loader() codeTable = {} self:_generate() + return codeTable end function codectx_mt:as_string() - self:_get_loader() return tab_concat(codeTable) end From bb5fe44ccadc5f327992b0b972ed57fe4482a90c Mon Sep 17 00:00:00 2001 From: sshniro Date: Wed, 18 Mar 2020 13:26:45 +0100 Subject: [PATCH 3/4] Refactoring to snake case --- lib/jsonschema.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/jsonschema.lua b/lib/jsonschema.lua index 5acc34e..7112739 100644 --- a/lib/jsonschema.lua +++ b/lib/jsonschema.lua @@ -134,10 +134,10 @@ end -- load doesn't like at all empty string, but sometimes it is easier to add -- some in the chunk buffer -local codeTable = {} +local code_table = {} local function insert_code(chunk) if chunk and chunk ~= '' then - tab_insert(codeTable, chunk) + tab_insert(code_table, chunk) end end @@ -177,18 +177,18 @@ function codectx_mt:_generate() end function codectx_mt:_get_loader() - codeTable = {} + code_table = {} self:_generate() - return codeTable + return code_table end function codectx_mt:as_string() - return tab_concat(codeTable) + return tab_concat(code_table) end function codectx_mt:as_func(name, ...) self:_get_loader() - local loader, err = loadstring(tab_concat(codeTable, ""), 'jsonschema:' .. (name or 'anonymous')) + local loader, err = loadstring(tab_concat(code_table, ""), 'jsonschema:' .. (name or 'anonymous')) if loader then local validator validator, err = loader(self._uservalues, ...) From d63cd0e23e942d42f4a8ced699e5ef93db37d4a0 Mon Sep 17 00:00:00 2001 From: sshniro Date: Thu, 19 Mar 2020 08:19:06 +0100 Subject: [PATCH 4/4] Refactoring to remove global variable --- lib/jsonschema.lua | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/jsonschema.lua b/lib/jsonschema.lua index 7112739..eb868dc 100644 --- a/lib/jsonschema.lua +++ b/lib/jsonschema.lua @@ -134,61 +134,60 @@ end -- load doesn't like at all empty string, but sometimes it is easier to add -- some in the chunk buffer -local code_table = {} -local function insert_code(chunk) +local function insert_code(chunk, code_table) if chunk and chunk ~= '' then tab_insert(code_table, chunk) end end -function codectx_mt:_generate() +function codectx_mt:_generate(code_table) local indent = '' if self._root == self then for _, stmt in ipairs(self._preface) do - insert_code(indent) + insert_code(indent, code_table) if getmetatable(stmt) == codectx_mt then - stmt:_generate() + stmt:_generate(code_table) else - insert_code(stmt) + insert_code(stmt, code_table) end end else - insert_code('function(') + insert_code('function(', code_table) for i=1, self._nparams do - insert_code('p_' .. i) - if i ~= self._nparams then insert_code(', ') end + insert_code('p_' .. i, code_table) + if i ~= self._nparams then insert_code(', ', code_table) end end - insert_code(')\n') + insert_code(')\n', code_table) indent = string.rep(' ', self._idx) end for _, stmt in ipairs(self._body) do - insert_code(indent) + insert_code(indent, code_table) if getmetatable(stmt) == codectx_mt then - stmt:_generate() + stmt:_generate(code_table) else - insert_code(stmt) + insert_code(stmt, code_table) end end if self._root ~= self then - insert_code('end') + insert_code('end', code_table) end end function codectx_mt:_get_loader() - code_table = {} - self:_generate() - return code_table + self._code_table = {} + self:_generate(self._code_table) + return self._code_table end function codectx_mt:as_string() - return tab_concat(code_table) + return tab_concat(self._code_table) end function codectx_mt:as_func(name, ...) self:_get_loader() - local loader, err = loadstring(tab_concat(code_table, ""), 'jsonschema:' .. (name or 'anonymous')) + local loader, err = loadstring(tab_concat(self._code_table, ""), 'jsonschema:' .. (name or 'anonymous')) if loader then local validator validator, err = loader(self._uservalues, ...) @@ -229,6 +228,7 @@ local function codectx(schema, options) local self = setmetatable({ _schema = store.new(schema, options.external_resolver), _id = schema.id, + _code_table = {}, _path = '', _idx = 0, -- code generation