summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2022-06-13 22:46:02 +0800
committer最萌小汐 <sumneko@hotmail.com>2022-06-13 22:46:02 +0800
commit32fbab2b26bdb6b4c00ed8f467f12e5161ba4c31 (patch)
treeb109f2746be0693a8967734393cd775a876cdb12
parented16d2f27b0c70d5d845435708da387db485719e (diff)
downloadlua-language-server-32fbab2b26bdb6b4c00ed8f467f12e5161ba4c31.zip
new runner
-rw-r--r--script/core/completion/completion.lua4
-rw-r--r--script/core/hover/description.lua9
-rw-r--r--script/vm/compiler.lua3
-rw-r--r--script/vm/infer.lua5
-rw-r--r--script/vm/runner-bak.lua444
-rw-r--r--script/vm/runner.lua454
-rw-r--r--script/vm/sign.lua4
7 files changed, 522 insertions, 401 deletions
diff --git a/script/core/completion/completion.lua b/script/core/completion/completion.lua
index d7c210c6..285f5bf2 100644
--- a/script/core/completion/completion.lua
+++ b/script/core/completion/completion.lua
@@ -1132,7 +1132,7 @@ local function checkTypingEnum(state, position, defs, str, results)
or def.type == 'doc.type.integer'
or def.type == 'doc.type.boolean' then
enums[#enums+1] = {
- label = vm.viewObject(def),
+ label = vm.viewObject(def, state.uri),
description = def.comment and def.comment.text,
kind = define.CompletionItemKind.EnumMember,
}
@@ -1427,7 +1427,7 @@ local function tryCallArg(state, position, results)
or src.type == 'doc.type.integer'
or src.type == 'doc.type.boolean' then
enums[#enums+1] = {
- label = vm.viewObject(src),
+ label = vm.viewObject(src, state.uri),
description = src.comment,
kind = define.CompletionItemKind.EnumMember,
}
diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index 3fef1a21..c96aaae3 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -144,7 +144,7 @@ local function tryDocModule(source)
return collectRequire('require', source.module, guide.getUri(source))
end
-local function buildEnumChunk(docType, name)
+local function buildEnumChunk(docType, name, uri)
if not docType then
return nil
end
@@ -175,7 +175,7 @@ local function buildEnumChunk(docType, name)
(enum.default and '->')
or (enum.additional and '+>')
or ' |',
- vm.viewObject(enum)
+ vm.viewObject(enum, uri)
)
if enum.comment then
local first = true
@@ -199,6 +199,7 @@ local function getBindEnums(source, docGroup)
return
end
+ local uri = guide.getUri(source)
local mark = {}
local chunks = {}
local returnIndex = 0
@@ -209,7 +210,7 @@ local function getBindEnums(source, docGroup)
goto CONTINUE
end
mark[name] = true
- chunks[#chunks+1] = buildEnumChunk(doc.extends, name)
+ chunks[#chunks+1] = buildEnumChunk(doc.extends, name, uri)
elseif doc.type == 'doc.return' then
for _, rtn in ipairs(doc.returns) do
returnIndex = returnIndex + 1
@@ -218,7 +219,7 @@ local function getBindEnums(source, docGroup)
goto CONTINUE
end
mark[name] = true
- chunks[#chunks+1] = buildEnumChunk(rtn, name)
+ chunks[#chunks+1] = buildEnumChunk(rtn, name, uri)
end
end
::CONTINUE::
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
index 9b871553..0cbe2b7a 100644
--- a/script/vm/compiler.lua
+++ b/script/vm/compiler.lua
@@ -998,8 +998,7 @@ local compilerSwitch = util.switch()
local hasMark = vm.getNode(source):getData 'hasDefined'
- local runner = vm.createRunner(source)
- runner:launch(function (src, node)
+ vm.launchRunner(source, function (src, node)
if src.type == 'setlocal' then
if src.bindDocs then
for _, doc in ipairs(src.bindDocs) do
diff --git a/script/vm/infer.lua b/script/vm/infer.lua
index 712cff10..9bcd3963 100644
--- a/script/vm/infer.lua
+++ b/script/vm/infer.lua
@@ -417,7 +417,8 @@ function mt:viewClass()
end
---@param source parser.object
+---@param uri uri
---@return string?
-function vm.viewObject(source)
- return viewNodeSwitch(source.type, source, {}, guide.getUri(source))
+function vm.viewObject(source, uri)
+ return viewNodeSwitch(source.type, source, {}, uri)
end
diff --git a/script/vm/runner-bak.lua b/script/vm/runner-bak.lua
new file mode 100644
index 00000000..5535f0ca
--- /dev/null
+++ b/script/vm/runner-bak.lua
@@ -0,0 +1,444 @@
+---@class vm
+local vm = require 'vm.vm'
+local guide = require 'parser.guide'
+
+---@class vm.runner-bak
+---@field loc parser.object
+---@field mainBlock parser.object
+---@field blocks table<parser.object, true>
+---@field steps vm.runner.step[]
+local mt = {}
+mt.__index = mt
+mt.index = 1
+
+---@class parser.object
+---@field _casts parser.object[]
+
+---@class vm.runner.step
+---@field type 'truthy' | 'falsy' | 'as' | 'add' | 'remove' | 'object' | 'save' | 'push' | 'merge' | 'cast'
+---@field pos integer
+---@field order? integer
+---@field node? vm.node
+---@field object? parser.object
+---@field name? string
+---@field cast? parser.object
+---@field tag? string
+---@field copy? boolean
+---@field new? boolean
+---@field ref1? vm.runner.step
+---@field ref2? vm.runner.step
+
+---@param filter parser.object
+---@param outStep vm.runner.step
+---@param blockStep vm.runner.step
+function mt:_compileNarrowByFilter(filter, outStep, blockStep)
+ if not filter then
+ return
+ end
+ if filter.type == 'paren' then
+ if filter.exp then
+ self:_compileNarrowByFilter(filter.exp, outStep, blockStep)
+ end
+ return
+ end
+ if filter.type == 'unary' then
+ if not filter.op
+ or not filter[1] then
+ return
+ end
+ if filter.op.type == 'not' then
+ local exp = filter[1]
+ if exp.type == 'getlocal' and exp.node == self.loc then
+ self.steps[#self.steps+1] = {
+ type = 'falsy',
+ pos = filter.finish,
+ new = true,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'truthy',
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ end
+ end
+ elseif filter.type == 'binary' then
+ if not filter.op
+ or not filter[1]
+ or not filter[2] then
+ return
+ end
+ if filter.op.type == 'and' then
+ local dummyStep = {
+ type = 'save',
+ copy = true,
+ ref1 = outStep,
+ pos = filter.start - 1,
+ }
+ self.steps[#self.steps+1] = dummyStep
+ self:_compileNarrowByFilter(filter[1], dummyStep, blockStep)
+ self:_compileNarrowByFilter(filter[2], dummyStep, blockStep)
+ end
+ if filter.op.type == 'or' then
+ self:_compileNarrowByFilter(filter[1], outStep, blockStep)
+ local dummyStep = {
+ type = 'push',
+ copy = true,
+ ref1 = outStep,
+ pos = filter.op.finish,
+ }
+ self.steps[#self.steps+1] = dummyStep
+ self:_compileNarrowByFilter(filter[2], outStep, dummyStep)
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ tag = 'or reset',
+ ref1 = blockStep,
+ pos = filter.finish,
+ }
+ end
+ if filter.op.type == '=='
+ or filter.op.type == '~=' then
+ local loc, exp
+ for i = 1, 2 do
+ loc = filter[i]
+ if loc.type == 'getlocal' and loc.node == self.loc then
+ exp = filter[i % 2 + 1]
+ break
+ end
+ end
+ if not loc or not exp then
+ return
+ end
+ if guide.isLiteral(exp) then
+ if filter.op.type == '==' then
+ self.steps[#self.steps+1] = {
+ type = 'remove',
+ name = exp.type,
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'as',
+ name = exp.type,
+ pos = filter.finish,
+ new = true,
+ }
+ end
+ if filter.op.type == '~=' then
+ self.steps[#self.steps+1] = {
+ type = 'as',
+ name = exp.type,
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'remove',
+ name = exp.type,
+ pos = filter.finish,
+ new = true,
+ }
+ end
+ end
+ end
+ else
+ if filter.type == 'getlocal' and filter.node == self.loc then
+ self.steps[#self.steps+1] = {
+ type = 'truthy',
+ pos = filter.finish,
+ new = true,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'falsy',
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ end
+ end
+end
+
+---@param block parser.object
+function mt:_compileBlock(block)
+ if self.blocks[block] then
+ return
+ end
+ self.blocks[block] = true
+ if block == self.mainBlock then
+ return
+ end
+
+ local parentBlock = guide.getParentBlock(block)
+ self:_compileBlock(parentBlock)
+
+ if block.type == 'if' then
+ ---@type vm.runner.step[]
+ local finals = {}
+ for i, childBlock in ipairs(block) do
+ local blockStep = {
+ type = 'save',
+ tag = 'block',
+ copy = true,
+ pos = childBlock.start,
+ }
+ local outStep = {
+ type = 'save',
+ tag = 'out',
+ copy = true,
+ pos = childBlock.start,
+ }
+ self.steps[#self.steps+1] = blockStep
+ self.steps[#self.steps+1] = outStep
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ ref1 = blockStep,
+ pos = childBlock.start,
+ }
+ self:_compileNarrowByFilter(childBlock.filter, outStep, blockStep)
+ if not childBlock.hasReturn
+ and not childBlock.hasGoTo
+ and not childBlock.hasBreak then
+ local finalStep = {
+ type = 'save',
+ pos = childBlock.finish,
+ tag = 'final #' .. i,
+ }
+ finals[#finals+1] = finalStep
+ self.steps[#self.steps+1] = finalStep
+ end
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ tag = 'reset child',
+ ref1 = outStep,
+ pos = childBlock.finish,
+ }
+ end
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ tag = 'reset if',
+ pos = block.finish,
+ copy = true,
+ }
+ for _, final in ipairs(finals) do
+ self.steps[#self.steps+1] = {
+ type = 'merge',
+ ref2 = final,
+ pos = block.finish,
+ }
+ end
+ end
+
+ if block.type == 'function'
+ or block.type == 'while'
+ or block.type == 'loop'
+ or block.type == 'in'
+ or block.type == 'repeat'
+ or block.type == 'for' then
+ local savePoint = {
+ type = 'save',
+ copy = true,
+ pos = block.start,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ copy = true,
+ pos = block.start,
+ }
+ self.steps[#self.steps+1] = savePoint
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ pos = block.finish,
+ ref1 = savePoint,
+ }
+ end
+end
+
+---@return parser.object[]
+function mt:_getCasts()
+ local root = guide.getRoot(self.loc)
+ if not root._casts then
+ root._casts = {}
+ local docs = root.docs
+ for _, doc in ipairs(docs) do
+ if doc.type == 'doc.cast' and doc.loc then
+ root._casts[#root._casts+1] = doc
+ end
+ end
+ end
+ return root._casts
+end
+
+function mt:_preCompile()
+ local startPos = self.loc.start
+ local finishPos = 0
+
+ for _, ref in ipairs(self.loc.ref) do
+ self.steps[#self.steps+1] = {
+ type = 'object',
+ object = ref,
+ pos = ref.range or ref.start,
+ }
+ if ref.start > finishPos then
+ finishPos = ref.start
+ end
+ local block = guide.getParentBlock(ref)
+ self:_compileBlock(block)
+ end
+
+ for i, step in ipairs(self.steps) do
+ if step.type ~= 'object' then
+ step.order = i
+ end
+ end
+
+ local casts = self:_getCasts()
+ for _, cast in ipairs(casts) do
+ if cast.loc[1] == self.loc[1]
+ and cast.start > startPos
+ and cast.finish < finishPos
+ and guide.getLocal(self.loc, self.loc[1], cast.start) == self.loc then
+ self.steps[#self.steps+1] = {
+ type = 'cast',
+ cast = cast,
+ pos = cast.start,
+ }
+ end
+ end
+
+ table.sort(self.steps, function (a, b)
+ if a.pos == b.pos then
+ return (a.order or 0) < (b.order or 0)
+ else
+ return a.pos < b.pos
+ end
+ end)
+end
+
+---@param loc parser.object
+---@param node vm.node
+---@return vm.node
+local function checkAssert(loc, node)
+ local parent = loc.parent
+ if parent.type == 'binary' then
+ if parent.op and (parent.op.type == '~=' or parent.op.type == '==') then
+ local exp
+ for i = 1, 2 do
+ if parent[i] == loc then
+ exp = parent[i % 2 + 1]
+ end
+ end
+ if exp and guide.isLiteral(exp) then
+ local callargs = parent.parent
+ if callargs.type == 'callargs'
+ and callargs.parent.node.special == 'assert'
+ and callargs[1] == parent then
+ if parent.op.type == '~=' then
+ node:remove(exp.type)
+ end
+ if parent.op.type == '==' then
+ node = vm.compileNode(exp)
+ end
+ end
+ end
+ end
+ end
+ if parent.type == 'callargs'
+ and parent.parent.node.special == 'assert'
+ and parent[1] == loc then
+ node:setTruthy()
+ end
+ return node
+end
+
+---@param callback fun(src: parser.object, node: vm.node)
+function mt:launch(callback)
+ local topNode = vm.getNode(self.loc):copy()
+ for _, step in ipairs(self.steps) do
+ local node = step.ref1 and step.ref1.node or topNode
+ if step.type == 'truthy' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:setTruthy()
+ elseif step.type == 'falsy' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:setFalsy()
+ elseif step.type == 'as' then
+ if step.new then
+ topNode = vm.createNode(vm.getGlobal('type', step.name))
+ else
+ node:clear()
+ node:merge(vm.getGlobal('type', step.name))
+ end
+ elseif step.type == 'add' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:merge(vm.getGlobal('type', step.name))
+ elseif step.type == 'remove' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:remove(step.name)
+ elseif step.type == 'object' then
+ topNode = callback(step.object, node) or node
+ if step.object.type == 'getlocal' then
+ topNode = checkAssert(step.object, node)
+ end
+ elseif step.type == 'save' then
+ if step.copy then
+ node = node:copy()
+ end
+ step.node = node
+ elseif step.type == 'push' then
+ if step.copy then
+ node = node:copy()
+ end
+ topNode = node
+ elseif step.type == 'merge' then
+ node:merge(step.ref2.node)
+ elseif step.type == 'cast' then
+ topNode = node:copy()
+ for _, cast in ipairs(step.cast.casts) do
+ if cast.mode == '+' then
+ if cast.optional then
+ topNode:addOptional()
+ end
+ if cast.extends then
+ topNode:merge(vm.compileNode(cast.extends))
+ end
+ elseif cast.mode == '-' then
+ if cast.optional then
+ topNode:removeOptional()
+ end
+ if cast.extends then
+ topNode:removeNode(vm.compileNode(cast.extends))
+ end
+ else
+ if cast.extends then
+ topNode:clear()
+ topNode:merge(vm.compileNode(cast.extends))
+ end
+ end
+ end
+ end
+ end
+end
+
+---@param loc parser.object
+---@return vm.runner
+function vm.launchRunner(loc)
+ local self = setmetatable({
+ loc = loc,
+ mainBlock = guide.getParentBlock(loc),
+ blocks = {},
+ steps = {},
+ }, mt)
+
+ self:_preCompile()
+
+ return self
+end
diff --git a/script/vm/runner.lua b/script/vm/runner.lua
index 9fe0f172..38b0b66c 100644
--- a/script/vm/runner.lua
+++ b/script/vm/runner.lua
@@ -2,257 +2,19 @@
local vm = require 'vm.vm'
local guide = require 'parser.guide'
+---@alias vm.runner.callback fun(src: parser.object, node: vm.node)
+
---@class vm.runner
----@field loc parser.object
----@field mainBlock parser.object
----@field blocks table<parser.object, true>
----@field steps vm.runner.step[]
+---@field _loc parser.object
+---@field _objs parser.object[]
+---@field _callback vm.runner.callback
local mt = {}
mt.__index = mt
-mt.index = 1
-
----@class parser.object
----@field _casts parser.object[]
-
----@class vm.runner.step
----@field type 'truthy' | 'falsy' | 'as' | 'add' | 'remove' | 'object' | 'save' | 'push' | 'merge' | 'cast'
----@field pos integer
----@field order? integer
----@field node? vm.node
----@field object? parser.object
----@field name? string
----@field cast? parser.object
----@field tag? string
----@field copy? boolean
----@field new? boolean
----@field ref1? vm.runner.step
----@field ref2? vm.runner.step
-
----@param filter parser.object
----@param outStep vm.runner.step
----@param blockStep vm.runner.step
-function mt:_compileNarrowByFilter(filter, outStep, blockStep)
- if not filter then
- return
- end
- if filter.type == 'paren' then
- if filter.exp then
- self:_compileNarrowByFilter(filter.exp, outStep, blockStep)
- end
- return
- end
- if filter.type == 'unary' then
- if not filter.op
- or not filter[1] then
- return
- end
- if filter.op.type == 'not' then
- local exp = filter[1]
- if exp.type == 'getlocal' and exp.node == self.loc then
- self.steps[#self.steps+1] = {
- type = 'falsy',
- pos = filter.finish,
- new = true,
- }
- self.steps[#self.steps+1] = {
- type = 'truthy',
- pos = filter.finish,
- ref1 = outStep,
- }
- end
- end
- elseif filter.type == 'binary' then
- if not filter.op
- or not filter[1]
- or not filter[2] then
- return
- end
- if filter.op.type == 'and' then
- local dummyStep = {
- type = 'save',
- copy = true,
- ref1 = outStep,
- pos = filter.start - 1,
- }
- self.steps[#self.steps+1] = dummyStep
- self:_compileNarrowByFilter(filter[1], dummyStep, blockStep)
- self:_compileNarrowByFilter(filter[2], dummyStep, blockStep)
- end
- if filter.op.type == 'or' then
- self:_compileNarrowByFilter(filter[1], outStep, blockStep)
- local dummyStep = {
- type = 'push',
- copy = true,
- ref1 = outStep,
- pos = filter.op.finish,
- }
- self.steps[#self.steps+1] = dummyStep
- self:_compileNarrowByFilter(filter[2], outStep, dummyStep)
- self.steps[#self.steps+1] = {
- type = 'push',
- tag = 'or reset',
- ref1 = blockStep,
- pos = filter.finish,
- }
- end
- if filter.op.type == '=='
- or filter.op.type == '~=' then
- local loc, exp
- for i = 1, 2 do
- loc = filter[i]
- if loc.type == 'getlocal' and loc.node == self.loc then
- exp = filter[i % 2 + 1]
- break
- end
- end
- if not loc or not exp then
- return
- end
- if guide.isLiteral(exp) then
- if filter.op.type == '==' then
- self.steps[#self.steps+1] = {
- type = 'remove',
- name = exp.type,
- pos = filter.finish,
- ref1 = outStep,
- }
- self.steps[#self.steps+1] = {
- type = 'as',
- name = exp.type,
- pos = filter.finish,
- new = true,
- }
- end
- if filter.op.type == '~=' then
- self.steps[#self.steps+1] = {
- type = 'as',
- name = exp.type,
- pos = filter.finish,
- ref1 = outStep,
- }
- self.steps[#self.steps+1] = {
- type = 'remove',
- name = exp.type,
- pos = filter.finish,
- new = true,
- }
- end
- end
- end
- else
- if filter.type == 'getlocal' and filter.node == self.loc then
- self.steps[#self.steps+1] = {
- type = 'truthy',
- pos = filter.finish,
- new = true,
- }
- self.steps[#self.steps+1] = {
- type = 'falsy',
- pos = filter.finish,
- ref1 = outStep,
- }
- end
- end
-end
-
----@param block parser.object
-function mt:_compileBlock(block)
- if self.blocks[block] then
- return
- end
- self.blocks[block] = true
- if block == self.mainBlock then
- return
- end
-
- local parentBlock = guide.getParentBlock(block)
- self:_compileBlock(parentBlock)
-
- if block.type == 'if' then
- ---@type vm.runner.step[]
- local finals = {}
- for i, childBlock in ipairs(block) do
- local blockStep = {
- type = 'save',
- tag = 'block',
- copy = true,
- pos = childBlock.start,
- }
- local outStep = {
- type = 'save',
- tag = 'out',
- copy = true,
- pos = childBlock.start,
- }
- self.steps[#self.steps+1] = blockStep
- self.steps[#self.steps+1] = outStep
- self.steps[#self.steps+1] = {
- type = 'push',
- ref1 = blockStep,
- pos = childBlock.start,
- }
- self:_compileNarrowByFilter(childBlock.filter, outStep, blockStep)
- if not childBlock.hasReturn
- and not childBlock.hasGoTo
- and not childBlock.hasBreak then
- local finalStep = {
- type = 'save',
- pos = childBlock.finish,
- tag = 'final #' .. i,
- }
- finals[#finals+1] = finalStep
- self.steps[#self.steps+1] = finalStep
- end
- self.steps[#self.steps+1] = {
- type = 'push',
- tag = 'reset child',
- ref1 = outStep,
- pos = childBlock.finish,
- }
- end
- self.steps[#self.steps+1] = {
- type = 'push',
- tag = 'reset if',
- pos = block.finish,
- copy = true,
- }
- for _, final in ipairs(finals) do
- self.steps[#self.steps+1] = {
- type = 'merge',
- ref2 = final,
- pos = block.finish,
- }
- end
- end
-
- if block.type == 'function'
- or block.type == 'while'
- or block.type == 'loop'
- or block.type == 'in'
- or block.type == 'repeat'
- or block.type == 'for' then
- local savePoint = {
- type = 'save',
- copy = true,
- pos = block.start,
- }
- self.steps[#self.steps+1] = {
- type = 'push',
- copy = true,
- pos = block.start,
- }
- self.steps[#self.steps+1] = savePoint
- self.steps[#self.steps+1] = {
- type = 'push',
- pos = block.finish,
- ref1 = savePoint,
- }
- end
-end
+mt._index = 1
---@return parser.object[]
function mt:_getCasts()
- local root = guide.getRoot(self.loc)
+ local root = guide.getRoot(self._loc)
if not root._casts then
root._casts = {}
local docs = root.docs
@@ -265,180 +27,94 @@ function mt:_getCasts()
return root._casts
end
-function mt:_preCompile()
- local startPos = self.loc.start
+function mt:_collect()
+ local startPos = self._loc.start
local finishPos = 0
- for _, ref in ipairs(self.loc.ref) do
- self.steps[#self.steps+1] = {
- type = 'object',
- object = ref,
- pos = ref.range or ref.start,
- }
- if ref.start > finishPos then
- finishPos = ref.start
+ for _, ref in ipairs(self._loc.ref) do
+ if ref.type == 'getlocal'
+ or ref.type == 'setlocal' then
+ self._objs[#self._objs+1] = ref
+ if ref.start > finishPos then
+ finishPos = ref.start
+ end
end
- local block = guide.getParentBlock(ref)
- self:_compileBlock(block)
end
- for i, step in ipairs(self.steps) do
- if step.type ~= 'object' then
- step.order = i
- end
+ if #self._objs == 0 then
+ return
end
local casts = self:_getCasts()
for _, cast in ipairs(casts) do
- if cast.loc[1] == self.loc[1]
+ if cast.loc[1] == self._loc[1]
and cast.start > startPos
and cast.finish < finishPos
- and guide.getLocal(self.loc, self.loc[1], cast.start) == self.loc then
- self.steps[#self.steps+1] = {
- type = 'cast',
- cast = cast,
- pos = cast.start,
- }
+ and guide.getLocal(self._loc, self._loc[1], cast.start) == self._loc then
+ self._objs[#self._objs+1] = cast
end
end
- table.sort(self.steps, function (a, b)
- if a.pos == b.pos then
- return (a.order or 0) < (b.order or 0)
- else
- return a.pos < b.pos
- end
+ table.sort(self._objs, function (a, b)
+ return (a.range or a.start) < (b.range or b.start)
end)
end
----@param loc parser.object
+
+---@param pos integer
---@param node vm.node
+---@return parser.object
---@return vm.node
-local function checkAssert(loc, node)
- local parent = loc.parent
- if parent.type == 'binary' then
- if parent.op and (parent.op.type == '~=' or parent.op.type == '==') then
- local exp
- for i = 1, 2 do
- if parent[i] == loc then
- exp = parent[i % 2 + 1]
- end
- end
- if exp and guide.isLiteral(exp) then
- local callargs = parent.parent
- if callargs.type == 'callargs'
- and callargs.parent.node.special == 'assert'
- and callargs[1] == parent then
- if parent.op.type == '~=' then
- node:remove(exp.type)
- end
- if parent.op.type == '==' then
- node = vm.compileNode(exp)
- end
- end
- end
+function mt:_fastWard(pos, node)
+ for i = self._index, #self._objs do
+ local obj = self._objs[i]
+ if obj.start > pos then
+ self._index = i
+ return obj, node
+ end
+ if obj.type == 'getlocal'
+ or obj.type == 'setlocal' then
+ node = self._callback(obj, node) or node
+ else
+ error('unexpected type: ' .. obj.type)
end
end
- if parent.type == 'callargs'
- and parent.parent.node.special == 'assert'
- and parent[1] == loc then
- node:setTruthy()
- end
- return node
end
----@param callback fun(src: parser.object, node: vm.node)
-function mt:launch(callback)
- local topNode = vm.getNode(self.loc):copy()
- for _, step in ipairs(self.steps) do
- local node = step.ref1 and step.ref1.node or topNode
- if step.type == 'truthy' then
- if step.new then
- node = node:copy()
- topNode = node
- end
- node:setTruthy()
- elseif step.type == 'falsy' then
- if step.new then
- node = node:copy()
- topNode = node
- end
- node:setFalsy()
- elseif step.type == 'as' then
- if step.new then
- topNode = vm.createNode(vm.getGlobal('type', step.name))
- else
- node:clear()
- node:merge(vm.getGlobal('type', step.name))
- end
- elseif step.type == 'add' then
- if step.new then
- node = node:copy()
- topNode = node
- end
- node:merge(vm.getGlobal('type', step.name))
- elseif step.type == 'remove' then
- if step.new then
- node = node:copy()
- topNode = node
- end
- node:remove(step.name)
- elseif step.type == 'object' then
- topNode = callback(step.object, node) or node
- if step.object.type == 'getlocal' then
- topNode = checkAssert(step.object, node)
- end
- elseif step.type == 'save' then
- if step.copy then
- node = node:copy()
- end
- step.node = node
- elseif step.type == 'push' then
- if step.copy then
- node = node:copy()
- end
- topNode = node
- elseif step.type == 'merge' then
- node:merge(step.ref2.node)
- elseif step.type == 'cast' then
- topNode = node:copy()
- for _, cast in ipairs(step.cast.casts) do
- if cast.mode == '+' then
- if cast.optional then
- topNode:addOptional()
- end
- if cast.extends then
- topNode:merge(vm.compileNode(cast.extends))
- end
- elseif cast.mode == '-' then
- if cast.optional then
- topNode:removeOptional()
- end
- if cast.extends then
- topNode:removeNode(vm.compileNode(cast.extends))
- end
- else
- if cast.extends then
- topNode:clear()
- topNode:merge(vm.compileNode(cast.extends))
- end
- end
- end
+---@param block parser.object
+---@param node vm.node
+function mt:_launchBlock(block, node)
+ local top, topNode = self:_fastWard(block.start, node)
+ if not top then
+ return
+ end
+ for _, action in ipairs(block) do
+ if action.finish < top.start then
+ goto CONTINUE
+ end
+ top, topNode = self:_fastWard(action.finish, topNode)
+ if not top then
+ return
end
+ ::CONTINUE::
end
+ self:_fastWard(math.huge, topNode)
end
---@param loc parser.object
----@return vm.runner
-function vm.createRunner(loc)
+---@param callback vm.runner.callback
+function vm.launchRunner(loc, callback)
local self = setmetatable({
- loc = loc,
- mainBlock = guide.getParentBlock(loc),
- blocks = {},
- steps = {},
+ _loc = loc,
+ _objs = {},
+ _callback = callback,
}, mt)
- self:_preCompile()
+ self:_collect()
+
+ if #self._objs == 0 then
+ return
+ end
- return self
+ self:_launchBlock(guide.getParentBlock(loc), vm.getNode(loc))
end
diff --git a/script/vm/sign.lua b/script/vm/sign.lua
index fe112bc2..5c17aa3d 100644
--- a/script/vm/sign.lua
+++ b/script/vm/sign.lua
@@ -111,7 +111,7 @@ function mt:resolve(uri, args, removeGeneric)
goto CONTINUE
end
end
- local view = vm.viewObject(obj)
+ local view = vm.viewObject(obj, uri)
if view then
knownTypes[view] = true
end
@@ -130,7 +130,7 @@ function mt:resolve(uri, args, removeGeneric)
if argNode:hasFalsy() then
goto CONTINUE
end
- local view = vm.viewObject(n)
+ local view = vm.viewObject(n, uri)
if knownTypes[view] then
goto CONTINUE
end