stuff
This commit is contained in:
parent
f0a9da387a
commit
a45aa2624e
8
LIB/init.lua
Normal file
8
LIB/init.lua
Normal file
@ -0,0 +1,8 @@
|
||||
local path = ...
|
||||
if path:sub(-4) == "init" then
|
||||
path = path:sub(1, -6)
|
||||
if not (path) then
|
||||
path = "."
|
||||
end
|
||||
end
|
||||
return require(tostring(path) .. "/slab")
|
7
LIB/init.moon
Normal file
7
LIB/init.moon
Normal file
@ -0,0 +1,7 @@
|
||||
path = ...
|
||||
|
||||
if path\sub(-4) == "init"
|
||||
path = path\sub 1, -6
|
||||
path = "." unless path
|
||||
|
||||
return require "#{path}/slab"
|
180
LIB/slab.lua
Normal file
180
LIB/slab.lua
Normal file
@ -0,0 +1,180 @@
|
||||
local path = (...):sub(1, -(("slab"):len() + 2))
|
||||
local graphics
|
||||
graphics = love.graphics
|
||||
local slab = {
|
||||
_VERSION = "0.1.0",
|
||||
_DESCRIPTION = "GUI library for LÖVE.",
|
||||
_URL = nil,
|
||||
_LICENSE = "The MIT License (MIT)",
|
||||
_AUTHOR = "Paul Liverman III"
|
||||
}
|
||||
slab.ui = {
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = graphics.getWidth(),
|
||||
h = graphics.getHeight()
|
||||
}
|
||||
slab.hovered = false
|
||||
slab.focused = false
|
||||
slab.types = {
|
||||
"menu",
|
||||
"spinner",
|
||||
"text"
|
||||
}
|
||||
slab.generic = require(tostring(path) .. "/types/generic")
|
||||
local _list_0 = slab.types
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local t = _list_0[_index_0]
|
||||
slab[t] = require(tostring(path) .. "/types/" .. tostring(t))
|
||||
end
|
||||
slab.make = function(element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
if parent == nil then
|
||||
parent = slab.ui
|
||||
end
|
||||
local _list_1 = slab.types
|
||||
for _index_0 = 1, #_list_1 do
|
||||
local t = _list_1[_index_0]
|
||||
if element[t] then
|
||||
return slab[t](element, parent)
|
||||
end
|
||||
end
|
||||
return slab.generic(element, parent)
|
||||
end
|
||||
slab.update = function(dt, element)
|
||||
if element == nil then
|
||||
element = slab.ui
|
||||
end
|
||||
if element.update then
|
||||
element:update(dt)
|
||||
end
|
||||
for _index_0 = 1, #element do
|
||||
local child = element[_index_0]
|
||||
slab.update(dt, child)
|
||||
end
|
||||
end
|
||||
slab.draw = function(element)
|
||||
if element == nil then
|
||||
element = slab.ui
|
||||
end
|
||||
if element == slab.ui then
|
||||
graphics.push("all")
|
||||
graphics.origin()
|
||||
end
|
||||
local drawChildren
|
||||
if element.visible and element.draw then
|
||||
drawChildren = element:draw()
|
||||
end
|
||||
if drawChildren ~= false then
|
||||
for _index_0 = 1, #element do
|
||||
local child = element[_index_0]
|
||||
slab.draw(child)
|
||||
end
|
||||
end
|
||||
if element == slab.ui then
|
||||
return graphics.pop()
|
||||
end
|
||||
end
|
||||
slab.mousemoved = function(x, y, dx, dy, element)
|
||||
if element == nil then
|
||||
element = slab.ui
|
||||
end
|
||||
local previous
|
||||
if element == slab.ui then
|
||||
previous = slab.hovered
|
||||
end
|
||||
if element.visible and element.hoverable and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
slab.hovered = element
|
||||
for _index_0 = 1, #element do
|
||||
local child = element[_index_0]
|
||||
slab.mousemoved(x, y, dx, dy, child)
|
||||
end
|
||||
end
|
||||
if element == slab.ui then
|
||||
if slab.hovered ~= previous then
|
||||
if previous and previous.hovered then
|
||||
previous:hovered(false)
|
||||
end
|
||||
if slab.hovered.hovered then
|
||||
slab.hovered:hovered(true)
|
||||
end
|
||||
end
|
||||
if slab.focused and slab.focused.mousemoved then
|
||||
return slab.focused:mousemoved(x - slab.focused.x, y - slab.focused.y, dx, dy)
|
||||
end
|
||||
end
|
||||
end
|
||||
slab.mousepressed = function(x, y, btn, element)
|
||||
if element == nil then
|
||||
element = screen.ui
|
||||
end
|
||||
if element == screen.ui then
|
||||
if btn == "wd" then
|
||||
return slab.wheelmoved(0, -1)
|
||||
elseif btn == "wu" then
|
||||
return slab.wheelmoved(0, 1)
|
||||
end
|
||||
end
|
||||
local handled = false
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
for i = #element, 1, -1 do
|
||||
handled = slab.mousepressed(x, y, btn, element[i])
|
||||
if handled ~= false then
|
||||
return handled
|
||||
end
|
||||
end
|
||||
if element.mousepressed then
|
||||
handled = element:mousepressed(btn, x, y)
|
||||
end
|
||||
end
|
||||
return handled
|
||||
end
|
||||
slab.mousereleased = function(x, y, btn, element)
|
||||
local clickHandled, mousereleaseHandled = false, false
|
||||
if element then
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
for i = #element, 1, -1 do
|
||||
clickHandled, mousereleaseHandled = slab.mousereleased(x, y, btn, element[i])
|
||||
if clickHandled ~= false or mousereleaseHandled ~= false then
|
||||
return clickHandled, mousereleaseHandled
|
||||
end
|
||||
end
|
||||
if element.clicked then
|
||||
clickHandled = element:clicked(btn, x - element.x, y - element.y)
|
||||
end
|
||||
if element.mousereleased then
|
||||
mousereleaseHandled = element:mousereleased(btn, x - element.x, y - element.y)
|
||||
end
|
||||
if clickHandled then
|
||||
slab.focused = element
|
||||
end
|
||||
end
|
||||
else
|
||||
do
|
||||
element = slab.focused
|
||||
if element then
|
||||
if element.mousereleased then
|
||||
mousereleaseHandled = element:mousereleased(btn, x - element.x, y - element.y)
|
||||
end
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
if element.clicked then
|
||||
clickHandled = element:clicked(btn, x - element.x, y - element.y)
|
||||
end
|
||||
end
|
||||
if clickHandled ~= false or mousereleaseHandled ~= false then
|
||||
return clickHandled, mousereleaseHandled
|
||||
end
|
||||
return slab.mousereleased(x, y, btn, slab.ui)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
slab.wheelmoved = function(x, y)
|
||||
if slab.hovered and slab.hovered.wheelmoved then
|
||||
return slab.hovered:wheelmoved(x, y)
|
||||
end
|
||||
return false
|
||||
end
|
||||
return slab
|
108
LIB/slab.moon
Normal file
108
LIB/slab.moon
Normal file
@ -0,0 +1,108 @@
|
||||
path = (...)\sub 1, -("slab"\len! + 2)
|
||||
|
||||
import graphics from love
|
||||
|
||||
slab = {
|
||||
_VERSION: "0.1.0"
|
||||
_DESCRIPTION: "GUI library for LÖVE."
|
||||
_URL: nil
|
||||
_LICENSE: "The MIT License (MIT)"
|
||||
_AUTHOR: "Paul Liverman III"
|
||||
}
|
||||
|
||||
slab.ui = { x: 0, y: 0, w: graphics.getWidth!, h: graphics.getHeight! }
|
||||
slab.hovered = false
|
||||
slab.focused = false
|
||||
slab.types = { "menu", "spinner", "text" }
|
||||
slab.generic = require "#{path}/types/generic"
|
||||
for t in *slab.types
|
||||
slab[t] = require "#{path}/types/#{t}"
|
||||
-- slab[t]\load slab if slab[t].load
|
||||
|
||||
slab.make = (element={}, parent=slab.ui) ->
|
||||
for t in *slab.types
|
||||
return slab[t](element, parent) if element[t]
|
||||
return slab.generic element, parent
|
||||
|
||||
slab.update = (dt, element=slab.ui) ->
|
||||
element\update dt if element.update
|
||||
for child in *element
|
||||
slab.update dt, child
|
||||
|
||||
slab.draw = (element=slab.ui) ->
|
||||
if element == slab.ui
|
||||
graphics.push "all"
|
||||
graphics.origin!
|
||||
|
||||
local drawChildren
|
||||
drawChildren = element\draw! if element.visible and element.draw
|
||||
if drawChildren != false
|
||||
for child in *element
|
||||
slab.draw child
|
||||
|
||||
if element == slab.ui
|
||||
graphics.pop!
|
||||
|
||||
slab.mousemoved = (x, y, dx, dy, element=slab.ui) ->
|
||||
local previous
|
||||
if element == slab.ui
|
||||
previous = slab.hovered
|
||||
|
||||
if element.visible and element.hoverable and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
slab.hovered = element
|
||||
for child in *element
|
||||
slab.mousemoved x, y, dx, dy, child
|
||||
|
||||
if element == slab.ui
|
||||
if slab.hovered != previous
|
||||
previous\hovered false if previous and previous.hovered
|
||||
slab.hovered\hovered true if slab.hovered.hovered
|
||||
if slab.focused and slab.focused.mousemoved
|
||||
return slab.focused\mousemoved x - slab.focused.x, y - slab.focused.y, dx, dy
|
||||
|
||||
slab.mousepressed = (x, y, btn, element=screen.ui) ->
|
||||
if element == screen.ui
|
||||
if btn == "wd"
|
||||
return slab.wheelmoved 0, -1
|
||||
elseif btn == "wu"
|
||||
return slab.wheelmoved 0, 1
|
||||
|
||||
handled = false
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
for i = #element, 1, -1
|
||||
handled = slab.mousepressed x, y, btn, element[i]
|
||||
return handled if handled != false
|
||||
|
||||
if element.mousepressed
|
||||
handled = element\mousepressed btn, x, y
|
||||
|
||||
return handled
|
||||
|
||||
slab.mousereleased = (x, y, btn, element) ->
|
||||
clickHandled, mousereleaseHandled = false, false
|
||||
if element
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
for i = #element, 1, -1
|
||||
clickHandled, mousereleaseHandled = slab.mousereleased x, y, btn, element[i]
|
||||
if clickHandled != false or mousereleaseHandled != false
|
||||
return clickHandled, mousereleaseHandled
|
||||
|
||||
clickHandled = element\clicked btn, x - element.x, y - element.y if element.clicked
|
||||
mousereleaseHandled = element\mousereleased btn, x - element.x, y - element.y if element.mousereleased
|
||||
slab.focused = element if clickHandled
|
||||
|
||||
elseif element = slab.focused
|
||||
mousereleaseHandled = element\mousereleased btn, x - element.x, y - element.y if element.mousereleased
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
clickHandled = element\clicked btn, x - element.x, y - element.y if element.clicked
|
||||
|
||||
if clickHandled != false or mousereleaseHandled != false
|
||||
return clickHandled, mousereleaseHandled
|
||||
|
||||
slab.mousereleased x, y, btn, slab.ui
|
||||
|
||||
slab.wheelmoved = (x, y) ->
|
||||
return slab.hovered\wheelmoved x, y if slab.hovered and slab.hovered.wheelmoved
|
||||
return false
|
||||
|
||||
return slab
|
122
LIB/types/generic.lua
Normal file
122
LIB/types/generic.lua
Normal file
@ -0,0 +1,122 @@
|
||||
local path = (...):sub(1, -(("generic"):len() + 2))
|
||||
local floor
|
||||
floor = math.floor
|
||||
local generic
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
draw = function(self)
|
||||
graphics.setColor(self.color)
|
||||
if self.background then
|
||||
return graphics.draw(self.background, self.x, self.y, 0, self.w / self.backgroundWidth, self.h / self.backgroundHeight)
|
||||
else
|
||||
return graphics.rectangle("fill", self.x, self.y, self.w, self.h)
|
||||
end
|
||||
end,
|
||||
_align = function(self)
|
||||
local _exp_0 = self.align
|
||||
if "top-left" == _exp_0 or "left" == _exp_0 or "bottom-left" == _exp_0 then
|
||||
self.x = self.parent.x
|
||||
elseif "top" == _exp_0 or "center" == _exp_0 or "bottom" == _exp_0 then
|
||||
self.x = self.parent.x + self.parent.w / 2 - self.w / 2
|
||||
elseif "top-right" == _exp_0 or "right" == _exp_0 or "bottom-right" == _exp_0 then
|
||||
self.x = self.parent.x + self.parent.w - self.w
|
||||
end
|
||||
local _exp_1 = self.align
|
||||
if "top-left" == _exp_1 or "top" == _exp_1 or "top-right" == _exp_1 then
|
||||
self.y = self.parent.y
|
||||
elseif "left" == _exp_1 or "center" == _exp_1 or "right" == _exp_1 then
|
||||
self.y = self.parent.y + self.parent.h / 2 - self.h / 2
|
||||
elseif "bottom-left" == _exp_1 or "bottom" == _exp_1 or "bottom-right" == _exp_1 then
|
||||
self.y = self.parent.y + self.parent.h - self.h
|
||||
end
|
||||
for _index_0 = 1, #self do
|
||||
local child = self[_index_0]
|
||||
child:_align()
|
||||
end
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
if not (parent) then
|
||||
error("No parent element!")
|
||||
end
|
||||
self.parent = parent
|
||||
for k, v in pairs(element) do
|
||||
self[k] = v
|
||||
end
|
||||
if self.visible == nil then
|
||||
self.visible = true
|
||||
end
|
||||
if self.hovered then
|
||||
self.hoverable = true
|
||||
end
|
||||
if self.hoverable == nil then
|
||||
self.hoverable = false
|
||||
end
|
||||
if self.width then
|
||||
if self.width <= 1 then
|
||||
self.w = floor(parent.w * self.width)
|
||||
else
|
||||
self.w = floor(self.width)
|
||||
end
|
||||
else
|
||||
self.width = 0
|
||||
self.w = 0
|
||||
end
|
||||
if self.height then
|
||||
if self.height <= 1 then
|
||||
self.h = floor(parent.h * self.height)
|
||||
else
|
||||
self.h = floor(self.height)
|
||||
end
|
||||
else
|
||||
self.height = 0
|
||||
self.h = 0
|
||||
end
|
||||
if "table" == type(self.background) then
|
||||
if not (self.color) then
|
||||
self.color = self.background
|
||||
end
|
||||
self.background = nil
|
||||
else
|
||||
if not (self.color) then
|
||||
self.color = {
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
}
|
||||
end
|
||||
end
|
||||
if self.background then
|
||||
self.backgroundWidth = self.background:getWidth()
|
||||
self.backgroundHeight = self.background:getHeight()
|
||||
self.aspectRatio = self.backgroundWidth / self.backgroundHeight
|
||||
end
|
||||
if #self > 0 then
|
||||
local slab = require(tostring(path:sub(1, -7)) .. "/slab")
|
||||
for i = 1, #self do
|
||||
self[i] = slab.make(self[i], self)
|
||||
end
|
||||
end
|
||||
return self:_align()
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "generic"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
generic = _class_0
|
||||
return _class_0
|
||||
end
|
88
LIB/types/generic.moon
Normal file
88
LIB/types/generic.moon
Normal file
@ -0,0 +1,88 @@
|
||||
path = (...)\sub 1, -("generic"\len! + 2)
|
||||
|
||||
import floor from math
|
||||
|
||||
-- local slab
|
||||
class generic
|
||||
-- @load: (lib) =>
|
||||
-- print "LOAD", self, lib
|
||||
-- slab = lib
|
||||
|
||||
new: (element={}, parent) =>
|
||||
error "No parent element!" unless parent
|
||||
@parent = parent
|
||||
for k,v in pairs element
|
||||
@[k] = v
|
||||
|
||||
@visible = true if @visible == nil
|
||||
@hoverable = true if @hovered
|
||||
@hoverable = false if @hoverable == nil
|
||||
|
||||
if @width
|
||||
if @width <= 1
|
||||
@w = floor parent.w * @width
|
||||
else
|
||||
@w = floor(@width)
|
||||
else
|
||||
@width = 0
|
||||
@w = 0
|
||||
|
||||
if @height
|
||||
if @height <= 1
|
||||
@h = floor parent.h * @height
|
||||
else
|
||||
@h = floor(@height)
|
||||
else
|
||||
@height = 0
|
||||
@h = 0
|
||||
|
||||
if "table" == type @background
|
||||
@color = @background unless @color
|
||||
@background = nil
|
||||
else
|
||||
@color = {255, 255, 255, 255} unless @color
|
||||
|
||||
if @background
|
||||
@backgroundWidth = @background\getWidth!
|
||||
@backgroundHeight = @background\getHeight!
|
||||
@aspectRatio = @backgroundWidth / @backgroundHeight
|
||||
|
||||
if #@ > 0
|
||||
slab = require "#{path\sub 1, -7}/slab"
|
||||
for i = 1, #@
|
||||
@[i] = slab.make(@[i], @)
|
||||
|
||||
@_align!
|
||||
|
||||
draw: =>
|
||||
graphics.setColor @color
|
||||
if @background
|
||||
graphics.draw @background, @x, @y, 0, @w / @backgroundWidth, @h / @backgroundHeight
|
||||
else
|
||||
graphics.rectangle "fill", @x, @y, @w, @h
|
||||
|
||||
_align: =>
|
||||
switch @align
|
||||
when "top-left", "left", "bottom-left"
|
||||
-- align left
|
||||
@x = @parent.x
|
||||
when "top", "center", "bottom"
|
||||
-- align center
|
||||
@x = @parent.x + @parent.w / 2 - @w / 2
|
||||
when "top-right", "right", "bottom-right"
|
||||
-- align right
|
||||
@x = @parent.x + @parent.w - @w
|
||||
|
||||
switch @align
|
||||
when "top-left", "top", "top-right"
|
||||
-- align top
|
||||
@y = @parent.y
|
||||
when "left", "center", "right"
|
||||
-- align center
|
||||
@y = @parent.y + @parent.h / 2 - @h / 2
|
||||
when "bottom-left", "bottom", "bottom-right"
|
||||
-- align bottom
|
||||
@y = @parent.y + @parent.h - @h
|
||||
|
||||
for child in *@
|
||||
child\_align!
|
72
LIB/types/menu.lua
Normal file
72
LIB/types/menu.lua
Normal file
@ -0,0 +1,72 @@
|
||||
local path = (...):sub(1, -(("menu"):len() + 2))
|
||||
local generic = require(tostring(path) .. "/generic")
|
||||
local insert
|
||||
insert = table.insert
|
||||
local menu
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = generic
|
||||
local _base_0 = { }
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
_class_0.__parent.__init(self, element, parent)
|
||||
for _index_0 = 1, #self do
|
||||
local child = self[_index_0]
|
||||
if self.menu.width then
|
||||
if not (child.width) then
|
||||
child.width = self.menu.width
|
||||
end
|
||||
end
|
||||
if self.menu.height then
|
||||
if not (child.height) then
|
||||
child.height = self.menu.height
|
||||
end
|
||||
end
|
||||
if self.menu.align then
|
||||
if not (child.align) then
|
||||
child.align = self.menu.align
|
||||
end
|
||||
end
|
||||
end
|
||||
if #self.menu > 0 then
|
||||
local slab = require(tostring(path:sub(1, -7)) .. "/slab")
|
||||
local _list_0 = self.menu
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local child = _list_0[_index_0]
|
||||
insert(self, slab.make(child, self))
|
||||
end
|
||||
end
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "menu",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
menu = _class_0
|
||||
return _class_0
|
||||
end
|
21
LIB/types/menu.moon
Normal file
21
LIB/types/menu.moon
Normal file
@ -0,0 +1,21 @@
|
||||
path = (...)\sub 1, -("menu"\len! + 2)
|
||||
generic = require "#{path}/generic"
|
||||
|
||||
import insert from table
|
||||
|
||||
class menu extends generic
|
||||
new: (element={}, parent) =>
|
||||
super element, parent
|
||||
|
||||
for child in *@
|
||||
if @menu.width
|
||||
child.width = @menu.width unless child.width
|
||||
if @menu.height
|
||||
child.height = @menu.height unless child.height
|
||||
if @menu.align
|
||||
child.align = @menu.align unless child.align
|
||||
|
||||
if #@menu > 0
|
||||
slab = require "#{path\sub 1, -7}/slab"
|
||||
for child in *@menu
|
||||
insert(@, slab.make child, @)
|
61
LIB/types/spinner.lua
Normal file
61
LIB/types/spinner.lua
Normal file
@ -0,0 +1,61 @@
|
||||
local path = (...):sub(1, -(("spinner"):len() + 2))
|
||||
local generic = require(tostring(path) .. "/generic")
|
||||
local graphics
|
||||
graphics = love.graphics
|
||||
local pi, min
|
||||
do
|
||||
local _obj_0 = math
|
||||
pi, min = _obj_0.pi, _obj_0.min
|
||||
end
|
||||
local tau = pi * 2
|
||||
local half_pi = pi / 2
|
||||
local spinner
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = generic
|
||||
local _base_0 = {
|
||||
draw = function(self)
|
||||
return graphics.arc("line", self.x + self.w / 2, self.y + self.h / 2, min(self.w, self.h), self.offset, self.offset + self.value * tau)
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
_class_0.__parent.__init(self, element, parent)
|
||||
if not (self.offset) then
|
||||
self.offset = 0
|
||||
end
|
||||
self.value = 0.1
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "spinner",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
spinner = _class_0
|
||||
return _class_0
|
||||
end
|
21
LIB/types/spinner.moon
Normal file
21
LIB/types/spinner.moon
Normal file
@ -0,0 +1,21 @@
|
||||
path = (...)\sub 1, -("spinner"\len! + 2)
|
||||
generic = require "#{path}/generic"
|
||||
|
||||
import graphics from love
|
||||
import pi, min from math
|
||||
tau = pi * 2
|
||||
half_pi = pi / 2
|
||||
|
||||
class spinner extends generic
|
||||
new: (element={}, parent) =>
|
||||
super element, parent
|
||||
|
||||
-- @offset = -half_pi
|
||||
@offset = 0 unless @offset
|
||||
@value = 0.1 -- temporary
|
||||
|
||||
-- slab = require "#{path\sub 1, -7}/slab"
|
||||
|
||||
draw: =>
|
||||
-- TODO should be setting color!
|
||||
graphics.arc "line", @x + @w / 2, @y + @h / 2, min(@w, @h), @offset, @offset + @value * tau
|
48
LIB/types/text.lua
Normal file
48
LIB/types/text.lua
Normal file
@ -0,0 +1,48 @@
|
||||
local path = (...):sub(1, -(("text"):len() + 2))
|
||||
local generic = require(tostring(path) .. "/generic")
|
||||
local graphics
|
||||
graphics = love.graphics
|
||||
local text
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = generic
|
||||
local _base_0 = {
|
||||
draw = function(self) end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
return _class_0.__parent.__init(self, element, parent)
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "text",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
text = _class_0
|
||||
return _class_0
|
||||
end
|
15
LIB/types/text.moon
Normal file
15
LIB/types/text.moon
Normal file
@ -0,0 +1,15 @@
|
||||
path = (...)\sub 1, -("text"\len! + 2)
|
||||
generic = require "#{path}/generic"
|
||||
|
||||
import graphics from love
|
||||
|
||||
class text extends generic
|
||||
new: (element={}, parent) =>
|
||||
super element, parent
|
||||
|
||||
-- @font = graphics.newFont 14
|
||||
|
||||
-- slab = require "#{path\sub 1, -7}/slab"
|
||||
|
||||
draw: =>
|
||||
-- TODO
|
4
main.lua
Normal file
4
main.lua
Normal file
@ -0,0 +1,4 @@
|
||||
for k in pairs(love.handlers) do
|
||||
print(k)
|
||||
end
|
||||
return print(type(love.graphics.newImage("prototypes/scp-clicker/gfx/banknote.png")))
|
@ -1,2 +1,4 @@
|
||||
for k in pairs love.handlers
|
||||
print k
|
||||
|
||||
print type love.graphics.newImage "prototypes/scp-clicker/gfx/banknote.png"
|
||||
|
48
prototypes/examples/app-bar.lua
Normal file
48
prototypes/examples/app-bar.lua
Normal file
@ -0,0 +1,48 @@
|
||||
local _ = {
|
||||
toolbar = {
|
||||
width = 1,
|
||||
padding = 5,
|
||||
{
|
||||
dropdown = {
|
||||
toggle = "click",
|
||||
background = hamburger,
|
||||
background = {
|
||||
hamburger,
|
||||
close_x
|
||||
},
|
||||
{ }
|
||||
}
|
||||
},
|
||||
{
|
||||
text = "Title",
|
||||
clicked = function(btn, x, y) end
|
||||
},
|
||||
{
|
||||
align = "right",
|
||||
dropdown = {
|
||||
toggle = "click",
|
||||
background = down_arrow,
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
toolbar = {
|
||||
padding = 5,
|
||||
{
|
||||
background = close_x
|
||||
},
|
||||
{
|
||||
textinput = {
|
||||
name = "title",
|
||||
value = "Title"
|
||||
}
|
||||
},
|
||||
{
|
||||
align = "right",
|
||||
text = "SAVE",
|
||||
clicked = function(btn, x, y) end
|
||||
}
|
||||
}
|
||||
}
|
18
prototypes/examples/badge.lua
Normal file
18
prototypes/examples/badge.lua
Normal file
@ -0,0 +1,18 @@
|
||||
return {
|
||||
background = some_image,
|
||||
badge = {
|
||||
text = 2,
|
||||
background = {
|
||||
255,
|
||||
0,
|
||||
0,
|
||||
255
|
||||
},
|
||||
color = {
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
}
|
||||
}
|
||||
}
|
17
prototypes/examples/bottom-navigation.lua
Normal file
17
prototypes/examples/bottom-navigation.lua
Normal file
@ -0,0 +1,17 @@
|
||||
return {
|
||||
toolbar = {
|
||||
width = 0.5,
|
||||
padding = 3,
|
||||
{
|
||||
background = recents_icon
|
||||
},
|
||||
{
|
||||
align = "center",
|
||||
background = favorites_icon
|
||||
},
|
||||
{
|
||||
align = "right",
|
||||
background = nearby_icon
|
||||
}
|
||||
}
|
||||
}
|
42
prototypes/examples/buttons.lua
Normal file
42
prototypes/examples/buttons.lua
Normal file
@ -0,0 +1,42 @@
|
||||
local _ = {
|
||||
text = "click me"
|
||||
}
|
||||
_ = {
|
||||
padding = 3,
|
||||
text = "button",
|
||||
background = {
|
||||
0,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
},
|
||||
color = {
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
}
|
||||
}
|
||||
_ = {
|
||||
padding = 8,
|
||||
text = "+",
|
||||
background = {
|
||||
0,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
},
|
||||
color = {
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
},
|
||||
round = true,
|
||||
round = 5
|
||||
}
|
||||
return {
|
||||
background = some_image,
|
||||
clicked = function(btn, x, y) end,
|
||||
hovered = function(x, y) end
|
||||
}
|
19
prototypes/examples/chip.lua
Normal file
19
prototypes/examples/chip.lua
Normal file
@ -0,0 +1,19 @@
|
||||
return {
|
||||
rounded = 5,
|
||||
background = some_color,
|
||||
padding = {
|
||||
right = 5
|
||||
},
|
||||
{
|
||||
rounded = true,
|
||||
background = an_image
|
||||
},
|
||||
{
|
||||
text = "Colored Chip"
|
||||
},
|
||||
{
|
||||
width = 0.8,
|
||||
rounded = true,
|
||||
background = an_x_image
|
||||
}
|
||||
}
|
8
prototypes/examples/divider.lua
Normal file
8
prototypes/examples/divider.lua
Normal file
@ -0,0 +1,8 @@
|
||||
local _ = {
|
||||
text = "some text"
|
||||
}
|
||||
return {
|
||||
newline = true,
|
||||
height = 1.0001,
|
||||
background = dark_grey
|
||||
}
|
72
prototypes/scp-clicker/main.lua
Normal file
72
prototypes/scp-clicker/main.lua
Normal file
@ -0,0 +1,72 @@
|
||||
local graphics
|
||||
graphics = love.graphics
|
||||
local slab = require("slab")
|
||||
local clickers = {
|
||||
width = 1 / 3,
|
||||
height = 1,
|
||||
align = "left",
|
||||
menu = {
|
||||
height = 1 / 3,
|
||||
align = "center",
|
||||
{
|
||||
name = "cash_clicker",
|
||||
clicked = function(self, btn, x, y) end,
|
||||
spinner = {
|
||||
value = 0
|
||||
},
|
||||
{
|
||||
size = 0.8,
|
||||
align = "center",
|
||||
background = graphics.newImage("gfx/banknote.png")
|
||||
},
|
||||
{
|
||||
align = "bottom-left",
|
||||
text = "Lv. 0"
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "danger_clicker",
|
||||
clicked = function(self, btn, x, y) end,
|
||||
spinner = {
|
||||
value = 0
|
||||
},
|
||||
{
|
||||
size = 0.8,
|
||||
align = "center",
|
||||
background = graphics.newImage("gfx/hazard-sign.png")
|
||||
},
|
||||
{
|
||||
align = "bottom-left",
|
||||
text = "Lv. 0"
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "research_clicker",
|
||||
clicked = function(self, btn, x, y) end,
|
||||
spinner = {
|
||||
value = 0
|
||||
},
|
||||
{
|
||||
size = 0.8,
|
||||
align = "center",
|
||||
background = graphics.newImage("gfx/fizzing-flask.png")
|
||||
},
|
||||
{
|
||||
align = "bottom-left",
|
||||
text = "Lv. 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
slab.make(clickers)
|
||||
love.update = function(dt)
|
||||
return slab.update(dt)
|
||||
end
|
||||
love.draw = function()
|
||||
return slab.draw()
|
||||
end
|
||||
love.keypressed = function(key)
|
||||
if key == "escape" then
|
||||
return love.event.quit()
|
||||
end
|
||||
end
|
@ -1,104 +1,72 @@
|
||||
import graphics from love
|
||||
|
||||
slab = {
|
||||
types: { "menu", "spinner" }
|
||||
screen: { x: 0, y: 0, width: graphics.getWidth!, height: graphics.getHeight! }
|
||||
}
|
||||
|
||||
slab.generic = class
|
||||
new: (element={}, parent=slab.screen) =>
|
||||
for k,v in pairs element
|
||||
@[k] = v
|
||||
|
||||
if @width
|
||||
if @width <= 1
|
||||
@w = parent.w * @width
|
||||
else
|
||||
@w = @width
|
||||
|
||||
if @height
|
||||
if @height <= 1
|
||||
@h = parent.h * @height
|
||||
else
|
||||
@h = @height
|
||||
|
||||
slab.menu = class
|
||||
new: (parent=slab.screen) =>
|
||||
for child in *@
|
||||
if @menu.width
|
||||
child.width = @menu.width
|
||||
if @menu.height
|
||||
child.height = @menu.height
|
||||
if @menu.align
|
||||
child.align = @menu.align
|
||||
|
||||
-- TODO menus are a vertical structure
|
||||
|
||||
slab.make = (element={}, parent=slab.screen) ->
|
||||
element = slab.generic element, parent
|
||||
for t in *slab.types
|
||||
element = slab[t](parent) if element[t]
|
||||
return element
|
||||
|
||||
-- b = slab.make sample_value: true
|
||||
-- print(b.sample_value)
|
||||
slab = require "slab"
|
||||
|
||||
clickers = {
|
||||
width: 1/3
|
||||
height: 1
|
||||
menu: { -- block elements default to minimum space required
|
||||
align: "left" -- if not set, will not align!
|
||||
menu: { -- block elements default to minimum space required (but we defined it)
|
||||
height: 1/3 -- defined here applies to children
|
||||
align: "center" -- defined here means the children are aligned this way
|
||||
{
|
||||
name: "cash_clicker"
|
||||
clicked: (btn, x, y) ->
|
||||
clicked: (btn, x, y) =>
|
||||
-- stuff
|
||||
spinner: {
|
||||
value: 0 -- a defined value means it is set instead of just spinning constantly ?
|
||||
{
|
||||
width: 0.8
|
||||
align: "center"
|
||||
background: graphics.newImage "gfx/banknote.png"
|
||||
}
|
||||
{
|
||||
align: "bottom-left"
|
||||
text: "Lv. 0"
|
||||
}
|
||||
}
|
||||
{
|
||||
size: 0.8
|
||||
align: "center"
|
||||
background: graphics.newImage "gfx/banknote.png"
|
||||
}
|
||||
{
|
||||
align: "bottom-left"
|
||||
text: "Lv. 0"
|
||||
}
|
||||
}
|
||||
{
|
||||
name: "danger_clicker"
|
||||
clicked: (btn, x, y) ->
|
||||
clicked: (btn, x, y) =>
|
||||
-- stuff
|
||||
spinner: {
|
||||
value: 0
|
||||
{
|
||||
width: 0.8
|
||||
align: "center"
|
||||
background: graphics.newImage "gfx/hazard-sign.png"
|
||||
}
|
||||
{
|
||||
align: "bottom-left"
|
||||
text: "Lv. 0"
|
||||
}
|
||||
spinner: { value: 0 }
|
||||
{
|
||||
size: 0.8
|
||||
align: "center"
|
||||
background: graphics.newImage "gfx/hazard-sign.png"
|
||||
}
|
||||
{
|
||||
align: "bottom-left"
|
||||
text: "Lv. 0"
|
||||
}
|
||||
}
|
||||
{
|
||||
name: "research_clicker"
|
||||
clicked: (btn, x, y) ->
|
||||
clicked: (btn, x, y) =>
|
||||
-- stuff
|
||||
spinner: {
|
||||
value: 0
|
||||
{
|
||||
width: 0.8
|
||||
align: "center"
|
||||
background: graphics.newImage "gfx/fizzing-flask.png"
|
||||
}
|
||||
{
|
||||
align: "bottom-left"
|
||||
text: "Lv. 0"
|
||||
}
|
||||
spinner: { value: 0 }
|
||||
{
|
||||
size: 0.8
|
||||
align: "center"
|
||||
background: graphics.newImage "gfx/fizzing-flask.png"
|
||||
}
|
||||
{
|
||||
align: "bottom-left"
|
||||
text: "Lv. 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slab.make clickers
|
||||
|
||||
love.update = (dt) ->
|
||||
slab.update dt
|
||||
|
||||
love.draw = ->
|
||||
slab.draw!
|
||||
|
||||
love.keypressed = (key) ->
|
||||
if key == "escape"
|
||||
love.event.quit!
|
||||
|
5
prototypes/scp-clicker/moon/all.lua
Normal file
5
prototypes/scp-clicker/moon/all.lua
Normal file
@ -0,0 +1,5 @@
|
||||
local moon = require("moon")
|
||||
for k, v in pairs(moon) do
|
||||
_G[k] = v
|
||||
end
|
||||
return moon
|
6
prototypes/scp-clicker/moon/all.moon
Normal file
6
prototypes/scp-clicker/moon/all.moon
Normal file
@ -0,0 +1,6 @@
|
||||
-- install moon into global scope
|
||||
moon = require "moon"
|
||||
for k,v in pairs moon
|
||||
_G[k] = v
|
||||
moon
|
||||
|
175
prototypes/scp-clicker/moon/init.lua
Normal file
175
prototypes/scp-clicker/moon/init.lua
Normal file
@ -0,0 +1,175 @@
|
||||
local lua = {
|
||||
debug = debug,
|
||||
type = type
|
||||
}
|
||||
local getfenv, setfenv, dump
|
||||
do
|
||||
local _obj_0 = require("moonscript.util")
|
||||
getfenv, setfenv, dump = _obj_0.getfenv, _obj_0.setfenv, _obj_0.dump
|
||||
end
|
||||
local p, is_object, type, debug, run_with_scope, bind_methods, defaultbl, extend, copy, mixin, mixin_object, mixin_table, fold
|
||||
p = function(...)
|
||||
return print(dump(...))
|
||||
end
|
||||
is_object = function(value)
|
||||
return lua.type(value) == "table" and value.__class
|
||||
end
|
||||
type = function(value)
|
||||
local base_type = lua.type(value)
|
||||
if base_type == "table" then
|
||||
local cls = value.__class
|
||||
if cls then
|
||||
return cls
|
||||
end
|
||||
end
|
||||
return base_type
|
||||
end
|
||||
debug = setmetatable({
|
||||
upvalue = function(fn, k, v)
|
||||
local upvalues = { }
|
||||
local i = 1
|
||||
while true do
|
||||
local name = lua.debug.getupvalue(fn, i)
|
||||
if name == nil then
|
||||
break
|
||||
end
|
||||
upvalues[name] = i
|
||||
i = i + 1
|
||||
end
|
||||
if not upvalues[k] then
|
||||
error("Failed to find upvalue: " .. tostring(k))
|
||||
end
|
||||
if not v then
|
||||
local _, value = lua.debug.getupvalue(fn, upvalues[k])
|
||||
return value
|
||||
else
|
||||
return lua.debug.setupvalue(fn, upvalues[k], v)
|
||||
end
|
||||
end
|
||||
}, {
|
||||
__index = lua.debug
|
||||
})
|
||||
run_with_scope = function(fn, scope, ...)
|
||||
local old_env = getfenv(fn)
|
||||
local env = setmetatable({ }, {
|
||||
__index = function(self, name)
|
||||
local val = scope[name]
|
||||
if val ~= nil then
|
||||
return val
|
||||
else
|
||||
return old_env[name]
|
||||
end
|
||||
end
|
||||
})
|
||||
setfenv(fn, env)
|
||||
return fn(...)
|
||||
end
|
||||
bind_methods = function(obj)
|
||||
return setmetatable({ }, {
|
||||
__index = function(self, name)
|
||||
local val = obj[name]
|
||||
if val and lua.type(val) == "function" then
|
||||
local bound
|
||||
bound = function(...)
|
||||
return val(obj, ...)
|
||||
end
|
||||
self[name] = bound
|
||||
return bound
|
||||
else
|
||||
return val
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
defaultbl = function(t, fn)
|
||||
if not fn then
|
||||
fn = t
|
||||
t = { }
|
||||
end
|
||||
return setmetatable(t, {
|
||||
__index = function(self, name)
|
||||
local val = fn(self, name)
|
||||
rawset(self, name, val)
|
||||
return val
|
||||
end
|
||||
})
|
||||
end
|
||||
extend = function(...)
|
||||
local tbls = {
|
||||
...
|
||||
}
|
||||
if #tbls < 2 then
|
||||
return
|
||||
end
|
||||
for i = 1, #tbls - 1 do
|
||||
local a = tbls[i]
|
||||
local b = tbls[i + 1]
|
||||
setmetatable(a, {
|
||||
__index = b
|
||||
})
|
||||
end
|
||||
return tbls[1]
|
||||
end
|
||||
copy = function(self)
|
||||
local _tbl_0 = { }
|
||||
for key, val in pairs(self) do
|
||||
_tbl_0[key] = val
|
||||
end
|
||||
return _tbl_0
|
||||
end
|
||||
mixin = function(self, cls, ...)
|
||||
for key, val in pairs(cls.__base) do
|
||||
if not key:match("^__") then
|
||||
self[key] = val
|
||||
end
|
||||
end
|
||||
return cls.__init(self, ...)
|
||||
end
|
||||
mixin_object = function(self, object, methods)
|
||||
for _index_0 = 1, #methods do
|
||||
local name = methods[_index_0]
|
||||
self[name] = function(parent, ...)
|
||||
return object[name](object, ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
mixin_table = function(self, tbl, keys)
|
||||
if keys then
|
||||
for _index_0 = 1, #keys do
|
||||
local key = keys[_index_0]
|
||||
self[key] = tbl[key]
|
||||
end
|
||||
else
|
||||
for key, val in pairs(tbl) do
|
||||
self[key] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
fold = function(items, fn)
|
||||
local len = #items
|
||||
if len > 1 then
|
||||
local accum = fn(items[1], items[2])
|
||||
for i = 3, len do
|
||||
accum = fn(accum, items[i])
|
||||
end
|
||||
return accum
|
||||
else
|
||||
return items[1]
|
||||
end
|
||||
end
|
||||
return {
|
||||
dump = dump,
|
||||
p = p,
|
||||
is_object = is_object,
|
||||
type = type,
|
||||
debug = debug,
|
||||
run_with_scope = run_with_scope,
|
||||
bind_methods = bind_methods,
|
||||
defaultbl = defaultbl,
|
||||
extend = extend,
|
||||
copy = copy,
|
||||
mixin = mixin,
|
||||
mixin_object = mixin_object,
|
||||
mixin_table = mixin_table,
|
||||
fold = fold
|
||||
}
|
133
prototypes/scp-clicker/moon/init.moon
Normal file
133
prototypes/scp-clicker/moon/init.moon
Normal file
@ -0,0 +1,133 @@
|
||||
|
||||
lua = { :debug, :type }
|
||||
import getfenv, setfenv, dump from require "moonscript.util"
|
||||
|
||||
local *
|
||||
|
||||
p = (...) ->
|
||||
print dump ...
|
||||
|
||||
is_object = (value) -> -- is a moonscript object
|
||||
lua.type(value) == "table" and value.__class
|
||||
|
||||
type = (value) -> -- class aware type
|
||||
base_type = lua.type value
|
||||
if base_type == "table"
|
||||
cls = value.__class
|
||||
return cls if cls
|
||||
base_type
|
||||
|
||||
debug = setmetatable {
|
||||
upvalue: (fn, k, v) ->
|
||||
upvalues = {}
|
||||
i = 1
|
||||
while true
|
||||
name = lua.debug.getupvalue(fn, i)
|
||||
break if name == nil
|
||||
upvalues[name] = i
|
||||
i += 1
|
||||
|
||||
if not upvalues[k]
|
||||
error "Failed to find upvalue: " .. tostring k
|
||||
|
||||
if not v
|
||||
_, value = lua.debug.getupvalue fn, upvalues[k]
|
||||
value
|
||||
else
|
||||
lua.debug.setupvalue fn, upvalues[k], v
|
||||
}, __index: lua.debug
|
||||
|
||||
-- run a function with scope injected before its function environment
|
||||
run_with_scope = (fn, scope, ...) ->
|
||||
old_env = getfenv fn
|
||||
env = setmetatable {}, {
|
||||
__index: (name) =>
|
||||
val = scope[name]
|
||||
if val != nil
|
||||
val
|
||||
else
|
||||
old_env[name]
|
||||
}
|
||||
setfenv fn, env
|
||||
fn ...
|
||||
|
||||
-- wrap obj such that calls to methods do not need a reference to self
|
||||
bind_methods = (obj) ->
|
||||
setmetatable {}, {
|
||||
__index: (name) =>
|
||||
val = obj[name]
|
||||
if val and lua.type(val) == "function"
|
||||
bound = (...) -> val obj, ...
|
||||
self[name] = bound
|
||||
bound
|
||||
else
|
||||
val
|
||||
}
|
||||
|
||||
-- use a function to provide default values to table
|
||||
-- optionally specify a starting table
|
||||
-- fibanocci table:
|
||||
-- t = defaultbl {[0]: 0, [1]: 1}, (i) -> self[i - 1] + self[i - 2]
|
||||
defaultbl = (t, fn) ->
|
||||
if not fn
|
||||
fn = t
|
||||
t = {}
|
||||
setmetatable t, {
|
||||
__index: (name) =>
|
||||
val = fn self, name
|
||||
rawset self, name, val
|
||||
val
|
||||
}
|
||||
|
||||
-- chain together tables by __index metatables
|
||||
extend = (...) ->
|
||||
tbls = {...}
|
||||
return if #tbls < 2
|
||||
|
||||
for i = 1, #tbls - 1
|
||||
a = tbls[i]
|
||||
b = tbls[i + 1]
|
||||
|
||||
setmetatable a, __index: b
|
||||
|
||||
tbls[1]
|
||||
|
||||
-- shallow copy
|
||||
copy = =>
|
||||
{key,val for key,val in pairs self}
|
||||
|
||||
-- mixin class properties into self, call new
|
||||
mixin = (cls, ...) =>
|
||||
for key, val in pairs cls.__base
|
||||
self[key] = val if not key\match"^__"
|
||||
cls.__init self, ...
|
||||
|
||||
-- mixin methods from an object into self
|
||||
mixin_object = (object, methods) =>
|
||||
for name in *methods
|
||||
self[name] = (parent, ...) ->
|
||||
object[name](object, ...)
|
||||
|
||||
-- mixin table values into self
|
||||
mixin_table = (tbl, keys) =>
|
||||
if keys
|
||||
for key in *keys
|
||||
self[key] = tbl[key]
|
||||
else
|
||||
for key, val in pairs tbl
|
||||
self[key] = val
|
||||
|
||||
fold = (items, fn)->
|
||||
len = #items
|
||||
if len > 1
|
||||
accum = fn items[1], items[2]
|
||||
for i=3,len
|
||||
accum = fn accum, items[i]
|
||||
accum
|
||||
else
|
||||
items[1]
|
||||
|
||||
{
|
||||
:dump, :p, :is_object, :type, :debug, :run_with_scope, :bind_methods,
|
||||
:defaultbl, :extend, :copy, :mixin, :mixin_object, :mixin_table, :fold
|
||||
}
|
153
prototypes/scp-clicker/moonscript/base.lua
Normal file
153
prototypes/scp-clicker/moonscript/base.lua
Normal file
@ -0,0 +1,153 @@
|
||||
local compile = require("moonscript.compile")
|
||||
local parse = require("moonscript.parse")
|
||||
local concat, insert, remove
|
||||
do
|
||||
local _obj_0 = table
|
||||
concat, insert, remove = _obj_0.concat, _obj_0.insert, _obj_0.remove
|
||||
end
|
||||
local split, dump, get_options, unpack
|
||||
do
|
||||
local _obj_0 = require("moonscript.util")
|
||||
split, dump, get_options, unpack = _obj_0.split, _obj_0.dump, _obj_0.get_options, _obj_0.unpack
|
||||
end
|
||||
local lua = {
|
||||
loadstring = loadstring,
|
||||
load = load
|
||||
}
|
||||
local dirsep, line_tables, create_moonpath, to_lua, moon_loader, loadstring, loadfile, dofile, insert_loader, remove_loader
|
||||
dirsep = "/"
|
||||
line_tables = require("moonscript.line_tables")
|
||||
create_moonpath = function(package_path)
|
||||
local moonpaths
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = split(package_path, ";")
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local path = _list_0[_index_0]
|
||||
local prefix = path:match("^(.-)%.lua$")
|
||||
if not (prefix) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
local _value_0 = prefix .. ".moon"
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
moonpaths = _accum_0
|
||||
end
|
||||
return concat(moonpaths, ";")
|
||||
end
|
||||
to_lua = function(text, options)
|
||||
if options == nil then
|
||||
options = { }
|
||||
end
|
||||
if "string" ~= type(text) then
|
||||
local t = type(text)
|
||||
return nil, "expecting string (got " .. t .. ")"
|
||||
end
|
||||
local tree, err = parse.string(text)
|
||||
if not tree then
|
||||
return nil, err
|
||||
end
|
||||
local code, ltable, pos = compile.tree(tree, options)
|
||||
if not code then
|
||||
return nil, compile.format_error(ltable, pos, text)
|
||||
end
|
||||
return code, ltable
|
||||
end
|
||||
moon_loader = function(name)
|
||||
local name_path = name:gsub("%.", dirsep)
|
||||
local file, file_path
|
||||
for path in package.moonpath:gmatch("[^;]+") do
|
||||
file_path = path:gsub("?", name_path)
|
||||
file = io.open(file_path)
|
||||
if file then
|
||||
break
|
||||
end
|
||||
end
|
||||
if file then
|
||||
local text = file:read("*a")
|
||||
file:close()
|
||||
local res, err = loadstring(text, "@" .. tostring(file_path))
|
||||
if not res then
|
||||
error(file_path .. ": " .. err)
|
||||
end
|
||||
return res
|
||||
end
|
||||
return nil, "Could not find moon file"
|
||||
end
|
||||
loadstring = function(...)
|
||||
local options, str, chunk_name, mode, env = get_options(...)
|
||||
chunk_name = chunk_name or "=(moonscript.loadstring)"
|
||||
local code, ltable_or_err = to_lua(str, options)
|
||||
if not (code) then
|
||||
return nil, ltable_or_err
|
||||
end
|
||||
if chunk_name then
|
||||
line_tables[chunk_name] = ltable_or_err
|
||||
end
|
||||
return (lua.loadstring or lua.load)(code, chunk_name, unpack({
|
||||
mode,
|
||||
env
|
||||
}))
|
||||
end
|
||||
loadfile = function(fname, ...)
|
||||
local file, err = io.open(fname)
|
||||
if not (file) then
|
||||
return nil, err
|
||||
end
|
||||
local text = assert(file:read("*a"))
|
||||
file:close()
|
||||
return loadstring(text, "@" .. tostring(fname), ...)
|
||||
end
|
||||
dofile = function(...)
|
||||
local f = assert(loadfile(...))
|
||||
return f()
|
||||
end
|
||||
insert_loader = function(pos)
|
||||
if pos == nil then
|
||||
pos = 2
|
||||
end
|
||||
if not package.moonpath then
|
||||
package.moonpath = create_moonpath(package.path)
|
||||
end
|
||||
local loaders = package.loaders or package.searchers
|
||||
for _index_0 = 1, #loaders do
|
||||
local loader = loaders[_index_0]
|
||||
if loader == moon_loader then
|
||||
return false
|
||||
end
|
||||
end
|
||||
insert(loaders, pos, moon_loader)
|
||||
return true
|
||||
end
|
||||
remove_loader = function()
|
||||
local loaders = package.loaders or package.searchers
|
||||
for i, loader in ipairs(loaders) do
|
||||
if loader == moon_loader then
|
||||
remove(loaders, i)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
return {
|
||||
_NAME = "moonscript",
|
||||
insert_loader = insert_loader,
|
||||
remove_loader = remove_loader,
|
||||
to_lua = to_lua,
|
||||
moon_loader = moon_loader,
|
||||
dirsep = dirsep,
|
||||
dofile = dofile,
|
||||
loadfile = loadfile,
|
||||
loadstring = loadstring,
|
||||
create_moonpath = create_moonpath
|
||||
}
|
108
prototypes/scp-clicker/moonscript/base.moon
Normal file
108
prototypes/scp-clicker/moonscript/base.moon
Normal file
@ -0,0 +1,108 @@
|
||||
compile = require "moonscript.compile"
|
||||
parse = require "moonscript.parse"
|
||||
|
||||
import concat, insert, remove from table
|
||||
import split, dump, get_options, unpack from require "moonscript.util"
|
||||
|
||||
lua = :loadstring, :load
|
||||
|
||||
local *
|
||||
|
||||
dirsep = "/"
|
||||
line_tables = require "moonscript.line_tables"
|
||||
|
||||
-- create moon path package from lua package path
|
||||
create_moonpath = (package_path) ->
|
||||
moonpaths = for path in *split package_path, ";"
|
||||
prefix = path\match "^(.-)%.lua$"
|
||||
continue unless prefix
|
||||
prefix .. ".moon"
|
||||
concat moonpaths, ";"
|
||||
|
||||
to_lua = (text, options={}) ->
|
||||
if "string" != type text
|
||||
t = type text
|
||||
return nil, "expecting string (got ".. t ..")"
|
||||
|
||||
tree, err = parse.string text
|
||||
if not tree
|
||||
return nil, err
|
||||
|
||||
code, ltable, pos = compile.tree tree, options
|
||||
if not code
|
||||
return nil, compile.format_error(ltable, pos, text)
|
||||
|
||||
code, ltable
|
||||
|
||||
moon_loader = (name) ->
|
||||
name_path = name\gsub "%.", dirsep
|
||||
|
||||
local file, file_path
|
||||
for path in package.moonpath\gmatch "[^;]+"
|
||||
file_path = path\gsub "?", name_path
|
||||
file = io.open file_path
|
||||
break if file
|
||||
|
||||
if file
|
||||
text = file\read "*a"
|
||||
file\close!
|
||||
res, err = loadstring text, "@#{file_path}"
|
||||
if not res
|
||||
error file_path .. ": " .. err
|
||||
|
||||
return res
|
||||
|
||||
return nil, "Could not find moon file"
|
||||
|
||||
|
||||
loadstring = (...) ->
|
||||
options, str, chunk_name, mode, env = get_options ...
|
||||
chunk_name or= "=(moonscript.loadstring)"
|
||||
|
||||
code, ltable_or_err = to_lua str, options
|
||||
unless code
|
||||
return nil, ltable_or_err
|
||||
|
||||
line_tables[chunk_name] = ltable_or_err if chunk_name
|
||||
-- the unpack prevents us from passing nil
|
||||
(lua.loadstring or lua.load) code, chunk_name, unpack { mode, env }
|
||||
|
||||
loadfile = (fname, ...) ->
|
||||
file, err = io.open fname
|
||||
return nil, err unless file
|
||||
text = assert file\read "*a"
|
||||
file\close!
|
||||
loadstring text, "@#{fname}", ...
|
||||
|
||||
-- throws errros
|
||||
dofile = (...) ->
|
||||
f = assert loadfile ...
|
||||
f!
|
||||
|
||||
insert_loader = (pos=2) ->
|
||||
if not package.moonpath
|
||||
package.moonpath = create_moonpath package.path
|
||||
|
||||
loaders = package.loaders or package.searchers
|
||||
for loader in *loaders
|
||||
return false if loader == moon_loader
|
||||
|
||||
insert loaders, pos, moon_loader
|
||||
true
|
||||
|
||||
remove_loader = ->
|
||||
loaders = package.loaders or package.searchers
|
||||
|
||||
for i, loader in ipairs loaders
|
||||
if loader == moon_loader
|
||||
remove loaders, i
|
||||
return true
|
||||
|
||||
false
|
||||
|
||||
{
|
||||
_NAME: "moonscript"
|
||||
:insert_loader, :remove_loader, :to_lua, :moon_loader, :dirsep,
|
||||
:dofile, :loadfile, :loadstring, :create_moonpath
|
||||
}
|
||||
|
69
prototypes/scp-clicker/moonscript/cmd/args.lua
Normal file
69
prototypes/scp-clicker/moonscript/cmd/args.lua
Normal file
@ -0,0 +1,69 @@
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
local parse_spec
|
||||
parse_spec = function(spec)
|
||||
local flags, words
|
||||
if type(spec) == "table" then
|
||||
flags, words = unpack(spec), spec
|
||||
else
|
||||
flags, words = spec, { }
|
||||
end
|
||||
assert("no flags for arguments")
|
||||
local out = { }
|
||||
for part in flags:gmatch("%w:?") do
|
||||
if part:match(":$") then
|
||||
out[part:sub(1, 1)] = {
|
||||
value = true
|
||||
}
|
||||
else
|
||||
out[part] = { }
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
local parse_arguments
|
||||
parse_arguments = function(spec, args)
|
||||
spec = parse_spec(spec)
|
||||
local out = { }
|
||||
local remaining = { }
|
||||
local last_flag = nil
|
||||
for _index_0 = 1, #args do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local arg = args[_index_0]
|
||||
local group = { }
|
||||
if last_flag then
|
||||
out[last_flag] = arg
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
do
|
||||
local flag = arg:match("-(%w+)")
|
||||
if flag then
|
||||
do
|
||||
local short_name = spec[flag]
|
||||
if short_name then
|
||||
out[short_name] = true
|
||||
else
|
||||
for char in flag:gmatch(".") do
|
||||
out[char] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
end
|
||||
table.insert(remaining, arg)
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return out, remaining
|
||||
end
|
||||
return {
|
||||
parse_arguments = parse_arguments,
|
||||
parse_spec = parse_spec
|
||||
}
|
47
prototypes/scp-clicker/moonscript/cmd/args.moon
Normal file
47
prototypes/scp-clicker/moonscript/cmd/args.moon
Normal file
@ -0,0 +1,47 @@
|
||||
import unpack from require "moonscript.util"
|
||||
parse_spec = (spec) ->
|
||||
flags, words = if type(spec) == "table"
|
||||
unpack(spec), spec
|
||||
else
|
||||
spec, {}
|
||||
|
||||
assert "no flags for arguments"
|
||||
|
||||
out = {}
|
||||
for part in flags\gmatch "%w:?"
|
||||
if part\match ":$"
|
||||
out[part\sub 1,1] = { value: true }
|
||||
else
|
||||
out[part] = {}
|
||||
|
||||
out
|
||||
|
||||
parse_arguments = (spec, args) ->
|
||||
spec = parse_spec spec
|
||||
|
||||
out = {}
|
||||
|
||||
remaining = {}
|
||||
last_flag = nil
|
||||
|
||||
for arg in *args
|
||||
group = {}
|
||||
if last_flag
|
||||
out[last_flag] = arg
|
||||
continue
|
||||
|
||||
if flag = arg\match "-(%w+)"
|
||||
if short_name = spec[flag]
|
||||
out[short_name] = true
|
||||
else
|
||||
for char in flag\gmatch "."
|
||||
out[char] = true
|
||||
continue
|
||||
|
||||
table.insert remaining, arg
|
||||
|
||||
out, remaining
|
||||
|
||||
|
||||
|
||||
{ :parse_arguments, :parse_spec }
|
144
prototypes/scp-clicker/moonscript/cmd/coverage.lua
Normal file
144
prototypes/scp-clicker/moonscript/cmd/coverage.lua
Normal file
@ -0,0 +1,144 @@
|
||||
local log
|
||||
log = function(str)
|
||||
if str == nil then
|
||||
str = ""
|
||||
end
|
||||
return io.stderr:write(str .. "\n")
|
||||
end
|
||||
local create_counter
|
||||
create_counter = function()
|
||||
return setmetatable({ }, {
|
||||
__index = function(self, name)
|
||||
do
|
||||
local tbl = setmetatable({ }, {
|
||||
__index = function(self)
|
||||
return 0
|
||||
end
|
||||
})
|
||||
self[name] = tbl
|
||||
return tbl
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
local position_to_lines
|
||||
position_to_lines = function(file_content, positions)
|
||||
local lines = { }
|
||||
local current_pos = 0
|
||||
local line_no = 1
|
||||
for char in file_content:gmatch(".") do
|
||||
do
|
||||
local count = rawget(positions, current_pos)
|
||||
if count then
|
||||
lines[line_no] = count
|
||||
end
|
||||
end
|
||||
if char == "\n" then
|
||||
line_no = line_no + 1
|
||||
end
|
||||
current_pos = current_pos + 1
|
||||
end
|
||||
return lines
|
||||
end
|
||||
local format_file
|
||||
format_file = function(fname, positions)
|
||||
fname = fname:gsub("^@", "")
|
||||
local file = assert(io.open(fname))
|
||||
local content = file:read("*a")
|
||||
file:close()
|
||||
local lines = position_to_lines(content, positions)
|
||||
log("------| @" .. tostring(fname))
|
||||
local line_no = 1
|
||||
for line in (content .. "\n"):gmatch("(.-)\n") do
|
||||
local foramtted_no = ("% 5d"):format(line_no)
|
||||
local sym = lines[line_no] and "*" or " "
|
||||
log(tostring(sym) .. tostring(foramtted_no) .. "| " .. tostring(line))
|
||||
line_no = line_no + 1
|
||||
end
|
||||
return log()
|
||||
end
|
||||
local CodeCoverage
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
reset = function(self)
|
||||
self.line_counts = create_counter()
|
||||
end,
|
||||
start = function(self)
|
||||
return debug.sethook((function()
|
||||
local _base_1 = self
|
||||
local _fn_0 = _base_1.process_line
|
||||
return function(...)
|
||||
return _fn_0(_base_1, ...)
|
||||
end
|
||||
end)(), "l")
|
||||
end,
|
||||
stop = function(self)
|
||||
return debug.sethook()
|
||||
end,
|
||||
print_results = function(self)
|
||||
return self:format_results()
|
||||
end,
|
||||
process_line = function(self, _, line_no)
|
||||
local debug_data = debug.getinfo(2, "S")
|
||||
local source = debug_data.source
|
||||
self.line_counts[source][line_no] = self.line_counts[source][line_no] + 1
|
||||
end,
|
||||
format_results = function(self)
|
||||
local line_table = require("moonscript.line_tables")
|
||||
local positions = create_counter()
|
||||
for file, lines in pairs(self.line_counts) do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local file_table = line_table[file]
|
||||
if not (file_table) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
for line, count in pairs(lines) do
|
||||
local _continue_1 = false
|
||||
repeat
|
||||
local position = file_table[line]
|
||||
if not (position) then
|
||||
_continue_1 = true
|
||||
break
|
||||
end
|
||||
positions[file][position] = positions[file][position] + count
|
||||
_continue_1 = true
|
||||
until true
|
||||
if not _continue_1 then
|
||||
break
|
||||
end
|
||||
end
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
for file, ps in pairs(positions) do
|
||||
format_file(file, ps)
|
||||
end
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self)
|
||||
return self:reset()
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "CodeCoverage"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
CodeCoverage = _class_0
|
||||
end
|
||||
return {
|
||||
CodeCoverage = CodeCoverage
|
||||
}
|
82
prototypes/scp-clicker/moonscript/cmd/coverage.moon
Normal file
82
prototypes/scp-clicker/moonscript/cmd/coverage.moon
Normal file
@ -0,0 +1,82 @@
|
||||
|
||||
log = (str="") ->
|
||||
io.stderr\write str .. "\n"
|
||||
|
||||
create_counter = ->
|
||||
setmetatable {}, __index: (name) =>
|
||||
with tbl = setmetatable {}, __index: => 0
|
||||
@[name] = tbl
|
||||
|
||||
position_to_lines = (file_content, positions) ->
|
||||
lines = {}
|
||||
current_pos = 0
|
||||
line_no = 1
|
||||
for char in file_content\gmatch "."
|
||||
if count = rawget positions, current_pos
|
||||
lines[line_no] = count
|
||||
|
||||
if char == "\n"
|
||||
line_no += 1
|
||||
|
||||
current_pos += 1
|
||||
|
||||
lines
|
||||
|
||||
format_file = (fname, positions) ->
|
||||
-- sources have @ in front of file names
|
||||
fname = fname\gsub "^@", ""
|
||||
|
||||
file = assert io.open fname
|
||||
content = file\read "*a"
|
||||
file\close!
|
||||
|
||||
lines = position_to_lines content, positions
|
||||
log "------| @#{fname}"
|
||||
line_no = 1
|
||||
for line in (content .. "\n")\gmatch "(.-)\n"
|
||||
foramtted_no = "% 5d"\format(line_no)
|
||||
sym = lines[line_no] and "*" or " "
|
||||
log "#{sym}#{foramtted_no}| #{line}"
|
||||
line_no += 1
|
||||
|
||||
log!
|
||||
|
||||
|
||||
class CodeCoverage
|
||||
new: =>
|
||||
@reset!
|
||||
|
||||
reset: =>
|
||||
@line_counts = create_counter!
|
||||
|
||||
start: =>
|
||||
debug.sethook @\process_line, "l"
|
||||
|
||||
stop: =>
|
||||
debug.sethook!
|
||||
|
||||
print_results: =>
|
||||
@format_results!
|
||||
|
||||
process_line: (_, line_no) =>
|
||||
debug_data = debug.getinfo 2, "S"
|
||||
source = debug_data.source
|
||||
@line_counts[source][line_no] += 1
|
||||
|
||||
format_results: =>
|
||||
line_table = require "moonscript.line_tables"
|
||||
positions = create_counter!
|
||||
|
||||
for file, lines in pairs @line_counts
|
||||
file_table = line_table[file]
|
||||
continue unless file_table
|
||||
|
||||
for line, count in pairs lines
|
||||
position = file_table[line]
|
||||
continue unless position
|
||||
positions[file][position] += count
|
||||
|
||||
for file, ps in pairs positions
|
||||
format_file file, ps
|
||||
|
||||
{ :CodeCoverage }
|
323
prototypes/scp-clicker/moonscript/cmd/lint.lua
Normal file
323
prototypes/scp-clicker/moonscript/cmd/lint.lua
Normal file
@ -0,0 +1,323 @@
|
||||
local insert
|
||||
insert = table.insert
|
||||
local Set
|
||||
Set = require("moonscript.data").Set
|
||||
local Block
|
||||
Block = require("moonscript.compile").Block
|
||||
local mtype
|
||||
mtype = require("moonscript.util").moon.type
|
||||
local default_whitelist = Set({
|
||||
'_G',
|
||||
'_VERSION',
|
||||
'assert',
|
||||
'bit32',
|
||||
'collectgarbage',
|
||||
'coroutine',
|
||||
'debug',
|
||||
'dofile',
|
||||
'error',
|
||||
'getfenv',
|
||||
'getmetatable',
|
||||
'io',
|
||||
'ipairs',
|
||||
'load',
|
||||
'loadfile',
|
||||
'loadstring',
|
||||
'math',
|
||||
'module',
|
||||
'next',
|
||||
'os',
|
||||
'package',
|
||||
'pairs',
|
||||
'pcall',
|
||||
'print',
|
||||
'rawequal',
|
||||
'rawget',
|
||||
'rawlen',
|
||||
'rawset',
|
||||
'require',
|
||||
'select',
|
||||
'setfenv',
|
||||
'setmetatable',
|
||||
'string',
|
||||
'table',
|
||||
'tonumber',
|
||||
'tostring',
|
||||
'type',
|
||||
'unpack',
|
||||
'xpcall',
|
||||
"nil",
|
||||
"true",
|
||||
"false"
|
||||
})
|
||||
local LinterBlock
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = Block
|
||||
local _base_0 = {
|
||||
lint_mark_used = function(self, name)
|
||||
if self.lint_unused_names and self.lint_unused_names[name] then
|
||||
self.lint_unused_names[name] = false
|
||||
return
|
||||
end
|
||||
if self.parent then
|
||||
return self.parent:lint_mark_used(name)
|
||||
end
|
||||
end,
|
||||
lint_check_unused = function(self)
|
||||
if not (self.lint_unused_names and next(self.lint_unused_names)) then
|
||||
return
|
||||
end
|
||||
local names_by_position = { }
|
||||
for name, pos in pairs(self.lint_unused_names) do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
if not (pos) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
names_by_position[pos] = names_by_position[pos] or { }
|
||||
insert(names_by_position[pos], name)
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
local tuples
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for pos, names in pairs(names_by_position) do
|
||||
_accum_0[_len_0] = {
|
||||
pos,
|
||||
names
|
||||
}
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
tuples = _accum_0
|
||||
end
|
||||
table.sort(tuples, function(a, b)
|
||||
return a[1] < b[1]
|
||||
end)
|
||||
for _index_0 = 1, #tuples do
|
||||
local _des_0 = tuples[_index_0]
|
||||
local pos, names
|
||||
pos, names = _des_0[1], _des_0[2]
|
||||
insert(self:get_root_block().lint_errors, {
|
||||
"assigned but unused " .. tostring(table.concat((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_1 = 1, #names do
|
||||
local n = names[_index_1]
|
||||
_accum_0[_len_0] = "`" .. tostring(n) .. "`"
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ", ")),
|
||||
pos
|
||||
})
|
||||
end
|
||||
end,
|
||||
render = function(self, ...)
|
||||
self:lint_check_unused()
|
||||
return _class_0.__parent.__base.render(self, ...)
|
||||
end,
|
||||
block = function(self, ...)
|
||||
do
|
||||
local _with_0 = _class_0.__parent.__base.block(self, ...)
|
||||
_with_0.block = self.block
|
||||
_with_0.render = self.render
|
||||
_with_0.get_root_block = self.get_root_block
|
||||
_with_0.lint_check_unused = self.lint_check_unused
|
||||
_with_0.lint_mark_used = self.lint_mark_used
|
||||
_with_0.value_compilers = self.value_compilers
|
||||
_with_0.statement_compilers = self.statement_compilers
|
||||
return _with_0
|
||||
end
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, whitelist_globals, ...)
|
||||
if whitelist_globals == nil then
|
||||
whitelist_globals = default_whitelist
|
||||
end
|
||||
_class_0.__parent.__init(self, ...)
|
||||
self.get_root_block = function()
|
||||
return self
|
||||
end
|
||||
self.lint_errors = { }
|
||||
local vc = self.value_compilers
|
||||
self.value_compilers = setmetatable({
|
||||
ref = function(block, val)
|
||||
local name = val[2]
|
||||
if not (block:has_name(name) or whitelist_globals[name] or name:match("%.")) then
|
||||
insert(self.lint_errors, {
|
||||
"accessing global `" .. tostring(name) .. "`",
|
||||
val[-1]
|
||||
})
|
||||
end
|
||||
block:lint_mark_used(name)
|
||||
return vc.ref(block, val)
|
||||
end
|
||||
}, {
|
||||
__index = vc
|
||||
})
|
||||
local sc = self.statement_compilers
|
||||
self.statement_compilers = setmetatable({
|
||||
assign = function(block, node)
|
||||
local names = node[2]
|
||||
for _index_0 = 1, #names do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local name = names[_index_0]
|
||||
if type(name) == "table" and name[1] == "temp_name" then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
local real_name, is_local = block:extract_assign_name(name)
|
||||
if not (is_local or real_name and not block:has_name(real_name, true)) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
if real_name == "_" then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
block.lint_unused_names = block.lint_unused_names or { }
|
||||
block.lint_unused_names[real_name] = node[-1] or 0
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return sc.assign(block, node)
|
||||
end
|
||||
}, {
|
||||
__index = sc
|
||||
})
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "LinterBlock",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
LinterBlock = _class_0
|
||||
end
|
||||
local format_lint
|
||||
format_lint = function(errors, code, header)
|
||||
if not (next(errors)) then
|
||||
return
|
||||
end
|
||||
local pos_to_line, get_line
|
||||
do
|
||||
local _obj_0 = require("moonscript.util")
|
||||
pos_to_line, get_line = _obj_0.pos_to_line, _obj_0.get_line
|
||||
end
|
||||
local formatted
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #errors do
|
||||
local _des_0 = errors[_index_0]
|
||||
local msg, pos
|
||||
msg, pos = _des_0[1], _des_0[2]
|
||||
if pos then
|
||||
local line = pos_to_line(code, pos)
|
||||
msg = "line " .. tostring(line) .. ": " .. tostring(msg)
|
||||
local line_text = "> " .. get_line(code, line)
|
||||
local sep_len = math.max(#msg, #line_text)
|
||||
_accum_0[_len_0] = table.concat({
|
||||
msg,
|
||||
("="):rep(sep_len),
|
||||
line_text
|
||||
}, "\n")
|
||||
else
|
||||
_accum_0[_len_0] = msg
|
||||
end
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
formatted = _accum_0
|
||||
end
|
||||
if header then
|
||||
table.insert(formatted, 1, header)
|
||||
end
|
||||
return table.concat(formatted, "\n\n")
|
||||
end
|
||||
local whitelist_for_file
|
||||
do
|
||||
local lint_config
|
||||
whitelist_for_file = function(fname)
|
||||
if not (lint_config) then
|
||||
lint_config = { }
|
||||
pcall(function()
|
||||
lint_config = require("lint_config")
|
||||
end)
|
||||
end
|
||||
if not (lint_config.whitelist_globals) then
|
||||
return default_whitelist
|
||||
end
|
||||
local final_list = { }
|
||||
for pattern, list in pairs(lint_config.whitelist_globals) do
|
||||
if fname:match(pattern) then
|
||||
for _index_0 = 1, #list do
|
||||
local item = list[_index_0]
|
||||
insert(final_list, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
return setmetatable(Set(final_list), {
|
||||
__index = default_whitelist
|
||||
})
|
||||
end
|
||||
end
|
||||
local lint_code
|
||||
lint_code = function(code, name, whitelist_globals)
|
||||
if name == nil then
|
||||
name = "string input"
|
||||
end
|
||||
local parse = require("moonscript.parse")
|
||||
local tree, err = parse.string(code)
|
||||
if not (tree) then
|
||||
return nil, err
|
||||
end
|
||||
local scope = LinterBlock(whitelist_globals)
|
||||
scope:stms(tree)
|
||||
scope:lint_check_unused()
|
||||
return format_lint(scope.lint_errors, code, name)
|
||||
end
|
||||
local lint_file
|
||||
lint_file = function(fname)
|
||||
local f, err = io.open(fname)
|
||||
if not (f) then
|
||||
return nil, err
|
||||
end
|
||||
return lint_code(f:read("*a"), fname, whitelist_for_file(fname))
|
||||
end
|
||||
return {
|
||||
lint_code = lint_code,
|
||||
lint_file = lint_file
|
||||
}
|
204
prototypes/scp-clicker/moonscript/cmd/lint.moon
Normal file
204
prototypes/scp-clicker/moonscript/cmd/lint.moon
Normal file
@ -0,0 +1,204 @@
|
||||
|
||||
import insert from table
|
||||
import Set from require "moonscript.data"
|
||||
import Block from require "moonscript.compile"
|
||||
|
||||
{type: mtype} = require("moonscript.util").moon
|
||||
|
||||
-- globals allowed to be referenced
|
||||
default_whitelist = Set {
|
||||
'_G'
|
||||
'_VERSION'
|
||||
'assert'
|
||||
'bit32'
|
||||
'collectgarbage'
|
||||
'coroutine'
|
||||
'debug'
|
||||
'dofile'
|
||||
'error'
|
||||
'getfenv'
|
||||
'getmetatable'
|
||||
'io'
|
||||
'ipairs'
|
||||
'load'
|
||||
'loadfile'
|
||||
'loadstring'
|
||||
'math'
|
||||
'module'
|
||||
'next'
|
||||
'os'
|
||||
'package'
|
||||
'pairs'
|
||||
'pcall'
|
||||
'print'
|
||||
'rawequal'
|
||||
'rawget'
|
||||
'rawlen'
|
||||
'rawset'
|
||||
'require'
|
||||
'select'
|
||||
'setfenv'
|
||||
'setmetatable'
|
||||
'string'
|
||||
'table'
|
||||
'tonumber'
|
||||
'tostring'
|
||||
'type'
|
||||
'unpack'
|
||||
'xpcall'
|
||||
|
||||
"nil"
|
||||
"true"
|
||||
"false"
|
||||
}
|
||||
|
||||
class LinterBlock extends Block
|
||||
new: (whitelist_globals=default_whitelist, ...) =>
|
||||
super ...
|
||||
@get_root_block = -> @
|
||||
|
||||
@lint_errors = {}
|
||||
|
||||
vc = @value_compilers
|
||||
@value_compilers = setmetatable {
|
||||
ref: (block, val) ->
|
||||
name = val[2]
|
||||
unless block\has_name(name) or whitelist_globals[name] or name\match "%."
|
||||
insert @lint_errors, {
|
||||
"accessing global `#{name}`"
|
||||
val[-1]
|
||||
}
|
||||
|
||||
block\lint_mark_used name
|
||||
vc.ref block, val
|
||||
}, __index: vc
|
||||
|
||||
sc = @statement_compilers
|
||||
@statement_compilers = setmetatable {
|
||||
assign: (block, node) ->
|
||||
names = node[2]
|
||||
-- extract the names to be declared
|
||||
for name in *names
|
||||
-- don't include autogenerated names
|
||||
if type(name) == "table" and name[1] == "temp_name"
|
||||
continue
|
||||
|
||||
real_name, is_local = block\extract_assign_name name
|
||||
-- already defined in some other scope
|
||||
unless is_local or real_name and not block\has_name real_name, true
|
||||
continue
|
||||
|
||||
continue if real_name == "_"
|
||||
|
||||
block.lint_unused_names or= {}
|
||||
block.lint_unused_names[real_name] = node[-1] or 0
|
||||
|
||||
sc.assign block, node
|
||||
}, __index: sc
|
||||
|
||||
lint_mark_used: (name) =>
|
||||
if @lint_unused_names and @lint_unused_names[name]
|
||||
@lint_unused_names[name] = false
|
||||
return
|
||||
|
||||
if @parent
|
||||
@parent\lint_mark_used name
|
||||
|
||||
lint_check_unused: =>
|
||||
return unless @lint_unused_names and next @lint_unused_names
|
||||
|
||||
names_by_position = {}
|
||||
for name, pos in pairs @lint_unused_names
|
||||
continue unless pos
|
||||
names_by_position[pos] or= {}
|
||||
insert names_by_position[pos], name
|
||||
|
||||
tuples = [{pos, names} for pos,names in pairs names_by_position]
|
||||
table.sort tuples, (a,b) -> a[1] < b[1]
|
||||
|
||||
for {pos, names} in *tuples
|
||||
insert @get_root_block!.lint_errors, {
|
||||
"assigned but unused #{table.concat ["`#{n}`" for n in *names], ", "}"
|
||||
pos
|
||||
}
|
||||
|
||||
render: (...) =>
|
||||
@lint_check_unused!
|
||||
super ...
|
||||
|
||||
block: (...) =>
|
||||
|
||||
with super ...
|
||||
.block = @block
|
||||
.render = @render
|
||||
.get_root_block = @get_root_block
|
||||
.lint_check_unused = @lint_check_unused
|
||||
.lint_mark_used = @lint_mark_used
|
||||
.value_compilers = @value_compilers
|
||||
.statement_compilers = @statement_compilers
|
||||
|
||||
format_lint = (errors, code, header) ->
|
||||
return unless next errors
|
||||
|
||||
import pos_to_line, get_line from require "moonscript.util"
|
||||
formatted = for {msg, pos} in *errors
|
||||
if pos
|
||||
line = pos_to_line code, pos
|
||||
msg = "line #{line}: #{msg}"
|
||||
line_text = "> " .. get_line code, line
|
||||
|
||||
sep_len = math.max #msg, #line_text
|
||||
table.concat {
|
||||
msg
|
||||
"="\rep sep_len
|
||||
line_text
|
||||
}, "\n"
|
||||
|
||||
else
|
||||
msg
|
||||
|
||||
table.insert formatted, 1, header if header
|
||||
table.concat formatted, "\n\n"
|
||||
|
||||
|
||||
-- {
|
||||
-- whitelist_globals: {
|
||||
-- ["some_file_pattern"]: {
|
||||
-- "some_var", "another_var"
|
||||
-- }
|
||||
-- }
|
||||
-- }
|
||||
whitelist_for_file = do
|
||||
local lint_config
|
||||
(fname) ->
|
||||
unless lint_config
|
||||
lint_config = {}
|
||||
pcall -> lint_config = require "lint_config"
|
||||
|
||||
return default_whitelist unless lint_config.whitelist_globals
|
||||
final_list = {}
|
||||
for pattern, list in pairs lint_config.whitelist_globals
|
||||
if fname\match(pattern)
|
||||
for item in *list
|
||||
insert final_list, item
|
||||
|
||||
setmetatable Set(final_list), __index: default_whitelist
|
||||
|
||||
lint_code = (code, name="string input", whitelist_globals) ->
|
||||
parse = require "moonscript.parse"
|
||||
tree, err = parse.string code
|
||||
return nil, err unless tree
|
||||
|
||||
scope = LinterBlock whitelist_globals
|
||||
scope\stms tree
|
||||
scope\lint_check_unused!
|
||||
|
||||
format_lint scope.lint_errors, code, name
|
||||
|
||||
lint_file = (fname) ->
|
||||
f, err = io.open fname
|
||||
return nil, err unless f
|
||||
lint_code f\read("*a"), fname, whitelist_for_file fname
|
||||
|
||||
|
||||
{ :lint_code, :lint_file }
|
197
prototypes/scp-clicker/moonscript/cmd/moonc.lua
Normal file
197
prototypes/scp-clicker/moonscript/cmd/moonc.lua
Normal file
@ -0,0 +1,197 @@
|
||||
local lfs = require("lfs")
|
||||
local split
|
||||
split = require("moonscript.util").split
|
||||
local dirsep, dirsep_chars, mkdir, normalize_dir, parse_dir, parse_file, convert_path, format_time, gettime, compile_file_text, write_file, compile_and_write, is_abs_path, path_to_target
|
||||
dirsep = package.config:sub(1, 1)
|
||||
if dirsep == "\\" then
|
||||
dirsep_chars = "\\/"
|
||||
else
|
||||
dirsep_chars = dirsep
|
||||
end
|
||||
mkdir = function(path)
|
||||
local chunks = split(path, dirsep)
|
||||
local accum
|
||||
for _index_0 = 1, #chunks do
|
||||
local dir = chunks[_index_0]
|
||||
accum = accum and tostring(accum) .. tostring(dirsep) .. tostring(dir) or dir
|
||||
lfs.mkdir(accum)
|
||||
end
|
||||
return lfs.attributes(path, "mode")
|
||||
end
|
||||
normalize_dir = function(path)
|
||||
return path:match("^(.-)[" .. tostring(dirsep_chars) .. "]*$") .. dirsep
|
||||
end
|
||||
parse_dir = function(path)
|
||||
return (path:match("^(.-)[^" .. tostring(dirsep_chars) .. "]*$"))
|
||||
end
|
||||
parse_file = function(path)
|
||||
return (path:match("^.-([^" .. tostring(dirsep_chars) .. "]*)$"))
|
||||
end
|
||||
convert_path = function(path)
|
||||
local new_path = path:gsub("%.moon$", ".lua")
|
||||
if new_path == path then
|
||||
new_path = path .. ".lua"
|
||||
end
|
||||
return new_path
|
||||
end
|
||||
format_time = function(time)
|
||||
return ("%.3fms"):format(time * 1000)
|
||||
end
|
||||
do
|
||||
local socket
|
||||
gettime = function()
|
||||
if socket == nil then
|
||||
pcall(function()
|
||||
socket = require("socket")
|
||||
end)
|
||||
if not (socket) then
|
||||
socket = false
|
||||
end
|
||||
end
|
||||
if socket then
|
||||
return socket.gettime()
|
||||
else
|
||||
return nil, "LuaSocket needed for benchmark"
|
||||
end
|
||||
end
|
||||
end
|
||||
compile_file_text = function(text, opts)
|
||||
if opts == nil then
|
||||
opts = { }
|
||||
end
|
||||
local parse = require("moonscript.parse")
|
||||
local compile = require("moonscript.compile")
|
||||
local parse_time
|
||||
if opts.benchmark then
|
||||
parse_time = assert(gettime())
|
||||
end
|
||||
local tree, err = parse.string(text)
|
||||
if not (tree) then
|
||||
return nil, err
|
||||
end
|
||||
if parse_time then
|
||||
parse_time = gettime() - parse_time
|
||||
end
|
||||
if opts.show_parse_tree then
|
||||
local dump = require("moonscript.dump")
|
||||
dump.tree(tree)
|
||||
return true
|
||||
end
|
||||
local compile_time
|
||||
if opts.benchmark then
|
||||
compile_time = gettime()
|
||||
end
|
||||
do
|
||||
local mod = opts.transform_module
|
||||
if mod then
|
||||
tree = assert(require(mod)(tree))
|
||||
end
|
||||
end
|
||||
local code, posmap_or_err, err_pos = compile.tree(tree)
|
||||
if not (code) then
|
||||
return nil, compile.format_error(posmap_or_err, err_pos, text)
|
||||
end
|
||||
if compile_time then
|
||||
compile_time = gettime() - compile_time
|
||||
end
|
||||
if opts.show_posmap then
|
||||
local debug_posmap
|
||||
debug_posmap = require("moonscript.util").debug_posmap
|
||||
print("Pos", "Lua", ">>", "Moon")
|
||||
print(debug_posmap(posmap_or_err, text, code))
|
||||
return true
|
||||
end
|
||||
if opts.benchmark then
|
||||
print(table.concat({
|
||||
opts.fname or "stdin",
|
||||
"Parse time \t" .. format_time(parse_time),
|
||||
"Compile time\t" .. format_time(compile_time),
|
||||
""
|
||||
}, "\n"))
|
||||
return true
|
||||
end
|
||||
return code
|
||||
end
|
||||
write_file = function(fname, code)
|
||||
mkdir(parse_dir(fname))
|
||||
local f, err = io.open(fname, "w")
|
||||
if not (f) then
|
||||
return nil, err
|
||||
end
|
||||
assert(f:write(code))
|
||||
assert(f:write("\n"))
|
||||
f:close()
|
||||
return "build"
|
||||
end
|
||||
compile_and_write = function(src, dest, opts)
|
||||
if opts == nil then
|
||||
opts = { }
|
||||
end
|
||||
local f = io.open(src)
|
||||
if not (f) then
|
||||
return nil, "Can't find file"
|
||||
end
|
||||
local text = assert(f:read("*a"))
|
||||
f:close()
|
||||
local code, err = compile_file_text(text, opts)
|
||||
if not code then
|
||||
return nil, err
|
||||
end
|
||||
if code == true then
|
||||
return true
|
||||
end
|
||||
if opts.print then
|
||||
print(code)
|
||||
return true
|
||||
end
|
||||
return write_file(dest, code)
|
||||
end
|
||||
is_abs_path = function(path)
|
||||
local first = path:sub(1, 1)
|
||||
if dirsep == "\\" then
|
||||
return first == "/" or first == "\\" or path:sub(2, 1) == ":"
|
||||
else
|
||||
return first == dirsep
|
||||
end
|
||||
end
|
||||
path_to_target = function(path, target_dir, base_dir)
|
||||
if target_dir == nil then
|
||||
target_dir = nil
|
||||
end
|
||||
if base_dir == nil then
|
||||
base_dir = nil
|
||||
end
|
||||
local target = convert_path(path)
|
||||
if target_dir then
|
||||
target_dir = normalize_dir(target_dir)
|
||||
end
|
||||
if base_dir and target_dir then
|
||||
local head = base_dir:match("^(.-)[^" .. tostring(dirsep_chars) .. "]*[" .. tostring(dirsep_chars) .. "]?$")
|
||||
if head then
|
||||
local start, stop = target:find(head, 1, true)
|
||||
if start == 1 then
|
||||
target = target:sub(stop + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
if target_dir then
|
||||
if is_abs_path(target) then
|
||||
target = parse_file(target)
|
||||
end
|
||||
target = target_dir .. target
|
||||
end
|
||||
return target
|
||||
end
|
||||
return {
|
||||
dirsep = dirsep,
|
||||
mkdir = mkdir,
|
||||
normalize_dir = normalize_dir,
|
||||
parse_dir = parse_dir,
|
||||
parse_file = parse_file,
|
||||
convert_path = convert_path,
|
||||
gettime = gettime,
|
||||
format_time = format_time,
|
||||
path_to_target = path_to_target,
|
||||
compile_file_text = compile_file_text,
|
||||
compile_and_write = compile_and_write
|
||||
}
|
195
prototypes/scp-clicker/moonscript/cmd/moonc.moon
Normal file
195
prototypes/scp-clicker/moonscript/cmd/moonc.moon
Normal file
@ -0,0 +1,195 @@
|
||||
-- assorted utilities for moonc command line tool
|
||||
|
||||
lfs = require "lfs"
|
||||
|
||||
import split from require "moonscript.util"
|
||||
|
||||
local *
|
||||
|
||||
dirsep = package.config\sub 1,1
|
||||
dirsep_chars = if dirsep == "\\"
|
||||
"\\/" -- windows
|
||||
else
|
||||
dirsep
|
||||
|
||||
-- similar to mkdir -p
|
||||
mkdir = (path) ->
|
||||
chunks = split path, dirsep
|
||||
|
||||
local accum
|
||||
for dir in *chunks
|
||||
accum = accum and "#{accum}#{dirsep}#{dir}" or dir
|
||||
lfs.mkdir accum
|
||||
|
||||
lfs.attributes path, "mode"
|
||||
|
||||
-- strips excess / and ensures path ends with /
|
||||
normalize_dir = (path) ->
|
||||
path\match("^(.-)[#{dirsep_chars}]*$") .. dirsep
|
||||
|
||||
-- parse the directory out of a path
|
||||
parse_dir = (path) ->
|
||||
(path\match "^(.-)[^#{dirsep_chars}]*$")
|
||||
|
||||
-- parse the filename out of a path
|
||||
parse_file = (path) ->
|
||||
(path\match "^.-([^#{dirsep_chars}]*)$")
|
||||
|
||||
-- converts .moon to a .lua path for calcuating compile target
|
||||
convert_path = (path) ->
|
||||
new_path = path\gsub "%.moon$", ".lua"
|
||||
if new_path == path
|
||||
new_path = path .. ".lua"
|
||||
new_path
|
||||
|
||||
format_time = (time) ->
|
||||
"%.3fms"\format time*1000
|
||||
|
||||
gettime = do
|
||||
local socket
|
||||
->
|
||||
if socket == nil
|
||||
pcall ->
|
||||
socket = require "socket"
|
||||
|
||||
unless socket
|
||||
socket = false
|
||||
|
||||
if socket
|
||||
socket.gettime()
|
||||
else
|
||||
nil, "LuaSocket needed for benchmark"
|
||||
|
||||
-- compiles file to lua, returns lua code
|
||||
-- returns nil, error on error
|
||||
-- returns true if some option handled the output instead
|
||||
compile_file_text = (text, opts={}) ->
|
||||
parse = require "moonscript.parse"
|
||||
compile = require "moonscript.compile"
|
||||
|
||||
parse_time = if opts.benchmark
|
||||
assert gettime!
|
||||
|
||||
tree, err = parse.string text
|
||||
return nil, err unless tree
|
||||
|
||||
if parse_time
|
||||
parse_time = gettime! - parse_time
|
||||
|
||||
if opts.show_parse_tree
|
||||
dump = require "moonscript.dump"
|
||||
dump.tree tree
|
||||
return true
|
||||
|
||||
compile_time = if opts.benchmark
|
||||
gettime!
|
||||
|
||||
if mod = opts.transform_module
|
||||
tree = assert require(mod) tree
|
||||
|
||||
code, posmap_or_err, err_pos = compile.tree tree
|
||||
|
||||
unless code
|
||||
return nil, compile.format_error posmap_or_err, err_pos, text
|
||||
|
||||
if compile_time
|
||||
compile_time = gettime() - compile_time
|
||||
|
||||
if opts.show_posmap
|
||||
import debug_posmap from require "moonscript.util"
|
||||
print "Pos", "Lua", ">>", "Moon"
|
||||
print debug_posmap posmap_or_err, text, code
|
||||
return true
|
||||
|
||||
if opts.benchmark
|
||||
print table.concat {
|
||||
opts.fname or "stdin",
|
||||
"Parse time \t" .. format_time(parse_time),
|
||||
"Compile time\t" .. format_time(compile_time),
|
||||
""
|
||||
}, "\n"
|
||||
return true
|
||||
|
||||
code
|
||||
|
||||
write_file = (fname, code) ->
|
||||
mkdir parse_dir fname
|
||||
f, err = io.open fname, "w"
|
||||
unless f
|
||||
return nil, err
|
||||
|
||||
assert f\write code
|
||||
assert f\write "\n"
|
||||
f\close!
|
||||
"build"
|
||||
|
||||
compile_and_write = (src, dest, opts={}) ->
|
||||
f = io.open src
|
||||
unless f
|
||||
return nil, "Can't find file"
|
||||
|
||||
text = assert f\read("*a")
|
||||
f\close!
|
||||
|
||||
code, err = compile_file_text text, opts
|
||||
|
||||
if not code
|
||||
return nil, err
|
||||
|
||||
if code == true
|
||||
return true
|
||||
|
||||
if opts.print
|
||||
print code
|
||||
return true
|
||||
|
||||
write_file dest, code
|
||||
|
||||
is_abs_path = (path) ->
|
||||
first = path\sub 1, 1
|
||||
if dirsep == "\\"
|
||||
first == "/" or first == "\\" or path\sub(2,1) == ":"
|
||||
else
|
||||
first == dirsep
|
||||
|
||||
|
||||
-- calcuate where a path should be compiled to
|
||||
-- target_dir: the directory to place the file (optional, from -t flag)
|
||||
-- base_dir: the directory where the file came from when globbing recursively
|
||||
path_to_target = (path, target_dir=nil, base_dir=nil) ->
|
||||
target = convert_path path
|
||||
|
||||
if target_dir
|
||||
target_dir = normalize_dir target_dir
|
||||
|
||||
if base_dir and target_dir
|
||||
-- one directory back
|
||||
head = base_dir\match("^(.-)[^#{dirsep_chars}]*[#{dirsep_chars}]?$")
|
||||
|
||||
if head
|
||||
start, stop = target\find head, 1, true
|
||||
if start == 1
|
||||
target = target\sub(stop + 1)
|
||||
|
||||
if target_dir
|
||||
if is_abs_path target
|
||||
target = parse_file target
|
||||
|
||||
target = target_dir .. target
|
||||
|
||||
target
|
||||
|
||||
{
|
||||
:dirsep
|
||||
:mkdir
|
||||
:normalize_dir
|
||||
:parse_dir
|
||||
:parse_file
|
||||
:convert_path
|
||||
:gettime
|
||||
:format_time
|
||||
:path_to_target
|
||||
|
||||
:compile_file_text
|
||||
:compile_and_write
|
||||
}
|
268
prototypes/scp-clicker/moonscript/cmd/watchers.lua
Normal file
268
prototypes/scp-clicker/moonscript/cmd/watchers.lua
Normal file
@ -0,0 +1,268 @@
|
||||
local remove_dupes
|
||||
remove_dupes = function(list, key_fn)
|
||||
local seen = { }
|
||||
return (function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #list do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local item = list[_index_0]
|
||||
local key
|
||||
if key_fn then
|
||||
key = key_fn(item)
|
||||
else
|
||||
key = item
|
||||
end
|
||||
if seen[key] then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
seen[key] = true
|
||||
local _value_0 = item
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return _accum_0
|
||||
end)()
|
||||
end
|
||||
local plural
|
||||
plural = function(count, word)
|
||||
return tostring(count) .. " " .. tostring(word) .. tostring(count == 1 and "" or "s")
|
||||
end
|
||||
local Watcher
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
start_msg = "Starting watch loop (Ctrl-C to exit)",
|
||||
print_start = function(self, mode, misc)
|
||||
return io.stderr:write(tostring(self.start_msg) .. " with " .. tostring(mode) .. " [" .. tostring(misc) .. "]\n")
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, file_list)
|
||||
self.file_list = file_list
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Watcher"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Watcher = _class_0
|
||||
end
|
||||
local InotifyWacher
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = Watcher
|
||||
local _base_0 = {
|
||||
get_dirs = function(self)
|
||||
local parse_dir
|
||||
parse_dir = require("moonscript.cmd.moonc").parse_dir
|
||||
local dirs
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = self.file_list
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local _des_0 = _list_0[_index_0]
|
||||
local file_path
|
||||
file_path = _des_0[1]
|
||||
local dir = parse_dir(file_path)
|
||||
if dir == "" then
|
||||
dir = "./"
|
||||
end
|
||||
local _value_0 = dir
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
dirs = _accum_0
|
||||
end
|
||||
return remove_dupes(dirs)
|
||||
end,
|
||||
each_update = function(self)
|
||||
return coroutine.wrap(function()
|
||||
local dirs = self:get_dirs()
|
||||
self:print_start("inotify", plural(#dirs, "dir"))
|
||||
local wd_table = { }
|
||||
local inotify = require("inotify")
|
||||
local handle = inotify.init()
|
||||
for _index_0 = 1, #dirs do
|
||||
local dir = dirs[_index_0]
|
||||
local wd = handle:addwatch(dir, inotify.IN_CLOSE_WRITE, inotify.IN_MOVED_TO)
|
||||
wd_table[wd] = dir
|
||||
end
|
||||
while true do
|
||||
local events = handle:read()
|
||||
if not (events) then
|
||||
break
|
||||
end
|
||||
for _index_0 = 1, #events do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local ev = events[_index_0]
|
||||
local fname = ev.name
|
||||
if not (fname:match("%.moon$")) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
local dir = wd_table[ev.wd]
|
||||
if dir ~= "./" then
|
||||
fname = dir .. fname
|
||||
end
|
||||
coroutine.yield(fname)
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, ...)
|
||||
return _class_0.__parent.__init(self, ...)
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "InotifyWacher",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
local self = _class_0
|
||||
self.available = function(self)
|
||||
return pcall(function()
|
||||
return require("inotify")
|
||||
end)
|
||||
end
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
InotifyWacher = _class_0
|
||||
end
|
||||
local SleepWatcher
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = Watcher
|
||||
local _base_0 = {
|
||||
polling_rate = 1.0,
|
||||
get_sleep_func = function(self)
|
||||
local sleep
|
||||
pcall(function()
|
||||
sleep = require("socket").sleep
|
||||
end)
|
||||
sleep = sleep or require("moonscript")._sleep
|
||||
if not (sleep) then
|
||||
error("Missing sleep function; install LuaSocket")
|
||||
end
|
||||
return sleep
|
||||
end,
|
||||
each_update = function(self)
|
||||
return coroutine.wrap(function()
|
||||
local lfs = require("lfs")
|
||||
local sleep = self:get_sleep_func()
|
||||
self:print_start("polling", plural(#self.file_list, "files"))
|
||||
local mod_time = { }
|
||||
while true do
|
||||
local _list_0 = self.file_list
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local _des_0 = _list_0[_index_0]
|
||||
local file
|
||||
file = _des_0[1]
|
||||
local time = lfs.attributes(file, "modification")
|
||||
if not (time) then
|
||||
mod_time[file] = nil
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
if not (mod_time[file]) then
|
||||
mod_time[file] = time
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
if time > mod_time[file] then
|
||||
mod_time[file] = time
|
||||
coroutine.yield(file)
|
||||
end
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
sleep(self.polling_rate)
|
||||
end
|
||||
end)
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, ...)
|
||||
return _class_0.__parent.__init(self, ...)
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "SleepWatcher",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
SleepWatcher = _class_0
|
||||
end
|
||||
return {
|
||||
Watcher = Watcher,
|
||||
SleepWatcher = SleepWatcher,
|
||||
InotifyWacher = InotifyWacher
|
||||
}
|
103
prototypes/scp-clicker/moonscript/cmd/watchers.moon
Normal file
103
prototypes/scp-clicker/moonscript/cmd/watchers.moon
Normal file
@ -0,0 +1,103 @@
|
||||
remove_dupes = (list, key_fn) ->
|
||||
seen = {}
|
||||
return for item in *list
|
||||
key = if key_fn then key_fn item else item
|
||||
continue if seen[key]
|
||||
seen[key] = true
|
||||
item
|
||||
|
||||
plural = (count, word) ->
|
||||
"#{count} #{word}#{count == 1 and "" or "s"}"
|
||||
|
||||
-- files is a list of tuples, {source, target}
|
||||
class Watcher
|
||||
start_msg: "Starting watch loop (Ctrl-C to exit)"
|
||||
new: (@file_list) =>
|
||||
|
||||
print_start: (mode, misc) =>
|
||||
io.stderr\write "#{@start_msg} with #{mode} [#{misc}]\n"
|
||||
|
||||
class InotifyWacher extends Watcher
|
||||
@available: =>
|
||||
pcall -> require "inotify"
|
||||
|
||||
get_dirs: =>
|
||||
import parse_dir from require "moonscript.cmd.moonc"
|
||||
dirs = for {file_path} in *@file_list
|
||||
dir = parse_dir file_path
|
||||
dir = "./" if dir == ""
|
||||
dir
|
||||
|
||||
remove_dupes dirs
|
||||
|
||||
-- creates an iterator that yields a file every time it's updated
|
||||
-- TODO: detect when new files are added to directories
|
||||
each_update: =>
|
||||
coroutine.wrap ->
|
||||
dirs = @get_dirs!
|
||||
|
||||
@print_start "inotify", plural #dirs, "dir"
|
||||
|
||||
wd_table = {}
|
||||
|
||||
inotify = require "inotify"
|
||||
handle = inotify.init!
|
||||
|
||||
for dir in *dirs
|
||||
wd = handle\addwatch dir, inotify.IN_CLOSE_WRITE, inotify.IN_MOVED_TO
|
||||
wd_table[wd] = dir
|
||||
|
||||
while true
|
||||
events = handle\read!
|
||||
break unless events -- error?
|
||||
|
||||
for ev in *events
|
||||
fname = ev.name
|
||||
continue unless fname\match "%.moon$"
|
||||
dir = wd_table[ev.wd]
|
||||
fname = dir .. fname if dir != "./"
|
||||
|
||||
-- TODO: check to make sure the file was in the original set
|
||||
coroutine.yield fname
|
||||
|
||||
class SleepWatcher extends Watcher
|
||||
polling_rate: 1.0
|
||||
|
||||
-- the windows mooonscript binaries provide their own sleep function
|
||||
get_sleep_func: =>
|
||||
local sleep
|
||||
|
||||
pcall ->
|
||||
sleep = require("socket").sleep
|
||||
|
||||
-- TODO: this is also loading moonloader, which isn't intentional
|
||||
sleep or= require("moonscript")._sleep
|
||||
error "Missing sleep function; install LuaSocket" unless sleep
|
||||
sleep
|
||||
|
||||
each_update: =>
|
||||
coroutine.wrap ->
|
||||
lfs = require "lfs"
|
||||
sleep = @get_sleep_func!
|
||||
|
||||
@print_start "polling", plural #@file_list, "files"
|
||||
mod_time = {}
|
||||
|
||||
while true
|
||||
for {file} in *@file_list
|
||||
time = lfs.attributes file, "modification"
|
||||
unless time -- file no longer exists
|
||||
mod_time[file] = nil
|
||||
continue
|
||||
|
||||
unless mod_time[file] -- file time scanned
|
||||
mod_time[file] = time
|
||||
continue
|
||||
|
||||
if time > mod_time[file]
|
||||
mod_time[file] = time
|
||||
coroutine.yield file
|
||||
|
||||
sleep @polling_rate
|
||||
|
||||
{:Watcher, :SleepWatcher, :InotifyWacher}
|
748
prototypes/scp-clicker/moonscript/compile.lua
Normal file
748
prototypes/scp-clicker/moonscript/compile.lua
Normal file
@ -0,0 +1,748 @@
|
||||
local util = require("moonscript.util")
|
||||
local dump = require("moonscript.dump")
|
||||
local transform = require("moonscript.transform")
|
||||
local NameProxy, LocalName
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.names")
|
||||
NameProxy, LocalName = _obj_0.NameProxy, _obj_0.LocalName
|
||||
end
|
||||
local Set
|
||||
Set = require("moonscript.data").Set
|
||||
local ntype, value_can_be_statement
|
||||
do
|
||||
local _obj_0 = require("moonscript.types")
|
||||
ntype, value_can_be_statement = _obj_0.ntype, _obj_0.value_can_be_statement
|
||||
end
|
||||
local statement_compilers = require("moonscript.compile.statement")
|
||||
local value_compilers = require("moonscript.compile.value")
|
||||
local concat, insert
|
||||
do
|
||||
local _obj_0 = table
|
||||
concat, insert = _obj_0.concat, _obj_0.insert
|
||||
end
|
||||
local pos_to_line, get_closest_line, trim, unpack
|
||||
pos_to_line, get_closest_line, trim, unpack = util.pos_to_line, util.get_closest_line, util.trim, util.unpack
|
||||
local mtype = util.moon.type
|
||||
local indent_char = " "
|
||||
local Line, DelayedLine, Lines, Block, RootBlock
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
mark_pos = function(self, pos, line)
|
||||
if line == nil then
|
||||
line = #self
|
||||
end
|
||||
if not (self.posmap[line]) then
|
||||
self.posmap[line] = pos
|
||||
end
|
||||
end,
|
||||
add = function(self, item)
|
||||
local _exp_0 = mtype(item)
|
||||
if Line == _exp_0 then
|
||||
item:render(self)
|
||||
elseif Block == _exp_0 then
|
||||
item:render(self)
|
||||
else
|
||||
self[#self + 1] = item
|
||||
end
|
||||
return self
|
||||
end,
|
||||
flatten_posmap = function(self, line_no, out)
|
||||
if line_no == nil then
|
||||
line_no = 0
|
||||
end
|
||||
if out == nil then
|
||||
out = { }
|
||||
end
|
||||
local posmap = self.posmap
|
||||
for i, l in ipairs(self) do
|
||||
local _exp_0 = mtype(l)
|
||||
if "string" == _exp_0 or DelayedLine == _exp_0 then
|
||||
line_no = line_no + 1
|
||||
out[line_no] = posmap[i]
|
||||
for _ in l:gmatch("\n") do
|
||||
line_no = line_no + 1
|
||||
end
|
||||
out[line_no] = posmap[i]
|
||||
elseif Lines == _exp_0 then
|
||||
local _
|
||||
_, line_no = l:flatten_posmap(line_no, out)
|
||||
else
|
||||
error("Unknown item in Lines: " .. tostring(l))
|
||||
end
|
||||
end
|
||||
return out, line_no
|
||||
end,
|
||||
flatten = function(self, indent, buffer)
|
||||
if indent == nil then
|
||||
indent = nil
|
||||
end
|
||||
if buffer == nil then
|
||||
buffer = { }
|
||||
end
|
||||
for i = 1, #self do
|
||||
local l = self[i]
|
||||
local t = mtype(l)
|
||||
if t == DelayedLine then
|
||||
l = l:render()
|
||||
t = "string"
|
||||
end
|
||||
local _exp_0 = t
|
||||
if "string" == _exp_0 then
|
||||
if indent then
|
||||
insert(buffer, indent)
|
||||
end
|
||||
insert(buffer, l)
|
||||
if "string" == type(self[i + 1]) then
|
||||
local lc = l:sub(-1)
|
||||
if (lc == ")" or lc == "]") and self[i + 1]:sub(1, 1) == "(" then
|
||||
insert(buffer, ";")
|
||||
end
|
||||
end
|
||||
insert(buffer, "\n")
|
||||
elseif Lines == _exp_0 then
|
||||
l:flatten(indent and indent .. indent_char or indent_char, buffer)
|
||||
else
|
||||
error("Unknown item in Lines: " .. tostring(l))
|
||||
end
|
||||
end
|
||||
return buffer
|
||||
end,
|
||||
__tostring = function(self)
|
||||
local strip
|
||||
strip = function(t)
|
||||
if "table" == type(t) then
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #t do
|
||||
local v = t[_index_0]
|
||||
_accum_0[_len_0] = strip(v)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
else
|
||||
return t
|
||||
end
|
||||
end
|
||||
return "Lines<" .. tostring(util.dump(strip(self)):sub(1, -2)) .. ">"
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self)
|
||||
self.posmap = { }
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Lines"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Lines = _class_0
|
||||
end
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
pos = nil,
|
||||
append_list = function(self, items, delim)
|
||||
for i = 1, #items do
|
||||
self:append(items[i])
|
||||
if i < #items then
|
||||
insert(self, delim)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
append = function(self, first, ...)
|
||||
if Line == mtype(first) then
|
||||
if not (self.pos) then
|
||||
self.pos = first.pos
|
||||
end
|
||||
for _index_0 = 1, #first do
|
||||
local value = first[_index_0]
|
||||
self:append(value)
|
||||
end
|
||||
else
|
||||
insert(self, first)
|
||||
end
|
||||
if ... then
|
||||
return self:append(...)
|
||||
end
|
||||
end,
|
||||
render = function(self, buffer)
|
||||
local current = { }
|
||||
local add_current
|
||||
add_current = function()
|
||||
buffer:add(concat(current))
|
||||
return buffer:mark_pos(self.pos)
|
||||
end
|
||||
for _index_0 = 1, #self do
|
||||
local chunk = self[_index_0]
|
||||
local _exp_0 = mtype(chunk)
|
||||
if Block == _exp_0 then
|
||||
local _list_0 = chunk:render(Lines())
|
||||
for _index_1 = 1, #_list_0 do
|
||||
local block_chunk = _list_0[_index_1]
|
||||
if "string" == type(block_chunk) then
|
||||
insert(current, block_chunk)
|
||||
else
|
||||
add_current()
|
||||
buffer:add(block_chunk)
|
||||
current = { }
|
||||
end
|
||||
end
|
||||
else
|
||||
insert(current, chunk)
|
||||
end
|
||||
end
|
||||
if current[1] then
|
||||
add_current()
|
||||
end
|
||||
return buffer
|
||||
end,
|
||||
__tostring = function(self)
|
||||
return "Line<" .. tostring(util.dump(self):sub(1, -2)) .. ">"
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function() end,
|
||||
__base = _base_0,
|
||||
__name = "Line"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Line = _class_0
|
||||
end
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
prepare = function() end,
|
||||
render = function(self)
|
||||
self:prepare()
|
||||
return concat(self)
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, fn)
|
||||
self.prepare = fn
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "DelayedLine"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
DelayedLine = _class_0
|
||||
end
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
header = "do",
|
||||
footer = "end",
|
||||
export_all = false,
|
||||
export_proper = false,
|
||||
value_compilers = value_compilers,
|
||||
statement_compilers = statement_compilers,
|
||||
__tostring = function(self)
|
||||
local h
|
||||
if "string" == type(self.header) then
|
||||
h = self.header
|
||||
else
|
||||
h = unpack(self.header:render({ }))
|
||||
end
|
||||
return "Block<" .. tostring(h) .. "> <- " .. tostring(self.parent)
|
||||
end,
|
||||
set = function(self, name, value)
|
||||
self._state[name] = value
|
||||
end,
|
||||
get = function(self, name)
|
||||
return self._state[name]
|
||||
end,
|
||||
get_current = function(self, name)
|
||||
return rawget(self._state, name)
|
||||
end,
|
||||
listen = function(self, name, fn)
|
||||
self._listeners[name] = fn
|
||||
end,
|
||||
unlisten = function(self, name)
|
||||
self._listeners[name] = nil
|
||||
end,
|
||||
send = function(self, name, ...)
|
||||
do
|
||||
local fn = self._listeners[name]
|
||||
if fn then
|
||||
return fn(self, ...)
|
||||
end
|
||||
end
|
||||
end,
|
||||
extract_assign_name = function(self, node)
|
||||
local is_local = false
|
||||
local real_name
|
||||
local _exp_0 = mtype(node)
|
||||
if LocalName == _exp_0 then
|
||||
is_local = true
|
||||
real_name = node:get_name(self)
|
||||
elseif NameProxy == _exp_0 then
|
||||
real_name = node:get_name(self)
|
||||
elseif "table" == _exp_0 then
|
||||
real_name = node[1] == "ref" and node[2]
|
||||
elseif "string" == _exp_0 then
|
||||
real_name = node
|
||||
end
|
||||
return real_name, is_local
|
||||
end,
|
||||
declare = function(self, names)
|
||||
local undeclared
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #names do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local name = names[_index_0]
|
||||
local real_name, is_local = self:extract_assign_name(name)
|
||||
if not (is_local or real_name and not self:has_name(real_name, true)) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
self:put_name(real_name)
|
||||
if self:name_exported(real_name) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
local _value_0 = real_name
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
undeclared = _accum_0
|
||||
end
|
||||
return undeclared
|
||||
end,
|
||||
whitelist_names = function(self, names)
|
||||
self._name_whitelist = Set(names)
|
||||
end,
|
||||
name_exported = function(self, name)
|
||||
if self.export_all then
|
||||
return true
|
||||
end
|
||||
if self.export_proper and name:match("^%u") then
|
||||
return true
|
||||
end
|
||||
end,
|
||||
put_name = function(self, name, ...)
|
||||
local value = ...
|
||||
if select("#", ...) == 0 then
|
||||
value = true
|
||||
end
|
||||
if NameProxy == mtype(name) then
|
||||
name = name:get_name(self)
|
||||
end
|
||||
self._names[name] = value
|
||||
end,
|
||||
has_name = function(self, name, skip_exports)
|
||||
if not skip_exports and self:name_exported(name) then
|
||||
return true
|
||||
end
|
||||
local yes = self._names[name]
|
||||
if yes == nil and self.parent then
|
||||
if not self._name_whitelist or self._name_whitelist[name] then
|
||||
return self.parent:has_name(name, true)
|
||||
end
|
||||
else
|
||||
return yes
|
||||
end
|
||||
end,
|
||||
is_local = function(self, node)
|
||||
local t = mtype(node)
|
||||
if t == "string" then
|
||||
return self:has_name(node, false)
|
||||
end
|
||||
if t == NameProxy or t == LocalName then
|
||||
return true
|
||||
end
|
||||
if t == "table" then
|
||||
if node[1] == "ref" or (node[1] == "chain" and #node == 2) then
|
||||
return self:is_local(node[2])
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
free_name = function(self, prefix, dont_put)
|
||||
prefix = prefix or "moon"
|
||||
local searching = true
|
||||
local name, i = nil, 0
|
||||
while searching do
|
||||
name = concat({
|
||||
"",
|
||||
prefix,
|
||||
i
|
||||
}, "_")
|
||||
i = i + 1
|
||||
searching = self:has_name(name, true)
|
||||
end
|
||||
if not dont_put then
|
||||
self:put_name(name)
|
||||
end
|
||||
return name
|
||||
end,
|
||||
init_free_var = function(self, prefix, value)
|
||||
local name = self:free_name(prefix, true)
|
||||
self:stm({
|
||||
"assign",
|
||||
{
|
||||
name
|
||||
},
|
||||
{
|
||||
value
|
||||
}
|
||||
})
|
||||
return name
|
||||
end,
|
||||
add = function(self, item, pos)
|
||||
do
|
||||
local _with_0 = self._lines
|
||||
_with_0:add(item)
|
||||
if pos then
|
||||
_with_0:mark_pos(pos)
|
||||
end
|
||||
end
|
||||
return item
|
||||
end,
|
||||
render = function(self, buffer)
|
||||
buffer:add(self.header)
|
||||
buffer:mark_pos(self.pos)
|
||||
if self.next then
|
||||
buffer:add(self._lines)
|
||||
self.next:render(buffer)
|
||||
else
|
||||
if #self._lines == 0 and "string" == type(buffer[#buffer]) then
|
||||
buffer[#buffer] = buffer[#buffer] .. (" " .. (unpack(Lines():add(self.footer))))
|
||||
else
|
||||
buffer:add(self._lines)
|
||||
buffer:add(self.footer)
|
||||
buffer:mark_pos(self.pos)
|
||||
end
|
||||
end
|
||||
return buffer
|
||||
end,
|
||||
block = function(self, header, footer)
|
||||
return Block(self, header, footer)
|
||||
end,
|
||||
line = function(self, ...)
|
||||
do
|
||||
local _with_0 = Line()
|
||||
_with_0:append(...)
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
is_stm = function(self, node)
|
||||
return self.statement_compilers[ntype(node)] ~= nil
|
||||
end,
|
||||
is_value = function(self, node)
|
||||
local t = ntype(node)
|
||||
return self.value_compilers[t] ~= nil or t == "value"
|
||||
end,
|
||||
name = function(self, node, ...)
|
||||
if type(node) == "string" then
|
||||
return node
|
||||
else
|
||||
return self:value(node, ...)
|
||||
end
|
||||
end,
|
||||
value = function(self, node, ...)
|
||||
node = self.transform.value(node)
|
||||
local action
|
||||
if type(node) ~= "table" then
|
||||
action = "raw_value"
|
||||
else
|
||||
action = node[1]
|
||||
end
|
||||
local fn = self.value_compilers[action]
|
||||
if not (fn) then
|
||||
error({
|
||||
"compile-error",
|
||||
"Failed to find value compiler for: " .. dump.value(node),
|
||||
node[-1]
|
||||
})
|
||||
end
|
||||
local out = fn(self, node, ...)
|
||||
if type(node) == "table" and node[-1] then
|
||||
if type(out) == "string" then
|
||||
do
|
||||
local _with_0 = Line()
|
||||
_with_0:append(out)
|
||||
out = _with_0
|
||||
end
|
||||
end
|
||||
out.pos = node[-1]
|
||||
end
|
||||
return out
|
||||
end,
|
||||
values = function(self, values, delim)
|
||||
delim = delim or ', '
|
||||
do
|
||||
local _with_0 = Line()
|
||||
_with_0:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #values do
|
||||
local v = values[_index_0]
|
||||
_accum_0[_len_0] = self:value(v)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), delim)
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
stm = function(self, node, ...)
|
||||
if not node then
|
||||
return
|
||||
end
|
||||
node = self.transform.statement(node)
|
||||
local result
|
||||
do
|
||||
local fn = self.statement_compilers[ntype(node)]
|
||||
if fn then
|
||||
result = fn(self, node, ...)
|
||||
else
|
||||
if value_can_be_statement(node) then
|
||||
result = self:value(node)
|
||||
else
|
||||
result = self:stm({
|
||||
"assign",
|
||||
{
|
||||
"_"
|
||||
},
|
||||
{
|
||||
node
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
if result then
|
||||
if type(node) == "table" and type(result) == "table" and node[-1] then
|
||||
result.pos = node[-1]
|
||||
end
|
||||
self:add(result)
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
stms = function(self, stms, ret)
|
||||
if ret then
|
||||
error("deprecated stms call, use transformer")
|
||||
end
|
||||
local current_stms, current_stm_i
|
||||
current_stms, current_stm_i = self.current_stms, self.current_stm_i
|
||||
self.current_stms = stms
|
||||
for i = 1, #stms do
|
||||
self.current_stm_i = i
|
||||
self:stm(stms[i])
|
||||
end
|
||||
self.current_stms = current_stms
|
||||
self.current_stm_i = current_stm_i
|
||||
return nil
|
||||
end,
|
||||
splice = function(self, fn)
|
||||
local lines = {
|
||||
"lines",
|
||||
self._lines
|
||||
}
|
||||
self._lines = Lines()
|
||||
return self:stms(fn(lines))
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, parent, header, footer)
|
||||
self.parent, self.header, self.footer = parent, header, footer
|
||||
self._lines = Lines()
|
||||
self._names = { }
|
||||
self._state = { }
|
||||
self._listeners = { }
|
||||
do
|
||||
self.transform = {
|
||||
value = transform.Value:bind(self),
|
||||
statement = transform.Statement:bind(self)
|
||||
}
|
||||
end
|
||||
if self.parent then
|
||||
self.root = self.parent.root
|
||||
self.indent = self.parent.indent + 1
|
||||
setmetatable(self._state, {
|
||||
__index = self.parent._state
|
||||
})
|
||||
return setmetatable(self._listeners, {
|
||||
__index = self.parent._listeners
|
||||
})
|
||||
else
|
||||
self.indent = 0
|
||||
end
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Block"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Block = _class_0
|
||||
end
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = Block
|
||||
local _base_0 = {
|
||||
__tostring = function(self)
|
||||
return "RootBlock<>"
|
||||
end,
|
||||
root_stms = function(self, stms)
|
||||
if not (self.options.implicitly_return_root == false) then
|
||||
stms = transform.Statement.transformers.root_stms(self, stms)
|
||||
end
|
||||
return self:stms(stms)
|
||||
end,
|
||||
render = function(self)
|
||||
local buffer = self._lines:flatten()
|
||||
if buffer[#buffer] == "\n" then
|
||||
buffer[#buffer] = nil
|
||||
end
|
||||
return table.concat(buffer)
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, options)
|
||||
self.options = options
|
||||
self.root = self
|
||||
return _class_0.__parent.__init(self)
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "RootBlock",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
RootBlock = _class_0
|
||||
end
|
||||
local format_error
|
||||
format_error = function(msg, pos, file_str)
|
||||
local line_message
|
||||
if pos then
|
||||
local line = pos_to_line(file_str, pos)
|
||||
local line_str
|
||||
line_str, line = get_closest_line(file_str, line)
|
||||
line_str = line_str or ""
|
||||
line_message = (" [%d] >> %s"):format(line, trim(line_str))
|
||||
end
|
||||
return concat({
|
||||
"Compile error: " .. msg,
|
||||
line_message
|
||||
}, "\n")
|
||||
end
|
||||
local value
|
||||
value = function(value)
|
||||
local out = nil
|
||||
do
|
||||
local _with_0 = RootBlock()
|
||||
_with_0:add(_with_0:value(value))
|
||||
out = _with_0:render()
|
||||
end
|
||||
return out
|
||||
end
|
||||
local tree
|
||||
tree = function(tree, options)
|
||||
if options == nil then
|
||||
options = { }
|
||||
end
|
||||
assert(tree, "missing tree")
|
||||
local scope = (options.scope or RootBlock)(options)
|
||||
local runner = coroutine.create(function()
|
||||
return scope:root_stms(tree)
|
||||
end)
|
||||
local success, err = coroutine.resume(runner)
|
||||
if not (success) then
|
||||
local error_msg, error_pos
|
||||
if type(err) == "table" then
|
||||
local _exp_0 = err[1]
|
||||
if "user-error" == _exp_0 or "compile-error" == _exp_0 then
|
||||
error_msg, error_pos = unpack(err, 2)
|
||||
else
|
||||
error_msg, error_pos = error("Unknown error thrown", util.dump(error_msg))
|
||||
end
|
||||
else
|
||||
error_msg, error_pos = concat({
|
||||
err,
|
||||
debug.traceback(runner)
|
||||
}, "\n")
|
||||
end
|
||||
return nil, error_msg, error_pos or scope.last_pos
|
||||
end
|
||||
local lua_code = scope:render()
|
||||
local posmap = scope._lines:flatten_posmap()
|
||||
return lua_code, posmap
|
||||
end
|
||||
do
|
||||
local data = require("moonscript.data")
|
||||
for name, cls in pairs({
|
||||
Line = Line,
|
||||
Lines = Lines,
|
||||
DelayedLine = DelayedLine
|
||||
}) do
|
||||
data[name] = cls
|
||||
end
|
||||
end
|
||||
return {
|
||||
tree = tree,
|
||||
value = value,
|
||||
format_error = format_error,
|
||||
Block = Block,
|
||||
RootBlock = RootBlock
|
||||
}
|
485
prototypes/scp-clicker/moonscript/compile.moon
Normal file
485
prototypes/scp-clicker/moonscript/compile.moon
Normal file
@ -0,0 +1,485 @@
|
||||
|
||||
util = require "moonscript.util"
|
||||
dump = require "moonscript.dump"
|
||||
transform = require "moonscript.transform"
|
||||
|
||||
import NameProxy, LocalName from require "moonscript.transform.names"
|
||||
import Set from require "moonscript.data"
|
||||
import ntype, value_can_be_statement from require "moonscript.types"
|
||||
|
||||
statement_compilers = require "moonscript.compile.statement"
|
||||
value_compilers = require "moonscript.compile.value"
|
||||
|
||||
import concat, insert from table
|
||||
import pos_to_line, get_closest_line, trim, unpack from util
|
||||
|
||||
mtype = util.moon.type
|
||||
|
||||
indent_char = " "
|
||||
|
||||
local Line, DelayedLine, Lines, Block, RootBlock
|
||||
|
||||
-- a buffer for building up lines
|
||||
class Lines
|
||||
new: =>
|
||||
@posmap = {}
|
||||
|
||||
mark_pos: (pos, line=#@) =>
|
||||
@posmap[line] = pos unless @posmap[line]
|
||||
|
||||
-- append a line or lines to the buffer
|
||||
add: (item) =>
|
||||
switch mtype item
|
||||
when Line
|
||||
item\render self
|
||||
when Block
|
||||
item\render self
|
||||
else -- also captures DelayedLine
|
||||
@[#@ + 1] = item
|
||||
@
|
||||
|
||||
flatten_posmap: (line_no=0, out={}) =>
|
||||
posmap = @posmap
|
||||
for i, l in ipairs @
|
||||
switch mtype l
|
||||
when "string", DelayedLine
|
||||
line_no += 1
|
||||
out[line_no] = posmap[i]
|
||||
|
||||
line_no += 1 for _ in l\gmatch"\n"
|
||||
out[line_no] = posmap[i]
|
||||
when Lines
|
||||
_, line_no = l\flatten_posmap line_no, out
|
||||
else
|
||||
error "Unknown item in Lines: #{l}"
|
||||
|
||||
out, line_no
|
||||
|
||||
flatten: (indent=nil, buffer={}) =>
|
||||
for i = 1, #@
|
||||
l = @[i]
|
||||
t = mtype l
|
||||
|
||||
if t == DelayedLine
|
||||
l = l\render!
|
||||
t = "string"
|
||||
|
||||
switch t
|
||||
when "string"
|
||||
insert buffer, indent if indent
|
||||
insert buffer, l
|
||||
|
||||
-- insert breaks between ambiguous statements
|
||||
if "string" == type @[i + 1]
|
||||
lc = l\sub(-1)
|
||||
if (lc == ")" or lc == "]") and @[i + 1]\sub(1,1) == "("
|
||||
insert buffer, ";"
|
||||
|
||||
insert buffer, "\n"
|
||||
when Lines
|
||||
l\flatten indent and indent .. indent_char or indent_char, buffer
|
||||
else
|
||||
error "Unknown item in Lines: #{l}"
|
||||
buffer
|
||||
|
||||
__tostring: =>
|
||||
-- strip non-array elements
|
||||
strip = (t) ->
|
||||
if "table" == type t
|
||||
[strip v for v in *t]
|
||||
else
|
||||
t
|
||||
|
||||
"Lines<#{util.dump(strip @)\sub 1, -2}>"
|
||||
|
||||
-- Buffer for building up a line
|
||||
-- A plain old table holding either strings or Block objects.
|
||||
-- Adding a line to a line will cause that line to be merged in.
|
||||
class Line
|
||||
pos: nil
|
||||
|
||||
append_list: (items, delim) =>
|
||||
for i = 1,#items
|
||||
@append items[i]
|
||||
if i < #items then insert self, delim
|
||||
nil
|
||||
|
||||
append: (first, ...) =>
|
||||
if Line == mtype first
|
||||
-- print "appending line to line", first.pos, first
|
||||
@pos = first.pos unless @pos -- bubble pos if there isn't one
|
||||
@append value for value in *first
|
||||
else
|
||||
insert self, first
|
||||
|
||||
if ...
|
||||
@append ...
|
||||
|
||||
-- todo: try to remove concats from here
|
||||
render: (buffer) =>
|
||||
current = {}
|
||||
|
||||
add_current = ->
|
||||
buffer\add concat current
|
||||
buffer\mark_pos @pos
|
||||
|
||||
for chunk in *@
|
||||
switch mtype chunk
|
||||
when Block
|
||||
for block_chunk in *chunk\render Lines!
|
||||
if "string" == type block_chunk
|
||||
insert current, block_chunk
|
||||
else
|
||||
add_current!
|
||||
buffer\add block_chunk
|
||||
current = {}
|
||||
else
|
||||
insert current, chunk
|
||||
|
||||
if current[1]
|
||||
add_current!
|
||||
|
||||
buffer
|
||||
|
||||
__tostring: =>
|
||||
"Line<#{util.dump(@)\sub 1, -2}>"
|
||||
|
||||
class DelayedLine
|
||||
new: (fn) =>
|
||||
@prepare = fn
|
||||
|
||||
prepare: ->
|
||||
|
||||
render: =>
|
||||
@prepare!
|
||||
concat @
|
||||
|
||||
class Block
|
||||
header: "do"
|
||||
footer: "end"
|
||||
|
||||
export_all: false
|
||||
export_proper: false
|
||||
|
||||
value_compilers: value_compilers
|
||||
statement_compilers: statement_compilers
|
||||
|
||||
__tostring: =>
|
||||
h = if "string" == type @header
|
||||
@header
|
||||
else
|
||||
unpack @header\render {}
|
||||
|
||||
"Block<#{h}> <- " .. tostring @parent
|
||||
|
||||
new: (@parent, @header, @footer) =>
|
||||
@_lines = Lines!
|
||||
|
||||
@_names = {}
|
||||
@_state = {}
|
||||
@_listeners = {}
|
||||
|
||||
with transform
|
||||
@transform = {
|
||||
value: .Value\bind self
|
||||
statement: .Statement\bind self
|
||||
}
|
||||
|
||||
if @parent
|
||||
@root = @parent.root
|
||||
@indent = @parent.indent + 1
|
||||
setmetatable @_state, { __index: @parent._state }
|
||||
setmetatable @_listeners, { __index: @parent._listeners }
|
||||
else
|
||||
@indent = 0
|
||||
|
||||
set: (name, value) =>
|
||||
@_state[name] = value
|
||||
|
||||
get: (name) =>
|
||||
@_state[name]
|
||||
|
||||
get_current: (name) =>
|
||||
rawget @_state, name
|
||||
|
||||
listen: (name, fn) =>
|
||||
@_listeners[name] = fn
|
||||
|
||||
unlisten: (name) =>
|
||||
@_listeners[name] = nil
|
||||
|
||||
send: (name, ...) =>
|
||||
if fn = @_listeners[name]
|
||||
fn self, ...
|
||||
|
||||
extract_assign_name: (node) =>
|
||||
is_local = false
|
||||
real_name = switch mtype node
|
||||
when LocalName
|
||||
is_local = true
|
||||
node\get_name self
|
||||
when NameProxy
|
||||
node\get_name self
|
||||
when "table"
|
||||
node[1] == "ref" and node[2]
|
||||
when "string"
|
||||
-- TOOD: some legacy transfomers might use string for ref
|
||||
node
|
||||
|
||||
real_name, is_local
|
||||
|
||||
declare: (names) =>
|
||||
undeclared = for name in *names
|
||||
real_name, is_local = @extract_assign_name name
|
||||
continue unless is_local or real_name and not @has_name real_name, true
|
||||
-- this also puts exported names so they can be assigned a new value in
|
||||
-- deeper scope
|
||||
@put_name real_name
|
||||
continue if @name_exported real_name
|
||||
real_name
|
||||
|
||||
undeclared
|
||||
|
||||
whitelist_names: (names) =>
|
||||
@_name_whitelist = Set names
|
||||
|
||||
name_exported: (name) =>
|
||||
return true if @export_all
|
||||
return true if @export_proper and name\match"^%u"
|
||||
|
||||
put_name: (name, ...) =>
|
||||
value = ...
|
||||
value = true if select("#", ...) == 0
|
||||
|
||||
name = name\get_name self if NameProxy == mtype name
|
||||
@_names[name] = value
|
||||
|
||||
-- Check if a name is defined in the current or any enclosing scope
|
||||
-- skip_exports: ignore names that have been exported using `export`
|
||||
has_name: (name, skip_exports) =>
|
||||
return true if not skip_exports and @name_exported name
|
||||
|
||||
yes = @_names[name]
|
||||
if yes == nil and @parent
|
||||
if not @_name_whitelist or @_name_whitelist[name]
|
||||
@parent\has_name name, true
|
||||
else
|
||||
yes
|
||||
|
||||
is_local: (node) =>
|
||||
t = mtype node
|
||||
|
||||
return @has_name(node, false) if t == "string"
|
||||
return true if t == NameProxy or t == LocalName
|
||||
|
||||
if t == "table"
|
||||
if node[1] == "ref" or (node[1] == "chain" and #node == 2)
|
||||
return @is_local node[2]
|
||||
|
||||
false
|
||||
|
||||
free_name: (prefix, dont_put) =>
|
||||
prefix = prefix or "moon"
|
||||
searching = true
|
||||
name, i = nil, 0
|
||||
while searching
|
||||
name = concat {"", prefix, i}, "_"
|
||||
i = i + 1
|
||||
searching = @has_name name, true
|
||||
|
||||
@put_name name if not dont_put
|
||||
name
|
||||
|
||||
init_free_var: (prefix, value) =>
|
||||
name = @free_name prefix, true
|
||||
@stm {"assign", {name}, {value}}
|
||||
name
|
||||
|
||||
-- add something to the line buffer
|
||||
add: (item, pos) =>
|
||||
with @_lines
|
||||
\add item
|
||||
\mark_pos pos if pos
|
||||
item
|
||||
|
||||
-- todo: pass in buffer as argument
|
||||
render: (buffer) =>
|
||||
buffer\add @header
|
||||
buffer\mark_pos @pos
|
||||
|
||||
if @next
|
||||
buffer\add @_lines
|
||||
@next\render buffer
|
||||
else
|
||||
-- join an empty block into a single line
|
||||
if #@_lines == 0 and "string" == type buffer[#buffer]
|
||||
buffer[#buffer] ..= " " .. (unpack Lines!\add @footer)
|
||||
else
|
||||
buffer\add @_lines
|
||||
buffer\add @footer
|
||||
buffer\mark_pos @pos
|
||||
|
||||
buffer
|
||||
|
||||
block: (header, footer) =>
|
||||
Block self, header, footer
|
||||
|
||||
line: (...) =>
|
||||
with Line!
|
||||
\append ...
|
||||
|
||||
is_stm: (node) =>
|
||||
@statement_compilers[ntype node] != nil
|
||||
|
||||
is_value: (node) =>
|
||||
t = ntype node
|
||||
@value_compilers[t] != nil or t == "value"
|
||||
|
||||
-- compile name for assign
|
||||
name: (node, ...) =>
|
||||
if type(node) == "string"
|
||||
node
|
||||
else
|
||||
@value node, ...
|
||||
|
||||
value: (node, ...) =>
|
||||
node = @transform.value node
|
||||
action = if type(node) != "table"
|
||||
"raw_value"
|
||||
else
|
||||
node[1]
|
||||
|
||||
fn = @value_compilers[action]
|
||||
unless fn
|
||||
error {
|
||||
"compile-error"
|
||||
"Failed to find value compiler for: " .. dump.value node
|
||||
node[-1]
|
||||
}
|
||||
|
||||
out = fn self, node, ...
|
||||
|
||||
-- store the pos, creating a line if necessary
|
||||
if type(node) == "table" and node[-1]
|
||||
if type(out) == "string"
|
||||
out = with Line! do \append out
|
||||
out.pos = node[-1]
|
||||
|
||||
out
|
||||
|
||||
values: (values, delim) =>
|
||||
delim = delim or ', '
|
||||
with Line!
|
||||
\append_list [@value v for v in *values], delim
|
||||
|
||||
stm: (node, ...) =>
|
||||
return if not node -- skip blank statements
|
||||
node = @transform.statement node
|
||||
|
||||
result = if fn = @statement_compilers[ntype(node)]
|
||||
fn @, node, ...
|
||||
else
|
||||
if value_can_be_statement node
|
||||
@value node
|
||||
else
|
||||
-- coerce value into statement
|
||||
@stm {"assign", {"_"}, {node}}
|
||||
|
||||
if result
|
||||
if type(node) == "table" and type(result) == "table" and node[-1]
|
||||
result.pos = node[-1]
|
||||
@add result
|
||||
|
||||
nil
|
||||
|
||||
stms: (stms, ret) =>
|
||||
error "deprecated stms call, use transformer" if ret
|
||||
{:current_stms, :current_stm_i} = @
|
||||
|
||||
@current_stms = stms
|
||||
for i=1,#stms
|
||||
@current_stm_i = i
|
||||
@stm stms[i]
|
||||
|
||||
@current_stms = current_stms
|
||||
@current_stm_i = current_stm_i
|
||||
|
||||
nil
|
||||
|
||||
-- takes the existing set of lines and replaces them with the result of
|
||||
-- calling fn on them
|
||||
splice: (fn) =>
|
||||
lines = {"lines", @_lines}
|
||||
@_lines = Lines!
|
||||
@stms fn lines
|
||||
|
||||
class RootBlock extends Block
|
||||
new: (@options) =>
|
||||
@root = self
|
||||
super!
|
||||
|
||||
__tostring: => "RootBlock<>"
|
||||
|
||||
root_stms: (stms) =>
|
||||
unless @options.implicitly_return_root == false
|
||||
stms = transform.Statement.transformers.root_stms self, stms
|
||||
@stms stms
|
||||
|
||||
render: =>
|
||||
-- print @_lines
|
||||
buffer = @_lines\flatten!
|
||||
buffer[#buffer] = nil if buffer[#buffer] == "\n"
|
||||
table.concat buffer
|
||||
|
||||
format_error = (msg, pos, file_str) ->
|
||||
line_message = if pos
|
||||
line = pos_to_line file_str, pos
|
||||
line_str, line = get_closest_line file_str, line
|
||||
line_str = line_str or ""
|
||||
(" [%d] >> %s")\format line, trim line_str
|
||||
|
||||
concat {
|
||||
"Compile error: "..msg
|
||||
line_message
|
||||
}, "\n"
|
||||
|
||||
value = (value) ->
|
||||
out = nil
|
||||
with RootBlock!
|
||||
\add \value value
|
||||
out = \render!
|
||||
out
|
||||
|
||||
tree = (tree, options={}) ->
|
||||
assert tree, "missing tree"
|
||||
|
||||
scope = (options.scope or RootBlock) options
|
||||
|
||||
runner = coroutine.create ->
|
||||
scope\root_stms tree
|
||||
|
||||
success, err = coroutine.resume runner
|
||||
|
||||
unless success
|
||||
error_msg, error_pos = if type(err) == "table"
|
||||
switch err[1]
|
||||
when "user-error", "compile-error"
|
||||
unpack err, 2
|
||||
else
|
||||
-- unknown error, bubble it
|
||||
error "Unknown error thrown", util.dump error_msg
|
||||
else
|
||||
concat {err, debug.traceback runner}, "\n"
|
||||
|
||||
return nil, error_msg, error_pos or scope.last_pos
|
||||
|
||||
lua_code = scope\render!
|
||||
posmap = scope._lines\flatten_posmap!
|
||||
lua_code, posmap
|
||||
|
||||
-- mmmm
|
||||
with data = require "moonscript.data"
|
||||
for name, cls in pairs {:Line, :Lines, :DelayedLine}
|
||||
data[name] = cls
|
||||
|
||||
{ :tree, :value, :format_error, :Block, :RootBlock }
|
240
prototypes/scp-clicker/moonscript/compile/statement.lua
Normal file
240
prototypes/scp-clicker/moonscript/compile/statement.lua
Normal file
@ -0,0 +1,240 @@
|
||||
local ntype
|
||||
ntype = require("moonscript.types").ntype
|
||||
local concat, insert
|
||||
do
|
||||
local _obj_0 = table
|
||||
concat, insert = _obj_0.concat, _obj_0.insert
|
||||
end
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
return {
|
||||
raw = function(self, node)
|
||||
return self:add(node[2])
|
||||
end,
|
||||
lines = function(self, node)
|
||||
local _list_0 = node[2]
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local line = _list_0[_index_0]
|
||||
self:add(line)
|
||||
end
|
||||
end,
|
||||
declare = function(self, node)
|
||||
local names = node[2]
|
||||
local undeclared = self:declare(names)
|
||||
if #undeclared > 0 then
|
||||
do
|
||||
local _with_0 = self:line("local ")
|
||||
_with_0:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #undeclared do
|
||||
local name = undeclared[_index_0]
|
||||
_accum_0[_len_0] = self:name(name)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ", ")
|
||||
return _with_0
|
||||
end
|
||||
end
|
||||
end,
|
||||
declare_with_shadows = function(self, node)
|
||||
local names = node[2]
|
||||
self:declare(names)
|
||||
do
|
||||
local _with_0 = self:line("local ")
|
||||
_with_0:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #names do
|
||||
local name = names[_index_0]
|
||||
_accum_0[_len_0] = self:name(name)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ", ")
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
assign = function(self, node)
|
||||
local names, values = unpack(node, 2)
|
||||
local undeclared = self:declare(names)
|
||||
local declare = "local " .. concat(undeclared, ", ")
|
||||
local has_fndef = false
|
||||
local i = 1
|
||||
while i <= #values do
|
||||
if ntype(values[i]) == "fndef" then
|
||||
has_fndef = true
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
do
|
||||
local _with_0 = self:line()
|
||||
if #undeclared == #names and not has_fndef then
|
||||
_with_0:append(declare)
|
||||
else
|
||||
if #undeclared > 0 then
|
||||
self:add(declare, node[-1])
|
||||
end
|
||||
_with_0:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #names do
|
||||
local name = names[_index_0]
|
||||
_accum_0[_len_0] = self:value(name)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ", ")
|
||||
end
|
||||
_with_0:append(" = ")
|
||||
_with_0:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #values do
|
||||
local v = values[_index_0]
|
||||
_accum_0[_len_0] = self:value(v)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ", ")
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
["return"] = function(self, node)
|
||||
return self:line("return ", (function()
|
||||
if node[2] ~= "" then
|
||||
return self:value(node[2])
|
||||
end
|
||||
end)())
|
||||
end,
|
||||
["break"] = function(self, node)
|
||||
return "break"
|
||||
end,
|
||||
["if"] = function(self, node)
|
||||
local cond, block = node[2], node[3]
|
||||
local root
|
||||
do
|
||||
local _with_0 = self:block(self:line("if ", self:value(cond), " then"))
|
||||
_with_0:stms(block)
|
||||
root = _with_0
|
||||
end
|
||||
local current = root
|
||||
local add_clause
|
||||
add_clause = function(clause)
|
||||
local type = clause[1]
|
||||
local i = 2
|
||||
local next
|
||||
if type == "else" then
|
||||
next = self:block("else")
|
||||
else
|
||||
i = i + 1
|
||||
next = self:block(self:line("elseif ", self:value(clause[2]), " then"))
|
||||
end
|
||||
next:stms(clause[i])
|
||||
current.next = next
|
||||
current = next
|
||||
end
|
||||
for _index_0 = 4, #node do
|
||||
local cond = node[_index_0]
|
||||
add_clause(cond)
|
||||
end
|
||||
return root
|
||||
end,
|
||||
["repeat"] = function(self, node)
|
||||
local cond, block = unpack(node, 2)
|
||||
do
|
||||
local _with_0 = self:block("repeat", self:line("until ", self:value(cond)))
|
||||
_with_0:stms(block)
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
["while"] = function(self, node)
|
||||
local cond, block = unpack(node, 2)
|
||||
do
|
||||
local _with_0 = self:block(self:line("while ", self:value(cond), " do"))
|
||||
_with_0:stms(block)
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
["for"] = function(self, node)
|
||||
local name, bounds, block = unpack(node, 2)
|
||||
local loop = self:line("for ", self:name(name), " = ", self:value({
|
||||
"explist",
|
||||
unpack(bounds)
|
||||
}), " do")
|
||||
do
|
||||
local _with_0 = self:block(loop)
|
||||
_with_0:declare({
|
||||
name
|
||||
})
|
||||
_with_0:stms(block)
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
foreach = function(self, node)
|
||||
local names, exps, block = unpack(node, 2)
|
||||
local loop
|
||||
do
|
||||
local _with_0 = self:line()
|
||||
_with_0:append("for ")
|
||||
loop = _with_0
|
||||
end
|
||||
do
|
||||
local _with_0 = self:block(loop)
|
||||
loop:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #names do
|
||||
local name = names[_index_0]
|
||||
_accum_0[_len_0] = _with_0:name(name, false)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ", ")
|
||||
loop:append(" in ")
|
||||
loop:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #exps do
|
||||
local exp = exps[_index_0]
|
||||
_accum_0[_len_0] = self:value(exp)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ",")
|
||||
loop:append(" do")
|
||||
_with_0:declare(names)
|
||||
_with_0:stms(block)
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
export = function(self, node)
|
||||
local names = unpack(node, 2)
|
||||
if type(names) == "string" then
|
||||
if names == "*" then
|
||||
self.export_all = true
|
||||
elseif names == "^" then
|
||||
self.export_proper = true
|
||||
end
|
||||
else
|
||||
self:declare(names)
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
run = function(self, code)
|
||||
code:call(self)
|
||||
return nil
|
||||
end,
|
||||
group = function(self, node)
|
||||
return self:stms(node[2])
|
||||
end,
|
||||
["do"] = function(self, node)
|
||||
do
|
||||
local _with_0 = self:block()
|
||||
_with_0:stms(node[2])
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
noop = function(self) end
|
||||
}
|
137
prototypes/scp-clicker/moonscript/compile/statement.moon
Normal file
137
prototypes/scp-clicker/moonscript/compile/statement.moon
Normal file
@ -0,0 +1,137 @@
|
||||
|
||||
import ntype from require "moonscript.types"
|
||||
import concat, insert from table
|
||||
|
||||
import unpack from require "moonscript.util"
|
||||
|
||||
{
|
||||
raw: (node) => @add node[2]
|
||||
|
||||
lines: (node) =>
|
||||
for line in *node[2]
|
||||
@add line
|
||||
|
||||
declare: (node) =>
|
||||
names = node[2]
|
||||
undeclared = @declare names
|
||||
if #undeclared > 0
|
||||
with @line "local "
|
||||
\append_list [@name name for name in *undeclared], ", "
|
||||
|
||||
-- this overrides the existing names with new locals, used for local keyword
|
||||
declare_with_shadows: (node) =>
|
||||
names = node[2]
|
||||
@declare names
|
||||
with @line "local "
|
||||
\append_list [@name name for name in *names], ", "
|
||||
|
||||
assign: (node) =>
|
||||
names, values = unpack node, 2
|
||||
|
||||
undeclared = @declare names
|
||||
declare = "local " .. concat(undeclared, ", ")
|
||||
|
||||
has_fndef = false
|
||||
i = 1
|
||||
while i <= #values
|
||||
if ntype(values[i]) == "fndef"
|
||||
has_fndef = true
|
||||
i = i +1
|
||||
|
||||
with @line!
|
||||
if #undeclared == #names and not has_fndef
|
||||
\append declare
|
||||
else
|
||||
@add declare, node[-1] if #undeclared > 0
|
||||
\append_list [@value name for name in *names], ", "
|
||||
|
||||
\append " = "
|
||||
\append_list [@value v for v in *values], ", "
|
||||
|
||||
return: (node) =>
|
||||
@line "return ", if node[2] != "" then @value node[2]
|
||||
|
||||
break: (node) =>
|
||||
"break"
|
||||
|
||||
if: (node) =>
|
||||
cond, block = node[2], node[3]
|
||||
root = with @block @line "if ", @value(cond), " then"
|
||||
\stms block
|
||||
|
||||
current = root
|
||||
add_clause = (clause)->
|
||||
type = clause[1]
|
||||
i = 2
|
||||
next = if type == "else"
|
||||
@block "else"
|
||||
else
|
||||
i += 1
|
||||
@block @line "elseif ", @value(clause[2]), " then"
|
||||
|
||||
next\stms clause[i]
|
||||
|
||||
current.next = next
|
||||
current = next
|
||||
|
||||
add_clause cond for cond in *node[4,]
|
||||
root
|
||||
|
||||
repeat: (node) =>
|
||||
cond, block = unpack node, 2
|
||||
with @block "repeat", @line "until ", @value cond
|
||||
\stms block
|
||||
|
||||
while: (node) =>
|
||||
cond, block = unpack node, 2
|
||||
with @block @line "while ", @value(cond), " do"
|
||||
\stms block
|
||||
|
||||
for: (node) =>
|
||||
name, bounds, block = unpack node, 2
|
||||
loop = @line "for ", @name(name), " = ", @value({"explist", unpack bounds}), " do"
|
||||
with @block loop
|
||||
\declare {name}
|
||||
\stms block
|
||||
|
||||
-- for x in y ...
|
||||
-- {"foreach", {names...}, {exp...}, body}
|
||||
foreach: (node) =>
|
||||
names, exps, block = unpack node, 2
|
||||
|
||||
loop = with @line!
|
||||
\append "for "
|
||||
|
||||
with @block loop
|
||||
loop\append_list [\name name, false for name in *names], ", "
|
||||
loop\append " in "
|
||||
loop\append_list [@value exp for exp in *exps], ","
|
||||
loop\append " do"
|
||||
|
||||
\declare names
|
||||
\stms block
|
||||
|
||||
export: (node) =>
|
||||
names = unpack node, 2
|
||||
if type(names) == "string"
|
||||
if names == "*"
|
||||
@export_all = true
|
||||
elseif names == "^"
|
||||
@export_proper = true
|
||||
else
|
||||
@declare names
|
||||
nil
|
||||
|
||||
run: (code) =>
|
||||
code\call self
|
||||
nil
|
||||
|
||||
group: (node) =>
|
||||
@stms node[2]
|
||||
|
||||
do: (node) =>
|
||||
with @block!
|
||||
\stms node[2]
|
||||
|
||||
noop: => -- nothing!
|
||||
}
|
325
prototypes/scp-clicker/moonscript/compile/value.lua
Normal file
325
prototypes/scp-clicker/moonscript/compile/value.lua
Normal file
@ -0,0 +1,325 @@
|
||||
local util = require("moonscript.util")
|
||||
local data = require("moonscript.data")
|
||||
local ntype
|
||||
ntype = require("moonscript.types").ntype
|
||||
local user_error
|
||||
user_error = require("moonscript.errors").user_error
|
||||
local concat, insert
|
||||
do
|
||||
local _obj_0 = table
|
||||
concat, insert = _obj_0.concat, _obj_0.insert
|
||||
end
|
||||
local unpack
|
||||
unpack = util.unpack
|
||||
local table_delim = ","
|
||||
local string_chars = {
|
||||
["\r"] = "\\r",
|
||||
["\n"] = "\\n"
|
||||
}
|
||||
return {
|
||||
scoped = function(self, node)
|
||||
local _, before, value, after
|
||||
_, before, value, after = node[1], node[2], node[3], node[4]
|
||||
_ = before and before:call(self)
|
||||
do
|
||||
local _with_0 = self:value(value)
|
||||
_ = after and after:call(self)
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
exp = function(self, node)
|
||||
local _comp
|
||||
_comp = function(i, value)
|
||||
if i % 2 == 1 and value == "!=" then
|
||||
value = "~="
|
||||
end
|
||||
return self:value(value)
|
||||
end
|
||||
do
|
||||
local _with_0 = self:line()
|
||||
_with_0:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for i, v in ipairs(node) do
|
||||
if i > 1 then
|
||||
_accum_0[_len_0] = _comp(i, v)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
end
|
||||
return _accum_0
|
||||
end)(), " ")
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
explist = function(self, node)
|
||||
do
|
||||
local _with_0 = self:line()
|
||||
_with_0:append_list((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 2, #node do
|
||||
local v = node[_index_0]
|
||||
_accum_0[_len_0] = self:value(v)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ", ")
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
parens = function(self, node)
|
||||
return self:line("(", self:value(node[2]), ")")
|
||||
end,
|
||||
string = function(self, node)
|
||||
local delim, inner = unpack(node, 2)
|
||||
local end_delim = delim:gsub("%[", "]")
|
||||
if delim == "'" or delim == '"' then
|
||||
inner = inner:gsub("[\r\n]", string_chars)
|
||||
end
|
||||
return delim .. inner .. end_delim
|
||||
end,
|
||||
chain = function(self, node)
|
||||
local callee = node[2]
|
||||
local callee_type = ntype(callee)
|
||||
local item_offset = 3
|
||||
if callee_type == "dot" or callee_type == "colon" or callee_type == "index" then
|
||||
callee = self:get("scope_var")
|
||||
if not (callee) then
|
||||
user_error("Short-dot syntax must be called within a with block")
|
||||
end
|
||||
item_offset = 2
|
||||
end
|
||||
if callee_type == "ref" and callee[2] == "super" or callee == "super" then
|
||||
do
|
||||
local sup = self:get("super")
|
||||
if sup then
|
||||
return self:value(sup(self, node))
|
||||
end
|
||||
end
|
||||
end
|
||||
local chain_item
|
||||
chain_item = function(node)
|
||||
local t, arg = unpack(node)
|
||||
if t == "call" then
|
||||
return "(", self:values(arg), ")"
|
||||
elseif t == "index" then
|
||||
return "[", self:value(arg), "]"
|
||||
elseif t == "dot" then
|
||||
return ".", tostring(arg)
|
||||
elseif t == "colon" then
|
||||
return ":", tostring(arg)
|
||||
elseif t == "colon_stub" then
|
||||
return user_error("Uncalled colon stub")
|
||||
else
|
||||
return error("Unknown chain action: " .. tostring(t))
|
||||
end
|
||||
end
|
||||
if (callee_type == "self" or callee_type == "self_class") and node[3] and ntype(node[3]) == "call" then
|
||||
callee[1] = callee_type .. "_colon"
|
||||
end
|
||||
local callee_value = self:value(callee)
|
||||
if ntype(callee) == "exp" then
|
||||
callee_value = self:line("(", callee_value, ")")
|
||||
end
|
||||
local actions
|
||||
do
|
||||
local _with_0 = self:line()
|
||||
for _index_0 = item_offset, #node do
|
||||
local action = node[_index_0]
|
||||
_with_0:append(chain_item(action))
|
||||
end
|
||||
actions = _with_0
|
||||
end
|
||||
return self:line(callee_value, actions)
|
||||
end,
|
||||
fndef = function(self, node)
|
||||
local args, whitelist, arrow, block = unpack(node, 2)
|
||||
local default_args = { }
|
||||
local self_args = { }
|
||||
local arg_names
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #args do
|
||||
local arg = args[_index_0]
|
||||
local name, default_value = unpack(arg)
|
||||
if type(name) == "string" then
|
||||
name = name
|
||||
else
|
||||
if name[1] == "self" or name[1] == "self_class" then
|
||||
insert(self_args, name)
|
||||
end
|
||||
name = name[2]
|
||||
end
|
||||
if default_value then
|
||||
insert(default_args, arg)
|
||||
end
|
||||
local _value_0 = name
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
arg_names = _accum_0
|
||||
end
|
||||
if arrow == "fat" then
|
||||
insert(arg_names, 1, "self")
|
||||
end
|
||||
do
|
||||
local _with_0 = self:block()
|
||||
if #whitelist > 0 then
|
||||
_with_0:whitelist_names(whitelist)
|
||||
end
|
||||
for _index_0 = 1, #arg_names do
|
||||
local name = arg_names[_index_0]
|
||||
_with_0:put_name(name)
|
||||
end
|
||||
for _index_0 = 1, #default_args do
|
||||
local default = default_args[_index_0]
|
||||
local name, value = unpack(default)
|
||||
if type(name) == "table" then
|
||||
name = name[2]
|
||||
end
|
||||
_with_0:stm({
|
||||
'if',
|
||||
{
|
||||
'exp',
|
||||
{
|
||||
"ref",
|
||||
name
|
||||
},
|
||||
'==',
|
||||
'nil'
|
||||
},
|
||||
{
|
||||
{
|
||||
'assign',
|
||||
{
|
||||
name
|
||||
},
|
||||
{
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
local self_arg_values
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #self_args do
|
||||
local arg = self_args[_index_0]
|
||||
_accum_0[_len_0] = arg[2]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
self_arg_values = _accum_0
|
||||
end
|
||||
if #self_args > 0 then
|
||||
_with_0:stm({
|
||||
"assign",
|
||||
self_args,
|
||||
self_arg_values
|
||||
})
|
||||
end
|
||||
_with_0:stms(block)
|
||||
if #args > #arg_names then
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #args do
|
||||
local arg = args[_index_0]
|
||||
_accum_0[_len_0] = arg[1]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
arg_names = _accum_0
|
||||
end
|
||||
end
|
||||
_with_0.header = "function(" .. concat(arg_names, ", ") .. ")"
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
table = function(self, node)
|
||||
local items = unpack(node, 2)
|
||||
do
|
||||
local _with_0 = self:block("{", "}")
|
||||
local format_line
|
||||
format_line = function(tuple)
|
||||
if #tuple == 2 then
|
||||
local key, value = unpack(tuple)
|
||||
if ntype(key) == "key_literal" and data.lua_keywords[key[2]] then
|
||||
key = {
|
||||
"string",
|
||||
'"',
|
||||
key[2]
|
||||
}
|
||||
end
|
||||
local assign
|
||||
if ntype(key) == "key_literal" then
|
||||
assign = key[2]
|
||||
else
|
||||
assign = self:line("[", _with_0:value(key), "]")
|
||||
end
|
||||
local out = self:line(assign, " = ", _with_0:value(value))
|
||||
return out
|
||||
else
|
||||
return self:line(_with_0:value(tuple[1]))
|
||||
end
|
||||
end
|
||||
if items then
|
||||
local count = #items
|
||||
for i, tuple in ipairs(items) do
|
||||
local line = format_line(tuple)
|
||||
if not (count == i) then
|
||||
line:append(table_delim)
|
||||
end
|
||||
_with_0:add(line)
|
||||
end
|
||||
end
|
||||
return _with_0
|
||||
end
|
||||
end,
|
||||
minus = function(self, node)
|
||||
return self:line("-", self:value(node[2]))
|
||||
end,
|
||||
temp_name = function(self, node, ...)
|
||||
return node:get_name(self, ...)
|
||||
end,
|
||||
number = function(self, node)
|
||||
return node[2]
|
||||
end,
|
||||
bitnot = function(self, node)
|
||||
return self:line("~", self:value(node[2]))
|
||||
end,
|
||||
length = function(self, node)
|
||||
return self:line("#", self:value(node[2]))
|
||||
end,
|
||||
["not"] = function(self, node)
|
||||
return self:line("not ", self:value(node[2]))
|
||||
end,
|
||||
self = function(self, node)
|
||||
return "self." .. self:name(node[2])
|
||||
end,
|
||||
self_class = function(self, node)
|
||||
return "self.__class." .. self:name(node[2])
|
||||
end,
|
||||
self_colon = function(self, node)
|
||||
return "self:" .. self:name(node[2])
|
||||
end,
|
||||
self_class_colon = function(self, node)
|
||||
return "self.__class:" .. self:name(node[2])
|
||||
end,
|
||||
ref = function(self, value)
|
||||
do
|
||||
local sup = value[2] == "super" and self:get("super")
|
||||
if sup then
|
||||
return self:value(sup(self))
|
||||
end
|
||||
end
|
||||
return tostring(value[2])
|
||||
end,
|
||||
raw_value = function(self, value)
|
||||
if value == "..." then
|
||||
self:send("varargs")
|
||||
end
|
||||
return tostring(value)
|
||||
end
|
||||
}
|
210
prototypes/scp-clicker/moonscript/compile/value.moon
Normal file
210
prototypes/scp-clicker/moonscript/compile/value.moon
Normal file
@ -0,0 +1,210 @@
|
||||
|
||||
util = require "moonscript.util"
|
||||
data = require "moonscript.data"
|
||||
|
||||
import ntype from require "moonscript.types"
|
||||
import user_error from require "moonscript.errors"
|
||||
import concat, insert from table
|
||||
import unpack from util
|
||||
|
||||
table_delim = ","
|
||||
|
||||
string_chars = {
|
||||
"\r": "\\r"
|
||||
"\n": "\\n"
|
||||
}
|
||||
|
||||
{
|
||||
scoped: (node) =>
|
||||
{_, before, value, after} = node
|
||||
before and before\call @
|
||||
with @value value
|
||||
after and after\call @
|
||||
|
||||
-- list of values separated by binary operators
|
||||
exp: (node) =>
|
||||
_comp = (i, value) ->
|
||||
if i % 2 == 1 and value == "!="
|
||||
value = "~="
|
||||
@value value
|
||||
|
||||
with @line!
|
||||
\append_list [_comp i,v for i,v in ipairs node when i > 1], " "
|
||||
|
||||
-- list of expressions separated by commas
|
||||
explist: (node) =>
|
||||
with @line!
|
||||
\append_list [@value v for v in *node[2,]], ", "
|
||||
|
||||
parens: (node) =>
|
||||
@line "(", @value(node[2]), ")"
|
||||
|
||||
string: (node) =>
|
||||
delim, inner = unpack node, 2
|
||||
end_delim = delim\gsub "%[", "]"
|
||||
if delim == "'" or delim == '"'
|
||||
inner = inner\gsub "[\r\n]", string_chars
|
||||
|
||||
delim..inner..end_delim
|
||||
|
||||
chain: (node) =>
|
||||
callee = node[2]
|
||||
callee_type = ntype callee
|
||||
item_offset = 3
|
||||
|
||||
if callee_type == "dot" or callee_type == "colon" or callee_type == "index"
|
||||
callee = @get "scope_var"
|
||||
unless callee
|
||||
user_error "Short-dot syntax must be called within a with block"
|
||||
item_offset = 2
|
||||
|
||||
-- TODO: don't use string literals as ref
|
||||
if callee_type == "ref" and callee[2] == "super" or callee == "super"
|
||||
if sup = @get "super"
|
||||
return @value sup self, node
|
||||
|
||||
chain_item = (node) ->
|
||||
t, arg = unpack node
|
||||
if t == "call"
|
||||
-- print arg, util.dump arg
|
||||
"(", @values(arg), ")"
|
||||
elseif t == "index"
|
||||
"[", @value(arg), "]"
|
||||
elseif t == "dot"
|
||||
".", tostring arg
|
||||
elseif t == "colon"
|
||||
":", tostring arg
|
||||
elseif t == "colon_stub"
|
||||
user_error "Uncalled colon stub"
|
||||
else
|
||||
error "Unknown chain action: #{t}"
|
||||
|
||||
if (callee_type == "self" or callee_type == "self_class") and node[3] and ntype(node[3]) == "call"
|
||||
callee[1] = callee_type.."_colon"
|
||||
|
||||
callee_value = @value callee
|
||||
callee_value = @line "(", callee_value, ")" if ntype(callee) == "exp"
|
||||
|
||||
actions = with @line!
|
||||
\append chain_item action for action in *node[item_offset,]
|
||||
|
||||
@line callee_value, actions
|
||||
|
||||
fndef: (node) =>
|
||||
args, whitelist, arrow, block = unpack node, 2
|
||||
|
||||
default_args = {}
|
||||
self_args = {}
|
||||
arg_names = for arg in *args
|
||||
name, default_value = unpack arg
|
||||
name = if type(name) == "string"
|
||||
name
|
||||
else
|
||||
if name[1] == "self" or name[1] == "self_class"
|
||||
insert self_args, name
|
||||
name[2]
|
||||
insert default_args, arg if default_value
|
||||
name
|
||||
|
||||
if arrow == "fat"
|
||||
insert arg_names, 1, "self"
|
||||
|
||||
with @block!
|
||||
if #whitelist > 0
|
||||
\whitelist_names whitelist
|
||||
|
||||
\put_name name for name in *arg_names
|
||||
|
||||
for default in *default_args
|
||||
name, value = unpack default
|
||||
name = name[2] if type(name) == "table"
|
||||
\stm {
|
||||
'if', {'exp', {"ref", name}, '==', 'nil'}, {
|
||||
{'assign', {name}, {value}}
|
||||
}
|
||||
}
|
||||
|
||||
self_arg_values = [arg[2] for arg in *self_args]
|
||||
\stm {"assign", self_args, self_arg_values} if #self_args > 0
|
||||
|
||||
\stms block
|
||||
|
||||
-- inject more args if the block manipulated arguments
|
||||
-- only varargs bubbling does this currently
|
||||
if #args > #arg_names -- will only work for simple adjustments
|
||||
arg_names = [arg[1] for arg in *args]
|
||||
|
||||
.header = "function("..concat(arg_names, ", ")..")"
|
||||
|
||||
table: (node) =>
|
||||
items = unpack node, 2
|
||||
with @block "{", "}"
|
||||
format_line = (tuple) ->
|
||||
if #tuple == 2
|
||||
key, value = unpack tuple
|
||||
|
||||
-- escape keys that are lua keywords
|
||||
if ntype(key) == "key_literal" and data.lua_keywords[key[2]]
|
||||
key = {"string", '"', key[2]}
|
||||
|
||||
assign = if ntype(key) == "key_literal"
|
||||
key[2]
|
||||
else
|
||||
@line "[", \value(key), "]"
|
||||
|
||||
out = @line assign, " = ", \value(value)
|
||||
out
|
||||
else
|
||||
@line \value tuple[1]
|
||||
|
||||
if items
|
||||
count = #items
|
||||
for i, tuple in ipairs items
|
||||
line = format_line tuple
|
||||
line\append table_delim unless count == i
|
||||
\add line
|
||||
|
||||
minus: (node) =>
|
||||
@line "-", @value node[2]
|
||||
|
||||
temp_name: (node, ...) =>
|
||||
node\get_name self, ...
|
||||
|
||||
number: (node) =>
|
||||
node[2]
|
||||
|
||||
bitnot: (node) =>
|
||||
@line "~", @value node[2]
|
||||
|
||||
length: (node) =>
|
||||
@line "#", @value node[2]
|
||||
|
||||
not: (node) =>
|
||||
@line "not ", @value node[2]
|
||||
|
||||
self: (node) =>
|
||||
"self."..@name node[2]
|
||||
|
||||
self_class: (node) =>
|
||||
"self.__class."..@name node[2]
|
||||
|
||||
self_colon: (node) =>
|
||||
"self:"..@name node[2]
|
||||
|
||||
self_class_colon: (node) =>
|
||||
"self.__class:"..@name node[2]
|
||||
|
||||
-- a variable reference
|
||||
ref: (value) =>
|
||||
if sup = value[2] == "super" and @get "super"
|
||||
return @value sup @
|
||||
|
||||
tostring value[2]
|
||||
|
||||
-- catch all pure string values
|
||||
raw_value: (value) =>
|
||||
if value == "..."
|
||||
@send "varargs"
|
||||
|
||||
tostring value
|
||||
}
|
83
prototypes/scp-clicker/moonscript/data.lua
Normal file
83
prototypes/scp-clicker/moonscript/data.lua
Normal file
@ -0,0 +1,83 @@
|
||||
local concat, remove, insert
|
||||
do
|
||||
local _obj_0 = table
|
||||
concat, remove, insert = _obj_0.concat, _obj_0.remove, _obj_0.insert
|
||||
end
|
||||
local Set
|
||||
Set = function(items)
|
||||
local _tbl_0 = { }
|
||||
for _index_0 = 1, #items do
|
||||
local k = items[_index_0]
|
||||
_tbl_0[k] = true
|
||||
end
|
||||
return _tbl_0
|
||||
end
|
||||
local Stack
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
__tostring = function(self)
|
||||
return "<Stack {" .. concat(self, ", ") .. "}>"
|
||||
end,
|
||||
pop = function(self)
|
||||
return remove(self)
|
||||
end,
|
||||
push = function(self, value, ...)
|
||||
insert(self, value)
|
||||
if ... then
|
||||
return self:push(...)
|
||||
else
|
||||
return value
|
||||
end
|
||||
end,
|
||||
top = function(self)
|
||||
return self[#self]
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, ...)
|
||||
self:push(...)
|
||||
return nil
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Stack"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Stack = _class_0
|
||||
end
|
||||
local lua_keywords = Set({
|
||||
'and',
|
||||
'break',
|
||||
'do',
|
||||
'else',
|
||||
'elseif',
|
||||
'end',
|
||||
'false',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'in',
|
||||
'local',
|
||||
'nil',
|
||||
'not',
|
||||
'or',
|
||||
'repeat',
|
||||
'return',
|
||||
'then',
|
||||
'true',
|
||||
'until',
|
||||
'while'
|
||||
})
|
||||
return {
|
||||
Set = Set,
|
||||
Stack = Stack,
|
||||
lua_keywords = lua_keywords
|
||||
}
|
37
prototypes/scp-clicker/moonscript/data.moon
Normal file
37
prototypes/scp-clicker/moonscript/data.moon
Normal file
@ -0,0 +1,37 @@
|
||||
-- data structures & static data
|
||||
|
||||
import concat, remove, insert from table
|
||||
|
||||
Set = (items) -> {k,true for k in *items}
|
||||
|
||||
class Stack
|
||||
__tostring: => "<Stack {"..concat(self, ", ").."}>"
|
||||
|
||||
new: (...) =>
|
||||
@push ...
|
||||
nil
|
||||
|
||||
pop: =>
|
||||
remove @
|
||||
|
||||
push: (value, ...) =>
|
||||
insert @, value
|
||||
if ...
|
||||
@push ...
|
||||
else
|
||||
value
|
||||
|
||||
top: =>
|
||||
self[#self]
|
||||
|
||||
|
||||
lua_keywords = Set {
|
||||
'and', 'break', 'do', 'else', 'elseif',
|
||||
'end', 'false', 'for', 'function', 'if',
|
||||
'in', 'local', 'nil', 'not', 'or',
|
||||
'repeat', 'return', 'then', 'true',
|
||||
'until', 'while'
|
||||
}
|
||||
|
||||
{ :Set, :Stack, :lua_keywords }
|
||||
|
41
prototypes/scp-clicker/moonscript/dump.lua
Normal file
41
prototypes/scp-clicker/moonscript/dump.lua
Normal file
@ -0,0 +1,41 @@
|
||||
local flat_value
|
||||
flat_value = function(op, depth)
|
||||
if depth == nil then
|
||||
depth = 1
|
||||
end
|
||||
if type(op) == "string" then
|
||||
return '"' .. op .. '"'
|
||||
end
|
||||
if type(op) ~= "table" then
|
||||
return tostring(op)
|
||||
end
|
||||
local items
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #op do
|
||||
local item = op[_index_0]
|
||||
_accum_0[_len_0] = flat_value(item, depth + 1)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
items = _accum_0
|
||||
end
|
||||
local pos = op[-1]
|
||||
return "{" .. (pos and "[" .. pos .. "] " or "") .. table.concat(items, ", ") .. "}"
|
||||
end
|
||||
local value
|
||||
value = function(op)
|
||||
return flat_value(op)
|
||||
end
|
||||
local tree
|
||||
tree = function(block)
|
||||
local _list_0 = block
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local value = _list_0[_index_0]
|
||||
print(flat_value(value))
|
||||
end
|
||||
end
|
||||
return {
|
||||
value = value,
|
||||
tree = tree
|
||||
}
|
18
prototypes/scp-clicker/moonscript/dump.moon
Normal file
18
prototypes/scp-clicker/moonscript/dump.moon
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
flat_value = (op, depth=1) ->
|
||||
return '"'..op..'"' if type(op) == "string"
|
||||
return tostring(op) if type(op) != "table"
|
||||
|
||||
items = [flat_value item, depth + 1 for item in *op]
|
||||
pos = op[-1]
|
||||
|
||||
"{"..(pos and "["..pos.."] " or "")..table.concat(items, ", ").."}"
|
||||
|
||||
value = (op) ->
|
||||
flat_value op
|
||||
|
||||
tree = (block) ->
|
||||
print flat_value value for value in *block
|
||||
|
||||
{ :value, :tree }
|
||||
|
117
prototypes/scp-clicker/moonscript/errors.lua
Normal file
117
prototypes/scp-clicker/moonscript/errors.lua
Normal file
@ -0,0 +1,117 @@
|
||||
local util = require("moonscript.util")
|
||||
local lpeg = require("lpeg")
|
||||
local concat, insert
|
||||
do
|
||||
local _obj_0 = table
|
||||
concat, insert = _obj_0.concat, _obj_0.insert
|
||||
end
|
||||
local split, pos_to_line
|
||||
split, pos_to_line = util.split, util.pos_to_line
|
||||
local user_error
|
||||
user_error = function(...)
|
||||
return error({
|
||||
"user-error",
|
||||
...
|
||||
})
|
||||
end
|
||||
local lookup_line
|
||||
lookup_line = function(fname, pos, cache)
|
||||
if not cache[fname] then
|
||||
do
|
||||
local _with_0 = assert(io.open(fname))
|
||||
cache[fname] = _with_0:read("*a")
|
||||
_with_0:close()
|
||||
end
|
||||
end
|
||||
return pos_to_line(cache[fname], pos)
|
||||
end
|
||||
local reverse_line_number
|
||||
reverse_line_number = function(fname, line_table, line_num, cache)
|
||||
for i = line_num, 0, -1 do
|
||||
if line_table[i] then
|
||||
return lookup_line(fname, line_table[i], cache)
|
||||
end
|
||||
end
|
||||
return "unknown"
|
||||
end
|
||||
local truncate_traceback
|
||||
truncate_traceback = function(traceback, chunk_func)
|
||||
if chunk_func == nil then
|
||||
chunk_func = "moonscript_chunk"
|
||||
end
|
||||
traceback = split(traceback, "\n")
|
||||
local stop = #traceback
|
||||
while stop > 1 do
|
||||
if traceback[stop]:match(chunk_func) then
|
||||
break
|
||||
end
|
||||
stop = stop - 1
|
||||
end
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _max_0 = stop
|
||||
for _index_0 = 1, _max_0 < 0 and #traceback + _max_0 or _max_0 do
|
||||
local t = traceback[_index_0]
|
||||
_accum_0[_len_0] = t
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
traceback = _accum_0
|
||||
end
|
||||
local rep = "function '" .. chunk_func .. "'"
|
||||
traceback[#traceback] = traceback[#traceback]:gsub(rep, "main chunk")
|
||||
return concat(traceback, "\n")
|
||||
end
|
||||
local rewrite_traceback
|
||||
rewrite_traceback = function(text, err)
|
||||
local line_tables = require("moonscript.line_tables")
|
||||
local V, S, Ct, C
|
||||
V, S, Ct, C = lpeg.V, lpeg.S, lpeg.Ct, lpeg.C
|
||||
local header_text = "stack traceback:"
|
||||
local Header, Line = V("Header"), V("Line")
|
||||
local Break = lpeg.S("\n")
|
||||
local g = lpeg.P({
|
||||
Header,
|
||||
Header = header_text * Break * Ct(Line ^ 1),
|
||||
Line = "\t" * C((1 - Break) ^ 0) * (Break + -1)
|
||||
})
|
||||
local cache = { }
|
||||
local rewrite_single
|
||||
rewrite_single = function(trace)
|
||||
local fname, line, msg = trace:match('^(.-):(%d+): (.*)$')
|
||||
local tbl = line_tables["@" .. tostring(fname)]
|
||||
if fname and tbl then
|
||||
return concat({
|
||||
fname,
|
||||
":",
|
||||
reverse_line_number(fname, tbl, line, cache),
|
||||
": ",
|
||||
"(",
|
||||
line,
|
||||
") ",
|
||||
msg
|
||||
})
|
||||
else
|
||||
return trace
|
||||
end
|
||||
end
|
||||
err = rewrite_single(err)
|
||||
local match = g:match(text)
|
||||
if not (match) then
|
||||
return nil
|
||||
end
|
||||
for i, trace in ipairs(match) do
|
||||
match[i] = rewrite_single(trace)
|
||||
end
|
||||
return concat({
|
||||
"moon: " .. err,
|
||||
header_text,
|
||||
"\t" .. concat(match, "\n\t")
|
||||
}, "\n")
|
||||
end
|
||||
return {
|
||||
rewrite_traceback = rewrite_traceback,
|
||||
truncate_traceback = truncate_traceback,
|
||||
user_error = user_error,
|
||||
reverse_line_number = reverse_line_number
|
||||
}
|
88
prototypes/scp-clicker/moonscript/errors.moon
Normal file
88
prototypes/scp-clicker/moonscript/errors.moon
Normal file
@ -0,0 +1,88 @@
|
||||
|
||||
util = require "moonscript.util"
|
||||
|
||||
lpeg = require "lpeg"
|
||||
|
||||
import concat, insert from table
|
||||
import split, pos_to_line from util
|
||||
|
||||
user_error = (...) ->
|
||||
error {"user-error", ...}
|
||||
|
||||
-- find the line number of `pos` chars into fname
|
||||
lookup_line = (fname, pos, cache) ->
|
||||
if not cache[fname]
|
||||
with assert io.open(fname)
|
||||
cache[fname] = \read "*a"
|
||||
\close!
|
||||
pos_to_line cache[fname], pos
|
||||
|
||||
-- reverse the line number in fname using line_table
|
||||
reverse_line_number = (fname, line_table, line_num, cache) ->
|
||||
for i = line_num,0,-1
|
||||
if line_table[i]
|
||||
return lookup_line fname, line_table[i], cache
|
||||
"unknown"
|
||||
|
||||
|
||||
-- strip traceback lines up to chunk func
|
||||
-- replace reference to chunk_func with "main chunk"
|
||||
truncate_traceback = (traceback, chunk_func="moonscript_chunk") ->
|
||||
traceback = split traceback, "\n"
|
||||
stop = #traceback
|
||||
while stop > 1
|
||||
break if traceback[stop]\match chunk_func
|
||||
stop -= 1
|
||||
|
||||
traceback = [t for t in *traceback[1,stop]]
|
||||
|
||||
rep = "function '" .. chunk_func .. "'"
|
||||
traceback[#traceback] = traceback[#traceback]\gsub rep, "main chunk"
|
||||
|
||||
concat traceback, "\n"
|
||||
|
||||
rewrite_traceback = (text, err) ->
|
||||
line_tables = require "moonscript.line_tables"
|
||||
import V, S, Ct, C from lpeg
|
||||
header_text = "stack traceback:"
|
||||
|
||||
Header, Line = V("Header"), V("Line")
|
||||
Break = lpeg.S "\n"
|
||||
g = lpeg.P {
|
||||
Header
|
||||
Header: header_text * Break * Ct(Line^1)
|
||||
Line: "\t" * C((1 - Break)^0) * (Break + -1)
|
||||
}
|
||||
|
||||
cache = {} -- loaded file cache
|
||||
rewrite_single = (trace) ->
|
||||
fname, line, msg = trace\match '^(.-):(%d+): (.*)$'
|
||||
tbl = line_tables["@#{fname}"]
|
||||
if fname and tbl
|
||||
concat {
|
||||
fname, ":"
|
||||
reverse_line_number(fname, tbl, line, cache)
|
||||
": "
|
||||
"(", line, ") "
|
||||
msg
|
||||
}
|
||||
else
|
||||
trace
|
||||
|
||||
err = rewrite_single err
|
||||
match = g\match text
|
||||
|
||||
return nil unless match
|
||||
|
||||
for i, trace in ipairs match
|
||||
match[i] = rewrite_single trace
|
||||
|
||||
concat {
|
||||
"moon: " .. err
|
||||
header_text
|
||||
"\t" .. concat match, "\n\t"
|
||||
}, "\n"
|
||||
|
||||
|
||||
{ :rewrite_traceback, :truncate_traceback, :user_error, :reverse_line_number }
|
||||
|
5
prototypes/scp-clicker/moonscript/init.lua
Normal file
5
prototypes/scp-clicker/moonscript/init.lua
Normal file
@ -0,0 +1,5 @@
|
||||
do
|
||||
local _with_0 = require("moonscript.base")
|
||||
_with_0.insert_loader()
|
||||
return _with_0
|
||||
end
|
2
prototypes/scp-clicker/moonscript/init.moon
Normal file
2
prototypes/scp-clicker/moonscript/init.moon
Normal file
@ -0,0 +1,2 @@
|
||||
with require "moonscript.base"
|
||||
.insert_loader!
|
1
prototypes/scp-clicker/moonscript/line_tables.lua
Normal file
1
prototypes/scp-clicker/moonscript/line_tables.lua
Normal file
@ -0,0 +1 @@
|
||||
return { }
|
1
prototypes/scp-clicker/moonscript/line_tables.moon
Normal file
1
prototypes/scp-clicker/moonscript/line_tables.moon
Normal file
@ -0,0 +1 @@
|
||||
{}
|
245
prototypes/scp-clicker/moonscript/parse.lua
Normal file
245
prototypes/scp-clicker/moonscript/parse.lua
Normal file
@ -0,0 +1,245 @@
|
||||
local debug_grammar = false
|
||||
local lpeg = require("lpeg")
|
||||
lpeg.setmaxstack(10000)
|
||||
local err_msg = "Failed to parse:%s\n [%d] >> %s"
|
||||
local Stack
|
||||
Stack = require("moonscript.data").Stack
|
||||
local trim, pos_to_line, get_line
|
||||
do
|
||||
local _obj_0 = require("moonscript.util")
|
||||
trim, pos_to_line, get_line = _obj_0.trim, _obj_0.pos_to_line, _obj_0.get_line
|
||||
end
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
local wrap_env
|
||||
wrap_env = require("moonscript.parse.env").wrap_env
|
||||
local R, S, V, P, C, Ct, Cmt, Cg, Cb, Cc
|
||||
R, S, V, P, C, Ct, Cmt, Cg, Cb, Cc = lpeg.R, lpeg.S, lpeg.V, lpeg.P, lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cb, lpeg.Cc
|
||||
local White, Break, Stop, Comment, Space, SomeSpace, SpaceBreak, EmptyLine, AlphaNum, Num, Shebang, L, _Name
|
||||
do
|
||||
local _obj_0 = require("moonscript.parse.literals")
|
||||
White, Break, Stop, Comment, Space, SomeSpace, SpaceBreak, EmptyLine, AlphaNum, Num, Shebang, L, _Name = _obj_0.White, _obj_0.Break, _obj_0.Stop, _obj_0.Comment, _obj_0.Space, _obj_0.SomeSpace, _obj_0.SpaceBreak, _obj_0.EmptyLine, _obj_0.AlphaNum, _obj_0.Num, _obj_0.Shebang, _obj_0.L, _obj_0.Name
|
||||
end
|
||||
local SpaceName = Space * _Name
|
||||
Num = Space * (Num / function(v)
|
||||
return {
|
||||
"number",
|
||||
v
|
||||
}
|
||||
end)
|
||||
local Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, wrap_decorator, check_lua_string, self_assign, got
|
||||
do
|
||||
local _obj_0 = require("moonscript.parse.util")
|
||||
Indent, Cut, ensure, extract_line, mark, pos, flatten_or_mark, is_assignable, check_assignable, format_assign, format_single_assign, sym, symx, simple_string, wrap_func_arg, join_chain, wrap_decorator, check_lua_string, self_assign, got = _obj_0.Indent, _obj_0.Cut, _obj_0.ensure, _obj_0.extract_line, _obj_0.mark, _obj_0.pos, _obj_0.flatten_or_mark, _obj_0.is_assignable, _obj_0.check_assignable, _obj_0.format_assign, _obj_0.format_single_assign, _obj_0.sym, _obj_0.symx, _obj_0.simple_string, _obj_0.wrap_func_arg, _obj_0.join_chain, _obj_0.wrap_decorator, _obj_0.check_lua_string, _obj_0.self_assign, _obj_0.got
|
||||
end
|
||||
local build_grammar = wrap_env(debug_grammar, function(root)
|
||||
local _indent = Stack(0)
|
||||
local _do_stack = Stack(0)
|
||||
local state = {
|
||||
last_pos = 0
|
||||
}
|
||||
local check_indent
|
||||
check_indent = function(str, pos, indent)
|
||||
state.last_pos = pos
|
||||
return _indent:top() == indent
|
||||
end
|
||||
local advance_indent
|
||||
advance_indent = function(str, pos, indent)
|
||||
local top = _indent:top()
|
||||
if top ~= -1 and indent > top then
|
||||
_indent:push(indent)
|
||||
return true
|
||||
end
|
||||
end
|
||||
local push_indent
|
||||
push_indent = function(str, pos, indent)
|
||||
_indent:push(indent)
|
||||
return true
|
||||
end
|
||||
local pop_indent
|
||||
pop_indent = function()
|
||||
assert(_indent:pop(), "unexpected outdent")
|
||||
return true
|
||||
end
|
||||
local check_do
|
||||
check_do = function(str, pos, do_node)
|
||||
local top = _do_stack:top()
|
||||
if top == nil or top then
|
||||
return true, do_node
|
||||
end
|
||||
return false
|
||||
end
|
||||
local disable_do
|
||||
disable_do = function()
|
||||
_do_stack:push(false)
|
||||
return true
|
||||
end
|
||||
local pop_do
|
||||
pop_do = function()
|
||||
assert(_do_stack:pop() ~= nil, "unexpected do pop")
|
||||
return true
|
||||
end
|
||||
local DisableDo = Cmt("", disable_do)
|
||||
local PopDo = Cmt("", pop_do)
|
||||
local keywords = { }
|
||||
local key
|
||||
key = function(chars)
|
||||
keywords[chars] = true
|
||||
return Space * chars * -AlphaNum
|
||||
end
|
||||
local op
|
||||
op = function(chars)
|
||||
local patt = Space * C(chars)
|
||||
if chars:match("^%w*$") then
|
||||
keywords[chars] = true
|
||||
patt = patt * -AlphaNum
|
||||
end
|
||||
return patt
|
||||
end
|
||||
local Name = Cmt(SpaceName, function(str, pos, name)
|
||||
if keywords[name] then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end) / trim
|
||||
local SelfName = Space * "@" * ("@" * (_Name / mark("self_class") + Cc("self.__class")) + _Name / mark("self") + Cc("self"))
|
||||
local KeyName = SelfName + Space * _Name / mark("key_literal")
|
||||
local VarArg = Space * P("...") / trim
|
||||
local g = P({
|
||||
root or File,
|
||||
File = Shebang ^ -1 * (Block + Ct("")),
|
||||
Block = Ct(Line * (Break ^ 1 * Line) ^ 0),
|
||||
CheckIndent = Cmt(Indent, check_indent),
|
||||
Line = (CheckIndent * Statement + Space * L(Stop)),
|
||||
Statement = pos(Import + While + With + For + ForEach + Switch + Return + Local + Export + BreakLoop + Ct(ExpList) * (Update + Assign) ^ -1 / format_assign) * Space * ((key("if") * Exp * (key("else") * Exp) ^ -1 * Space / mark("if") + key("unless") * Exp / mark("unless") + CompInner / mark("comprehension")) * Space) ^ -1 / wrap_decorator,
|
||||
Body = Space ^ -1 * Break * EmptyLine ^ 0 * InBlock + Ct(Statement),
|
||||
Advance = L(Cmt(Indent, advance_indent)),
|
||||
PushIndent = Cmt(Indent, push_indent),
|
||||
PreventIndent = Cmt(Cc(-1), push_indent),
|
||||
PopIndent = Cmt("", pop_indent),
|
||||
InBlock = Advance * Block * PopIndent,
|
||||
Local = key("local") * ((op("*") + op("^")) / mark("declare_glob") + Ct(NameList) / mark("declare_with_shadows")),
|
||||
Import = key("import") * Ct(ImportNameList) * SpaceBreak ^ 0 * key("from") * Exp / mark("import"),
|
||||
ImportName = (sym("\\") * Ct(Cc("colon") * Name) + Name),
|
||||
ImportNameList = SpaceBreak ^ 0 * ImportName * ((SpaceBreak ^ 1 + sym(",") * SpaceBreak ^ 0) * ImportName) ^ 0,
|
||||
BreakLoop = Ct(key("break") / trim) + Ct(key("continue") / trim),
|
||||
Return = key("return") * (ExpListLow / mark("explist") + C("")) / mark("return"),
|
||||
WithExp = Ct(ExpList) * Assign ^ -1 / format_assign,
|
||||
With = key("with") * DisableDo * ensure(WithExp, PopDo) * key("do") ^ -1 * Body / mark("with"),
|
||||
Switch = key("switch") * DisableDo * ensure(Exp, PopDo) * key("do") ^ -1 * Space ^ -1 * Break * SwitchBlock / mark("switch"),
|
||||
SwitchBlock = EmptyLine ^ 0 * Advance * Ct(SwitchCase * (Break ^ 1 * SwitchCase) ^ 0 * (Break ^ 1 * SwitchElse) ^ -1) * PopIndent,
|
||||
SwitchCase = key("when") * Ct(ExpList) * key("then") ^ -1 * Body / mark("case"),
|
||||
SwitchElse = key("else") * Body / mark("else"),
|
||||
IfCond = Exp * Assign ^ -1 / format_single_assign,
|
||||
IfElse = (Break * EmptyLine ^ 0 * CheckIndent) ^ -1 * key("else") * Body / mark("else"),
|
||||
IfElseIf = (Break * EmptyLine ^ 0 * CheckIndent) ^ -1 * key("elseif") * pos(IfCond) * key("then") ^ -1 * Body / mark("elseif"),
|
||||
If = key("if") * IfCond * key("then") ^ -1 * Body * IfElseIf ^ 0 * IfElse ^ -1 / mark("if"),
|
||||
Unless = key("unless") * IfCond * key("then") ^ -1 * Body * IfElseIf ^ 0 * IfElse ^ -1 / mark("unless"),
|
||||
While = key("while") * DisableDo * ensure(Exp, PopDo) * key("do") ^ -1 * Body / mark("while"),
|
||||
For = key("for") * DisableDo * ensure(Name * sym("=") * Ct(Exp * sym(",") * Exp * (sym(",") * Exp) ^ -1), PopDo) * key("do") ^ -1 * Body / mark("for"),
|
||||
ForEach = key("for") * Ct(AssignableNameList) * key("in") * DisableDo * ensure(Ct(sym("*") * Exp / mark("unpack") + ExpList), PopDo) * key("do") ^ -1 * Body / mark("foreach"),
|
||||
Do = key("do") * Body / mark("do"),
|
||||
Comprehension = sym("[") * Exp * CompInner * sym("]") / mark("comprehension"),
|
||||
TblComprehension = sym("{") * Ct(Exp * (sym(",") * Exp) ^ -1) * CompInner * sym("}") / mark("tblcomprehension"),
|
||||
CompInner = Ct((CompForEach + CompFor) * CompClause ^ 0),
|
||||
CompForEach = key("for") * Ct(AssignableNameList) * key("in") * (sym("*") * Exp / mark("unpack") + Exp) / mark("foreach"),
|
||||
CompFor = key("for" * Name * sym("=") * Ct(Exp * sym(",") * Exp * (sym(",") * Exp) ^ -1) / mark("for")),
|
||||
CompClause = CompFor + CompForEach + key("when") * Exp / mark("when"),
|
||||
Assign = sym("=") * (Ct(With + If + Switch) + Ct(TableBlock + ExpListLow)) / mark("assign"),
|
||||
Update = ((sym("..=") + sym("+=") + sym("-=") + sym("*=") + sym("/=") + sym("%=") + sym("or=") + sym("and=") + sym("&=") + sym("|=") + sym(">>=") + sym("<<=")) / trim) * Exp / mark("update"),
|
||||
CharOperators = Space * C(S("+-*/%^><|&")),
|
||||
WordOperators = op("or") + op("and") + op("<=") + op(">=") + op("~=") + op("!=") + op("==") + op("..") + op("<<") + op(">>") + op("//"),
|
||||
BinaryOperator = (WordOperators + CharOperators) * SpaceBreak ^ 0,
|
||||
Assignable = Cmt(Chain, check_assignable) + Name + SelfName,
|
||||
Exp = Ct(Value * (BinaryOperator * Value) ^ 0) / flatten_or_mark("exp"),
|
||||
SimpleValue = If + Unless + Switch + With + ClassDecl + ForEach + For + While + Cmt(Do, check_do) + sym("-") * -SomeSpace * Exp / mark("minus") + sym("#") * Exp / mark("length") + sym("~") * Exp / mark("bitnot") + key("not") * Exp / mark("not") + TblComprehension + TableLit + Comprehension + FunLit + Num,
|
||||
ChainValue = (Chain + Callable) * Ct(InvokeArgs ^ -1) / join_chain,
|
||||
Value = pos(SimpleValue + Ct(KeyValueList) / mark("table") + ChainValue + String),
|
||||
SliceValue = Exp,
|
||||
String = Space * DoubleString + Space * SingleString + LuaString,
|
||||
SingleString = simple_string("'"),
|
||||
DoubleString = simple_string('"', true),
|
||||
LuaString = Cg(LuaStringOpen, "string_open") * Cb("string_open") * Break ^ -1 * C((1 - Cmt(C(LuaStringClose) * Cb("string_open"), check_lua_string)) ^ 0) * LuaStringClose / mark("string"),
|
||||
LuaStringOpen = sym("[") * P("=") ^ 0 * "[" / trim,
|
||||
LuaStringClose = "]" * P("=") ^ 0 * "]",
|
||||
Callable = pos(Name / mark("ref")) + SelfName + VarArg + Parens / mark("parens"),
|
||||
Parens = sym("(") * SpaceBreak ^ 0 * Exp * SpaceBreak ^ 0 * sym(")"),
|
||||
FnArgs = symx("(") * SpaceBreak ^ 0 * Ct(FnArgsExpList ^ -1) * SpaceBreak ^ 0 * sym(")") + sym("!") * -P("=") * Ct(""),
|
||||
FnArgsExpList = Exp * ((Break + sym(",")) * White * Exp) ^ 0,
|
||||
Chain = (Callable + String + -S(".\\")) * ChainItems / mark("chain") + Space * (DotChainItem * ChainItems ^ -1 + ColonChain) / mark("chain"),
|
||||
ChainItems = ChainItem ^ 1 * ColonChain ^ -1 + ColonChain,
|
||||
ChainItem = Invoke + DotChainItem + Slice + symx("[") * Exp / mark("index") * sym("]"),
|
||||
DotChainItem = symx(".") * _Name / mark("dot"),
|
||||
ColonChainItem = symx("\\") * _Name / mark("colon"),
|
||||
ColonChain = ColonChainItem * (Invoke * ChainItems ^ -1) ^ -1,
|
||||
Slice = symx("[") * (SliceValue + Cc(1)) * sym(",") * (SliceValue + Cc("")) * (sym(",") * SliceValue) ^ -1 * sym("]") / mark("slice"),
|
||||
Invoke = FnArgs / mark("call") + SingleString / wrap_func_arg + DoubleString / wrap_func_arg + L(P("[")) * LuaString / wrap_func_arg,
|
||||
TableValue = KeyValue + Ct(Exp),
|
||||
TableLit = sym("{") * Ct(TableValueList ^ -1 * sym(",") ^ -1 * (SpaceBreak * TableLitLine * (sym(",") ^ -1 * SpaceBreak * TableLitLine) ^ 0 * sym(",") ^ -1) ^ -1) * White * sym("}") / mark("table"),
|
||||
TableValueList = TableValue * (sym(",") * TableValue) ^ 0,
|
||||
TableLitLine = PushIndent * ((TableValueList * PopIndent) + (PopIndent * Cut)) + Space,
|
||||
TableBlockInner = Ct(KeyValueLine * (SpaceBreak ^ 1 * KeyValueLine) ^ 0),
|
||||
TableBlock = SpaceBreak ^ 1 * Advance * ensure(TableBlockInner, PopIndent) / mark("table"),
|
||||
ClassDecl = key("class") * -P(":") * (Assignable + Cc(nil)) * (key("extends") * PreventIndent * ensure(Exp, PopIndent) + C("")) ^ -1 * (ClassBlock + Ct("")) / mark("class"),
|
||||
ClassBlock = SpaceBreak ^ 1 * Advance * Ct(ClassLine * (SpaceBreak ^ 1 * ClassLine) ^ 0) * PopIndent,
|
||||
ClassLine = CheckIndent * ((KeyValueList / mark("props") + Statement / mark("stm") + Exp / mark("stm")) * sym(",") ^ -1),
|
||||
Export = key("export") * (Cc("class") * ClassDecl + op("*") + op("^") + Ct(NameList) * (sym("=") * Ct(ExpListLow)) ^ -1) / mark("export"),
|
||||
KeyValue = (sym(":") * -SomeSpace * Name * lpeg.Cp()) / self_assign + Ct((KeyName + sym("[") * Exp * sym("]") + Space * DoubleString + Space * SingleString) * symx(":") * (Exp + TableBlock + SpaceBreak ^ 1 * Exp)),
|
||||
KeyValueList = KeyValue * (sym(",") * KeyValue) ^ 0,
|
||||
KeyValueLine = CheckIndent * KeyValueList * sym(",") ^ -1,
|
||||
FnArgsDef = sym("(") * White * Ct(FnArgDefList ^ -1) * (key("using") * Ct(NameList + Space * "nil") + Ct("")) * White * sym(")") + Ct("") * Ct(""),
|
||||
FnArgDefList = FnArgDef * ((sym(",") + Break) * White * FnArgDef) ^ 0 * ((sym(",") + Break) * White * Ct(VarArg)) ^ 0 + Ct(VarArg),
|
||||
FnArgDef = Ct((Name + SelfName) * (sym("=") * Exp) ^ -1),
|
||||
FunLit = FnArgsDef * (sym("->") * Cc("slim") + sym("=>") * Cc("fat")) * (Body + Ct("")) / mark("fndef"),
|
||||
NameList = Name * (sym(",") * Name) ^ 0,
|
||||
NameOrDestructure = Name + TableLit,
|
||||
AssignableNameList = NameOrDestructure * (sym(",") * NameOrDestructure) ^ 0,
|
||||
ExpList = Exp * (sym(",") * Exp) ^ 0,
|
||||
ExpListLow = Exp * ((sym(",") + sym(";")) * Exp) ^ 0,
|
||||
InvokeArgs = -P("-") * (ExpList * (sym(",") * (TableBlock + SpaceBreak * Advance * ArgBlock * TableBlock ^ -1) + TableBlock) ^ -1 + TableBlock),
|
||||
ArgBlock = ArgLine * (sym(",") * SpaceBreak * ArgLine) ^ 0 * PopIndent,
|
||||
ArgLine = CheckIndent * ExpList
|
||||
})
|
||||
return g, state
|
||||
end)
|
||||
local file_parser
|
||||
file_parser = function()
|
||||
local g, state = build_grammar()
|
||||
local file_grammar = White * g * White * -1
|
||||
return {
|
||||
match = function(self, str)
|
||||
local tree
|
||||
local _, err = xpcall((function()
|
||||
tree = file_grammar:match(str)
|
||||
end), function(err)
|
||||
return debug.traceback(err, 2)
|
||||
end)
|
||||
if type(err) == "string" then
|
||||
return nil, err
|
||||
end
|
||||
if not (tree) then
|
||||
local msg
|
||||
local err_pos = state.last_pos
|
||||
if err then
|
||||
local node
|
||||
node, msg = unpack(err)
|
||||
if msg then
|
||||
msg = " " .. msg
|
||||
end
|
||||
err_pos = node[-1]
|
||||
end
|
||||
local line_no = pos_to_line(str, err_pos)
|
||||
local line_str = get_line(str, line_no) or ""
|
||||
return nil, err_msg:format(msg or "", line_no, trim(line_str))
|
||||
end
|
||||
return tree
|
||||
end
|
||||
}
|
||||
end
|
||||
return {
|
||||
extract_line = extract_line,
|
||||
build_grammar = build_grammar,
|
||||
string = function(str)
|
||||
return file_parser():match(str)
|
||||
end
|
||||
}
|
361
prototypes/scp-clicker/moonscript/parse.moon
Normal file
361
prototypes/scp-clicker/moonscript/parse.moon
Normal file
@ -0,0 +1,361 @@
|
||||
debug_grammar = false
|
||||
lpeg = require "lpeg"
|
||||
|
||||
lpeg.setmaxstack 10000 -- whoa
|
||||
|
||||
err_msg = "Failed to parse:%s\n [%d] >> %s"
|
||||
|
||||
import Stack from require "moonscript.data"
|
||||
import trim, pos_to_line, get_line from require "moonscript.util"
|
||||
import unpack from require "moonscript.util"
|
||||
import wrap_env from require "moonscript.parse.env"
|
||||
|
||||
{
|
||||
:R, :S, :V, :P, :C, :Ct, :Cmt, :Cg, :Cb, :Cc
|
||||
} = lpeg
|
||||
|
||||
{
|
||||
:White, :Break, :Stop, :Comment, :Space, :SomeSpace, :SpaceBreak, :EmptyLine,
|
||||
:AlphaNum, :Num, :Shebang, :L
|
||||
Name: _Name
|
||||
} = require "moonscript.parse.literals"
|
||||
|
||||
SpaceName = Space * _Name
|
||||
Num = Space * (Num / (v) -> {"number", v})
|
||||
|
||||
{
|
||||
:Indent, :Cut, :ensure, :extract_line, :mark, :pos, :flatten_or_mark,
|
||||
:is_assignable, :check_assignable, :format_assign, :format_single_assign,
|
||||
:sym, :symx, :simple_string, :wrap_func_arg, :join_chain,
|
||||
:wrap_decorator, :check_lua_string, :self_assign, :got
|
||||
|
||||
} = require "moonscript.parse.util"
|
||||
|
||||
|
||||
build_grammar = wrap_env debug_grammar, (root) ->
|
||||
_indent = Stack 0
|
||||
_do_stack = Stack 0
|
||||
|
||||
state = {
|
||||
-- last pos we saw, used to report error location
|
||||
last_pos: 0
|
||||
}
|
||||
|
||||
check_indent = (str, pos, indent) ->
|
||||
state.last_pos = pos
|
||||
_indent\top! == indent
|
||||
|
||||
advance_indent = (str, pos, indent) ->
|
||||
top = _indent\top!
|
||||
if top != -1 and indent > top
|
||||
_indent\push indent
|
||||
true
|
||||
|
||||
push_indent = (str, pos, indent) ->
|
||||
_indent\push indent
|
||||
true
|
||||
|
||||
pop_indent = ->
|
||||
assert _indent\pop!, "unexpected outdent"
|
||||
true
|
||||
|
||||
check_do = (str, pos, do_node) ->
|
||||
top = _do_stack\top!
|
||||
if top == nil or top
|
||||
return true, do_node
|
||||
false
|
||||
|
||||
disable_do = ->
|
||||
_do_stack\push false
|
||||
true
|
||||
|
||||
pop_do = ->
|
||||
assert _do_stack\pop! != nil, "unexpected do pop"
|
||||
true
|
||||
|
||||
DisableDo = Cmt "", disable_do
|
||||
PopDo = Cmt "", pop_do
|
||||
|
||||
keywords = {}
|
||||
key = (chars) ->
|
||||
keywords[chars] = true
|
||||
Space * chars * -AlphaNum
|
||||
|
||||
op = (chars) ->
|
||||
patt = Space * C chars
|
||||
-- it's a word, treat like keyword
|
||||
if chars\match "^%w*$"
|
||||
keywords[chars] = true
|
||||
patt *= -AlphaNum
|
||||
|
||||
patt
|
||||
|
||||
Name = Cmt(SpaceName, (str, pos, name) ->
|
||||
return false if keywords[name]
|
||||
true
|
||||
) / trim
|
||||
|
||||
SelfName = Space * "@" * (
|
||||
"@" * (_Name / mark"self_class" + Cc"self.__class") +
|
||||
_Name / mark"self" +
|
||||
Cc"self" -- @ by itself
|
||||
)
|
||||
|
||||
KeyName = SelfName + Space * _Name / mark"key_literal"
|
||||
VarArg = Space * P"..." / trim
|
||||
|
||||
g = P {
|
||||
root or File
|
||||
File: Shebang^-1 * (Block + Ct"")
|
||||
Block: Ct(Line * (Break^1 * Line)^0)
|
||||
CheckIndent: Cmt(Indent, check_indent), -- validates line is in correct indent
|
||||
Line: (CheckIndent * Statement + Space * L(Stop))
|
||||
|
||||
Statement: pos(
|
||||
Import + While + With + For + ForEach + Switch + Return +
|
||||
Local + Export + BreakLoop +
|
||||
Ct(ExpList) * (Update + Assign)^-1 / format_assign
|
||||
) * Space * ((
|
||||
-- statement decorators
|
||||
key"if" * Exp * (key"else" * Exp)^-1 * Space / mark"if" +
|
||||
key"unless" * Exp / mark"unless" +
|
||||
CompInner / mark"comprehension"
|
||||
) * Space)^-1 / wrap_decorator
|
||||
|
||||
Body: Space^-1 * Break * EmptyLine^0 * InBlock + Ct(Statement) -- either a statement, or an indented block
|
||||
|
||||
Advance: L Cmt(Indent, advance_indent) -- Advances the indent, gives back whitespace for CheckIndent
|
||||
PushIndent: Cmt(Indent, push_indent)
|
||||
PreventIndent: Cmt(Cc(-1), push_indent)
|
||||
PopIndent: Cmt("", pop_indent)
|
||||
InBlock: Advance * Block * PopIndent
|
||||
|
||||
Local: key"local" * ((op"*" + op"^") / mark"declare_glob" + Ct(NameList) / mark"declare_with_shadows")
|
||||
|
||||
Import: key"import" * Ct(ImportNameList) * SpaceBreak^0 * key"from" * Exp / mark"import"
|
||||
ImportName: (sym"\\" * Ct(Cc"colon" * Name) + Name)
|
||||
ImportNameList: SpaceBreak^0 * ImportName * ((SpaceBreak^1 + sym"," * SpaceBreak^0) * ImportName)^0
|
||||
|
||||
BreakLoop: Ct(key"break"/trim) + Ct(key"continue"/trim)
|
||||
|
||||
Return: key"return" * (ExpListLow/mark"explist" + C"") / mark"return"
|
||||
|
||||
WithExp: Ct(ExpList) * Assign^-1 / format_assign
|
||||
With: key"with" * DisableDo * ensure(WithExp, PopDo) * key"do"^-1 * Body / mark"with"
|
||||
|
||||
Switch: key"switch" * DisableDo * ensure(Exp, PopDo) * key"do"^-1 * Space^-1 * Break * SwitchBlock / mark"switch"
|
||||
|
||||
SwitchBlock: EmptyLine^0 * Advance * Ct(SwitchCase * (Break^1 * SwitchCase)^0 * (Break^1 * SwitchElse)^-1) * PopIndent
|
||||
SwitchCase: key"when" * Ct(ExpList) * key"then"^-1 * Body / mark"case"
|
||||
SwitchElse: key"else" * Body / mark"else"
|
||||
|
||||
IfCond: Exp * Assign^-1 / format_single_assign
|
||||
|
||||
IfElse: (Break * EmptyLine^0 * CheckIndent)^-1 * key"else" * Body / mark"else"
|
||||
IfElseIf: (Break * EmptyLine^0 * CheckIndent)^-1 * key"elseif" * pos(IfCond) * key"then"^-1 * Body / mark"elseif"
|
||||
|
||||
If: key"if" * IfCond * key"then"^-1 * Body * IfElseIf^0 * IfElse^-1 / mark"if"
|
||||
Unless: key"unless" * IfCond * key"then"^-1 * Body * IfElseIf^0 * IfElse^-1 / mark"unless"
|
||||
|
||||
While: key"while" * DisableDo * ensure(Exp, PopDo) * key"do"^-1 * Body / mark"while"
|
||||
|
||||
For: key"for" * DisableDo * ensure(Name * sym"=" * Ct(Exp * sym"," * Exp * (sym"," * Exp)^-1), PopDo) *
|
||||
key"do"^-1 * Body / mark"for"
|
||||
|
||||
ForEach: key"for" * Ct(AssignableNameList) * key"in" * DisableDo * ensure(Ct(sym"*" * Exp / mark"unpack" + ExpList), PopDo) * key"do"^-1 * Body / mark"foreach"
|
||||
|
||||
Do: key"do" * Body / mark"do"
|
||||
|
||||
Comprehension: sym"[" * Exp * CompInner * sym"]" / mark"comprehension"
|
||||
|
||||
TblComprehension: sym"{" * Ct(Exp * (sym"," * Exp)^-1) * CompInner * sym"}" / mark"tblcomprehension"
|
||||
|
||||
CompInner: Ct((CompForEach + CompFor) * CompClause^0)
|
||||
CompForEach: key"for" * Ct(AssignableNameList) * key"in" * (sym"*" * Exp / mark"unpack" + Exp) / mark"foreach"
|
||||
CompFor: key "for" * Name * sym"=" * Ct(Exp * sym"," * Exp * (sym"," * Exp)^-1) / mark"for"
|
||||
CompClause: CompFor + CompForEach + key"when" * Exp / mark"when"
|
||||
|
||||
Assign: sym"=" * (Ct(With + If + Switch) + Ct(TableBlock + ExpListLow)) / mark"assign"
|
||||
Update: ((sym"..=" + sym"+=" + sym"-=" + sym"*=" + sym"/=" + sym"%=" + sym"or=" + sym"and=" + sym"&=" + sym"|=" + sym">>=" + sym"<<=") / trim) * Exp / mark"update"
|
||||
|
||||
CharOperators: Space * C(S"+-*/%^><|&")
|
||||
WordOperators: op"or" + op"and" + op"<=" + op">=" + op"~=" + op"!=" + op"==" + op".." + op"<<" + op">>" + op"//"
|
||||
BinaryOperator: (WordOperators + CharOperators) * SpaceBreak^0
|
||||
|
||||
Assignable: Cmt(Chain, check_assignable) + Name + SelfName
|
||||
Exp: Ct(Value * (BinaryOperator * Value)^0) / flatten_or_mark"exp"
|
||||
|
||||
SimpleValue:
|
||||
If + Unless +
|
||||
Switch +
|
||||
With +
|
||||
ClassDecl +
|
||||
ForEach + For + While +
|
||||
Cmt(Do, check_do) +
|
||||
sym"-" * -SomeSpace * Exp / mark"minus" +
|
||||
sym"#" * Exp / mark"length" +
|
||||
sym"~" * Exp / mark"bitnot" +
|
||||
key"not" * Exp / mark"not" +
|
||||
TblComprehension +
|
||||
TableLit +
|
||||
Comprehension +
|
||||
FunLit +
|
||||
Num
|
||||
|
||||
-- a function call or an object access
|
||||
ChainValue: (Chain + Callable) * Ct(InvokeArgs^-1) / join_chain
|
||||
|
||||
Value: pos(
|
||||
SimpleValue +
|
||||
Ct(KeyValueList) / mark"table" +
|
||||
ChainValue +
|
||||
String)
|
||||
|
||||
SliceValue: Exp
|
||||
|
||||
String: Space * DoubleString + Space * SingleString + LuaString
|
||||
SingleString: simple_string("'")
|
||||
DoubleString: simple_string('"', true)
|
||||
|
||||
LuaString: Cg(LuaStringOpen, "string_open") * Cb"string_open" * Break^-1 *
|
||||
C((1 - Cmt(C(LuaStringClose) * Cb"string_open", check_lua_string))^0) *
|
||||
LuaStringClose / mark"string"
|
||||
|
||||
LuaStringOpen: sym"[" * P"="^0 * "[" / trim
|
||||
LuaStringClose: "]" * P"="^0 * "]"
|
||||
|
||||
Callable: pos(Name / mark"ref") + SelfName + VarArg + Parens / mark"parens"
|
||||
Parens: sym"(" * SpaceBreak^0 * Exp * SpaceBreak^0 * sym")"
|
||||
|
||||
FnArgs: symx"(" * SpaceBreak^0 * Ct(FnArgsExpList^-1) * SpaceBreak^0 * sym")" + sym"!" * -P"=" * Ct""
|
||||
FnArgsExpList: Exp * ((Break + sym",") * White * Exp)^0
|
||||
|
||||
Chain: (Callable + String + -S".\\") * ChainItems / mark"chain" +
|
||||
Space * (DotChainItem * ChainItems^-1 + ColonChain) / mark"chain"
|
||||
|
||||
ChainItems: ChainItem^1 * ColonChain^-1 + ColonChain
|
||||
|
||||
ChainItem:
|
||||
Invoke +
|
||||
DotChainItem +
|
||||
Slice +
|
||||
symx"[" * Exp/mark"index" * sym"]"
|
||||
|
||||
DotChainItem: symx"." * _Name/mark"dot"
|
||||
ColonChainItem: symx"\\" * _Name / mark"colon"
|
||||
ColonChain: ColonChainItem * (Invoke * ChainItems^-1)^-1
|
||||
|
||||
Slice: symx"[" * (SliceValue + Cc(1)) * sym"," * (SliceValue + Cc"") *
|
||||
(sym"," * SliceValue)^-1 *sym"]" / mark"slice"
|
||||
|
||||
Invoke: FnArgs / mark"call" +
|
||||
SingleString / wrap_func_arg +
|
||||
DoubleString / wrap_func_arg +
|
||||
L(P"[") * LuaString / wrap_func_arg
|
||||
|
||||
TableValue: KeyValue + Ct(Exp)
|
||||
|
||||
TableLit: sym"{" * Ct(
|
||||
TableValueList^-1 * sym","^-1 *
|
||||
(SpaceBreak * TableLitLine * (sym","^-1 * SpaceBreak * TableLitLine)^0 * sym","^-1)^-1
|
||||
) * White * sym"}" / mark"table"
|
||||
|
||||
TableValueList: TableValue * (sym"," * TableValue)^0
|
||||
TableLitLine: PushIndent * ((TableValueList * PopIndent) + (PopIndent * Cut)) + Space
|
||||
|
||||
-- the unbounded table
|
||||
TableBlockInner: Ct(KeyValueLine * (SpaceBreak^1 * KeyValueLine)^0)
|
||||
TableBlock: SpaceBreak^1 * Advance * ensure(TableBlockInner, PopIndent) / mark"table"
|
||||
|
||||
ClassDecl: key"class" * -P":" * (Assignable + Cc(nil)) * (key"extends" * PreventIndent * ensure(Exp, PopIndent) + C"")^-1 * (ClassBlock + Ct("")) / mark"class"
|
||||
|
||||
ClassBlock: SpaceBreak^1 * Advance *
|
||||
Ct(ClassLine * (SpaceBreak^1 * ClassLine)^0) * PopIndent
|
||||
ClassLine: CheckIndent * ((
|
||||
KeyValueList / mark"props" +
|
||||
Statement / mark"stm" +
|
||||
Exp / mark"stm"
|
||||
) * sym","^-1)
|
||||
|
||||
Export: key"export" * (
|
||||
Cc"class" * ClassDecl +
|
||||
op"*" + op"^" +
|
||||
Ct(NameList) * (sym"=" * Ct(ExpListLow))^-1) / mark"export"
|
||||
|
||||
KeyValue: (sym":" * -SomeSpace * Name * lpeg.Cp!) / self_assign +
|
||||
Ct(
|
||||
(KeyName + sym"[" * Exp * sym"]" +Space * DoubleString + Space * SingleString) *
|
||||
symx":" *
|
||||
(Exp + TableBlock + SpaceBreak^1 * Exp)
|
||||
)
|
||||
|
||||
KeyValueList: KeyValue * (sym"," * KeyValue)^0
|
||||
KeyValueLine: CheckIndent * KeyValueList * sym","^-1
|
||||
|
||||
FnArgsDef: sym"(" * White * Ct(FnArgDefList^-1) *
|
||||
(key"using" * Ct(NameList + Space * "nil") + Ct"") *
|
||||
White * sym")" + Ct"" * Ct""
|
||||
|
||||
FnArgDefList: FnArgDef * ((sym"," + Break) * White * FnArgDef)^0 * ((sym"," + Break) * White * Ct(VarArg))^0 + Ct(VarArg)
|
||||
FnArgDef: Ct((Name + SelfName) * (sym"=" * Exp)^-1)
|
||||
|
||||
FunLit: FnArgsDef *
|
||||
(sym"->" * Cc"slim" + sym"=>" * Cc"fat") *
|
||||
(Body + Ct"") / mark"fndef"
|
||||
|
||||
NameList: Name * (sym"," * Name)^0
|
||||
NameOrDestructure: Name + TableLit
|
||||
AssignableNameList: NameOrDestructure * (sym"," * NameOrDestructure)^0
|
||||
|
||||
ExpList: Exp * (sym"," * Exp)^0
|
||||
ExpListLow: Exp * ((sym"," + sym";") * Exp)^0
|
||||
|
||||
-- open args
|
||||
InvokeArgs: -P"-" * (ExpList * (sym"," * (TableBlock + SpaceBreak * Advance * ArgBlock * TableBlock^-1) + TableBlock)^-1 + TableBlock)
|
||||
ArgBlock: ArgLine * (sym"," * SpaceBreak * ArgLine)^0 * PopIndent
|
||||
ArgLine: CheckIndent * ExpList
|
||||
}
|
||||
|
||||
g, state
|
||||
|
||||
file_parser = ->
|
||||
g, state = build_grammar!
|
||||
file_grammar = White * g * White * -1
|
||||
|
||||
{
|
||||
match: (str) =>
|
||||
local tree
|
||||
_, err = xpcall (->
|
||||
tree = file_grammar\match str
|
||||
), (err) ->
|
||||
debug.traceback err, 2
|
||||
|
||||
-- regular error, let it bubble up
|
||||
if type(err) == "string"
|
||||
return nil, err
|
||||
|
||||
unless tree
|
||||
local msg
|
||||
err_pos = state.last_pos
|
||||
|
||||
if err
|
||||
node, msg = unpack err
|
||||
msg = " " .. msg if msg
|
||||
err_pos = node[-1]
|
||||
|
||||
line_no = pos_to_line str, err_pos
|
||||
line_str = get_line(str, line_no) or ""
|
||||
return nil, err_msg\format msg or "", line_no, trim line_str
|
||||
|
||||
tree
|
||||
}
|
||||
|
||||
{
|
||||
:extract_line
|
||||
:build_grammar
|
||||
|
||||
-- parse a string as a file
|
||||
-- returns tree, or nil and error message
|
||||
string: (str) -> file_parser!\match str
|
||||
}
|
||||
|
70
prototypes/scp-clicker/moonscript/parse/env.lua
Normal file
70
prototypes/scp-clicker/moonscript/parse/env.lua
Normal file
@ -0,0 +1,70 @@
|
||||
local getfenv, setfenv
|
||||
do
|
||||
local _obj_0 = require("moonscript.util")
|
||||
getfenv, setfenv = _obj_0.getfenv, _obj_0.setfenv
|
||||
end
|
||||
local wrap_env
|
||||
wrap_env = function(debug, fn)
|
||||
local V, Cmt
|
||||
do
|
||||
local _obj_0 = require("lpeg")
|
||||
V, Cmt = _obj_0.V, _obj_0.Cmt
|
||||
end
|
||||
local env = getfenv(fn)
|
||||
local wrap_name = V
|
||||
if debug then
|
||||
local indent = 0
|
||||
local indent_char = " "
|
||||
local iprint
|
||||
iprint = function(...)
|
||||
local args = table.concat((function(...)
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = {
|
||||
...
|
||||
}
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local a = _list_0[_index_0]
|
||||
_accum_0[_len_0] = tostring(a)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(...), ", ")
|
||||
return io.stderr:write(tostring(indent_char:rep(indent)) .. tostring(args) .. "\n")
|
||||
end
|
||||
wrap_name = function(name)
|
||||
local v = V(name)
|
||||
v = Cmt("", function(str, pos)
|
||||
local rest = str:sub(pos, -1):match("^([^\n]*)")
|
||||
iprint("* " .. tostring(name) .. " (" .. tostring(rest) .. ")")
|
||||
indent = indent + 1
|
||||
return true
|
||||
end) * Cmt(v, function(str, pos, ...)
|
||||
iprint(name, true)
|
||||
indent = indent - 1
|
||||
return true, ...
|
||||
end) + Cmt("", function()
|
||||
iprint(name, false)
|
||||
indent = indent - 1
|
||||
return false
|
||||
end)
|
||||
return v
|
||||
end
|
||||
end
|
||||
return setfenv(fn, setmetatable({ }, {
|
||||
__index = function(self, name)
|
||||
local value = env[name]
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
if name:match("^[A-Z][A-Za-z0-9]*$") then
|
||||
local v = wrap_name(name)
|
||||
return v
|
||||
end
|
||||
return error("unknown variable referenced: " .. tostring(name))
|
||||
end
|
||||
}))
|
||||
end
|
||||
return {
|
||||
wrap_env = wrap_env
|
||||
}
|
51
prototypes/scp-clicker/moonscript/parse/env.moon
Normal file
51
prototypes/scp-clicker/moonscript/parse/env.moon
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
import getfenv, setfenv from require "moonscript.util"
|
||||
|
||||
-- all undefined Proper globals are automaticlly converted into lpeg.V
|
||||
wrap_env = (debug, fn) ->
|
||||
import V, Cmt from require "lpeg"
|
||||
|
||||
env = getfenv fn
|
||||
wrap_name = V
|
||||
|
||||
if debug
|
||||
indent = 0
|
||||
indent_char = " "
|
||||
|
||||
iprint = (...) ->
|
||||
args = table.concat [tostring a for a in *{...}], ", "
|
||||
io.stderr\write "#{indent_char\rep(indent)}#{args}\n"
|
||||
|
||||
wrap_name = (name) ->
|
||||
v = V name
|
||||
v = Cmt("", (str, pos) ->
|
||||
rest = str\sub(pos, -1)\match "^([^\n]*)"
|
||||
|
||||
iprint "* #{name} (#{rest})"
|
||||
indent += 1
|
||||
true
|
||||
) * Cmt(v, (str, pos, ...) ->
|
||||
iprint name, true
|
||||
indent -= 1
|
||||
true, ...
|
||||
) + Cmt("", ->
|
||||
iprint name, false
|
||||
indent -= 1
|
||||
false
|
||||
)
|
||||
|
||||
v
|
||||
|
||||
setfenv fn, setmetatable {}, {
|
||||
__index: (name) =>
|
||||
value = env[name]
|
||||
return value if value != nil
|
||||
|
||||
if name\match"^[A-Z][A-Za-z0-9]*$"
|
||||
v = wrap_name name
|
||||
return v
|
||||
|
||||
error "unknown variable referenced: #{name}"
|
||||
}
|
||||
|
||||
{ :wrap_env }
|
39
prototypes/scp-clicker/moonscript/parse/literals.lua
Normal file
39
prototypes/scp-clicker/moonscript/parse/literals.lua
Normal file
@ -0,0 +1,39 @@
|
||||
local safe_module
|
||||
safe_module = require("moonscript.util").safe_module
|
||||
local S, P, R, C
|
||||
do
|
||||
local _obj_0 = require("lpeg")
|
||||
S, P, R, C = _obj_0.S, _obj_0.P, _obj_0.R, _obj_0.C
|
||||
end
|
||||
local lpeg = require("lpeg")
|
||||
local L = lpeg.luversion and lpeg.L or function(v)
|
||||
return #v
|
||||
end
|
||||
local White = S(" \t\r\n") ^ 0
|
||||
local plain_space = S(" \t") ^ 0
|
||||
local Break = P("\r") ^ -1 * P("\n")
|
||||
local Stop = Break + -1
|
||||
local Comment = P("--") * (1 - S("\r\n")) ^ 0 * L(Stop)
|
||||
local Space = plain_space * Comment ^ -1
|
||||
local SomeSpace = S(" \t") ^ 1 * Comment ^ -1
|
||||
local SpaceBreak = Space * Break
|
||||
local EmptyLine = SpaceBreak
|
||||
local AlphaNum = R("az", "AZ", "09", "__")
|
||||
local Name = C(R("az", "AZ", "__") * AlphaNum ^ 0)
|
||||
local Num = P("0x") * R("09", "af", "AF") ^ 1 * (S("uU") ^ -1 * S("lL") ^ 2) ^ -1 + R("09") ^ 1 * (S("uU") ^ -1 * S("lL") ^ 2) + (R("09") ^ 1 * (P(".") * R("09") ^ 1) ^ -1 + P(".") * R("09") ^ 1) * (S("eE") * P("-") ^ -1 * R("09") ^ 1) ^ -1
|
||||
local Shebang = P("#!") * P(1 - Stop) ^ 0
|
||||
return safe_module("moonscript.parse.literals", {
|
||||
L = L,
|
||||
White = White,
|
||||
Break = Break,
|
||||
Stop = Stop,
|
||||
Comment = Comment,
|
||||
Space = Space,
|
||||
SomeSpace = SomeSpace,
|
||||
SpaceBreak = SpaceBreak,
|
||||
EmptyLine = EmptyLine,
|
||||
AlphaNum = AlphaNum,
|
||||
Name = Name,
|
||||
Num = Num,
|
||||
Shebang = Shebang
|
||||
})
|
38
prototypes/scp-clicker/moonscript/parse/literals.moon
Normal file
38
prototypes/scp-clicker/moonscript/parse/literals.moon
Normal file
@ -0,0 +1,38 @@
|
||||
-- non-recursive parsers
|
||||
import safe_module from require "moonscript.util"
|
||||
import S, P, R, C from require "lpeg"
|
||||
|
||||
lpeg = require "lpeg"
|
||||
L = lpeg.luversion and lpeg.L or (v) -> #v
|
||||
|
||||
White = S" \t\r\n"^0
|
||||
plain_space = S" \t"^0
|
||||
|
||||
Break = P"\r"^-1 * P"\n"
|
||||
Stop = Break + -1
|
||||
|
||||
Comment = P"--" * (1 - S"\r\n")^0 * L(Stop)
|
||||
Space = plain_space * Comment^-1
|
||||
SomeSpace = S" \t"^1 * Comment^-1
|
||||
|
||||
SpaceBreak = Space * Break
|
||||
EmptyLine = SpaceBreak
|
||||
|
||||
AlphaNum = R "az", "AZ", "09", "__"
|
||||
|
||||
Name = C R("az", "AZ", "__") * AlphaNum^0
|
||||
|
||||
Num = P"0x" * R("09", "af", "AF")^1 * (S"uU"^-1 * S"lL"^2)^-1 +
|
||||
R"09"^1 * (S"uU"^-1 * S"lL"^2) +
|
||||
(
|
||||
R"09"^1 * (P"." * R"09"^1)^-1 +
|
||||
P"." * R"09"^1
|
||||
) * (S"eE" * P"-"^-1 * R"09"^1)^-1
|
||||
|
||||
Shebang = P"#!" * P(1 - Stop)^0
|
||||
|
||||
safe_module "moonscript.parse.literals", {
|
||||
:L
|
||||
:White, :Break, :Stop, :Comment, :Space, :SomeSpace, :SpaceBreak, :EmptyLine,
|
||||
:AlphaNum, :Name, :Num, :Shebang
|
||||
}
|
306
prototypes/scp-clicker/moonscript/parse/util.lua
Normal file
306
prototypes/scp-clicker/moonscript/parse/util.lua
Normal file
@ -0,0 +1,306 @@
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
local P, C, S, Cp, Cmt, V
|
||||
do
|
||||
local _obj_0 = require("lpeg")
|
||||
P, C, S, Cp, Cmt, V = _obj_0.P, _obj_0.C, _obj_0.S, _obj_0.Cp, _obj_0.Cmt, _obj_0.V
|
||||
end
|
||||
local ntype
|
||||
ntype = require("moonscript.types").ntype
|
||||
local Space
|
||||
Space = require("moonscript.parse.literals").Space
|
||||
local Indent = C(S("\t ") ^ 0) / function(str)
|
||||
do
|
||||
local sum = 0
|
||||
for v in str:gmatch("[\t ]") do
|
||||
local _exp_0 = v
|
||||
if " " == _exp_0 then
|
||||
sum = sum + 1
|
||||
elseif "\t" == _exp_0 then
|
||||
sum = sum + 4
|
||||
end
|
||||
end
|
||||
return sum
|
||||
end
|
||||
end
|
||||
local Cut = P(function()
|
||||
return false
|
||||
end)
|
||||
local ensure
|
||||
ensure = function(patt, finally)
|
||||
return patt * finally + finally * Cut
|
||||
end
|
||||
local extract_line
|
||||
extract_line = function(str, start_pos)
|
||||
str = str:sub(start_pos)
|
||||
do
|
||||
local m = str:match("^(.-)\n")
|
||||
if m then
|
||||
return m
|
||||
end
|
||||
end
|
||||
return str:match("^.-$")
|
||||
end
|
||||
local show_line_position
|
||||
show_line_position = function(str, pos, context)
|
||||
if context == nil then
|
||||
context = true
|
||||
end
|
||||
local lines = {
|
||||
{ }
|
||||
}
|
||||
for c in str:gmatch(".") do
|
||||
lines[#lines] = lines[#lines] or { }
|
||||
table.insert(lines[#lines], c)
|
||||
if c == "\n" then
|
||||
lines[#lines + 1] = { }
|
||||
end
|
||||
end
|
||||
for i, line in ipairs(lines) do
|
||||
lines[i] = table.concat(line)
|
||||
end
|
||||
local out
|
||||
local remaining = pos - 1
|
||||
for k, line in ipairs(lines) do
|
||||
if remaining < #line then
|
||||
local left = line:sub(1, remaining)
|
||||
local right = line:sub(remaining + 1)
|
||||
out = {
|
||||
tostring(left) .. "◉" .. tostring(right)
|
||||
}
|
||||
if context then
|
||||
do
|
||||
local before = lines[k - 1]
|
||||
if before then
|
||||
table.insert(out, 1, before)
|
||||
end
|
||||
end
|
||||
do
|
||||
local after = lines[k + 1]
|
||||
if after then
|
||||
table.insert(out, after)
|
||||
end
|
||||
end
|
||||
end
|
||||
break
|
||||
else
|
||||
remaining = remaining - #line
|
||||
end
|
||||
end
|
||||
if not (out) then
|
||||
return "-"
|
||||
end
|
||||
out = table.concat(out)
|
||||
return (out:gsub("\n*$", ""))
|
||||
end
|
||||
local mark
|
||||
mark = function(name)
|
||||
return function(...)
|
||||
return {
|
||||
name,
|
||||
...
|
||||
}
|
||||
end
|
||||
end
|
||||
local pos
|
||||
pos = function(patt)
|
||||
return (Cp() * patt) / function(pos, value)
|
||||
if type(value) == "table" then
|
||||
value[-1] = pos
|
||||
end
|
||||
return value
|
||||
end
|
||||
end
|
||||
local got
|
||||
got = function(what, context)
|
||||
if context == nil then
|
||||
context = true
|
||||
end
|
||||
return Cmt("", function(str, pos)
|
||||
print("++ got " .. tostring(what), "[" .. tostring(show_line_position(str, pos, context)) .. "]")
|
||||
return true
|
||||
end)
|
||||
end
|
||||
local flatten_or_mark
|
||||
flatten_or_mark = function(name)
|
||||
return function(tbl)
|
||||
if #tbl == 1 then
|
||||
return tbl[1]
|
||||
end
|
||||
table.insert(tbl, 1, name)
|
||||
return tbl
|
||||
end
|
||||
end
|
||||
local is_assignable
|
||||
do
|
||||
local chain_assignable = {
|
||||
index = true,
|
||||
dot = true,
|
||||
slice = true
|
||||
}
|
||||
is_assignable = function(node)
|
||||
if node == "..." then
|
||||
return false
|
||||
end
|
||||
local _exp_0 = ntype(node)
|
||||
if "ref" == _exp_0 or "self" == _exp_0 or "value" == _exp_0 or "self_class" == _exp_0 or "table" == _exp_0 then
|
||||
return true
|
||||
elseif "chain" == _exp_0 then
|
||||
return chain_assignable[ntype(node[#node])]
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
local check_assignable
|
||||
check_assignable = function(str, pos, value)
|
||||
if is_assignable(value) then
|
||||
return true, value
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
local format_assign
|
||||
do
|
||||
local flatten_explist = flatten_or_mark("explist")
|
||||
format_assign = function(lhs_exps, assign)
|
||||
if not (assign) then
|
||||
return flatten_explist(lhs_exps)
|
||||
end
|
||||
for _index_0 = 1, #lhs_exps do
|
||||
local assign_exp = lhs_exps[_index_0]
|
||||
if not (is_assignable(assign_exp)) then
|
||||
error({
|
||||
assign_exp,
|
||||
"left hand expression is not assignable"
|
||||
})
|
||||
end
|
||||
end
|
||||
local t = ntype(assign)
|
||||
local _exp_0 = t
|
||||
if "assign" == _exp_0 then
|
||||
return {
|
||||
"assign",
|
||||
lhs_exps,
|
||||
unpack(assign, 2)
|
||||
}
|
||||
elseif "update" == _exp_0 then
|
||||
return {
|
||||
"update",
|
||||
lhs_exps[1],
|
||||
unpack(assign, 2)
|
||||
}
|
||||
else
|
||||
return error("unknown assign expression: " .. tostring(t))
|
||||
end
|
||||
end
|
||||
end
|
||||
local format_single_assign
|
||||
format_single_assign = function(lhs, assign)
|
||||
if assign then
|
||||
return format_assign({
|
||||
lhs
|
||||
}, assign)
|
||||
else
|
||||
return lhs
|
||||
end
|
||||
end
|
||||
local sym
|
||||
sym = function(chars)
|
||||
return Space * chars
|
||||
end
|
||||
local symx
|
||||
symx = function(chars)
|
||||
return chars
|
||||
end
|
||||
local simple_string
|
||||
simple_string = function(delim, allow_interpolation)
|
||||
local inner = P("\\" .. tostring(delim)) + "\\\\" + (1 - P(delim))
|
||||
if allow_interpolation then
|
||||
local interp = symx('#{') * V("Exp") * sym('}')
|
||||
inner = (C((inner - interp) ^ 1) + interp / mark("interpolate")) ^ 0
|
||||
else
|
||||
inner = C(inner ^ 0)
|
||||
end
|
||||
return C(symx(delim)) * inner * sym(delim) / mark("string")
|
||||
end
|
||||
local wrap_func_arg
|
||||
wrap_func_arg = function(value)
|
||||
return {
|
||||
"call",
|
||||
{
|
||||
value
|
||||
}
|
||||
}
|
||||
end
|
||||
local join_chain
|
||||
join_chain = function(callee, args)
|
||||
if #args == 0 then
|
||||
return callee
|
||||
end
|
||||
args = {
|
||||
"call",
|
||||
args
|
||||
}
|
||||
if ntype(callee) == "chain" then
|
||||
table.insert(callee, args)
|
||||
return callee
|
||||
end
|
||||
return {
|
||||
"chain",
|
||||
callee,
|
||||
args
|
||||
}
|
||||
end
|
||||
local wrap_decorator
|
||||
wrap_decorator = function(stm, dec)
|
||||
if not (dec) then
|
||||
return stm
|
||||
end
|
||||
return {
|
||||
"decorated",
|
||||
stm,
|
||||
dec
|
||||
}
|
||||
end
|
||||
local check_lua_string
|
||||
check_lua_string = function(str, pos, right, left)
|
||||
return #left == #right
|
||||
end
|
||||
local self_assign
|
||||
self_assign = function(name, pos)
|
||||
return {
|
||||
{
|
||||
"key_literal",
|
||||
name
|
||||
},
|
||||
{
|
||||
"ref",
|
||||
name,
|
||||
[-1] = pos
|
||||
}
|
||||
}
|
||||
end
|
||||
return {
|
||||
Indent = Indent,
|
||||
Cut = Cut,
|
||||
ensure = ensure,
|
||||
extract_line = extract_line,
|
||||
mark = mark,
|
||||
pos = pos,
|
||||
flatten_or_mark = flatten_or_mark,
|
||||
is_assignable = is_assignable,
|
||||
check_assignable = check_assignable,
|
||||
format_assign = format_assign,
|
||||
format_single_assign = format_single_assign,
|
||||
sym = sym,
|
||||
symx = symx,
|
||||
simple_string = simple_string,
|
||||
wrap_func_arg = wrap_func_arg,
|
||||
join_chain = join_chain,
|
||||
wrap_decorator = wrap_decorator,
|
||||
check_lua_string = check_lua_string,
|
||||
self_assign = self_assign,
|
||||
got = got,
|
||||
show_line_position = show_line_position
|
||||
}
|
197
prototypes/scp-clicker/moonscript/parse/util.moon
Normal file
197
prototypes/scp-clicker/moonscript/parse/util.moon
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
import unpack from require "moonscript.util"
|
||||
import P, C, S, Cp, Cmt, V from require "lpeg"
|
||||
import ntype from require "moonscript.types"
|
||||
import Space from require "moonscript.parse.literals"
|
||||
|
||||
-- captures an indentation, returns indent depth
|
||||
Indent = C(S"\t "^0) / (str) ->
|
||||
with sum = 0
|
||||
for v in str\gmatch "[\t ]"
|
||||
switch v
|
||||
when " "
|
||||
sum += 1
|
||||
when "\t"
|
||||
sum += 4
|
||||
|
||||
|
||||
-- causes pattern in progress to be rejected
|
||||
-- can't have P(false) because it causes preceding patterns not to run
|
||||
Cut = P -> false
|
||||
|
||||
-- ensures finally runs regardless of whether pattern fails or passes
|
||||
ensure = (patt, finally) ->
|
||||
patt * finally + finally * Cut
|
||||
|
||||
-- take rest of line from pos out of str
|
||||
extract_line = (str, start_pos) ->
|
||||
str = str\sub start_pos
|
||||
if m = str\match "^(.-)\n"
|
||||
return m
|
||||
|
||||
str\match "^.-$"
|
||||
|
||||
-- print the line with a token showing the position
|
||||
show_line_position = (str, pos, context=true) ->
|
||||
lines = { {} }
|
||||
for c in str\gmatch "."
|
||||
lines[#lines] or= {}
|
||||
table.insert lines[#lines], c
|
||||
if c == "\n"
|
||||
lines[#lines + 1] = {}
|
||||
|
||||
for i, line in ipairs lines
|
||||
lines[i] = table.concat line
|
||||
|
||||
local out
|
||||
|
||||
remaining = pos - 1
|
||||
for k, line in ipairs lines
|
||||
if remaining < #line
|
||||
left = line\sub 1, remaining
|
||||
right = line\sub remaining + 1
|
||||
out = {
|
||||
"#{left}◉#{right}"
|
||||
}
|
||||
|
||||
if context
|
||||
if before = lines[k - 1]
|
||||
table.insert out, 1, before
|
||||
|
||||
if after = lines[k + 1]
|
||||
table.insert out, after
|
||||
|
||||
break
|
||||
else
|
||||
remaining -= #line
|
||||
|
||||
|
||||
return "-" unless out
|
||||
|
||||
out = table.concat out
|
||||
(out\gsub "\n*$", "")
|
||||
|
||||
-- used to identify a capture with a label
|
||||
mark = (name) ->
|
||||
(...) -> {name, ...}
|
||||
|
||||
-- wraps pattern to capture pos into node
|
||||
-- pos is the character offset from the buffer where the node was parsed from.
|
||||
-- Used to generate error messages
|
||||
pos = (patt) ->
|
||||
(Cp! * patt) / (pos, value) ->
|
||||
if type(value) == "table"
|
||||
value[-1] = pos
|
||||
value
|
||||
|
||||
-- generates a debug pattern that always succeeds and prints out where we are
|
||||
-- in the buffer with a label
|
||||
got = (what, context=true) ->
|
||||
Cmt "", (str, pos) ->
|
||||
print "++ got #{what}", "[#{show_line_position str, pos, context}]"
|
||||
true
|
||||
|
||||
-- converts 1 element array to its value, otherwise marks it
|
||||
flatten_or_mark = (name) ->
|
||||
(tbl) ->
|
||||
return tbl[1] if #tbl == 1
|
||||
table.insert tbl, 1, name
|
||||
tbl
|
||||
|
||||
-- determines if node is able to be on left side of assignment
|
||||
is_assignable = do
|
||||
chain_assignable = { index: true, dot: true, slice: true }
|
||||
|
||||
(node) ->
|
||||
return false if node == "..."
|
||||
switch ntype node
|
||||
when "ref", "self", "value", "self_class", "table"
|
||||
true
|
||||
when "chain"
|
||||
chain_assignable[ntype node[#node]]
|
||||
else
|
||||
false
|
||||
|
||||
check_assignable = (str, pos, value) ->
|
||||
if is_assignable value
|
||||
true, value
|
||||
else
|
||||
false
|
||||
|
||||
-- joins the two parts of an assign parse into a single node
|
||||
format_assign = do
|
||||
flatten_explist = flatten_or_mark "explist"
|
||||
|
||||
(lhs_exps, assign) ->
|
||||
unless assign
|
||||
return flatten_explist lhs_exps
|
||||
|
||||
for assign_exp in *lhs_exps
|
||||
unless is_assignable assign_exp
|
||||
error {assign_exp, "left hand expression is not assignable"}
|
||||
|
||||
t = ntype assign
|
||||
switch t
|
||||
when "assign"
|
||||
{"assign", lhs_exps, unpack assign, 2}
|
||||
when "update"
|
||||
{"update", lhs_exps[1], unpack assign, 2}
|
||||
else
|
||||
error "unknown assign expression: #{t}"
|
||||
|
||||
-- helper for if statement, which only has single lhs
|
||||
format_single_assign = (lhs, assign) ->
|
||||
if assign
|
||||
format_assign {lhs}, assign
|
||||
else
|
||||
lhs
|
||||
|
||||
|
||||
-- a symbol
|
||||
sym = (chars) -> Space * chars
|
||||
-- a symbol that doesn't accept whitespace before it
|
||||
symx = (chars) -> chars
|
||||
|
||||
-- a constructor for quote delimited strings
|
||||
simple_string = (delim, allow_interpolation) ->
|
||||
inner = P("\\#{delim}") + "\\\\" + (1 - P delim)
|
||||
|
||||
inner = if allow_interpolation
|
||||
interp = symx'#{' * V"Exp" * sym'}'
|
||||
(C((inner - interp)^1) + interp / mark"interpolate")^0
|
||||
else
|
||||
C inner^0
|
||||
|
||||
C(symx(delim)) * inner * sym(delim) / mark"string"
|
||||
|
||||
-- wraps a single value in format needed to be passed as function arguments
|
||||
wrap_func_arg = (value) -> {"call", {value}}
|
||||
|
||||
-- chains are parsed in two captures, the chain and then the open arguments
|
||||
-- if there are open arguments, then append them to the end of the chain as a call
|
||||
join_chain = (callee, args) ->
|
||||
return callee if #args == 0
|
||||
args = {"call", args}
|
||||
|
||||
if ntype(callee) == "chain"
|
||||
table.insert callee, args
|
||||
return callee
|
||||
|
||||
{"chain", callee, args}
|
||||
|
||||
-- constructor for decorator node
|
||||
wrap_decorator = (stm, dec) ->
|
||||
return stm unless dec
|
||||
{"decorated", stm, dec}
|
||||
|
||||
check_lua_string = (str, pos, right, left) ->
|
||||
#left == #right
|
||||
|
||||
-- constructor for :name self assignments in table literals
|
||||
self_assign = (name, pos) ->
|
||||
{{"key_literal", name}, {"ref", name, [-1]: pos}}
|
||||
|
||||
{ :Indent, :Cut, :ensure, :extract_line, :mark, :pos, :flatten_or_mark,
|
||||
:is_assignable, :check_assignable, :format_assign, :format_single_assign,
|
||||
:sym, :symx, :simple_string, :wrap_func_arg, :join_chain, :wrap_decorator,
|
||||
:check_lua_string, :self_assign, :got, :show_line_position }
|
4
prototypes/scp-clicker/moonscript/transform.lua
Normal file
4
prototypes/scp-clicker/moonscript/transform.lua
Normal file
@ -0,0 +1,4 @@
|
||||
return {
|
||||
Statement = require("moonscript.transform.statement"),
|
||||
Value = require("moonscript.transform.value")
|
||||
}
|
5
prototypes/scp-clicker/moonscript/transform.moon
Normal file
5
prototypes/scp-clicker/moonscript/transform.moon
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
{
|
||||
Statement: require "moonscript.transform.statement"
|
||||
Value: require "moonscript.transform.value"
|
||||
}
|
110
prototypes/scp-clicker/moonscript/transform/accumulator.lua
Normal file
110
prototypes/scp-clicker/moonscript/transform/accumulator.lua
Normal file
@ -0,0 +1,110 @@
|
||||
local types = require("moonscript.types")
|
||||
local build, ntype, NOOP
|
||||
build, ntype, NOOP = types.build, types.ntype, types.NOOP
|
||||
local NameProxy
|
||||
NameProxy = require("moonscript.transform.names").NameProxy
|
||||
local insert
|
||||
insert = table.insert
|
||||
local is_singular
|
||||
is_singular = function(body)
|
||||
if #body ~= 1 then
|
||||
return false
|
||||
end
|
||||
if "group" == ntype(body) then
|
||||
return is_singular(body[2])
|
||||
else
|
||||
return body[1]
|
||||
end
|
||||
end
|
||||
local transform_last_stm
|
||||
transform_last_stm = require("moonscript.transform.statements").transform_last_stm
|
||||
local Accumulator
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
body_idx = {
|
||||
["for"] = 4,
|
||||
["while"] = 3,
|
||||
foreach = 4
|
||||
},
|
||||
convert = function(self, node)
|
||||
local index = self.body_idx[ntype(node)]
|
||||
node[index] = self:mutate_body(node[index])
|
||||
return self:wrap(node)
|
||||
end,
|
||||
wrap = function(self, node, group_type)
|
||||
if group_type == nil then
|
||||
group_type = "block_exp"
|
||||
end
|
||||
return build[group_type]({
|
||||
build.assign_one(self.accum_name, build.table()),
|
||||
build.assign_one(self.len_name, 1),
|
||||
node,
|
||||
group_type == "block_exp" and self.accum_name or NOOP
|
||||
})
|
||||
end,
|
||||
mutate_body = function(self, body)
|
||||
local single_stm = is_singular(body)
|
||||
local val
|
||||
if single_stm and types.is_value(single_stm) then
|
||||
body = { }
|
||||
val = single_stm
|
||||
else
|
||||
body = transform_last_stm(body, function(n)
|
||||
if types.is_value(n) then
|
||||
return build.assign_one(self.value_name, n)
|
||||
else
|
||||
return build.group({
|
||||
{
|
||||
"declare",
|
||||
{
|
||||
self.value_name
|
||||
}
|
||||
},
|
||||
n
|
||||
})
|
||||
end
|
||||
end)
|
||||
val = self.value_name
|
||||
end
|
||||
local update = {
|
||||
build.assign_one(NameProxy.index(self.accum_name, self.len_name), val),
|
||||
{
|
||||
"update",
|
||||
self.len_name,
|
||||
"+=",
|
||||
1
|
||||
}
|
||||
}
|
||||
insert(body, build.group(update))
|
||||
return body
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, accum_name)
|
||||
self.accum_name = NameProxy("accum")
|
||||
self.value_name = NameProxy("value")
|
||||
self.len_name = NameProxy("len")
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Accumulator"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Accumulator = _class_0
|
||||
end
|
||||
local default_accumulator
|
||||
default_accumulator = function(self, node)
|
||||
return Accumulator():convert(node)
|
||||
end
|
||||
return {
|
||||
Accumulator = Accumulator,
|
||||
default_accumulator = default_accumulator
|
||||
}
|
71
prototypes/scp-clicker/moonscript/transform/accumulator.moon
Normal file
71
prototypes/scp-clicker/moonscript/transform/accumulator.moon
Normal file
@ -0,0 +1,71 @@
|
||||
types = require "moonscript.types"
|
||||
|
||||
import build, ntype, NOOP from types
|
||||
import NameProxy from require "moonscript.transform.names"
|
||||
|
||||
import insert from table
|
||||
|
||||
-- is a body a single expression/statement
|
||||
is_singular = (body) ->
|
||||
return false if #body != 1
|
||||
if "group" == ntype body
|
||||
is_singular body[2]
|
||||
else
|
||||
body[1]
|
||||
|
||||
import transform_last_stm from require "moonscript.transform.statements"
|
||||
|
||||
class Accumulator
|
||||
body_idx: { for: 4, while: 3, foreach: 4 }
|
||||
|
||||
new: (accum_name) =>
|
||||
@accum_name = NameProxy "accum"
|
||||
@value_name = NameProxy "value"
|
||||
@len_name = NameProxy "len"
|
||||
|
||||
-- wraps node and mutates body
|
||||
convert: (node) =>
|
||||
index = @body_idx[ntype node]
|
||||
node[index] = @mutate_body node[index]
|
||||
@wrap node
|
||||
|
||||
-- wrap the node into a block_exp
|
||||
wrap: (node, group_type="block_exp") =>
|
||||
build[group_type] {
|
||||
build.assign_one @accum_name, build.table!
|
||||
build.assign_one @len_name, 1
|
||||
node
|
||||
group_type == "block_exp" and @accum_name or NOOP
|
||||
}
|
||||
|
||||
-- mutates the body of a loop construct to save last value into accumulator
|
||||
mutate_body: (body) =>
|
||||
-- shortcut to write simpler code if body is a single expression
|
||||
single_stm = is_singular body
|
||||
val = if single_stm and types.is_value single_stm
|
||||
body = {}
|
||||
single_stm
|
||||
else
|
||||
body = transform_last_stm body, (n) ->
|
||||
if types.is_value n
|
||||
build.assign_one @value_name, n
|
||||
else
|
||||
-- just ignore it
|
||||
build.group {
|
||||
{"declare", {@value_name}}
|
||||
n
|
||||
}
|
||||
@value_name
|
||||
|
||||
update = {
|
||||
build.assign_one NameProxy.index(@accum_name, @len_name), val
|
||||
{"update", @len_name, "+=", 1}
|
||||
}
|
||||
|
||||
insert body, build.group update
|
||||
body
|
||||
|
||||
default_accumulator = (node) =>
|
||||
Accumulator!\convert node
|
||||
|
||||
{ :Accumulator, :default_accumulator }
|
486
prototypes/scp-clicker/moonscript/transform/class.lua
Normal file
486
prototypes/scp-clicker/moonscript/transform/class.lua
Normal file
@ -0,0 +1,486 @@
|
||||
local NameProxy, LocalName
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.names")
|
||||
NameProxy, LocalName = _obj_0.NameProxy, _obj_0.LocalName
|
||||
end
|
||||
local Run
|
||||
Run = require("moonscript.transform.statements").Run
|
||||
local CONSTRUCTOR_NAME = "new"
|
||||
local insert
|
||||
insert = table.insert
|
||||
local build, ntype, NOOP
|
||||
do
|
||||
local _obj_0 = require("moonscript.types")
|
||||
build, ntype, NOOP = _obj_0.build, _obj_0.ntype, _obj_0.NOOP
|
||||
end
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
local transform_super
|
||||
transform_super = function(cls_name, on_base, block, chain)
|
||||
if on_base == nil then
|
||||
on_base = true
|
||||
end
|
||||
local relative_parent = {
|
||||
"chain",
|
||||
cls_name,
|
||||
{
|
||||
"dot",
|
||||
"__parent"
|
||||
}
|
||||
}
|
||||
if not (chain) then
|
||||
return relative_parent
|
||||
end
|
||||
local chain_tail = {
|
||||
unpack(chain, 3)
|
||||
}
|
||||
local head = chain_tail[1]
|
||||
if head == nil then
|
||||
return relative_parent
|
||||
end
|
||||
local new_chain = relative_parent
|
||||
local _exp_0 = head[1]
|
||||
if "call" == _exp_0 then
|
||||
if on_base then
|
||||
insert(new_chain, {
|
||||
"dot",
|
||||
"__base"
|
||||
})
|
||||
end
|
||||
local calling_name = block:get("current_method")
|
||||
assert(calling_name, "missing calling name")
|
||||
chain_tail[1] = {
|
||||
"call",
|
||||
{
|
||||
"self",
|
||||
unpack(head[2])
|
||||
}
|
||||
}
|
||||
if ntype(calling_name) == "key_literal" then
|
||||
insert(new_chain, {
|
||||
"dot",
|
||||
calling_name[2]
|
||||
})
|
||||
else
|
||||
insert(new_chain, {
|
||||
"index",
|
||||
calling_name
|
||||
})
|
||||
end
|
||||
elseif "colon" == _exp_0 then
|
||||
local call = chain_tail[2]
|
||||
if call and call[1] == "call" then
|
||||
chain_tail[1] = {
|
||||
"dot",
|
||||
head[2]
|
||||
}
|
||||
chain_tail[2] = {
|
||||
"call",
|
||||
{
|
||||
"self",
|
||||
unpack(call[2])
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
for _index_0 = 1, #chain_tail do
|
||||
local item = chain_tail[_index_0]
|
||||
insert(new_chain, item)
|
||||
end
|
||||
return new_chain
|
||||
end
|
||||
local super_scope
|
||||
super_scope = function(value, t, key)
|
||||
local prev_method
|
||||
return {
|
||||
"scoped",
|
||||
Run(function(self)
|
||||
prev_method = self:get("current_method")
|
||||
self:set("current_method", key)
|
||||
return self:set("super", t)
|
||||
end),
|
||||
value,
|
||||
Run(function(self)
|
||||
return self:set("current_method", prev_method)
|
||||
end)
|
||||
}
|
||||
end
|
||||
return function(self, node, ret, parent_assign)
|
||||
local name, parent_val, body = unpack(node, 2)
|
||||
if parent_val == "" then
|
||||
parent_val = nil
|
||||
end
|
||||
local parent_cls_name = NameProxy("parent")
|
||||
local base_name = NameProxy("base")
|
||||
local self_name = NameProxy("self")
|
||||
local cls_name = NameProxy("class")
|
||||
local cls_instance_super
|
||||
cls_instance_super = function(...)
|
||||
return transform_super(cls_name, true, ...)
|
||||
end
|
||||
local cls_super
|
||||
cls_super = function(...)
|
||||
return transform_super(cls_name, false, ...)
|
||||
end
|
||||
local statements = { }
|
||||
local properties = { }
|
||||
for _index_0 = 1, #body do
|
||||
local item = body[_index_0]
|
||||
local _exp_0 = item[1]
|
||||
if "stm" == _exp_0 then
|
||||
insert(statements, item[2])
|
||||
elseif "props" == _exp_0 then
|
||||
for _index_1 = 2, #item do
|
||||
local tuple = item[_index_1]
|
||||
if ntype(tuple[1]) == "self" then
|
||||
local k, v
|
||||
k, v = tuple[1], tuple[2]
|
||||
v = super_scope(v, cls_super, {
|
||||
"key_literal",
|
||||
k[2]
|
||||
})
|
||||
insert(statements, build.assign_one(k, v))
|
||||
else
|
||||
insert(properties, tuple)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local constructor
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #properties do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local tuple = properties[_index_0]
|
||||
local key = tuple[1]
|
||||
local _value_0
|
||||
if key[1] == "key_literal" and key[2] == CONSTRUCTOR_NAME then
|
||||
constructor = tuple[2]
|
||||
_continue_0 = true
|
||||
break
|
||||
else
|
||||
local val
|
||||
key, val = tuple[1], tuple[2]
|
||||
_value_0 = {
|
||||
key,
|
||||
super_scope(val, cls_instance_super, key)
|
||||
}
|
||||
end
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
properties = _accum_0
|
||||
end
|
||||
if not (constructor) then
|
||||
if parent_val then
|
||||
constructor = build.fndef({
|
||||
args = {
|
||||
{
|
||||
"..."
|
||||
}
|
||||
},
|
||||
arrow = "fat",
|
||||
body = {
|
||||
build.chain({
|
||||
base = "super",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
"..."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
else
|
||||
constructor = build.fndef()
|
||||
end
|
||||
end
|
||||
local real_name = name or parent_assign and parent_assign[2][1]
|
||||
local _exp_0 = ntype(real_name)
|
||||
if "chain" == _exp_0 then
|
||||
local last = real_name[#real_name]
|
||||
local _exp_1 = ntype(last)
|
||||
if "dot" == _exp_1 then
|
||||
real_name = {
|
||||
"string",
|
||||
'"',
|
||||
last[2]
|
||||
}
|
||||
elseif "index" == _exp_1 then
|
||||
real_name = last[2]
|
||||
else
|
||||
real_name = "nil"
|
||||
end
|
||||
elseif "nil" == _exp_0 then
|
||||
real_name = "nil"
|
||||
else
|
||||
local name_t = type(real_name)
|
||||
local flattened_name
|
||||
if name_t == "string" then
|
||||
flattened_name = real_name
|
||||
elseif name_t == "table" and real_name[1] == "ref" then
|
||||
flattened_name = real_name[2]
|
||||
else
|
||||
flattened_name = error("don't know how to extract name from " .. tostring(name_t))
|
||||
end
|
||||
real_name = {
|
||||
"string",
|
||||
'"',
|
||||
flattened_name
|
||||
}
|
||||
end
|
||||
local cls = build.table({
|
||||
{
|
||||
"__init",
|
||||
super_scope(constructor, cls_super, {
|
||||
"key_literal",
|
||||
"__init"
|
||||
})
|
||||
},
|
||||
{
|
||||
"__base",
|
||||
base_name
|
||||
},
|
||||
{
|
||||
"__name",
|
||||
real_name
|
||||
},
|
||||
parent_val and {
|
||||
"__parent",
|
||||
parent_cls_name
|
||||
} or nil
|
||||
})
|
||||
local class_index
|
||||
if parent_val then
|
||||
local class_lookup = build["if"]({
|
||||
cond = {
|
||||
"exp",
|
||||
{
|
||||
"ref",
|
||||
"val"
|
||||
},
|
||||
"==",
|
||||
"nil"
|
||||
},
|
||||
["then"] = {
|
||||
build.assign_one(LocalName("parent"), build.chain({
|
||||
base = "rawget",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
{
|
||||
"ref",
|
||||
"cls"
|
||||
},
|
||||
{
|
||||
"string",
|
||||
'"',
|
||||
"__parent"
|
||||
}
|
||||
}
|
||||
}
|
||||
})),
|
||||
build["if"]({
|
||||
cond = LocalName("parent"),
|
||||
["then"] = {
|
||||
build.chain({
|
||||
base = LocalName("parent"),
|
||||
{
|
||||
"index",
|
||||
"name"
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
insert(class_lookup, {
|
||||
"else",
|
||||
{
|
||||
"val"
|
||||
}
|
||||
})
|
||||
class_index = build.fndef({
|
||||
args = {
|
||||
{
|
||||
"cls"
|
||||
},
|
||||
{
|
||||
"name"
|
||||
}
|
||||
},
|
||||
body = {
|
||||
build.assign_one(LocalName("val"), build.chain({
|
||||
base = "rawget",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
base_name,
|
||||
{
|
||||
"ref",
|
||||
"name"
|
||||
}
|
||||
}
|
||||
}
|
||||
})),
|
||||
class_lookup
|
||||
}
|
||||
})
|
||||
else
|
||||
class_index = base_name
|
||||
end
|
||||
local cls_mt = build.table({
|
||||
{
|
||||
"__index",
|
||||
class_index
|
||||
},
|
||||
{
|
||||
"__call",
|
||||
build.fndef({
|
||||
args = {
|
||||
{
|
||||
"cls"
|
||||
},
|
||||
{
|
||||
"..."
|
||||
}
|
||||
},
|
||||
body = {
|
||||
build.assign_one(self_name, build.chain({
|
||||
base = "setmetatable",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
"{}",
|
||||
base_name
|
||||
}
|
||||
}
|
||||
})),
|
||||
build.chain({
|
||||
base = "cls.__init",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
self_name,
|
||||
"..."
|
||||
}
|
||||
}
|
||||
}),
|
||||
self_name
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
cls = build.chain({
|
||||
base = "setmetatable",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
cls,
|
||||
cls_mt
|
||||
}
|
||||
}
|
||||
})
|
||||
local value = nil
|
||||
do
|
||||
local out_body = {
|
||||
Run(function(self)
|
||||
if name then
|
||||
return self:put_name(name)
|
||||
end
|
||||
end),
|
||||
{
|
||||
"declare",
|
||||
{
|
||||
cls_name
|
||||
}
|
||||
},
|
||||
{
|
||||
"declare_glob",
|
||||
"*"
|
||||
},
|
||||
parent_val and build.assign_one(parent_cls_name, parent_val) or NOOP,
|
||||
build.assign_one(base_name, {
|
||||
"table",
|
||||
properties
|
||||
}),
|
||||
build.assign_one(base_name:chain("__index"), base_name),
|
||||
parent_val and build.chain({
|
||||
base = "setmetatable",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
base_name,
|
||||
build.chain({
|
||||
base = parent_cls_name,
|
||||
{
|
||||
"dot",
|
||||
"__base"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}) or NOOP,
|
||||
build.assign_one(cls_name, cls),
|
||||
build.assign_one(base_name:chain("__class"), cls_name),
|
||||
build.group((function()
|
||||
if #statements > 0 then
|
||||
return {
|
||||
build.assign_one(LocalName("self"), cls_name),
|
||||
build.group(statements)
|
||||
}
|
||||
end
|
||||
end)()),
|
||||
parent_val and build["if"]({
|
||||
cond = {
|
||||
"exp",
|
||||
parent_cls_name:chain("__inherited")
|
||||
},
|
||||
["then"] = {
|
||||
parent_cls_name:chain("__inherited", {
|
||||
"call",
|
||||
{
|
||||
parent_cls_name,
|
||||
cls_name
|
||||
}
|
||||
})
|
||||
}
|
||||
}) or NOOP,
|
||||
build.group((function()
|
||||
if name then
|
||||
return {
|
||||
build.assign_one(name, cls_name)
|
||||
}
|
||||
end
|
||||
end)()),
|
||||
(function()
|
||||
if ret then
|
||||
return ret(cls_name)
|
||||
end
|
||||
end)()
|
||||
}
|
||||
value = build.group({
|
||||
build.group((function()
|
||||
if ntype(name) == "value" then
|
||||
return {
|
||||
build.declare({
|
||||
names = {
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
end
|
||||
end)()),
|
||||
build["do"](out_body)
|
||||
})
|
||||
end
|
||||
return value
|
||||
end
|
285
prototypes/scp-clicker/moonscript/transform/class.moon
Normal file
285
prototypes/scp-clicker/moonscript/transform/class.moon
Normal file
@ -0,0 +1,285 @@
|
||||
import NameProxy, LocalName from require "moonscript.transform.names"
|
||||
import Run from require "moonscript.transform.statements"
|
||||
|
||||
CONSTRUCTOR_NAME = "new"
|
||||
|
||||
import insert from table
|
||||
import build, ntype, NOOP from require "moonscript.types"
|
||||
import unpack from require "moonscript.util"
|
||||
|
||||
transform_super = (cls_name, on_base=true, block, chain) ->
|
||||
relative_parent = {
|
||||
"chain",
|
||||
cls_name
|
||||
{"dot", "__parent"}
|
||||
}
|
||||
|
||||
return relative_parent unless chain
|
||||
|
||||
chain_tail = { unpack chain, 3 }
|
||||
head = chain_tail[1]
|
||||
|
||||
if head == nil
|
||||
return relative_parent
|
||||
|
||||
new_chain = relative_parent
|
||||
|
||||
switch head[1]
|
||||
-- calling super, inject calling name and self into chain
|
||||
when "call"
|
||||
if on_base
|
||||
insert new_chain, {"dot", "__base"}
|
||||
|
||||
calling_name = block\get "current_method"
|
||||
assert calling_name, "missing calling name"
|
||||
chain_tail[1] = {"call", {"self", unpack head[2]}}
|
||||
|
||||
if ntype(calling_name) == "key_literal"
|
||||
insert new_chain, {"dot", calling_name[2]}
|
||||
else
|
||||
insert new_chain, {"index", calling_name}
|
||||
|
||||
-- colon call on super, replace class with self as first arg
|
||||
when "colon"
|
||||
call = chain_tail[2]
|
||||
-- calling chain tail
|
||||
if call and call[1] == "call"
|
||||
chain_tail[1] = {
|
||||
"dot"
|
||||
head[2]
|
||||
}
|
||||
|
||||
chain_tail[2] = {
|
||||
"call"
|
||||
{
|
||||
"self"
|
||||
unpack call[2]
|
||||
}
|
||||
}
|
||||
|
||||
insert new_chain, item for item in *chain_tail
|
||||
new_chain
|
||||
|
||||
|
||||
super_scope = (value, t, key) ->
|
||||
local prev_method
|
||||
|
||||
{
|
||||
"scoped",
|
||||
Run =>
|
||||
prev_method = @get "current_method"
|
||||
@set "current_method", key
|
||||
@set "super", t
|
||||
value
|
||||
Run =>
|
||||
@set "current_method", prev_method
|
||||
}
|
||||
|
||||
(node, ret, parent_assign) =>
|
||||
name, parent_val, body = unpack node, 2
|
||||
parent_val = nil if parent_val == ""
|
||||
|
||||
parent_cls_name = NameProxy "parent"
|
||||
base_name = NameProxy "base"
|
||||
self_name = NameProxy "self"
|
||||
cls_name = NameProxy "class"
|
||||
|
||||
-- super call on instance
|
||||
cls_instance_super = (...) -> transform_super cls_name, true, ...
|
||||
|
||||
-- super call on parent class
|
||||
cls_super = (...) -> transform_super cls_name, false, ...
|
||||
|
||||
-- split apart properties and statements
|
||||
statements = {}
|
||||
properties = {}
|
||||
for item in *body
|
||||
switch item[1]
|
||||
when "stm"
|
||||
insert statements, item[2]
|
||||
when "props"
|
||||
for tuple in *item[2,]
|
||||
if ntype(tuple[1]) == "self"
|
||||
{k,v} = tuple
|
||||
v = super_scope v, cls_super, {"key_literal", k[2]}
|
||||
insert statements, build.assign_one k, v
|
||||
else
|
||||
insert properties, tuple
|
||||
|
||||
-- find constructor
|
||||
local constructor
|
||||
properties = for tuple in *properties
|
||||
key = tuple[1]
|
||||
if key[1] == "key_literal" and key[2] == CONSTRUCTOR_NAME
|
||||
constructor = tuple[2]
|
||||
continue
|
||||
else
|
||||
{key, val} = tuple
|
||||
{key, super_scope val, cls_instance_super, key}
|
||||
|
||||
|
||||
unless constructor
|
||||
constructor = if parent_val
|
||||
build.fndef {
|
||||
args: {{"..."}}
|
||||
arrow: "fat"
|
||||
body: {
|
||||
build.chain { base: "super", {"call", {"..."}} }
|
||||
}
|
||||
}
|
||||
else
|
||||
build.fndef!
|
||||
|
||||
real_name = name or parent_assign and parent_assign[2][1]
|
||||
real_name = switch ntype real_name
|
||||
when "chain"
|
||||
last = real_name[#real_name]
|
||||
switch ntype last
|
||||
when "dot"
|
||||
{"string", '"', last[2]}
|
||||
when "index"
|
||||
last[2]
|
||||
else
|
||||
"nil"
|
||||
when "nil"
|
||||
"nil"
|
||||
else
|
||||
name_t = type real_name
|
||||
-- TODO: don't use string literal as ref
|
||||
flattened_name = if name_t == "string"
|
||||
real_name
|
||||
elseif name_t == "table" and real_name[1] == "ref"
|
||||
real_name[2]
|
||||
else
|
||||
error "don't know how to extract name from #{name_t}"
|
||||
|
||||
{"string", '"', flattened_name}
|
||||
|
||||
cls = build.table {
|
||||
{"__init", super_scope constructor, cls_super, {"key_literal", "__init"}}
|
||||
{"__base", base_name}
|
||||
{"__name", real_name} -- "quote the string"
|
||||
parent_val and {"__parent", parent_cls_name} or nil
|
||||
}
|
||||
|
||||
-- looking up a name in the class object
|
||||
class_index = if parent_val
|
||||
class_lookup = build["if"] {
|
||||
cond: { "exp", {"ref", "val"}, "==", "nil" }
|
||||
then: {
|
||||
build.assign_one LocalName"parent", build.chain {
|
||||
base: "rawget"
|
||||
{
|
||||
"call", {
|
||||
{"ref", "cls"}
|
||||
{"string", '"', "__parent"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build.if {
|
||||
cond: LocalName "parent"
|
||||
then: {
|
||||
build.chain {
|
||||
base: LocalName "parent"
|
||||
{"index", "name"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
insert class_lookup, {"else", {"val"}}
|
||||
|
||||
build.fndef {
|
||||
args: {{"cls"}, {"name"}}
|
||||
body: {
|
||||
build.assign_one LocalName"val", build.chain {
|
||||
base: "rawget", {"call", {base_name, {"ref", "name"}}}
|
||||
}
|
||||
class_lookup
|
||||
}
|
||||
}
|
||||
else
|
||||
base_name
|
||||
|
||||
cls_mt = build.table {
|
||||
{"__index", class_index}
|
||||
{"__call", build.fndef {
|
||||
args: {{"cls"}, {"..."}}
|
||||
body: {
|
||||
build.assign_one self_name, build.chain {
|
||||
base: "setmetatable"
|
||||
{"call", {"{}", base_name}}
|
||||
}
|
||||
build.chain {
|
||||
base: "cls.__init"
|
||||
{"call", {self_name, "..."}}
|
||||
}
|
||||
self_name
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
cls = build.chain {
|
||||
base: "setmetatable"
|
||||
{"call", {cls, cls_mt}}
|
||||
}
|
||||
|
||||
value = nil
|
||||
with build
|
||||
out_body = {
|
||||
Run =>
|
||||
-- make sure we don't assign the class to a local inside the do
|
||||
@put_name name if name
|
||||
|
||||
{"declare", { cls_name }}
|
||||
{"declare_glob", "*"}
|
||||
|
||||
parent_val and .assign_one(parent_cls_name, parent_val) or NOOP
|
||||
|
||||
.assign_one base_name, {"table", properties}
|
||||
.assign_one base_name\chain"__index", base_name
|
||||
|
||||
parent_val and .chain({
|
||||
base: "setmetatable"
|
||||
{"call", {
|
||||
base_name,
|
||||
.chain { base: parent_cls_name, {"dot", "__base"}}
|
||||
}}
|
||||
}) or NOOP
|
||||
|
||||
.assign_one cls_name, cls
|
||||
.assign_one base_name\chain"__class", cls_name
|
||||
|
||||
.group if #statements > 0 then {
|
||||
.assign_one LocalName"self", cls_name
|
||||
.group statements
|
||||
}
|
||||
|
||||
-- run the inherited callback
|
||||
parent_val and .if({
|
||||
cond: {"exp", parent_cls_name\chain "__inherited" }
|
||||
then: {
|
||||
parent_cls_name\chain "__inherited", {"call", {
|
||||
parent_cls_name, cls_name
|
||||
}}
|
||||
}
|
||||
}) or NOOP
|
||||
|
||||
.group if name then {
|
||||
.assign_one name, cls_name
|
||||
}
|
||||
|
||||
if ret
|
||||
ret cls_name
|
||||
}
|
||||
|
||||
value = .group {
|
||||
.group if ntype(name) == "value" then {
|
||||
.declare names: {name}
|
||||
}
|
||||
|
||||
.do out_body
|
||||
}
|
||||
|
||||
value
|
@ -0,0 +1,54 @@
|
||||
local is_value
|
||||
is_value = require("moonscript.types").is_value
|
||||
local construct_comprehension
|
||||
construct_comprehension = function(inner, clauses)
|
||||
local current_stms = inner
|
||||
for i = #clauses, 1, -1 do
|
||||
local clause = clauses[i]
|
||||
local t = clause[1]
|
||||
local _exp_0 = t
|
||||
if "for" == _exp_0 then
|
||||
local _, name, bounds
|
||||
_, name, bounds = clause[1], clause[2], clause[3]
|
||||
current_stms = {
|
||||
"for",
|
||||
name,
|
||||
bounds,
|
||||
current_stms
|
||||
}
|
||||
elseif "foreach" == _exp_0 then
|
||||
local _, names, iter
|
||||
_, names, iter = clause[1], clause[2], clause[3]
|
||||
current_stms = {
|
||||
"foreach",
|
||||
names,
|
||||
{
|
||||
iter
|
||||
},
|
||||
current_stms
|
||||
}
|
||||
elseif "when" == _exp_0 then
|
||||
local _, cond
|
||||
_, cond = clause[1], clause[2]
|
||||
current_stms = {
|
||||
"if",
|
||||
cond,
|
||||
current_stms
|
||||
}
|
||||
else
|
||||
current_stms = error("Unknown comprehension clause: " .. t)
|
||||
end
|
||||
current_stms = {
|
||||
current_stms
|
||||
}
|
||||
end
|
||||
return current_stms[1]
|
||||
end
|
||||
local comprehension_has_value
|
||||
comprehension_has_value = function(comp)
|
||||
return is_value(comp[2])
|
||||
end
|
||||
return {
|
||||
construct_comprehension = construct_comprehension,
|
||||
comprehension_has_value = comprehension_has_value
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
|
||||
import is_value from require "moonscript.types"
|
||||
|
||||
construct_comprehension = (inner, clauses) ->
|
||||
current_stms = inner
|
||||
for i=#clauses,1,-1
|
||||
clause = clauses[i]
|
||||
t = clause[1]
|
||||
|
||||
current_stms = switch t
|
||||
when "for"
|
||||
{_, name, bounds} = clause
|
||||
{"for", name, bounds, current_stms}
|
||||
when "foreach"
|
||||
{_, names, iter} = clause
|
||||
{"foreach", names, {iter}, current_stms}
|
||||
when "when"
|
||||
{_, cond} = clause
|
||||
{"if", cond, current_stms}
|
||||
else
|
||||
error "Unknown comprehension clause: "..t
|
||||
|
||||
current_stms = {current_stms}
|
||||
|
||||
current_stms[1]
|
||||
|
||||
comprehension_has_value = (comp) ->
|
||||
is_value comp[2]
|
||||
|
||||
{:construct_comprehension, :comprehension_has_value}
|
233
prototypes/scp-clicker/moonscript/transform/destructure.lua
Normal file
233
prototypes/scp-clicker/moonscript/transform/destructure.lua
Normal file
@ -0,0 +1,233 @@
|
||||
local ntype, mtype, build
|
||||
do
|
||||
local _obj_0 = require("moonscript.types")
|
||||
ntype, mtype, build = _obj_0.ntype, _obj_0.mtype, _obj_0.build
|
||||
end
|
||||
local NameProxy
|
||||
NameProxy = require("moonscript.transform.names").NameProxy
|
||||
local insert
|
||||
insert = table.insert
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
local user_error
|
||||
user_error = require("moonscript.errors").user_error
|
||||
local join
|
||||
join = function(...)
|
||||
do
|
||||
local out = { }
|
||||
local i = 1
|
||||
local _list_0 = {
|
||||
...
|
||||
}
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local tbl = _list_0[_index_0]
|
||||
for _index_1 = 1, #tbl do
|
||||
local v = tbl[_index_1]
|
||||
out[i] = v
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
end
|
||||
local has_destructure
|
||||
has_destructure = function(names)
|
||||
for _index_0 = 1, #names do
|
||||
local n = names[_index_0]
|
||||
if ntype(n) == "table" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
local extract_assign_names
|
||||
extract_assign_names = function(name, accum, prefix)
|
||||
if accum == nil then
|
||||
accum = { }
|
||||
end
|
||||
if prefix == nil then
|
||||
prefix = { }
|
||||
end
|
||||
local i = 1
|
||||
local _list_0 = name[2]
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local tuple = _list_0[_index_0]
|
||||
local value, suffix
|
||||
if #tuple == 1 then
|
||||
local s = {
|
||||
"index",
|
||||
{
|
||||
"number",
|
||||
i
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
value, suffix = tuple[1], s
|
||||
else
|
||||
local key = tuple[1]
|
||||
local s
|
||||
if ntype(key) == "key_literal" then
|
||||
local key_name = key[2]
|
||||
if ntype(key_name) == "colon" then
|
||||
s = key_name
|
||||
else
|
||||
s = {
|
||||
"dot",
|
||||
key_name
|
||||
}
|
||||
end
|
||||
else
|
||||
s = {
|
||||
"index",
|
||||
key
|
||||
}
|
||||
end
|
||||
value, suffix = tuple[2], s
|
||||
end
|
||||
suffix = join(prefix, {
|
||||
suffix
|
||||
})
|
||||
local _exp_0 = ntype(value)
|
||||
if "value" == _exp_0 or "ref" == _exp_0 or "chain" == _exp_0 or "self" == _exp_0 then
|
||||
insert(accum, {
|
||||
value,
|
||||
suffix
|
||||
})
|
||||
elseif "table" == _exp_0 then
|
||||
extract_assign_names(value, accum, suffix)
|
||||
else
|
||||
user_error("Can't destructure value of type: " .. tostring(ntype(value)))
|
||||
end
|
||||
end
|
||||
return accum
|
||||
end
|
||||
local build_assign
|
||||
build_assign = function(scope, destruct_literal, receiver)
|
||||
local extracted_names = extract_assign_names(destruct_literal)
|
||||
local names = { }
|
||||
local values = { }
|
||||
local inner = {
|
||||
"assign",
|
||||
names,
|
||||
values
|
||||
}
|
||||
local obj
|
||||
if scope:is_local(receiver) or #extracted_names == 1 then
|
||||
obj = receiver
|
||||
else
|
||||
do
|
||||
obj = NameProxy("obj")
|
||||
inner = build["do"]({
|
||||
build.assign_one(obj, receiver),
|
||||
{
|
||||
"assign",
|
||||
names,
|
||||
values
|
||||
}
|
||||
})
|
||||
obj = obj
|
||||
end
|
||||
end
|
||||
for _index_0 = 1, #extracted_names do
|
||||
local tuple = extracted_names[_index_0]
|
||||
insert(names, tuple[1])
|
||||
local chain
|
||||
if obj then
|
||||
chain = NameProxy.chain(obj, unpack(tuple[2]))
|
||||
else
|
||||
chain = "nil"
|
||||
end
|
||||
insert(values, chain)
|
||||
end
|
||||
return build.group({
|
||||
{
|
||||
"declare",
|
||||
names
|
||||
},
|
||||
inner
|
||||
})
|
||||
end
|
||||
local split_assign
|
||||
split_assign = function(scope, assign)
|
||||
local names, values = unpack(assign, 2)
|
||||
local g = { }
|
||||
local total_names = #names
|
||||
local total_values = #values
|
||||
local start = 1
|
||||
for i, n in ipairs(names) do
|
||||
if ntype(n) == "table" then
|
||||
if i > start then
|
||||
local stop = i - 1
|
||||
insert(g, {
|
||||
"assign",
|
||||
(function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for i = start, stop do
|
||||
_accum_0[_len_0] = names[i]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(),
|
||||
(function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for i = start, stop do
|
||||
_accum_0[_len_0] = values[i]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)()
|
||||
})
|
||||
end
|
||||
insert(g, build_assign(scope, n, values[i]))
|
||||
start = i + 1
|
||||
end
|
||||
end
|
||||
if total_names >= start or total_values >= start then
|
||||
local name_slice
|
||||
if total_names < start then
|
||||
name_slice = {
|
||||
"_"
|
||||
}
|
||||
else
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for i = start, total_names do
|
||||
_accum_0[_len_0] = names[i]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
name_slice = _accum_0
|
||||
end
|
||||
end
|
||||
local value_slice
|
||||
if total_values < start then
|
||||
value_slice = {
|
||||
"nil"
|
||||
}
|
||||
else
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for i = start, total_values do
|
||||
_accum_0[_len_0] = values[i]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
value_slice = _accum_0
|
||||
end
|
||||
end
|
||||
insert(g, {
|
||||
"assign",
|
||||
name_slice,
|
||||
value_slice
|
||||
})
|
||||
end
|
||||
return build.group(g)
|
||||
end
|
||||
return {
|
||||
has_destructure = has_destructure,
|
||||
split_assign = split_assign,
|
||||
build_assign = build_assign,
|
||||
extract_assign_names = extract_assign_names
|
||||
}
|
128
prototypes/scp-clicker/moonscript/transform/destructure.moon
Normal file
128
prototypes/scp-clicker/moonscript/transform/destructure.moon
Normal file
@ -0,0 +1,128 @@
|
||||
|
||||
import ntype, mtype, build from require "moonscript.types"
|
||||
import NameProxy from require "moonscript.transform.names"
|
||||
import insert from table
|
||||
import unpack from require "moonscript.util"
|
||||
|
||||
import user_error from require "moonscript.errors"
|
||||
|
||||
join = (...) ->
|
||||
with out = {}
|
||||
i = 1
|
||||
for tbl in *{...}
|
||||
for v in *tbl
|
||||
out[i] = v
|
||||
i += 1
|
||||
|
||||
has_destructure = (names) ->
|
||||
for n in *names
|
||||
return true if ntype(n) == "table"
|
||||
false
|
||||
|
||||
extract_assign_names = (name, accum={}, prefix={}) ->
|
||||
|
||||
i = 1
|
||||
for tuple in *name[2]
|
||||
value, suffix = if #tuple == 1
|
||||
s = {"index", {"number", i}}
|
||||
i += 1
|
||||
tuple[1], s
|
||||
else
|
||||
key = tuple[1]
|
||||
|
||||
s = if ntype(key) == "key_literal"
|
||||
key_name = key[2]
|
||||
if ntype(key_name) == "colon"
|
||||
key_name
|
||||
else
|
||||
{"dot", key_name}
|
||||
else
|
||||
{"index", key}
|
||||
|
||||
tuple[2], s
|
||||
|
||||
suffix = join prefix, {suffix}
|
||||
|
||||
switch ntype value
|
||||
when "value", "ref", "chain", "self"
|
||||
insert accum, {value, suffix}
|
||||
when "table"
|
||||
extract_assign_names value, accum, suffix
|
||||
else
|
||||
user_error "Can't destructure value of type: #{ntype value}"
|
||||
|
||||
accum
|
||||
|
||||
build_assign = (scope, destruct_literal, receiver) ->
|
||||
extracted_names = extract_assign_names destruct_literal
|
||||
|
||||
names = {}
|
||||
values = {}
|
||||
|
||||
inner = {"assign", names, values}
|
||||
|
||||
obj = if scope\is_local(receiver) or #extracted_names == 1
|
||||
receiver
|
||||
else
|
||||
with obj = NameProxy "obj"
|
||||
inner = build.do {
|
||||
build.assign_one obj, receiver
|
||||
{"assign", names, values}
|
||||
}
|
||||
|
||||
for tuple in *extracted_names
|
||||
insert names, tuple[1]
|
||||
chain = if obj
|
||||
NameProxy.chain obj, unpack tuple[2]
|
||||
else
|
||||
"nil"
|
||||
insert values, chain
|
||||
|
||||
build.group {
|
||||
{"declare", names}
|
||||
inner
|
||||
}
|
||||
|
||||
-- applies to destructuring to a assign node
|
||||
split_assign = (scope, assign) ->
|
||||
names, values = unpack assign, 2
|
||||
|
||||
g = {}
|
||||
total_names = #names
|
||||
total_values = #values
|
||||
|
||||
-- We have to break apart the assign into groups of regular
|
||||
-- assigns, and then the destructuring assignments
|
||||
start = 1
|
||||
for i, n in ipairs names
|
||||
if ntype(n) == "table"
|
||||
if i > start
|
||||
stop = i - 1
|
||||
insert g, {
|
||||
"assign"
|
||||
for i=start,stop
|
||||
names[i]
|
||||
for i=start,stop
|
||||
values[i]
|
||||
}
|
||||
|
||||
insert g, build_assign scope, n, values[i]
|
||||
|
||||
start = i + 1
|
||||
|
||||
if total_names >= start or total_values >= start
|
||||
name_slice = if total_names < start
|
||||
{"_"}
|
||||
else
|
||||
for i=start,total_names do names[i]
|
||||
|
||||
value_slice = if total_values < start
|
||||
{"nil"}
|
||||
else
|
||||
for i=start,total_values do values[i]
|
||||
|
||||
insert g, {"assign", name_slice, value_slice}
|
||||
|
||||
build.group g
|
||||
|
||||
{ :has_destructure, :split_assign, :build_assign, :extract_assign_names }
|
118
prototypes/scp-clicker/moonscript/transform/names.lua
Normal file
118
prototypes/scp-clicker/moonscript/transform/names.lua
Normal file
@ -0,0 +1,118 @@
|
||||
local build
|
||||
build = require("moonscript.types").build
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
local LocalName
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
get_name = function(self)
|
||||
return self.name
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, name)
|
||||
self.name = name
|
||||
self[1] = "temp_name"
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "LocalName"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
LocalName = _class_0
|
||||
end
|
||||
local NameProxy
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
get_name = function(self, scope, dont_put)
|
||||
if dont_put == nil then
|
||||
dont_put = true
|
||||
end
|
||||
if not self.name then
|
||||
self.name = scope:free_name(self.prefix, dont_put)
|
||||
end
|
||||
return self.name
|
||||
end,
|
||||
chain = function(self, ...)
|
||||
local items = {
|
||||
base = self,
|
||||
...
|
||||
}
|
||||
for k, v in ipairs(items) do
|
||||
if type(v) == "string" then
|
||||
items[k] = {
|
||||
"dot",
|
||||
v
|
||||
}
|
||||
else
|
||||
items[k] = v
|
||||
end
|
||||
end
|
||||
return build.chain(items)
|
||||
end,
|
||||
index = function(self, key)
|
||||
if type(key) == "string" then
|
||||
key = {
|
||||
"ref",
|
||||
key
|
||||
}
|
||||
end
|
||||
return build.chain({
|
||||
base = self,
|
||||
{
|
||||
"index",
|
||||
key
|
||||
}
|
||||
})
|
||||
end,
|
||||
__tostring = function(self)
|
||||
if self.name then
|
||||
return ("name<%s>"):format(self.name)
|
||||
else
|
||||
return ("name<prefix(%s)>"):format(self.prefix)
|
||||
end
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, prefix)
|
||||
self.prefix = prefix
|
||||
self[1] = "temp_name"
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "NameProxy"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
NameProxy = _class_0
|
||||
end
|
||||
local is_name_proxy
|
||||
is_name_proxy = function(v)
|
||||
if not (type(v) == "table") then
|
||||
return false
|
||||
end
|
||||
local _exp_0 = v.__class
|
||||
if LocalName == _exp_0 or NameProxy == _exp_0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return {
|
||||
NameProxy = NameProxy,
|
||||
LocalName = LocalName,
|
||||
is_name_proxy = is_name_proxy
|
||||
}
|
51
prototypes/scp-clicker/moonscript/transform/names.moon
Normal file
51
prototypes/scp-clicker/moonscript/transform/names.moon
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
import build from require "moonscript.types"
|
||||
import unpack from require "moonscript.util"
|
||||
|
||||
-- always declares as local
|
||||
class LocalName
|
||||
new: (@name) => self[1] = "temp_name"
|
||||
get_name: => @name
|
||||
|
||||
-- creates a unique name when used
|
||||
class NameProxy
|
||||
new: (@prefix) =>
|
||||
self[1] = "temp_name"
|
||||
|
||||
get_name: (scope, dont_put=true) =>
|
||||
if not @name
|
||||
@name = scope\free_name @prefix, dont_put
|
||||
@name
|
||||
|
||||
chain: (...) =>
|
||||
items = { base: @, ... }
|
||||
for k,v in ipairs items
|
||||
items[k] = if type(v) == "string"
|
||||
{"dot", v}
|
||||
else
|
||||
v
|
||||
|
||||
build.chain items
|
||||
|
||||
index: (key) =>
|
||||
if type(key) == "string"
|
||||
key = {"ref", key}
|
||||
|
||||
build.chain {
|
||||
base: self, {"index", key}
|
||||
}
|
||||
|
||||
__tostring: =>
|
||||
if @name
|
||||
("name<%s>")\format @name
|
||||
else
|
||||
("name<prefix(%s)>")\format @prefix
|
||||
|
||||
is_name_proxy = (v) ->
|
||||
return false unless type(v) == "table"
|
||||
|
||||
switch v.__class
|
||||
when LocalName, NameProxy
|
||||
true
|
||||
|
||||
{ :NameProxy, :LocalName, :is_name_proxy }
|
847
prototypes/scp-clicker/moonscript/transform/statement.lua
Normal file
847
prototypes/scp-clicker/moonscript/transform/statement.lua
Normal file
@ -0,0 +1,847 @@
|
||||
local Transformer
|
||||
Transformer = require("moonscript.transform.transformer").Transformer
|
||||
local NameProxy, LocalName, is_name_proxy
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.names")
|
||||
NameProxy, LocalName, is_name_proxy = _obj_0.NameProxy, _obj_0.LocalName, _obj_0.is_name_proxy
|
||||
end
|
||||
local Run, transform_last_stm, implicitly_return, last_stm
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.statements")
|
||||
Run, transform_last_stm, implicitly_return, last_stm = _obj_0.Run, _obj_0.transform_last_stm, _obj_0.implicitly_return, _obj_0.last_stm
|
||||
end
|
||||
local types = require("moonscript.types")
|
||||
local build, ntype, is_value, smart_node, value_is_singular, is_slice, NOOP
|
||||
build, ntype, is_value, smart_node, value_is_singular, is_slice, NOOP = types.build, types.ntype, types.is_value, types.smart_node, types.value_is_singular, types.is_slice, types.NOOP
|
||||
local insert
|
||||
insert = table.insert
|
||||
local destructure = require("moonscript.transform.destructure")
|
||||
local construct_comprehension
|
||||
construct_comprehension = require("moonscript.transform.comprehension").construct_comprehension
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
local with_continue_listener
|
||||
with_continue_listener = function(body)
|
||||
local continue_name = nil
|
||||
return {
|
||||
Run(function(self)
|
||||
return self:listen("continue", function()
|
||||
if not (continue_name) then
|
||||
continue_name = NameProxy("continue")
|
||||
self:put_name(continue_name)
|
||||
end
|
||||
return continue_name
|
||||
end)
|
||||
end),
|
||||
build.group(body),
|
||||
Run(function(self)
|
||||
if not (continue_name) then
|
||||
return
|
||||
end
|
||||
local last = last_stm(body)
|
||||
local enclose_lines = types.terminating[last and ntype(last)]
|
||||
self:put_name(continue_name, nil)
|
||||
return self:splice(function(lines)
|
||||
if enclose_lines then
|
||||
lines = {
|
||||
"do",
|
||||
{
|
||||
lines
|
||||
}
|
||||
}
|
||||
end
|
||||
return {
|
||||
{
|
||||
"assign",
|
||||
{
|
||||
continue_name
|
||||
},
|
||||
{
|
||||
"false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"repeat",
|
||||
"true",
|
||||
{
|
||||
lines,
|
||||
{
|
||||
"assign",
|
||||
{
|
||||
continue_name
|
||||
},
|
||||
{
|
||||
"true"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if",
|
||||
{
|
||||
"not",
|
||||
continue_name
|
||||
},
|
||||
{
|
||||
{
|
||||
"break"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end)
|
||||
end)
|
||||
}
|
||||
end
|
||||
local extract_declarations
|
||||
extract_declarations = function(self, body, start, out)
|
||||
if body == nil then
|
||||
body = self.current_stms
|
||||
end
|
||||
if start == nil then
|
||||
start = self.current_stm_i + 1
|
||||
end
|
||||
if out == nil then
|
||||
out = { }
|
||||
end
|
||||
for i = start, #body do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local stm = body[i]
|
||||
if stm == nil then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
stm = self.transform.statement(stm)
|
||||
body[i] = stm
|
||||
local _exp_0 = stm[1]
|
||||
if "assign" == _exp_0 or "declare" == _exp_0 then
|
||||
local _list_0 = stm[2]
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local name = _list_0[_index_0]
|
||||
if ntype(name) == "ref" then
|
||||
insert(out, name)
|
||||
elseif type(name) == "string" then
|
||||
insert(out, name)
|
||||
end
|
||||
end
|
||||
elseif "group" == _exp_0 then
|
||||
extract_declarations(self, stm[2], 1, out)
|
||||
end
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
local expand_elseif_assign
|
||||
expand_elseif_assign = function(ifstm)
|
||||
for i = 4, #ifstm do
|
||||
local case = ifstm[i]
|
||||
if ntype(case) == "elseif" and ntype(case[2]) == "assign" then
|
||||
local split = {
|
||||
unpack(ifstm, 1, i - 1)
|
||||
}
|
||||
insert(split, {
|
||||
"else",
|
||||
{
|
||||
{
|
||||
"if",
|
||||
case[2],
|
||||
case[3],
|
||||
unpack(ifstm, i + 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
return split
|
||||
end
|
||||
end
|
||||
return ifstm
|
||||
end
|
||||
return Transformer({
|
||||
transform = function(self, tuple)
|
||||
local _, node, fn
|
||||
_, node, fn = tuple[1], tuple[2], tuple[3]
|
||||
return fn(node)
|
||||
end,
|
||||
root_stms = function(self, body)
|
||||
return transform_last_stm(body, implicitly_return(self))
|
||||
end,
|
||||
["return"] = function(self, node)
|
||||
local ret_val = node[2]
|
||||
local ret_val_type = ntype(ret_val)
|
||||
if ret_val_type == "explist" and #ret_val == 2 then
|
||||
ret_val = ret_val[2]
|
||||
ret_val_type = ntype(ret_val)
|
||||
end
|
||||
if types.cascading[ret_val_type] then
|
||||
return implicitly_return(self)(ret_val)
|
||||
end
|
||||
if ret_val_type == "chain" or ret_val_type == "comprehension" or ret_val_type == "tblcomprehension" then
|
||||
local Value = require("moonscript.transform.value")
|
||||
ret_val = Value:transform_once(self, ret_val)
|
||||
if ntype(ret_val) == "block_exp" then
|
||||
return build.group(transform_last_stm(ret_val[2], function(stm)
|
||||
return {
|
||||
"return",
|
||||
stm
|
||||
}
|
||||
end))
|
||||
end
|
||||
end
|
||||
node[2] = ret_val
|
||||
return node
|
||||
end,
|
||||
declare_glob = function(self, node)
|
||||
local names = extract_declarations(self)
|
||||
if node[2] == "^" then
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #names do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
local name = names[_index_0]
|
||||
local str_name
|
||||
if ntype(name) == "ref" then
|
||||
str_name = name[2]
|
||||
else
|
||||
str_name = name
|
||||
end
|
||||
if not (str_name:match("^%u")) then
|
||||
_continue_0 = true
|
||||
break
|
||||
end
|
||||
local _value_0 = name
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
_continue_0 = true
|
||||
until true
|
||||
if not _continue_0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
names = _accum_0
|
||||
end
|
||||
end
|
||||
return {
|
||||
"declare",
|
||||
names
|
||||
}
|
||||
end,
|
||||
assign = function(self, node)
|
||||
local names, values = unpack(node, 2)
|
||||
local num_values = #values
|
||||
local num_names = #values
|
||||
if num_names == 1 and num_values == 1 then
|
||||
local first_value = values[1]
|
||||
local first_name = names[1]
|
||||
local first_type = ntype(first_value)
|
||||
if first_type == "chain" then
|
||||
local Value = require("moonscript.transform.value")
|
||||
first_value = Value:transform_once(self, first_value)
|
||||
first_type = ntype(first_value)
|
||||
end
|
||||
local _exp_0 = ntype(first_value)
|
||||
if "block_exp" == _exp_0 then
|
||||
local block_body = first_value[2]
|
||||
local idx = #block_body
|
||||
block_body[idx] = build.assign_one(first_name, block_body[idx])
|
||||
return build.group({
|
||||
{
|
||||
"declare",
|
||||
{
|
||||
first_name
|
||||
}
|
||||
},
|
||||
{
|
||||
"do",
|
||||
block_body
|
||||
}
|
||||
})
|
||||
elseif "comprehension" == _exp_0 or "tblcomprehension" == _exp_0 or "foreach" == _exp_0 or "for" == _exp_0 or "while" == _exp_0 then
|
||||
local Value = require("moonscript.transform.value")
|
||||
return build.assign_one(first_name, Value:transform_once(self, first_value))
|
||||
else
|
||||
values[1] = first_value
|
||||
end
|
||||
end
|
||||
local transformed
|
||||
if num_values == 1 then
|
||||
local value = values[1]
|
||||
local t = ntype(value)
|
||||
if t == "decorated" then
|
||||
value = self.transform.statement(value)
|
||||
t = ntype(value)
|
||||
end
|
||||
if types.cascading[t] then
|
||||
local ret
|
||||
ret = function(stm)
|
||||
if is_value(stm) then
|
||||
return {
|
||||
"assign",
|
||||
names,
|
||||
{
|
||||
stm
|
||||
}
|
||||
}
|
||||
else
|
||||
return stm
|
||||
end
|
||||
end
|
||||
transformed = build.group({
|
||||
{
|
||||
"declare",
|
||||
names
|
||||
},
|
||||
self.transform.statement(value, ret, node)
|
||||
})
|
||||
end
|
||||
end
|
||||
node = transformed or node
|
||||
if destructure.has_destructure(names) then
|
||||
return destructure.split_assign(self, node)
|
||||
end
|
||||
return node
|
||||
end,
|
||||
continue = function(self, node)
|
||||
local continue_name = self:send("continue")
|
||||
if not (continue_name) then
|
||||
error("continue must be inside of a loop")
|
||||
end
|
||||
return build.group({
|
||||
build.assign_one(continue_name, "true"),
|
||||
{
|
||||
"break"
|
||||
}
|
||||
})
|
||||
end,
|
||||
export = function(self, node)
|
||||
if #node > 2 then
|
||||
if node[2] == "class" then
|
||||
local cls = smart_node(node[3])
|
||||
return build.group({
|
||||
{
|
||||
"export",
|
||||
{
|
||||
cls.name
|
||||
}
|
||||
},
|
||||
cls
|
||||
})
|
||||
else
|
||||
return build.group({
|
||||
{
|
||||
"export",
|
||||
node[2]
|
||||
},
|
||||
build.assign({
|
||||
names = node[2],
|
||||
values = node[3]
|
||||
})
|
||||
})
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
update = function(self, node)
|
||||
local name, op, exp = unpack(node, 2)
|
||||
local op_final = op:match("^(.+)=$")
|
||||
if not op_final then
|
||||
error("Unknown op: " .. op)
|
||||
end
|
||||
local lifted
|
||||
if ntype(name) == "chain" then
|
||||
lifted = { }
|
||||
local new_chain
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 3, #name do
|
||||
local part = name[_index_0]
|
||||
if ntype(part) == "index" then
|
||||
local proxy = NameProxy("update")
|
||||
table.insert(lifted, {
|
||||
proxy,
|
||||
part[2]
|
||||
})
|
||||
_accum_0[_len_0] = {
|
||||
"index",
|
||||
proxy
|
||||
}
|
||||
else
|
||||
_accum_0[_len_0] = part
|
||||
end
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
new_chain = _accum_0
|
||||
end
|
||||
if next(lifted) then
|
||||
name = {
|
||||
name[1],
|
||||
name[2],
|
||||
unpack(new_chain)
|
||||
}
|
||||
end
|
||||
end
|
||||
if not (value_is_singular(exp)) then
|
||||
exp = {
|
||||
"parens",
|
||||
exp
|
||||
}
|
||||
end
|
||||
local out = build.assign_one(name, {
|
||||
"exp",
|
||||
name,
|
||||
op_final,
|
||||
exp
|
||||
})
|
||||
if lifted and next(lifted) then
|
||||
local names
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #lifted do
|
||||
local l = lifted[_index_0]
|
||||
_accum_0[_len_0] = l[1]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
names = _accum_0
|
||||
end
|
||||
local values
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #lifted do
|
||||
local l = lifted[_index_0]
|
||||
_accum_0[_len_0] = l[2]
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
values = _accum_0
|
||||
end
|
||||
out = build.group({
|
||||
{
|
||||
"assign",
|
||||
names,
|
||||
values
|
||||
},
|
||||
out
|
||||
})
|
||||
end
|
||||
return out
|
||||
end,
|
||||
import = function(self, node)
|
||||
local names, source = unpack(node, 2)
|
||||
local table_values
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #names do
|
||||
local name = names[_index_0]
|
||||
local dest_name
|
||||
if ntype(name) == "colon" then
|
||||
dest_name = name[2]
|
||||
else
|
||||
dest_name = name
|
||||
end
|
||||
local _value_0 = {
|
||||
{
|
||||
"key_literal",
|
||||
name
|
||||
},
|
||||
dest_name
|
||||
}
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
table_values = _accum_0
|
||||
end
|
||||
local dest = {
|
||||
"table",
|
||||
table_values
|
||||
}
|
||||
return {
|
||||
"assign",
|
||||
{
|
||||
dest
|
||||
},
|
||||
{
|
||||
source
|
||||
},
|
||||
[-1] = node[-1]
|
||||
}
|
||||
end,
|
||||
comprehension = function(self, node, action)
|
||||
local exp, clauses = unpack(node, 2)
|
||||
action = action or function(exp)
|
||||
return {
|
||||
exp
|
||||
}
|
||||
end
|
||||
return construct_comprehension(action(exp), clauses)
|
||||
end,
|
||||
["do"] = function(self, node, ret)
|
||||
if ret then
|
||||
node[2] = transform_last_stm(node[2], ret)
|
||||
end
|
||||
return node
|
||||
end,
|
||||
decorated = function(self, node)
|
||||
local stm, dec = unpack(node, 2)
|
||||
local wrapped
|
||||
local _exp_0 = dec[1]
|
||||
if "if" == _exp_0 then
|
||||
local cond, fail = unpack(dec, 2)
|
||||
if fail then
|
||||
fail = {
|
||||
"else",
|
||||
{
|
||||
fail
|
||||
}
|
||||
}
|
||||
end
|
||||
wrapped = {
|
||||
"if",
|
||||
cond,
|
||||
{
|
||||
stm
|
||||
},
|
||||
fail
|
||||
}
|
||||
elseif "unless" == _exp_0 then
|
||||
wrapped = {
|
||||
"unless",
|
||||
dec[2],
|
||||
{
|
||||
stm
|
||||
}
|
||||
}
|
||||
elseif "comprehension" == _exp_0 then
|
||||
wrapped = {
|
||||
"comprehension",
|
||||
stm,
|
||||
dec[2]
|
||||
}
|
||||
else
|
||||
wrapped = error("Unknown decorator " .. dec[1])
|
||||
end
|
||||
if ntype(stm) == "assign" then
|
||||
wrapped = build.group({
|
||||
build.declare({
|
||||
names = (function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = stm[2]
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local name = _list_0[_index_0]
|
||||
if ntype(name) == "ref" then
|
||||
_accum_0[_len_0] = name
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
end
|
||||
return _accum_0
|
||||
end)()
|
||||
}),
|
||||
wrapped
|
||||
})
|
||||
end
|
||||
return wrapped
|
||||
end,
|
||||
unless = function(self, node)
|
||||
local clause = node[2]
|
||||
if ntype(clause) == "assign" then
|
||||
if destructure.has_destructure(clause[2]) then
|
||||
error("destructure not allowed in unless assignment")
|
||||
end
|
||||
return build["do"]({
|
||||
clause,
|
||||
{
|
||||
"if",
|
||||
{
|
||||
"not",
|
||||
clause[2][1]
|
||||
},
|
||||
unpack(node, 3)
|
||||
}
|
||||
})
|
||||
else
|
||||
return {
|
||||
"if",
|
||||
{
|
||||
"not",
|
||||
{
|
||||
"parens",
|
||||
clause
|
||||
}
|
||||
},
|
||||
unpack(node, 3)
|
||||
}
|
||||
end
|
||||
end,
|
||||
["if"] = function(self, node, ret)
|
||||
if ntype(node[2]) == "assign" then
|
||||
local assign, body = unpack(node, 2)
|
||||
if destructure.has_destructure(assign[2]) then
|
||||
local name = NameProxy("des")
|
||||
body = {
|
||||
destructure.build_assign(self, assign[2][1], name),
|
||||
build.group(node[3])
|
||||
}
|
||||
return build["do"]({
|
||||
build.assign_one(name, assign[3][1]),
|
||||
{
|
||||
"if",
|
||||
name,
|
||||
body,
|
||||
unpack(node, 4)
|
||||
}
|
||||
})
|
||||
else
|
||||
local name = assign[2][1]
|
||||
return build["do"]({
|
||||
assign,
|
||||
{
|
||||
"if",
|
||||
name,
|
||||
unpack(node, 3)
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
node = expand_elseif_assign(node)
|
||||
if ret then
|
||||
smart_node(node)
|
||||
node['then'] = transform_last_stm(node['then'], ret)
|
||||
for i = 4, #node do
|
||||
local case = node[i]
|
||||
local body_idx = #node[i]
|
||||
case[body_idx] = transform_last_stm(case[body_idx], ret)
|
||||
end
|
||||
end
|
||||
return node
|
||||
end,
|
||||
with = function(self, node, ret)
|
||||
local exp, block = unpack(node, 2)
|
||||
local copy_scope = true
|
||||
local scope_name, named_assign
|
||||
do
|
||||
local last = last_stm(block)
|
||||
if last then
|
||||
if types.terminating[ntype(last)] then
|
||||
ret = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if ntype(exp) == "assign" then
|
||||
local names, values = unpack(exp, 2)
|
||||
local first_name = names[1]
|
||||
if ntype(first_name) == "ref" then
|
||||
scope_name = first_name
|
||||
named_assign = exp
|
||||
exp = values[1]
|
||||
copy_scope = false
|
||||
else
|
||||
scope_name = NameProxy("with")
|
||||
exp = values[1]
|
||||
values[1] = scope_name
|
||||
named_assign = {
|
||||
"assign",
|
||||
names,
|
||||
values
|
||||
}
|
||||
end
|
||||
elseif self:is_local(exp) then
|
||||
scope_name = exp
|
||||
copy_scope = false
|
||||
end
|
||||
scope_name = scope_name or NameProxy("with")
|
||||
local out = build["do"]({
|
||||
copy_scope and build.assign_one(scope_name, exp) or NOOP,
|
||||
named_assign or NOOP,
|
||||
Run(function(self)
|
||||
return self:set("scope_var", scope_name)
|
||||
end),
|
||||
unpack(block)
|
||||
})
|
||||
if ret then
|
||||
table.insert(out[2], ret(scope_name))
|
||||
end
|
||||
return out
|
||||
end,
|
||||
foreach = function(self, node, _)
|
||||
smart_node(node)
|
||||
local source = unpack(node.iter)
|
||||
local destructures = { }
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for i, name in ipairs(node.names) do
|
||||
if ntype(name) == "table" then
|
||||
do
|
||||
local proxy = NameProxy("des")
|
||||
insert(destructures, destructure.build_assign(self, name, proxy))
|
||||
_accum_0[_len_0] = proxy
|
||||
end
|
||||
else
|
||||
_accum_0[_len_0] = name
|
||||
end
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
node.names = _accum_0
|
||||
end
|
||||
if next(destructures) then
|
||||
insert(destructures, build.group(node.body))
|
||||
node.body = destructures
|
||||
end
|
||||
if ntype(source) == "unpack" then
|
||||
local list = source[2]
|
||||
local index_name = NameProxy("index")
|
||||
local list_name = self:is_local(list) and list or NameProxy("list")
|
||||
local slice_var = nil
|
||||
local bounds
|
||||
if is_slice(list) then
|
||||
local slice = list[#list]
|
||||
table.remove(list)
|
||||
table.remove(slice, 1)
|
||||
if self:is_local(list) then
|
||||
list_name = list
|
||||
end
|
||||
if slice[2] and slice[2] ~= "" then
|
||||
local max_tmp_name = NameProxy("max")
|
||||
slice_var = build.assign_one(max_tmp_name, slice[2])
|
||||
slice[2] = {
|
||||
"exp",
|
||||
max_tmp_name,
|
||||
"<",
|
||||
0,
|
||||
"and",
|
||||
{
|
||||
"length",
|
||||
list_name
|
||||
},
|
||||
"+",
|
||||
max_tmp_name,
|
||||
"or",
|
||||
max_tmp_name
|
||||
}
|
||||
else
|
||||
slice[2] = {
|
||||
"length",
|
||||
list_name
|
||||
}
|
||||
end
|
||||
bounds = slice
|
||||
else
|
||||
bounds = {
|
||||
1,
|
||||
{
|
||||
"length",
|
||||
list_name
|
||||
}
|
||||
}
|
||||
end
|
||||
local names
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = node.names
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local n = _list_0[_index_0]
|
||||
_accum_0[_len_0] = is_name_proxy(n) and n or LocalName(n) or n
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
names = _accum_0
|
||||
end
|
||||
return build.group({
|
||||
list_name ~= list and build.assign_one(list_name, list) or NOOP,
|
||||
slice_var or NOOP,
|
||||
build["for"]({
|
||||
name = index_name,
|
||||
bounds = bounds,
|
||||
body = {
|
||||
{
|
||||
"assign",
|
||||
names,
|
||||
{
|
||||
NameProxy.index(list_name, index_name)
|
||||
}
|
||||
},
|
||||
build.group(node.body)
|
||||
}
|
||||
})
|
||||
})
|
||||
end
|
||||
node.body = with_continue_listener(node.body)
|
||||
end,
|
||||
["while"] = function(self, node)
|
||||
smart_node(node)
|
||||
node.body = with_continue_listener(node.body)
|
||||
end,
|
||||
["for"] = function(self, node)
|
||||
smart_node(node)
|
||||
node.body = with_continue_listener(node.body)
|
||||
end,
|
||||
switch = function(self, node, ret)
|
||||
local exp, conds = unpack(node, 2)
|
||||
local exp_name = NameProxy("exp")
|
||||
local convert_cond
|
||||
convert_cond = function(cond)
|
||||
local t, case_exps, body = unpack(cond)
|
||||
local out = { }
|
||||
insert(out, t == "case" and "elseif" or "else")
|
||||
if t ~= "else" then
|
||||
local cond_exp = { }
|
||||
for i, case in ipairs(case_exps) do
|
||||
if i == 1 then
|
||||
insert(cond_exp, "exp")
|
||||
else
|
||||
insert(cond_exp, "or")
|
||||
end
|
||||
if not (value_is_singular(case)) then
|
||||
case = {
|
||||
"parens",
|
||||
case
|
||||
}
|
||||
end
|
||||
insert(cond_exp, {
|
||||
"exp",
|
||||
case,
|
||||
"==",
|
||||
exp_name
|
||||
})
|
||||
end
|
||||
insert(out, cond_exp)
|
||||
else
|
||||
body = case_exps
|
||||
end
|
||||
if ret then
|
||||
body = transform_last_stm(body, ret)
|
||||
end
|
||||
insert(out, body)
|
||||
return out
|
||||
end
|
||||
local first = true
|
||||
local if_stm = {
|
||||
"if"
|
||||
}
|
||||
for _index_0 = 1, #conds do
|
||||
local cond = conds[_index_0]
|
||||
local if_cond = convert_cond(cond)
|
||||
if first then
|
||||
first = false
|
||||
insert(if_stm, if_cond[2])
|
||||
insert(if_stm, if_cond[3])
|
||||
else
|
||||
insert(if_stm, if_cond)
|
||||
end
|
||||
end
|
||||
return build.group({
|
||||
build.assign_one(exp_name, exp),
|
||||
if_stm
|
||||
})
|
||||
end,
|
||||
class = require("moonscript.transform.class")
|
||||
})
|
522
prototypes/scp-clicker/moonscript/transform/statement.moon
Normal file
522
prototypes/scp-clicker/moonscript/transform/statement.moon
Normal file
@ -0,0 +1,522 @@
|
||||
import Transformer from require "moonscript.transform.transformer"
|
||||
|
||||
import NameProxy, LocalName, is_name_proxy from require "moonscript.transform.names"
|
||||
|
||||
import Run, transform_last_stm, implicitly_return, last_stm
|
||||
from require "moonscript.transform.statements"
|
||||
|
||||
types = require "moonscript.types"
|
||||
|
||||
import build, ntype, is_value, smart_node, value_is_singular, is_slice, NOOP
|
||||
from types
|
||||
|
||||
import insert from table
|
||||
|
||||
destructure = require "moonscript.transform.destructure"
|
||||
import construct_comprehension from require "moonscript.transform.comprehension"
|
||||
|
||||
import unpack from require "moonscript.util"
|
||||
|
||||
with_continue_listener = (body) ->
|
||||
continue_name = nil
|
||||
|
||||
{
|
||||
Run =>
|
||||
@listen "continue", ->
|
||||
unless continue_name
|
||||
continue_name = NameProxy"continue"
|
||||
@put_name continue_name
|
||||
continue_name
|
||||
|
||||
build.group body
|
||||
|
||||
Run =>
|
||||
return unless continue_name
|
||||
last = last_stm body
|
||||
enclose_lines = types.terminating[last and ntype(last)]
|
||||
|
||||
@put_name continue_name, nil
|
||||
@splice (lines) ->
|
||||
lines = {"do", {lines}} if enclose_lines
|
||||
|
||||
{
|
||||
{"assign", {continue_name}, {"false"}}
|
||||
{"repeat", "true", {
|
||||
lines
|
||||
{"assign", {continue_name}, {"true"}}
|
||||
}}
|
||||
{"if", {"not", continue_name}, {
|
||||
{"break"}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- this mutates body searching for assigns
|
||||
extract_declarations = (body=@current_stms, start=@current_stm_i + 1, out={}) =>
|
||||
for i=start,#body
|
||||
stm = body[i]
|
||||
continue if stm == nil
|
||||
stm = @transform.statement stm
|
||||
body[i] = stm
|
||||
switch stm[1]
|
||||
when "assign", "declare"
|
||||
for name in *stm[2]
|
||||
if ntype(name) == "ref"
|
||||
insert out, name
|
||||
elseif type(name) == "string"
|
||||
-- TODO: don't use string literal as ref
|
||||
insert out, name
|
||||
when "group"
|
||||
extract_declarations @, stm[2], 1, out
|
||||
out
|
||||
|
||||
expand_elseif_assign = (ifstm) ->
|
||||
for i = 4, #ifstm
|
||||
case = ifstm[i]
|
||||
if ntype(case) == "elseif" and ntype(case[2]) == "assign"
|
||||
split = { unpack ifstm, 1, i - 1 }
|
||||
insert split, {
|
||||
"else", {
|
||||
{"if", case[2], case[3], unpack ifstm, i + 1}
|
||||
}
|
||||
}
|
||||
return split
|
||||
|
||||
ifstm
|
||||
|
||||
|
||||
Transformer {
|
||||
transform: (tuple) =>
|
||||
{_, node, fn} = tuple
|
||||
fn node
|
||||
|
||||
root_stms: (body) =>
|
||||
transform_last_stm body, implicitly_return @
|
||||
|
||||
return: (node) =>
|
||||
ret_val = node[2]
|
||||
ret_val_type = ntype ret_val
|
||||
|
||||
if ret_val_type == "explist" and #ret_val == 2
|
||||
ret_val = ret_val[2]
|
||||
ret_val_type = ntype ret_val
|
||||
|
||||
if types.cascading[ret_val_type]
|
||||
return implicitly_return(@) ret_val
|
||||
|
||||
-- flatten things that create block exp
|
||||
if ret_val_type == "chain" or ret_val_type == "comprehension" or ret_val_type == "tblcomprehension"
|
||||
-- TODO: clean this up
|
||||
Value = require "moonscript.transform.value"
|
||||
ret_val = Value\transform_once @, ret_val
|
||||
if ntype(ret_val) == "block_exp"
|
||||
return build.group transform_last_stm ret_val[2], (stm)->
|
||||
{"return", stm}
|
||||
|
||||
node[2] = ret_val
|
||||
node
|
||||
|
||||
declare_glob: (node) =>
|
||||
names = extract_declarations @
|
||||
|
||||
if node[2] == "^"
|
||||
names = for name in *names
|
||||
str_name = if ntype(name) == "ref"
|
||||
name[2]
|
||||
else
|
||||
name
|
||||
|
||||
continue unless str_name\match "^%u"
|
||||
name
|
||||
|
||||
{"declare", names}
|
||||
|
||||
assign: (node) =>
|
||||
names, values = unpack node, 2
|
||||
|
||||
num_values = #values
|
||||
num_names = #values
|
||||
|
||||
-- special code simplifications for single assigns
|
||||
if num_names == 1 and num_values == 1
|
||||
first_value = values[1]
|
||||
first_name = names[1]
|
||||
first_type = ntype first_value
|
||||
|
||||
-- reduce colon stub chain to block exp
|
||||
if first_type == "chain"
|
||||
-- TODO: clean this up
|
||||
Value = require "moonscript.transform.value"
|
||||
first_value = Value\transform_once @, first_value
|
||||
first_type = ntype first_value
|
||||
|
||||
switch ntype first_value
|
||||
when "block_exp"
|
||||
block_body = first_value[2]
|
||||
idx = #block_body
|
||||
block_body[idx] = build.assign_one first_name, block_body[idx]
|
||||
|
||||
return build.group {
|
||||
{"declare", {first_name}}
|
||||
{"do", block_body}
|
||||
}
|
||||
|
||||
when "comprehension", "tblcomprehension", "foreach", "for", "while"
|
||||
-- TODO: clean this up
|
||||
Value = require "moonscript.transform.value"
|
||||
return build.assign_one first_name, Value\transform_once @, first_value
|
||||
else
|
||||
values[1] = first_value
|
||||
|
||||
-- bubble cascading assigns
|
||||
transformed = if num_values == 1
|
||||
value = values[1]
|
||||
t = ntype value
|
||||
|
||||
if t == "decorated"
|
||||
value = @transform.statement value
|
||||
t = ntype value
|
||||
|
||||
if types.cascading[t]
|
||||
ret = (stm) ->
|
||||
if is_value stm
|
||||
{"assign", names, {stm}}
|
||||
else
|
||||
stm
|
||||
|
||||
build.group {
|
||||
{"declare", names}
|
||||
@transform.statement value, ret, node
|
||||
}
|
||||
|
||||
node = transformed or node
|
||||
|
||||
if destructure.has_destructure names
|
||||
return destructure.split_assign @, node
|
||||
|
||||
node
|
||||
|
||||
continue: (node) =>
|
||||
continue_name = @send "continue"
|
||||
error "continue must be inside of a loop" unless continue_name
|
||||
build.group {
|
||||
build.assign_one continue_name, "true"
|
||||
{"break"}
|
||||
}
|
||||
|
||||
export: (node) =>
|
||||
-- assign values if they are included
|
||||
if #node > 2
|
||||
if node[2] == "class"
|
||||
cls = smart_node node[3]
|
||||
build.group {
|
||||
{"export", {cls.name}}
|
||||
cls
|
||||
}
|
||||
else
|
||||
-- pull out vawlues and assign them after the export
|
||||
build.group {
|
||||
{ "export", node[2] }
|
||||
build.assign {
|
||||
names: node[2]
|
||||
values: node[3]
|
||||
}
|
||||
}
|
||||
else
|
||||
nil
|
||||
|
||||
update: (node) =>
|
||||
name, op, exp = unpack node, 2
|
||||
op_final = op\match "^(.+)=$"
|
||||
|
||||
error "Unknown op: "..op if not op_final
|
||||
|
||||
local lifted
|
||||
|
||||
if ntype(name) == "chain"
|
||||
lifted = {}
|
||||
new_chain = for part in *name[3,]
|
||||
if ntype(part) == "index"
|
||||
proxy = NameProxy "update"
|
||||
table.insert lifted, { proxy, part[2] }
|
||||
{ "index", proxy }
|
||||
else
|
||||
part
|
||||
|
||||
if next lifted
|
||||
name = {name[1], name[2], unpack new_chain}
|
||||
|
||||
exp = {"parens", exp} unless value_is_singular exp
|
||||
out = build.assign_one name, {"exp", name, op_final, exp}
|
||||
|
||||
if lifted and next lifted
|
||||
names = [l[1] for l in *lifted]
|
||||
values = [l[2] for l in *lifted]
|
||||
|
||||
out = build.group {
|
||||
{"assign", names, values}
|
||||
out
|
||||
}
|
||||
|
||||
out
|
||||
|
||||
import: (node) =>
|
||||
names, source = unpack node, 2
|
||||
table_values = for name in *names
|
||||
dest_name = if ntype(name) == "colon"
|
||||
name[2]
|
||||
else
|
||||
name
|
||||
|
||||
{{"key_literal", name}, dest_name}
|
||||
|
||||
dest = { "table", table_values }
|
||||
{ "assign", {dest}, {source}, [-1]: node[-1] }
|
||||
|
||||
comprehension: (node, action) =>
|
||||
exp, clauses = unpack node, 2
|
||||
|
||||
action = action or (exp) -> {exp}
|
||||
construct_comprehension action(exp), clauses
|
||||
|
||||
do: (node, ret) =>
|
||||
node[2] = transform_last_stm node[2], ret if ret
|
||||
node
|
||||
|
||||
decorated: (node) =>
|
||||
stm, dec = unpack node, 2
|
||||
|
||||
wrapped = switch dec[1]
|
||||
when "if"
|
||||
cond, fail = unpack dec, 2
|
||||
fail = { "else", { fail } } if fail
|
||||
{ "if", cond, { stm }, fail }
|
||||
when "unless"
|
||||
{ "unless", dec[2], { stm } }
|
||||
when "comprehension"
|
||||
{ "comprehension", stm, dec[2] }
|
||||
else
|
||||
error "Unknown decorator " .. dec[1]
|
||||
|
||||
if ntype(stm) == "assign"
|
||||
wrapped = build.group {
|
||||
build.declare names: [name for name in *stm[2] when ntype(name) == "ref"]
|
||||
wrapped
|
||||
}
|
||||
|
||||
wrapped
|
||||
|
||||
unless: (node) =>
|
||||
clause = node[2]
|
||||
|
||||
if ntype(clause) == "assign"
|
||||
if destructure.has_destructure clause[2]
|
||||
error "destructure not allowed in unless assignment"
|
||||
|
||||
build.do {
|
||||
clause
|
||||
{ "if", {"not", clause[2][1]}, unpack node, 3 }
|
||||
}
|
||||
|
||||
else
|
||||
{ "if", {"not", {"parens", clause}}, unpack node, 3 }
|
||||
|
||||
if: (node, ret) =>
|
||||
-- expand assign in cond
|
||||
if ntype(node[2]) == "assign"
|
||||
assign, body = unpack node, 2
|
||||
if destructure.has_destructure assign[2]
|
||||
name = NameProxy "des"
|
||||
|
||||
body = {
|
||||
destructure.build_assign @, assign[2][1], name
|
||||
build.group node[3]
|
||||
}
|
||||
|
||||
return build.do {
|
||||
build.assign_one name, assign[3][1]
|
||||
{"if", name, body, unpack node, 4}
|
||||
}
|
||||
else
|
||||
name = assign[2][1]
|
||||
return build.do {
|
||||
assign
|
||||
{"if", name, unpack node, 3}
|
||||
}
|
||||
|
||||
node = expand_elseif_assign node
|
||||
|
||||
-- apply cascading return decorator
|
||||
if ret
|
||||
smart_node node
|
||||
-- mutate all the bodies
|
||||
node['then'] = transform_last_stm node['then'], ret
|
||||
for i = 4, #node
|
||||
case = node[i]
|
||||
body_idx = #node[i]
|
||||
case[body_idx] = transform_last_stm case[body_idx], ret
|
||||
|
||||
node
|
||||
|
||||
with: (node, ret) =>
|
||||
exp, block = unpack node, 2
|
||||
|
||||
copy_scope = true
|
||||
local scope_name, named_assign
|
||||
|
||||
if last = last_stm block
|
||||
ret = false if types.terminating[ntype(last)]
|
||||
|
||||
if ntype(exp) == "assign"
|
||||
names, values = unpack exp, 2
|
||||
first_name = names[1]
|
||||
|
||||
if ntype(first_name) == "ref"
|
||||
scope_name = first_name
|
||||
named_assign = exp
|
||||
exp = values[1]
|
||||
copy_scope = false
|
||||
else
|
||||
scope_name = NameProxy "with"
|
||||
exp = values[1]
|
||||
values[1] = scope_name
|
||||
named_assign = {"assign", names, values}
|
||||
|
||||
elseif @is_local exp
|
||||
scope_name = exp
|
||||
copy_scope = false
|
||||
|
||||
scope_name or= NameProxy "with"
|
||||
|
||||
out = build.do {
|
||||
copy_scope and build.assign_one(scope_name, exp) or NOOP
|
||||
named_assign or NOOP
|
||||
Run => @set "scope_var", scope_name
|
||||
unpack block
|
||||
}
|
||||
|
||||
if ret
|
||||
table.insert out[2], ret scope_name
|
||||
|
||||
out
|
||||
|
||||
foreach: (node, _) =>
|
||||
smart_node node
|
||||
source = unpack node.iter
|
||||
|
||||
destructures = {}
|
||||
node.names = for i, name in ipairs node.names
|
||||
if ntype(name) == "table"
|
||||
with proxy = NameProxy "des"
|
||||
insert destructures, destructure.build_assign @, name, proxy
|
||||
else
|
||||
name
|
||||
|
||||
if next destructures
|
||||
insert destructures, build.group node.body
|
||||
node.body = destructures
|
||||
|
||||
if ntype(source) == "unpack"
|
||||
list = source[2]
|
||||
|
||||
index_name = NameProxy "index"
|
||||
|
||||
list_name = @is_local(list) and list or NameProxy "list"
|
||||
|
||||
slice_var = nil
|
||||
bounds = if is_slice list
|
||||
slice = list[#list]
|
||||
table.remove list
|
||||
table.remove slice, 1
|
||||
|
||||
list_name = list if @is_local list
|
||||
|
||||
slice[2] = if slice[2] and slice[2] != ""
|
||||
max_tmp_name = NameProxy "max"
|
||||
slice_var = build.assign_one max_tmp_name, slice[2]
|
||||
{"exp", max_tmp_name, "<", 0
|
||||
"and", {"length", list_name}, "+", max_tmp_name
|
||||
"or", max_tmp_name }
|
||||
else
|
||||
{"length", list_name}
|
||||
|
||||
slice
|
||||
else
|
||||
{1, {"length", list_name}}
|
||||
|
||||
names = [is_name_proxy(n) and n or LocalName(n) or n for n in *node.names]
|
||||
|
||||
return build.group {
|
||||
list_name != list and build.assign_one(list_name, list) or NOOP
|
||||
slice_var or NOOP
|
||||
build["for"] {
|
||||
name: index_name
|
||||
bounds: bounds
|
||||
body: {
|
||||
{"assign", names, { NameProxy.index list_name, index_name }}
|
||||
build.group node.body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
while: (node) =>
|
||||
smart_node node
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
for: (node) =>
|
||||
smart_node node
|
||||
node.body = with_continue_listener node.body
|
||||
|
||||
switch: (node, ret) =>
|
||||
exp, conds = unpack node, 2
|
||||
exp_name = NameProxy "exp"
|
||||
|
||||
-- convert switch conds into if statment conds
|
||||
convert_cond = (cond) ->
|
||||
t, case_exps, body = unpack cond
|
||||
out = {}
|
||||
insert out, t == "case" and "elseif" or "else"
|
||||
if t != "else"
|
||||
cond_exp = {}
|
||||
for i, case in ipairs case_exps
|
||||
if i == 1
|
||||
insert cond_exp, "exp"
|
||||
else
|
||||
insert cond_exp, "or"
|
||||
|
||||
case = {"parens", case} unless value_is_singular case
|
||||
insert cond_exp, {"exp", case, "==", exp_name}
|
||||
|
||||
insert out, cond_exp
|
||||
else
|
||||
body = case_exps
|
||||
|
||||
if ret
|
||||
body = transform_last_stm body, ret
|
||||
|
||||
insert out, body
|
||||
|
||||
out
|
||||
|
||||
first = true
|
||||
if_stm = {"if"}
|
||||
for cond in *conds
|
||||
if_cond = convert_cond cond
|
||||
if first
|
||||
first = false
|
||||
insert if_stm, if_cond[2]
|
||||
insert if_stm, if_cond[3]
|
||||
else
|
||||
insert if_stm, if_cond
|
||||
|
||||
build.group {
|
||||
build.assign_one exp_name, exp
|
||||
if_stm
|
||||
}
|
||||
|
||||
class: require "moonscript.transform.class"
|
||||
|
||||
}
|
115
prototypes/scp-clicker/moonscript/transform/statements.lua
Normal file
115
prototypes/scp-clicker/moonscript/transform/statements.lua
Normal file
@ -0,0 +1,115 @@
|
||||
local types = require("moonscript.types")
|
||||
local ntype, mtype, is_value, NOOP
|
||||
ntype, mtype, is_value, NOOP = types.ntype, types.mtype, types.is_value, types.NOOP
|
||||
local comprehension_has_value
|
||||
comprehension_has_value = require("moonscript.transform.comprehension").comprehension_has_value
|
||||
local Run
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
call = function(self, state)
|
||||
return self.fn(state)
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, fn)
|
||||
self.fn = fn
|
||||
self[1] = "run"
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Run"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Run = _class_0
|
||||
end
|
||||
local last_stm
|
||||
last_stm = function(stms)
|
||||
local last_exp_id = 0
|
||||
for i = #stms, 1, -1 do
|
||||
local stm = stms[i]
|
||||
if stm and mtype(stm) ~= Run then
|
||||
if ntype(stm) == "group" then
|
||||
return last_stm(stm[2])
|
||||
end
|
||||
last_exp_id = i
|
||||
break
|
||||
end
|
||||
end
|
||||
return stms[last_exp_id], last_exp_id, stms
|
||||
end
|
||||
local transform_last_stm
|
||||
transform_last_stm = function(stms, fn)
|
||||
local _, last_idx, _stms = last_stm(stms)
|
||||
if _stms ~= stms then
|
||||
error("cannot transform last node in group")
|
||||
end
|
||||
return (function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for i, stm in ipairs(stms) do
|
||||
if i == last_idx then
|
||||
_accum_0[_len_0] = {
|
||||
"transform",
|
||||
stm,
|
||||
fn
|
||||
}
|
||||
else
|
||||
_accum_0[_len_0] = stm
|
||||
end
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)()
|
||||
end
|
||||
local chain_is_stub
|
||||
chain_is_stub = function(chain)
|
||||
local stub = chain[#chain]
|
||||
return stub and ntype(stub) == "colon"
|
||||
end
|
||||
local implicitly_return
|
||||
implicitly_return = function(scope)
|
||||
local is_top = true
|
||||
local fn
|
||||
fn = function(stm)
|
||||
local t = ntype(stm)
|
||||
if t == "decorated" then
|
||||
stm = scope.transform.statement(stm)
|
||||
t = ntype(stm)
|
||||
end
|
||||
if types.cascading[t] then
|
||||
is_top = false
|
||||
return scope.transform.statement(stm, fn)
|
||||
elseif types.manual_return[t] or not is_value(stm) then
|
||||
if is_top and t == "return" and stm[2] == "" then
|
||||
return NOOP
|
||||
else
|
||||
return stm
|
||||
end
|
||||
else
|
||||
if t == "comprehension" and not comprehension_has_value(stm) then
|
||||
return stm
|
||||
else
|
||||
return {
|
||||
"return",
|
||||
stm
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
return fn
|
||||
end
|
||||
return {
|
||||
Run = Run,
|
||||
last_stm = last_stm,
|
||||
transform_last_stm = transform_last_stm,
|
||||
chain_is_stub = chain_is_stub,
|
||||
implicitly_return = implicitly_return
|
||||
}
|
78
prototypes/scp-clicker/moonscript/transform/statements.moon
Normal file
78
prototypes/scp-clicker/moonscript/transform/statements.moon
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
types = require "moonscript.types"
|
||||
import ntype, mtype, is_value, NOOP from types
|
||||
|
||||
import comprehension_has_value from require "moonscript.transform.comprehension"
|
||||
|
||||
-- A Run is a special statement node that lets a function run and mutate the
|
||||
-- state of the compiler
|
||||
class Run
|
||||
new: (@fn) =>
|
||||
@[1] = "run"
|
||||
|
||||
call: (state) =>
|
||||
@.fn state
|
||||
|
||||
-- extract the last statment from an array of statements
|
||||
-- is group aware
|
||||
-- returns: the last statement, the index, the table it was fetched from
|
||||
last_stm = (stms) ->
|
||||
last_exp_id = 0
|
||||
for i = #stms, 1, -1
|
||||
stm = stms[i]
|
||||
if stm and mtype(stm) != Run
|
||||
if ntype(stm) == "group"
|
||||
return last_stm stm[2]
|
||||
|
||||
last_exp_id = i
|
||||
break
|
||||
|
||||
stms[last_exp_id], last_exp_id, stms
|
||||
|
||||
-- transform the last stm is a list of stms
|
||||
-- will puke on group
|
||||
transform_last_stm = (stms, fn) ->
|
||||
_, last_idx, _stms = last_stm stms
|
||||
|
||||
if _stms != stms
|
||||
error "cannot transform last node in group"
|
||||
|
||||
return for i, stm in ipairs stms
|
||||
if i == last_idx
|
||||
{"transform", stm, fn}
|
||||
else
|
||||
stm
|
||||
|
||||
chain_is_stub = (chain) ->
|
||||
stub = chain[#chain]
|
||||
stub and ntype(stub) == "colon"
|
||||
|
||||
implicitly_return = (scope) ->
|
||||
is_top = true
|
||||
fn = (stm) ->
|
||||
t = ntype stm
|
||||
|
||||
-- expand decorated
|
||||
if t == "decorated"
|
||||
stm = scope.transform.statement stm
|
||||
t = ntype stm
|
||||
|
||||
if types.cascading[t]
|
||||
is_top = false
|
||||
scope.transform.statement stm, fn
|
||||
elseif types.manual_return[t] or not is_value stm
|
||||
-- remove blank return statement
|
||||
if is_top and t == "return" and stm[2] == ""
|
||||
NOOP
|
||||
else
|
||||
stm
|
||||
else
|
||||
if t == "comprehension" and not comprehension_has_value stm
|
||||
stm
|
||||
else
|
||||
{"return", stm}
|
||||
|
||||
fn
|
||||
|
||||
{:Run, :last_stm, :transform_last_stm, :chain_is_stub, :implicitly_return }
|
||||
|
74
prototypes/scp-clicker/moonscript/transform/transformer.lua
Normal file
74
prototypes/scp-clicker/moonscript/transform/transformer.lua
Normal file
@ -0,0 +1,74 @@
|
||||
local ntype
|
||||
ntype = require("moonscript.types").ntype
|
||||
local Transformer
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
transform_once = function(self, scope, node, ...)
|
||||
if self.seen_nodes[node] then
|
||||
return node
|
||||
end
|
||||
self.seen_nodes[node] = true
|
||||
local transformer = self.transformers[ntype(node)]
|
||||
if transformer then
|
||||
return transformer(scope, node, ...) or node
|
||||
else
|
||||
return node
|
||||
end
|
||||
end,
|
||||
transform = function(self, scope, node, ...)
|
||||
if self.seen_nodes[node] then
|
||||
return node
|
||||
end
|
||||
self.seen_nodes[node] = true
|
||||
while true do
|
||||
local transformer = self.transformers[ntype(node)]
|
||||
local res
|
||||
if transformer then
|
||||
res = transformer(scope, node, ...) or node
|
||||
else
|
||||
res = node
|
||||
end
|
||||
if res == node then
|
||||
return node
|
||||
end
|
||||
node = res
|
||||
end
|
||||
return node
|
||||
end,
|
||||
bind = function(self, scope)
|
||||
return function(...)
|
||||
return self:transform(scope, ...)
|
||||
end
|
||||
end,
|
||||
__call = function(self, ...)
|
||||
return self:transform(...)
|
||||
end,
|
||||
can_transform = function(self, node)
|
||||
return self.transformers[ntype(node)] ~= nil
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, transformers)
|
||||
self.transformers = transformers
|
||||
self.seen_nodes = setmetatable({ }, {
|
||||
__mode = "k"
|
||||
})
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "Transformer"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
Transformer = _class_0
|
||||
end
|
||||
return {
|
||||
Transformer = Transformer
|
||||
}
|
42
prototypes/scp-clicker/moonscript/transform/transformer.moon
Normal file
42
prototypes/scp-clicker/moonscript/transform/transformer.moon
Normal file
@ -0,0 +1,42 @@
|
||||
import ntype from require "moonscript.types"
|
||||
|
||||
class Transformer
|
||||
new: (@transformers) =>
|
||||
@seen_nodes = setmetatable {}, __mode: "k"
|
||||
|
||||
transform_once: (scope, node, ...) =>
|
||||
return node if @seen_nodes[node]
|
||||
@seen_nodes[node] = true
|
||||
|
||||
transformer = @transformers[ntype node]
|
||||
if transformer
|
||||
transformer(scope, node, ...) or node
|
||||
else
|
||||
node
|
||||
|
||||
transform: (scope, node, ...) =>
|
||||
return node if @seen_nodes[node]
|
||||
|
||||
@seen_nodes[node] = true
|
||||
while true
|
||||
transformer = @transformers[ntype node]
|
||||
res = if transformer
|
||||
transformer(scope, node, ...) or node
|
||||
else
|
||||
node
|
||||
|
||||
return node if res == node
|
||||
node = res
|
||||
|
||||
node
|
||||
|
||||
bind: (scope) =>
|
||||
(...) -> @transform scope, ...
|
||||
|
||||
__call: (...) => @transform ...
|
||||
|
||||
can_transform: (node) =>
|
||||
@transformers[ntype node] != nil
|
||||
|
||||
|
||||
{ :Transformer }
|
265
prototypes/scp-clicker/moonscript/transform/value.lua
Normal file
265
prototypes/scp-clicker/moonscript/transform/value.lua
Normal file
@ -0,0 +1,265 @@
|
||||
local Transformer
|
||||
Transformer = require("moonscript.transform.transformer").Transformer
|
||||
local build, ntype, smart_node
|
||||
do
|
||||
local _obj_0 = require("moonscript.types")
|
||||
build, ntype, smart_node = _obj_0.build, _obj_0.ntype, _obj_0.smart_node
|
||||
end
|
||||
local NameProxy
|
||||
NameProxy = require("moonscript.transform.names").NameProxy
|
||||
local Accumulator, default_accumulator
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.accumulator")
|
||||
Accumulator, default_accumulator = _obj_0.Accumulator, _obj_0.default_accumulator
|
||||
end
|
||||
local lua_keywords
|
||||
lua_keywords = require("moonscript.data").lua_keywords
|
||||
local Run, transform_last_stm, implicitly_return, chain_is_stub
|
||||
do
|
||||
local _obj_0 = require("moonscript.transform.statements")
|
||||
Run, transform_last_stm, implicitly_return, chain_is_stub = _obj_0.Run, _obj_0.transform_last_stm, _obj_0.implicitly_return, _obj_0.chain_is_stub
|
||||
end
|
||||
local construct_comprehension
|
||||
construct_comprehension = require("moonscript.transform.comprehension").construct_comprehension
|
||||
local insert
|
||||
insert = table.insert
|
||||
local unpack
|
||||
unpack = require("moonscript.util").unpack
|
||||
return Transformer({
|
||||
["for"] = default_accumulator,
|
||||
["while"] = default_accumulator,
|
||||
foreach = default_accumulator,
|
||||
["do"] = function(self, node)
|
||||
return build.block_exp(node[2])
|
||||
end,
|
||||
decorated = function(self, node)
|
||||
return self.transform.statement(node)
|
||||
end,
|
||||
class = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
string = function(self, node)
|
||||
local delim = node[2]
|
||||
local convert_part
|
||||
convert_part = function(part)
|
||||
if type(part) == "string" or part == nil then
|
||||
return {
|
||||
"string",
|
||||
delim,
|
||||
part or ""
|
||||
}
|
||||
else
|
||||
return build.chain({
|
||||
base = "tostring",
|
||||
{
|
||||
"call",
|
||||
{
|
||||
part[2]
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
if #node <= 3 then
|
||||
if type(node[3]) == "string" then
|
||||
return node
|
||||
else
|
||||
return convert_part(node[3])
|
||||
end
|
||||
end
|
||||
local e = {
|
||||
"exp",
|
||||
convert_part(node[3])
|
||||
}
|
||||
for i = 4, #node do
|
||||
insert(e, "..")
|
||||
insert(e, convert_part(node[i]))
|
||||
end
|
||||
return e
|
||||
end,
|
||||
comprehension = function(self, node)
|
||||
local a = Accumulator()
|
||||
node = self.transform.statement(node, function(exp)
|
||||
return a:mutate_body({
|
||||
exp
|
||||
})
|
||||
end)
|
||||
return a:wrap(node)
|
||||
end,
|
||||
tblcomprehension = function(self, node)
|
||||
local explist, clauses = unpack(node, 2)
|
||||
local key_exp, value_exp = unpack(explist)
|
||||
local accum = NameProxy("tbl")
|
||||
local inner
|
||||
if value_exp then
|
||||
local dest = build.chain({
|
||||
base = accum,
|
||||
{
|
||||
"index",
|
||||
key_exp
|
||||
}
|
||||
})
|
||||
inner = {
|
||||
build.assign_one(dest, value_exp)
|
||||
}
|
||||
else
|
||||
local key_name, val_name = NameProxy("key"), NameProxy("val")
|
||||
local dest = build.chain({
|
||||
base = accum,
|
||||
{
|
||||
"index",
|
||||
key_name
|
||||
}
|
||||
})
|
||||
inner = {
|
||||
build.assign({
|
||||
names = {
|
||||
key_name,
|
||||
val_name
|
||||
},
|
||||
values = {
|
||||
key_exp
|
||||
}
|
||||
}),
|
||||
build.assign_one(dest, val_name)
|
||||
}
|
||||
end
|
||||
return build.block_exp({
|
||||
build.assign_one(accum, build.table()),
|
||||
construct_comprehension(inner, clauses),
|
||||
accum
|
||||
})
|
||||
end,
|
||||
fndef = function(self, node)
|
||||
smart_node(node)
|
||||
node.body = transform_last_stm(node.body, implicitly_return(self))
|
||||
node.body = {
|
||||
Run(function(self)
|
||||
return self:listen("varargs", function() end)
|
||||
end),
|
||||
unpack(node.body)
|
||||
}
|
||||
return node
|
||||
end,
|
||||
["if"] = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
unless = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
with = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
switch = function(self, node)
|
||||
return build.block_exp({
|
||||
node
|
||||
})
|
||||
end,
|
||||
chain = function(self, node)
|
||||
for i = 2, #node do
|
||||
local part = node[i]
|
||||
if ntype(part) == "dot" and lua_keywords[part[2]] then
|
||||
node[i] = {
|
||||
"index",
|
||||
{
|
||||
"string",
|
||||
'"',
|
||||
part[2]
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
if ntype(node[2]) == "string" then
|
||||
node[2] = {
|
||||
"parens",
|
||||
node[2]
|
||||
}
|
||||
end
|
||||
if chain_is_stub(node) then
|
||||
local base_name = NameProxy("base")
|
||||
local fn_name = NameProxy("fn")
|
||||
local colon = table.remove(node)
|
||||
local is_super = ntype(node[2]) == "ref" and node[2][2] == "super"
|
||||
return build.block_exp({
|
||||
build.assign({
|
||||
names = {
|
||||
base_name
|
||||
},
|
||||
values = {
|
||||
node
|
||||
}
|
||||
}),
|
||||
build.assign({
|
||||
names = {
|
||||
fn_name
|
||||
},
|
||||
values = {
|
||||
build.chain({
|
||||
base = base_name,
|
||||
{
|
||||
"dot",
|
||||
colon[2]
|
||||
}
|
||||
})
|
||||
}
|
||||
}),
|
||||
build.fndef({
|
||||
args = {
|
||||
{
|
||||
"..."
|
||||
}
|
||||
},
|
||||
body = {
|
||||
build.chain({
|
||||
base = fn_name,
|
||||
{
|
||||
"call",
|
||||
{
|
||||
is_super and "self" or base_name,
|
||||
"..."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
end
|
||||
end,
|
||||
block_exp = function(self, node)
|
||||
local body = unpack(node, 2)
|
||||
local fn = nil
|
||||
local arg_list = { }
|
||||
fn = smart_node(build.fndef({
|
||||
body = {
|
||||
Run(function(self)
|
||||
return self:listen("varargs", function()
|
||||
insert(arg_list, "...")
|
||||
insert(fn.args, {
|
||||
"..."
|
||||
})
|
||||
return self:unlisten("varargs")
|
||||
end)
|
||||
end),
|
||||
unpack(body)
|
||||
}
|
||||
}))
|
||||
return build.chain({
|
||||
base = {
|
||||
"parens",
|
||||
fn
|
||||
},
|
||||
{
|
||||
"call",
|
||||
arg_list
|
||||
}
|
||||
})
|
||||
end
|
||||
})
|
164
prototypes/scp-clicker/moonscript/transform/value.moon
Normal file
164
prototypes/scp-clicker/moonscript/transform/value.moon
Normal file
@ -0,0 +1,164 @@
|
||||
import Transformer from require "moonscript.transform.transformer"
|
||||
import build, ntype, smart_node from require "moonscript.types"
|
||||
|
||||
import NameProxy from require "moonscript.transform.names"
|
||||
import Accumulator, default_accumulator from require "moonscript.transform.accumulator"
|
||||
import lua_keywords from require "moonscript.data"
|
||||
|
||||
import Run, transform_last_stm, implicitly_return, chain_is_stub from require "moonscript.transform.statements"
|
||||
|
||||
import construct_comprehension from require "moonscript.transform.comprehension"
|
||||
|
||||
import insert from table
|
||||
import unpack from require "moonscript.util"
|
||||
|
||||
Transformer {
|
||||
for: default_accumulator
|
||||
while: default_accumulator
|
||||
foreach: default_accumulator
|
||||
|
||||
do: (node) =>
|
||||
build.block_exp node[2]
|
||||
|
||||
decorated: (node) =>
|
||||
@transform.statement node
|
||||
|
||||
class: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
string: (node) =>
|
||||
delim = node[2]
|
||||
|
||||
convert_part = (part) ->
|
||||
if type(part) == "string" or part == nil
|
||||
{"string", delim, part or ""}
|
||||
else
|
||||
build.chain { base: "tostring", {"call", {part[2]}} }
|
||||
|
||||
-- reduced to single item
|
||||
if #node <= 3
|
||||
return if type(node[3]) == "string"
|
||||
node
|
||||
else
|
||||
convert_part node[3]
|
||||
|
||||
e = {"exp", convert_part node[3]}
|
||||
|
||||
for i=4, #node
|
||||
insert e, ".."
|
||||
insert e, convert_part node[i]
|
||||
e
|
||||
|
||||
comprehension: (node) =>
|
||||
a = Accumulator!
|
||||
node = @transform.statement node, (exp) ->
|
||||
a\mutate_body {exp}
|
||||
a\wrap node
|
||||
|
||||
tblcomprehension: (node) =>
|
||||
explist, clauses = unpack node, 2
|
||||
key_exp, value_exp = unpack explist
|
||||
|
||||
accum = NameProxy "tbl"
|
||||
|
||||
inner = if value_exp
|
||||
dest = build.chain { base: accum, {"index", key_exp} }
|
||||
{ build.assign_one dest, value_exp }
|
||||
else
|
||||
-- If we only have single expression then
|
||||
-- unpack the result into key and value
|
||||
key_name, val_name = NameProxy"key", NameProxy"val"
|
||||
dest = build.chain { base: accum, {"index", key_name} }
|
||||
{
|
||||
build.assign names: {key_name, val_name}, values: {key_exp}
|
||||
build.assign_one dest, val_name
|
||||
}
|
||||
|
||||
build.block_exp {
|
||||
build.assign_one accum, build.table!
|
||||
construct_comprehension inner, clauses
|
||||
accum
|
||||
}
|
||||
|
||||
fndef: (node) =>
|
||||
smart_node node
|
||||
node.body = transform_last_stm node.body, implicitly_return self
|
||||
node.body = {
|
||||
Run => @listen "varargs", -> -- capture event
|
||||
unpack node.body
|
||||
}
|
||||
|
||||
node
|
||||
|
||||
if: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
unless: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
with: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
switch: (node) =>
|
||||
build.block_exp { node }
|
||||
|
||||
-- pull out colon chain
|
||||
chain: (node) =>
|
||||
-- escape lua keywords used in dot accessors
|
||||
for i=2,#node
|
||||
part = node[i]
|
||||
if ntype(part) == "dot" and lua_keywords[part[2]]
|
||||
node[i] = { "index", {"string", '"', part[2]} }
|
||||
|
||||
if ntype(node[2]) == "string"
|
||||
-- add parens if callee is raw string
|
||||
node[2] = {"parens", node[2] }
|
||||
|
||||
if chain_is_stub node
|
||||
base_name = NameProxy "base"
|
||||
fn_name = NameProxy "fn"
|
||||
colon = table.remove node
|
||||
|
||||
is_super = ntype(node[2]) == "ref" and node[2][2] == "super"
|
||||
build.block_exp {
|
||||
build.assign {
|
||||
names: {base_name}
|
||||
values: {node}
|
||||
}
|
||||
|
||||
build.assign {
|
||||
names: {fn_name}
|
||||
values: {
|
||||
build.chain { base: base_name, {"dot", colon[2]} }
|
||||
}
|
||||
}
|
||||
|
||||
build.fndef {
|
||||
args: {{"..."}}
|
||||
body: {
|
||||
build.chain {
|
||||
base: fn_name, {"call", {is_super and "self" or base_name, "..."}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_exp: (node) =>
|
||||
body = unpack node, 2
|
||||
|
||||
fn = nil
|
||||
arg_list = {}
|
||||
|
||||
fn = smart_node build.fndef body: {
|
||||
Run =>
|
||||
@listen "varargs", ->
|
||||
insert arg_list, "..."
|
||||
insert fn.args, {"..."}
|
||||
@unlisten "varargs"
|
||||
|
||||
unpack body
|
||||
}
|
||||
|
||||
build.chain { base: {"parens", fn}, {"call", arg_list} }
|
||||
}
|
||||
|
323
prototypes/scp-clicker/moonscript/types.lua
Normal file
323
prototypes/scp-clicker/moonscript/types.lua
Normal file
@ -0,0 +1,323 @@
|
||||
local util = require("moonscript.util")
|
||||
local Set
|
||||
Set = require("moonscript.data").Set
|
||||
local insert
|
||||
insert = table.insert
|
||||
local unpack
|
||||
unpack = util.unpack
|
||||
local manual_return = Set({
|
||||
"foreach",
|
||||
"for",
|
||||
"while",
|
||||
"return"
|
||||
})
|
||||
local cascading = Set({
|
||||
"if",
|
||||
"unless",
|
||||
"with",
|
||||
"switch",
|
||||
"class",
|
||||
"do"
|
||||
})
|
||||
local terminating = Set({
|
||||
"return",
|
||||
"break"
|
||||
})
|
||||
local ntype
|
||||
ntype = function(node)
|
||||
local _exp_0 = type(node)
|
||||
if "nil" == _exp_0 then
|
||||
return "nil"
|
||||
elseif "table" == _exp_0 then
|
||||
return node[1]
|
||||
else
|
||||
return "value"
|
||||
end
|
||||
end
|
||||
local mtype
|
||||
do
|
||||
local moon_type = util.moon.type
|
||||
mtype = function(val)
|
||||
local mt = getmetatable(val)
|
||||
if mt and mt.smart_node then
|
||||
return "table"
|
||||
end
|
||||
return moon_type(val)
|
||||
end
|
||||
end
|
||||
local value_can_be_statement
|
||||
value_can_be_statement = function(node)
|
||||
if not (ntype(node) == "chain") then
|
||||
return false
|
||||
end
|
||||
return ntype(node[#node]) == "call"
|
||||
end
|
||||
local is_value
|
||||
is_value = function(stm)
|
||||
local compile = require("moonscript.compile")
|
||||
local transform = require("moonscript.transform")
|
||||
return compile.Block:is_value(stm) or transform.Value:can_transform(stm)
|
||||
end
|
||||
local value_is_singular
|
||||
value_is_singular = function(node)
|
||||
return type(node) ~= "table" or node[1] ~= "exp" or #node == 2
|
||||
end
|
||||
local is_slice
|
||||
is_slice = function(node)
|
||||
return ntype(node) == "chain" and ntype(node[#node]) == "slice"
|
||||
end
|
||||
local t = { }
|
||||
local node_types = {
|
||||
class = {
|
||||
{
|
||||
"name",
|
||||
"Tmp"
|
||||
},
|
||||
{
|
||||
"body",
|
||||
t
|
||||
}
|
||||
},
|
||||
fndef = {
|
||||
{
|
||||
"args",
|
||||
t
|
||||
},
|
||||
{
|
||||
"whitelist",
|
||||
t
|
||||
},
|
||||
{
|
||||
"arrow",
|
||||
"slim"
|
||||
},
|
||||
{
|
||||
"body",
|
||||
t
|
||||
}
|
||||
},
|
||||
foreach = {
|
||||
{
|
||||
"names",
|
||||
t
|
||||
},
|
||||
{
|
||||
"iter"
|
||||
},
|
||||
{
|
||||
"body",
|
||||
t
|
||||
}
|
||||
},
|
||||
["for"] = {
|
||||
{
|
||||
"name"
|
||||
},
|
||||
{
|
||||
"bounds",
|
||||
t
|
||||
},
|
||||
{
|
||||
"body",
|
||||
t
|
||||
}
|
||||
},
|
||||
["while"] = {
|
||||
{
|
||||
"cond",
|
||||
t
|
||||
},
|
||||
{
|
||||
"body",
|
||||
t
|
||||
}
|
||||
},
|
||||
assign = {
|
||||
{
|
||||
"names",
|
||||
t
|
||||
},
|
||||
{
|
||||
"values",
|
||||
t
|
||||
}
|
||||
},
|
||||
declare = {
|
||||
{
|
||||
"names",
|
||||
t
|
||||
}
|
||||
},
|
||||
["if"] = {
|
||||
{
|
||||
"cond",
|
||||
t
|
||||
},
|
||||
{
|
||||
"then",
|
||||
t
|
||||
}
|
||||
}
|
||||
}
|
||||
local build_table
|
||||
build_table = function()
|
||||
local key_table = { }
|
||||
for node_name, args in pairs(node_types) do
|
||||
local index = { }
|
||||
for i, tuple in ipairs(args) do
|
||||
local prop_name = tuple[1]
|
||||
index[prop_name] = i + 1
|
||||
end
|
||||
key_table[node_name] = index
|
||||
end
|
||||
return key_table
|
||||
end
|
||||
local key_table = build_table()
|
||||
local make_builder
|
||||
make_builder = function(name)
|
||||
local spec = node_types[name]
|
||||
if not spec then
|
||||
error("don't know how to build node: " .. name)
|
||||
end
|
||||
return function(props)
|
||||
if props == nil then
|
||||
props = { }
|
||||
end
|
||||
local node = {
|
||||
name
|
||||
}
|
||||
for i, arg in ipairs(spec) do
|
||||
local key, default_value = unpack(arg)
|
||||
local val
|
||||
if props[key] then
|
||||
val = props[key]
|
||||
else
|
||||
val = default_value
|
||||
end
|
||||
if val == t then
|
||||
val = { }
|
||||
end
|
||||
node[i + 1] = val
|
||||
end
|
||||
return node
|
||||
end
|
||||
end
|
||||
local build = nil
|
||||
build = setmetatable({
|
||||
group = function(body)
|
||||
if body == nil then
|
||||
body = { }
|
||||
end
|
||||
return {
|
||||
"group",
|
||||
body
|
||||
}
|
||||
end,
|
||||
["do"] = function(body)
|
||||
return {
|
||||
"do",
|
||||
body
|
||||
}
|
||||
end,
|
||||
assign_one = function(name, value)
|
||||
return build.assign({
|
||||
names = {
|
||||
name
|
||||
},
|
||||
values = {
|
||||
value
|
||||
}
|
||||
})
|
||||
end,
|
||||
table = function(tbl)
|
||||
if tbl == nil then
|
||||
tbl = { }
|
||||
end
|
||||
for _index_0 = 1, #tbl do
|
||||
local tuple = tbl[_index_0]
|
||||
if type(tuple[1]) == "string" then
|
||||
tuple[1] = {
|
||||
"key_literal",
|
||||
tuple[1]
|
||||
}
|
||||
end
|
||||
end
|
||||
return {
|
||||
"table",
|
||||
tbl
|
||||
}
|
||||
end,
|
||||
block_exp = function(body)
|
||||
return {
|
||||
"block_exp",
|
||||
body
|
||||
}
|
||||
end,
|
||||
chain = function(parts)
|
||||
local base = parts.base or error("expecting base property for chain")
|
||||
if type(base) == "string" then
|
||||
base = {
|
||||
"ref",
|
||||
base
|
||||
}
|
||||
end
|
||||
local node = {
|
||||
"chain",
|
||||
base
|
||||
}
|
||||
for _index_0 = 1, #parts do
|
||||
local part = parts[_index_0]
|
||||
insert(node, part)
|
||||
end
|
||||
return node
|
||||
end
|
||||
}, {
|
||||
__index = function(self, name)
|
||||
self[name] = make_builder(name)
|
||||
return rawget(self, name)
|
||||
end
|
||||
})
|
||||
local smart_node_mt = setmetatable({ }, {
|
||||
__index = function(self, node_type)
|
||||
local index = key_table[node_type]
|
||||
local mt = {
|
||||
smart_node = true,
|
||||
__index = function(node, key)
|
||||
if index[key] then
|
||||
return rawget(node, index[key])
|
||||
elseif type(key) == "string" then
|
||||
return error("unknown key: `" .. key .. "` on node type: `" .. ntype(node) .. "`")
|
||||
end
|
||||
end,
|
||||
__newindex = function(node, key, value)
|
||||
if index[key] then
|
||||
key = index[key]
|
||||
end
|
||||
return rawset(node, key, value)
|
||||
end
|
||||
}
|
||||
self[node_type] = mt
|
||||
return mt
|
||||
end
|
||||
})
|
||||
local smart_node
|
||||
smart_node = function(node)
|
||||
return setmetatable(node, smart_node_mt[ntype(node)])
|
||||
end
|
||||
local NOOP = {
|
||||
"noop"
|
||||
}
|
||||
return {
|
||||
ntype = ntype,
|
||||
smart_node = smart_node,
|
||||
build = build,
|
||||
is_value = is_value,
|
||||
is_slice = is_slice,
|
||||
manual_return = manual_return,
|
||||
cascading = cascading,
|
||||
value_is_singular = value_is_singular,
|
||||
value_can_be_statement = value_can_be_statement,
|
||||
mtype = mtype,
|
||||
terminating = terminating,
|
||||
NOOP = NOOP
|
||||
}
|
197
prototypes/scp-clicker/moonscript/types.moon
Normal file
197
prototypes/scp-clicker/moonscript/types.moon
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
util = require "moonscript.util"
|
||||
import Set from require "moonscript.data"
|
||||
|
||||
import insert from table
|
||||
import unpack from util
|
||||
|
||||
-- implicit return does not work on these statements
|
||||
manual_return = Set {
|
||||
"foreach", "for", "while", "return"
|
||||
}
|
||||
|
||||
-- Assigns and returns are bubbled into their bodies.
|
||||
-- All cascading statement transform functions accept a second arugment that
|
||||
-- is the transformation to apply to the last statement in their body
|
||||
cascading = Set {
|
||||
"if", "unless", "with", "switch", "class", "do"
|
||||
}
|
||||
|
||||
terminating = Set {
|
||||
"return", "break"
|
||||
}
|
||||
|
||||
-- type of node as string
|
||||
ntype = (node) ->
|
||||
switch type node
|
||||
when "nil"
|
||||
"nil"
|
||||
when "table"
|
||||
node[1]
|
||||
else
|
||||
"value"
|
||||
|
||||
-- gets the class of a type if possible
|
||||
mtype = do
|
||||
moon_type = util.moon.type
|
||||
-- lets us check a smart node without throwing an error
|
||||
(val) ->
|
||||
mt = getmetatable val
|
||||
return "table" if mt and mt.smart_node
|
||||
moon_type val
|
||||
|
||||
-- can this value be compiled in a line by itself
|
||||
value_can_be_statement = (node) ->
|
||||
return false unless ntype(node) == "chain"
|
||||
-- it's a function call
|
||||
ntype(node[#node]) == "call"
|
||||
|
||||
is_value = (stm) ->
|
||||
compile = require "moonscript.compile"
|
||||
transform = require "moonscript.transform"
|
||||
|
||||
compile.Block\is_value(stm) or transform.Value\can_transform stm
|
||||
|
||||
value_is_singular = (node) ->
|
||||
type(node) != "table" or node[1] != "exp" or #node == 2
|
||||
|
||||
is_slice = (node) ->
|
||||
ntype(node) == "chain" and ntype(node[#node]) == "slice"
|
||||
|
||||
t = {}
|
||||
node_types = {
|
||||
class: {
|
||||
{"name", "Tmp"}
|
||||
{"body", t}
|
||||
}
|
||||
fndef: {
|
||||
{"args", t}
|
||||
{"whitelist", t}
|
||||
{"arrow", "slim"}
|
||||
{"body", t}
|
||||
}
|
||||
foreach: {
|
||||
{"names", t}
|
||||
{"iter"}
|
||||
{"body", t}
|
||||
}
|
||||
for: {
|
||||
{"name"}
|
||||
{"bounds", t}
|
||||
{"body", t}
|
||||
}
|
||||
while: {
|
||||
{"cond", t}
|
||||
{"body", t}
|
||||
}
|
||||
assign: {
|
||||
{"names", t}
|
||||
{"values", t}
|
||||
}
|
||||
declare: {
|
||||
{"names", t}
|
||||
}
|
||||
if: {
|
||||
{"cond", t}
|
||||
{"then", t}
|
||||
}
|
||||
}
|
||||
|
||||
build_table = ->
|
||||
key_table = {}
|
||||
for node_name, args in pairs node_types
|
||||
index = {}
|
||||
for i, tuple in ipairs args
|
||||
prop_name = tuple[1]
|
||||
index[prop_name] = i + 1
|
||||
key_table[node_name] = index
|
||||
key_table
|
||||
|
||||
key_table = build_table!
|
||||
|
||||
|
||||
make_builder = (name) ->
|
||||
spec = node_types[name]
|
||||
error "don't know how to build node: "..name if not spec
|
||||
(props={}) ->
|
||||
node = { name }
|
||||
for i, arg in ipairs spec
|
||||
key, default_value = unpack arg
|
||||
val = if props[key] then props[key] else default_value
|
||||
val = {} if val == t
|
||||
node[i + 1] = val
|
||||
node
|
||||
|
||||
build = nil
|
||||
build = setmetatable {
|
||||
group: (body={}) ->
|
||||
{"group", body}
|
||||
|
||||
do: (body) ->
|
||||
{"do", body}
|
||||
|
||||
assign_one: (name, value) ->
|
||||
build.assign {
|
||||
names: {name}
|
||||
values: {value}
|
||||
}
|
||||
|
||||
table: (tbl={}) ->
|
||||
-- convert strings to key literals
|
||||
for tuple in *tbl
|
||||
if type(tuple[1]) == "string"
|
||||
tuple[1] = {"key_literal", tuple[1]}
|
||||
|
||||
{"table", tbl}
|
||||
block_exp: (body) ->
|
||||
{"block_exp", body}
|
||||
|
||||
chain: (parts) ->
|
||||
base = parts.base or error"expecting base property for chain"
|
||||
|
||||
if type(base) == "string"
|
||||
base = {"ref", base}
|
||||
|
||||
node = {"chain", base}
|
||||
for part in *parts
|
||||
insert node, part
|
||||
node
|
||||
}, {
|
||||
__index: (name) =>
|
||||
self[name] = make_builder name
|
||||
rawget self, name
|
||||
}
|
||||
|
||||
smart_node_mt = setmetatable {}, {
|
||||
__index: (node_type) =>
|
||||
index = key_table[node_type]
|
||||
mt = {
|
||||
smart_node: true
|
||||
|
||||
__index: (node, key) ->
|
||||
if index[key]
|
||||
rawget node, index[key]
|
||||
elseif type(key) == "string"
|
||||
error "unknown key: `"..key.."` on node type: `"..ntype(node).. "`"
|
||||
|
||||
__newindex: (node, key, value) ->
|
||||
key = index[key] if index[key]
|
||||
rawset node, key, value
|
||||
}
|
||||
self[node_type] = mt
|
||||
mt
|
||||
}
|
||||
|
||||
-- makes it so node properties can be accessed by name instead of index
|
||||
smart_node = (node) ->
|
||||
setmetatable node, smart_node_mt[ntype node]
|
||||
|
||||
NOOP = {"noop"}
|
||||
|
||||
{
|
||||
:ntype, :smart_node, :build, :is_value, :is_slice, :manual_return,
|
||||
:cascading, :value_is_singular,
|
||||
:value_can_be_statement, :mtype, :terminating
|
||||
:NOOP
|
||||
}
|
||||
|
213
prototypes/scp-clicker/moonscript/util.lua
Normal file
213
prototypes/scp-clicker/moonscript/util.lua
Normal file
@ -0,0 +1,213 @@
|
||||
local concat
|
||||
concat = table.concat
|
||||
local unpack = unpack or table.unpack
|
||||
local type = type
|
||||
local moon = {
|
||||
is_object = function(value)
|
||||
return type(value) == "table" and value.__class
|
||||
end,
|
||||
is_a = function(thing, t)
|
||||
if not (type(thing) == "table") then
|
||||
return false
|
||||
end
|
||||
local cls = thing.__class
|
||||
while cls do
|
||||
if cls == t then
|
||||
return true
|
||||
end
|
||||
cls = cls.__parent
|
||||
end
|
||||
return false
|
||||
end,
|
||||
type = function(value)
|
||||
local base_type = type(value)
|
||||
if base_type == "table" then
|
||||
local cls = value.__class
|
||||
if cls then
|
||||
return cls
|
||||
end
|
||||
end
|
||||
return base_type
|
||||
end
|
||||
}
|
||||
local pos_to_line
|
||||
pos_to_line = function(str, pos)
|
||||
local line = 1
|
||||
for _ in str:sub(1, pos):gmatch("\n") do
|
||||
line = line + 1
|
||||
end
|
||||
return line
|
||||
end
|
||||
local trim
|
||||
trim = function(str)
|
||||
return str:match("^%s*(.-)%s*$")
|
||||
end
|
||||
local get_line
|
||||
get_line = function(str, line_num)
|
||||
for line in str:gmatch("([^\n]*)\n?") do
|
||||
if line_num == 1 then
|
||||
return line
|
||||
end
|
||||
line_num = line_num - 1
|
||||
end
|
||||
end
|
||||
local get_closest_line
|
||||
get_closest_line = function(str, line_num)
|
||||
local line = get_line(str, line_num)
|
||||
if (not line or trim(line) == "") and line_num > 1 then
|
||||
return get_closest_line(str, line_num - 1)
|
||||
else
|
||||
return line, line_num
|
||||
end
|
||||
end
|
||||
local split
|
||||
split = function(str, delim)
|
||||
if str == "" then
|
||||
return { }
|
||||
end
|
||||
str = str .. delim
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for m in str:gmatch("(.-)" .. delim) do
|
||||
_accum_0[_len_0] = m
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end
|
||||
local dump
|
||||
dump = function(what)
|
||||
local seen = { }
|
||||
local _dump
|
||||
_dump = function(what, depth)
|
||||
if depth == nil then
|
||||
depth = 0
|
||||
end
|
||||
local t = type(what)
|
||||
if t == "string" then
|
||||
return '"' .. what .. '"\n'
|
||||
elseif t == "table" then
|
||||
if seen[what] then
|
||||
return "recursion(" .. tostring(what) .. ")...\n"
|
||||
end
|
||||
seen[what] = true
|
||||
depth = depth + 1
|
||||
local lines
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for k, v in pairs(what) do
|
||||
_accum_0[_len_0] = (" "):rep(depth * 4) .. "[" .. tostring(k) .. "] = " .. _dump(v, depth)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
lines = _accum_0
|
||||
end
|
||||
seen[what] = false
|
||||
return "{\n" .. concat(lines) .. (" "):rep((depth - 1) * 4) .. "}\n"
|
||||
else
|
||||
return tostring(what) .. "\n"
|
||||
end
|
||||
end
|
||||
return _dump(what)
|
||||
end
|
||||
local debug_posmap
|
||||
debug_posmap = function(posmap, moon_code, lua_code)
|
||||
local tuples
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for k, v in pairs(posmap) do
|
||||
_accum_0[_len_0] = {
|
||||
k,
|
||||
v
|
||||
}
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
tuples = _accum_0
|
||||
end
|
||||
table.sort(tuples, function(a, b)
|
||||
return a[1] < b[1]
|
||||
end)
|
||||
local lines
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #tuples do
|
||||
local pair = tuples[_index_0]
|
||||
local lua_line, pos = unpack(pair)
|
||||
local moon_line = pos_to_line(moon_code, pos)
|
||||
local lua_text = get_line(lua_code, lua_line)
|
||||
local moon_text = get_closest_line(moon_code, moon_line)
|
||||
local _value_0 = tostring(pos) .. "\t " .. tostring(lua_line) .. ":[ " .. tostring(trim(lua_text)) .. " ] >> " .. tostring(moon_line) .. ":[ " .. tostring(trim(moon_text)) .. " ]"
|
||||
_accum_0[_len_0] = _value_0
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
lines = _accum_0
|
||||
end
|
||||
return concat(lines, "\n")
|
||||
end
|
||||
local setfenv = setfenv or function(fn, env)
|
||||
local name
|
||||
local i = 1
|
||||
while true do
|
||||
name = debug.getupvalue(fn, i)
|
||||
if not name or name == "_ENV" then
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
if name then
|
||||
debug.upvaluejoin(fn, i, (function()
|
||||
return env
|
||||
end), 1)
|
||||
end
|
||||
return fn
|
||||
end
|
||||
local getfenv = getfenv or function(fn)
|
||||
local i = 1
|
||||
while true do
|
||||
local name, val = debug.getupvalue(fn, i)
|
||||
if not (name) then
|
||||
break
|
||||
end
|
||||
if name == "_ENV" then
|
||||
return val
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return nil
|
||||
end
|
||||
local get_options
|
||||
get_options = function(...)
|
||||
local count = select("#", ...)
|
||||
local opts = select(count, ...)
|
||||
if type(opts) == "table" then
|
||||
return opts, unpack({
|
||||
...
|
||||
}, nil, count - 1)
|
||||
else
|
||||
return { }, ...
|
||||
end
|
||||
end
|
||||
local safe_module
|
||||
safe_module = function(name, tbl)
|
||||
return setmetatable(tbl, {
|
||||
__index = function(self, key)
|
||||
return error("Attempted to import non-existent `" .. tostring(key) .. "` from " .. tostring(name))
|
||||
end
|
||||
})
|
||||
end
|
||||
return {
|
||||
moon = moon,
|
||||
pos_to_line = pos_to_line,
|
||||
get_closest_line = get_closest_line,
|
||||
get_line = get_line,
|
||||
trim = trim,
|
||||
split = split,
|
||||
dump = dump,
|
||||
debug_posmap = debug_posmap,
|
||||
getfenv = getfenv,
|
||||
setfenv = setfenv,
|
||||
get_options = get_options,
|
||||
unpack = unpack,
|
||||
safe_module = safe_module
|
||||
}
|
137
prototypes/scp-clicker/moonscript/util.moon
Normal file
137
prototypes/scp-clicker/moonscript/util.moon
Normal file
@ -0,0 +1,137 @@
|
||||
|
||||
import concat from table
|
||||
|
||||
unpack = unpack or table.unpack
|
||||
type = type
|
||||
|
||||
moon =
|
||||
is_object: (value) -> -- is a moonscript object
|
||||
type(value) == "table" and value.__class
|
||||
|
||||
is_a: (thing, t) ->
|
||||
return false unless type(thing) == "table"
|
||||
cls = thing.__class
|
||||
while cls
|
||||
if cls == t
|
||||
return true
|
||||
cls = cls.__parent
|
||||
|
||||
false
|
||||
|
||||
type: (value) -> -- the moonscript object class
|
||||
base_type = type value
|
||||
if base_type == "table"
|
||||
cls = value.__class
|
||||
return cls if cls
|
||||
base_type
|
||||
|
||||
-- convet position in text to line number
|
||||
pos_to_line = (str, pos) ->
|
||||
line = 1
|
||||
for _ in str\sub(1, pos)\gmatch("\n")
|
||||
line += 1
|
||||
line
|
||||
|
||||
trim = (str) ->
|
||||
str\match "^%s*(.-)%s*$"
|
||||
|
||||
get_line = (str, line_num) ->
|
||||
-- todo: this returns an extra blank line at the end
|
||||
for line in str\gmatch "([^\n]*)\n?"
|
||||
return line if line_num == 1
|
||||
line_num -= 1
|
||||
|
||||
get_closest_line = (str, line_num) ->
|
||||
line = get_line str, line_num
|
||||
if (not line or trim(line) == "") and line_num > 1
|
||||
get_closest_line(str, line_num - 1)
|
||||
else
|
||||
line, line_num
|
||||
|
||||
split = (str, delim) ->
|
||||
return {} if str == ""
|
||||
str ..= delim
|
||||
[m for m in str\gmatch("(.-)"..delim)]
|
||||
|
||||
dump = (what) ->
|
||||
seen = {}
|
||||
_dump = (what, depth=0) ->
|
||||
t = type what
|
||||
if t == "string"
|
||||
'"'..what..'"\n'
|
||||
elseif t == "table"
|
||||
if seen[what]
|
||||
return "recursion("..tostring(what) ..")...\n"
|
||||
seen[what] = true
|
||||
|
||||
depth += 1
|
||||
lines = for k,v in pairs what
|
||||
(" ")\rep(depth*4).."["..tostring(k).."] = ".._dump(v, depth)
|
||||
|
||||
seen[what] = false
|
||||
|
||||
"{\n" .. concat(lines) .. (" ")\rep((depth - 1)*4) .. "}\n"
|
||||
else
|
||||
tostring(what).."\n"
|
||||
|
||||
_dump what
|
||||
|
||||
|
||||
debug_posmap = (posmap, moon_code, lua_code) ->
|
||||
tuples = [{k, v} for k, v in pairs posmap]
|
||||
table.sort tuples, (a, b) -> a[1] < b[1]
|
||||
|
||||
lines = for pair in *tuples
|
||||
lua_line, pos = unpack pair
|
||||
moon_line = pos_to_line moon_code, pos
|
||||
|
||||
lua_text = get_line lua_code, lua_line
|
||||
moon_text = get_closest_line moon_code, moon_line
|
||||
|
||||
"#{pos}\t #{lua_line}:[ #{trim lua_text} ] >> #{moon_line}:[ #{trim moon_text} ]"
|
||||
|
||||
concat(lines, "\n")
|
||||
|
||||
setfenv = setfenv or (fn, env) ->
|
||||
local name
|
||||
i = 1
|
||||
while true
|
||||
name = debug.getupvalue fn, i
|
||||
break if not name or name == "_ENV"
|
||||
i += 1
|
||||
|
||||
if name
|
||||
debug.upvaluejoin fn, i, (-> env), 1
|
||||
|
||||
fn
|
||||
|
||||
getfenv = getfenv or (fn) ->
|
||||
i = 1
|
||||
while true
|
||||
name, val = debug.getupvalue fn, i
|
||||
break unless name
|
||||
return val if name == "_ENV"
|
||||
i += 1
|
||||
nil
|
||||
|
||||
-- moves the last argument to the front if it's a table, or returns empty table
|
||||
-- inserted to the front of args
|
||||
get_options = (...) ->
|
||||
count = select "#", ...
|
||||
opts = select count, ...
|
||||
if type(opts) == "table"
|
||||
opts, unpack {...}, nil, count - 1
|
||||
else
|
||||
{}, ...
|
||||
|
||||
safe_module = (name, tbl) ->
|
||||
setmetatable tbl, {
|
||||
__index: (key) =>
|
||||
error "Attempted to import non-existent `#{key}` from #{name}"
|
||||
}
|
||||
|
||||
{
|
||||
:moon, :pos_to_line, :get_closest_line, :get_line, :trim, :split, :dump,
|
||||
:debug_posmap, :getfenv, :setfenv, :get_options, :unpack, :safe_module
|
||||
}
|
||||
|
7
prototypes/scp-clicker/moonscript/version.lua
Normal file
7
prototypes/scp-clicker/moonscript/version.lua
Normal file
@ -0,0 +1,7 @@
|
||||
local version = "0.5.0"
|
||||
return {
|
||||
version = version,
|
||||
print_version = function()
|
||||
return print("MoonScript version " .. tostring(version))
|
||||
end
|
||||
}
|
8
prototypes/scp-clicker/moonscript/version.moon
Normal file
8
prototypes/scp-clicker/moonscript/version.moon
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
version = "0.5.0"
|
||||
|
||||
{
|
||||
version: version,
|
||||
print_version: ->
|
||||
print "MoonScript version #{version}"
|
||||
}
|
8
prototypes/scp-clicker/slab/init.lua
Normal file
8
prototypes/scp-clicker/slab/init.lua
Normal file
@ -0,0 +1,8 @@
|
||||
local path = ...
|
||||
if path:sub(-4) == "init" then
|
||||
path = path:sub(1, -6)
|
||||
if not (path) then
|
||||
path = "."
|
||||
end
|
||||
end
|
||||
return require(tostring(path) .. "/slab")
|
7
prototypes/scp-clicker/slab/init.moon
Normal file
7
prototypes/scp-clicker/slab/init.moon
Normal file
@ -0,0 +1,7 @@
|
||||
path = ...
|
||||
|
||||
if path\sub(-4) == "init"
|
||||
path = path\sub 1, -6
|
||||
path = "." unless path
|
||||
|
||||
return require "#{path}/slab"
|
180
prototypes/scp-clicker/slab/slab.lua
Normal file
180
prototypes/scp-clicker/slab/slab.lua
Normal file
@ -0,0 +1,180 @@
|
||||
local path = (...):sub(1, -(("slab"):len() + 2))
|
||||
local graphics
|
||||
graphics = love.graphics
|
||||
local slab = {
|
||||
_VERSION = "0.1.0",
|
||||
_DESCRIPTION = "GUI library for LÖVE.",
|
||||
_URL = nil,
|
||||
_LICENSE = "The MIT License (MIT)",
|
||||
_AUTHOR = "Paul Liverman III"
|
||||
}
|
||||
slab.ui = {
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = graphics.getWidth(),
|
||||
h = graphics.getHeight()
|
||||
}
|
||||
slab.hovered = false
|
||||
slab.focused = false
|
||||
slab.types = {
|
||||
"menu",
|
||||
"spinner",
|
||||
"text"
|
||||
}
|
||||
slab.generic = require(tostring(path) .. "/types/generic")
|
||||
local _list_0 = slab.types
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local t = _list_0[_index_0]
|
||||
slab[t] = require(tostring(path) .. "/types/" .. tostring(t))
|
||||
end
|
||||
slab.make = function(element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
if parent == nil then
|
||||
parent = slab.ui
|
||||
end
|
||||
local _list_1 = slab.types
|
||||
for _index_0 = 1, #_list_1 do
|
||||
local t = _list_1[_index_0]
|
||||
if element[t] then
|
||||
return slab[t](element, parent)
|
||||
end
|
||||
end
|
||||
return slab.generic(element, parent)
|
||||
end
|
||||
slab.update = function(dt, element)
|
||||
if element == nil then
|
||||
element = slab.ui
|
||||
end
|
||||
if element.update then
|
||||
element:update(dt)
|
||||
end
|
||||
for _index_0 = 1, #element do
|
||||
local child = element[_index_0]
|
||||
slab.update(dt, child)
|
||||
end
|
||||
end
|
||||
slab.draw = function(element)
|
||||
if element == nil then
|
||||
element = slab.ui
|
||||
end
|
||||
if element == slab.ui then
|
||||
graphics.push("all")
|
||||
graphics.origin()
|
||||
end
|
||||
local drawChildren
|
||||
if element.visible and element.draw then
|
||||
drawChildren = element:draw()
|
||||
end
|
||||
if drawChildren ~= false then
|
||||
for _index_0 = 1, #element do
|
||||
local child = element[_index_0]
|
||||
slab.draw(child)
|
||||
end
|
||||
end
|
||||
if element == slab.ui then
|
||||
return graphics.pop()
|
||||
end
|
||||
end
|
||||
slab.mousemoved = function(x, y, dx, dy, element)
|
||||
if element == nil then
|
||||
element = slab.ui
|
||||
end
|
||||
local previous
|
||||
if element == slab.ui then
|
||||
previous = slab.hovered
|
||||
end
|
||||
if element.visible and element.hoverable and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
slab.hovered = element
|
||||
for _index_0 = 1, #element do
|
||||
local child = element[_index_0]
|
||||
slab.mousemoved(x, y, dx, dy, child)
|
||||
end
|
||||
end
|
||||
if element == slab.ui then
|
||||
if slab.hovered ~= previous then
|
||||
if previous and previous.hovered then
|
||||
previous:hovered(false)
|
||||
end
|
||||
if slab.hovered.hovered then
|
||||
slab.hovered:hovered(true)
|
||||
end
|
||||
end
|
||||
if slab.focused and slab.focused.mousemoved then
|
||||
return slab.focused:mousemoved(x - slab.focused.x, y - slab.focused.y, dx, dy)
|
||||
end
|
||||
end
|
||||
end
|
||||
slab.mousepressed = function(x, y, btn, element)
|
||||
if element == nil then
|
||||
element = screen.ui
|
||||
end
|
||||
if element == screen.ui then
|
||||
if btn == "wd" then
|
||||
return slab.wheelmoved(0, -1)
|
||||
elseif btn == "wu" then
|
||||
return slab.wheelmoved(0, 1)
|
||||
end
|
||||
end
|
||||
local handled = false
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
for i = #element, 1, -1 do
|
||||
handled = slab.mousepressed(x, y, btn, element[i])
|
||||
if handled ~= false then
|
||||
return handled
|
||||
end
|
||||
end
|
||||
if element.mousepressed then
|
||||
handled = element:mousepressed(btn, x, y)
|
||||
end
|
||||
end
|
||||
return handled
|
||||
end
|
||||
slab.mousereleased = function(x, y, btn, element)
|
||||
local clickHandled, mousereleaseHandled = false, false
|
||||
if element then
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
for i = #element, 1, -1 do
|
||||
clickHandled, mousereleaseHandled = slab.mousereleased(x, y, btn, element[i])
|
||||
if clickHandled ~= false or mousereleaseHandled ~= false then
|
||||
return clickHandled, mousereleaseHandled
|
||||
end
|
||||
end
|
||||
if element.clicked then
|
||||
clickHandled = element:clicked(btn, x - element.x, y - element.y)
|
||||
end
|
||||
if element.mousereleased then
|
||||
mousereleaseHandled = element:mousereleased(btn, x - element.x, y - element.y)
|
||||
end
|
||||
if clickHandled then
|
||||
slab.focused = element
|
||||
end
|
||||
end
|
||||
else
|
||||
do
|
||||
element = slab.focused
|
||||
if element then
|
||||
if element.mousereleased then
|
||||
mousereleaseHandled = element:mousereleased(btn, x - element.x, y - element.y)
|
||||
end
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h then
|
||||
if element.clicked then
|
||||
clickHandled = element:clicked(btn, x - element.x, y - element.y)
|
||||
end
|
||||
end
|
||||
if clickHandled ~= false or mousereleaseHandled ~= false then
|
||||
return clickHandled, mousereleaseHandled
|
||||
end
|
||||
return slab.mousereleased(x, y, btn, slab.ui)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
slab.wheelmoved = function(x, y)
|
||||
if slab.hovered and slab.hovered.wheelmoved then
|
||||
return slab.hovered:wheelmoved(x, y)
|
||||
end
|
||||
return false
|
||||
end
|
||||
return slab
|
108
prototypes/scp-clicker/slab/slab.moon
Normal file
108
prototypes/scp-clicker/slab/slab.moon
Normal file
@ -0,0 +1,108 @@
|
||||
path = (...)\sub 1, -("slab"\len! + 2)
|
||||
|
||||
import graphics from love
|
||||
|
||||
slab = {
|
||||
_VERSION: "0.1.0"
|
||||
_DESCRIPTION: "GUI library for LÖVE."
|
||||
_URL: nil
|
||||
_LICENSE: "The MIT License (MIT)"
|
||||
_AUTHOR: "Paul Liverman III"
|
||||
}
|
||||
|
||||
slab.ui = { x: 0, y: 0, w: graphics.getWidth!, h: graphics.getHeight! }
|
||||
slab.hovered = false
|
||||
slab.focused = false
|
||||
slab.types = { "menu", "spinner", "text" }
|
||||
slab.generic = require "#{path}/types/generic"
|
||||
for t in *slab.types
|
||||
slab[t] = require "#{path}/types/#{t}"
|
||||
-- slab[t]\load slab if slab[t].load
|
||||
|
||||
slab.make = (element={}, parent=slab.ui) ->
|
||||
for t in *slab.types
|
||||
return slab[t](element, parent) if element[t]
|
||||
return slab.generic element, parent
|
||||
|
||||
slab.update = (dt, element=slab.ui) ->
|
||||
element\update dt if element.update
|
||||
for child in *element
|
||||
slab.update dt, child
|
||||
|
||||
slab.draw = (element=slab.ui) ->
|
||||
if element == slab.ui
|
||||
graphics.push "all"
|
||||
graphics.origin!
|
||||
|
||||
local drawChildren
|
||||
drawChildren = element\draw! if element.visible and element.draw
|
||||
if drawChildren != false
|
||||
for child in *element
|
||||
slab.draw child
|
||||
|
||||
if element == slab.ui
|
||||
graphics.pop!
|
||||
|
||||
slab.mousemoved = (x, y, dx, dy, element=slab.ui) ->
|
||||
local previous
|
||||
if element == slab.ui
|
||||
previous = slab.hovered
|
||||
|
||||
if element.visible and element.hoverable and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
slab.hovered = element
|
||||
for child in *element
|
||||
slab.mousemoved x, y, dx, dy, child
|
||||
|
||||
if element == slab.ui
|
||||
if slab.hovered != previous
|
||||
previous\hovered false if previous and previous.hovered
|
||||
slab.hovered\hovered true if slab.hovered.hovered
|
||||
if slab.focused and slab.focused.mousemoved
|
||||
return slab.focused\mousemoved x - slab.focused.x, y - slab.focused.y, dx, dy
|
||||
|
||||
slab.mousepressed = (x, y, btn, element=screen.ui) ->
|
||||
if element == screen.ui
|
||||
if btn == "wd"
|
||||
return slab.wheelmoved 0, -1
|
||||
elseif btn == "wu"
|
||||
return slab.wheelmoved 0, 1
|
||||
|
||||
handled = false
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
for i = #element, 1, -1
|
||||
handled = slab.mousepressed x, y, btn, element[i]
|
||||
return handled if handled != false
|
||||
|
||||
if element.mousepressed
|
||||
handled = element\mousepressed btn, x, y
|
||||
|
||||
return handled
|
||||
|
||||
slab.mousereleased = (x, y, btn, element) ->
|
||||
clickHandled, mousereleaseHandled = false, false
|
||||
if element
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
for i = #element, 1, -1
|
||||
clickHandled, mousereleaseHandled = slab.mousereleased x, y, btn, element[i]
|
||||
if clickHandled != false or mousereleaseHandled != false
|
||||
return clickHandled, mousereleaseHandled
|
||||
|
||||
clickHandled = element\clicked btn, x - element.x, y - element.y if element.clicked
|
||||
mousereleaseHandled = element\mousereleased btn, x - element.x, y - element.y if element.mousereleased
|
||||
slab.focused = element if clickHandled
|
||||
|
||||
elseif element = slab.focused
|
||||
mousereleaseHandled = element\mousereleased btn, x - element.x, y - element.y if element.mousereleased
|
||||
if element.visible and x >= element.x and x <= element.x + element.w and y >= element.y and y <= element.y + element.h
|
||||
clickHandled = element\clicked btn, x - element.x, y - element.y if element.clicked
|
||||
|
||||
if clickHandled != false or mousereleaseHandled != false
|
||||
return clickHandled, mousereleaseHandled
|
||||
|
||||
slab.mousereleased x, y, btn, slab.ui
|
||||
|
||||
slab.wheelmoved = (x, y) ->
|
||||
return slab.hovered\wheelmoved x, y if slab.hovered and slab.hovered.wheelmoved
|
||||
return false
|
||||
|
||||
return slab
|
90
prototypes/scp-clicker/slab/types/generic.lua
Normal file
90
prototypes/scp-clicker/slab/types/generic.lua
Normal file
@ -0,0 +1,90 @@
|
||||
local path = (...):sub(1, -(("generic"):len() + 2))
|
||||
local floor
|
||||
floor = math.floor
|
||||
local generic
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
draw = function(self) end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
if parent == nil then
|
||||
parent = slab.ui
|
||||
end
|
||||
for k, v in pairs(element) do
|
||||
self[k] = v
|
||||
end
|
||||
if self.visible == nil then
|
||||
self.visible = true
|
||||
end
|
||||
if self.hovered then
|
||||
self.hoverable = true
|
||||
end
|
||||
if self.hoverable == nil then
|
||||
self.hoverable = false
|
||||
end
|
||||
if self.width then
|
||||
if self.width <= 1 then
|
||||
self.w = floor(parent.w * self.width)
|
||||
else
|
||||
self.w = floor(self.width)
|
||||
end
|
||||
else
|
||||
self.width = 0
|
||||
self.w = 0
|
||||
end
|
||||
if self.height then
|
||||
if self.height <= 1 then
|
||||
self.h = floor(parent.h * self.height)
|
||||
else
|
||||
self.h = floor(self.height)
|
||||
end
|
||||
else
|
||||
self.height = 0
|
||||
self.h = 0
|
||||
end
|
||||
if "table" == type(self.background) then
|
||||
if not (self.color) then
|
||||
self.color = self.background
|
||||
end
|
||||
self.background = nil
|
||||
else
|
||||
if not (self.color) then
|
||||
self.color = {
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
}
|
||||
end
|
||||
end
|
||||
if self.background then
|
||||
self.aspectRatio = self.background:getWidth() / self.background:getHeight()
|
||||
end
|
||||
if #self > 0 then
|
||||
local slab = require(tostring(path:sub(1, -7)) .. "/slab")
|
||||
for _index_0 = 1, #self do
|
||||
local child = self[_index_0]
|
||||
slab.make(child, self)
|
||||
end
|
||||
end
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "generic"
|
||||
}, {
|
||||
__index = _base_0,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
generic = _class_0
|
||||
return _class_0
|
||||
end
|
76
prototypes/scp-clicker/slab/types/generic.moon
Normal file
76
prototypes/scp-clicker/slab/types/generic.moon
Normal file
@ -0,0 +1,76 @@
|
||||
path = (...)\sub 1, -("generic"\len! + 2)
|
||||
|
||||
import floor from math
|
||||
|
||||
-- local slab
|
||||
class generic
|
||||
-- @load: (lib) =>
|
||||
-- print "LOAD", self, lib
|
||||
-- slab = lib
|
||||
|
||||
new: (element={}, parent=slab.ui) =>
|
||||
for k,v in pairs element
|
||||
@[k] = v
|
||||
|
||||
@visible = true if @visible == nil
|
||||
@hoverable = true if @hovered
|
||||
@hoverable = false if @hoverable == nil
|
||||
|
||||
if @width
|
||||
if @width <= 1
|
||||
@w = floor parent.w * @width
|
||||
else
|
||||
@w = floor(@width)
|
||||
else
|
||||
@width = 0
|
||||
@w = 0
|
||||
|
||||
if @height
|
||||
if @height <= 1
|
||||
@h = floor parent.h * @height
|
||||
else
|
||||
@h = floor(@height)
|
||||
else
|
||||
@height = 0
|
||||
@h = 0
|
||||
|
||||
if "table" == type @background
|
||||
@color = @background unless @color
|
||||
@background = nil
|
||||
else
|
||||
@color = {255, 255, 255, 255} unless @color
|
||||
|
||||
if @background
|
||||
@aspectRatio = @background\getWidth! / @background\getHeight!
|
||||
|
||||
-- print @__class.__name
|
||||
if #@ > 0
|
||||
slab = require "#{path\sub 1, -7}/slab"
|
||||
for child in *@
|
||||
slab.make child, @
|
||||
|
||||
-- align if generic
|
||||
|
||||
draw: =>
|
||||
-- TODO set color and draw background
|
||||
|
||||
------------------------ NOTE s -----------------------
|
||||
-- 1. menu size set (full height, 1/3 width)
|
||||
-- 2. spinner size set to zero width, 1/3 height, no aspect ratio
|
||||
-- 3. generic size set to zero width (0.8*0) and zero height
|
||||
-- 4. text -> size handled by font
|
||||
|
||||
-- { -- menu (set size based on parent)
|
||||
-- { -- spinner (size NOT set, menu sets height only, need to set width based on aspect ratio (which is unknown initially))
|
||||
-- {} -- generic (w background image) (width set based on parent, height needs to be set by aspect ratio)
|
||||
-- {} -- text (size will be set by slab's default font size)
|
||||
-- }
|
||||
-- { -- spinner
|
||||
-- {} -- generic (w background image)
|
||||
-- {} -- text
|
||||
-- }
|
||||
-- { -- spinner
|
||||
-- {} -- generic (w background image)
|
||||
-- {} -- text
|
||||
-- }
|
||||
-- }
|
72
prototypes/scp-clicker/slab/types/menu.lua
Normal file
72
prototypes/scp-clicker/slab/types/menu.lua
Normal file
@ -0,0 +1,72 @@
|
||||
local path = (...):sub(1, -(("menu"):len() + 2))
|
||||
local generic = require(tostring(path) .. "/generic")
|
||||
local insert
|
||||
insert = table.insert
|
||||
local menu
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = generic
|
||||
local _base_0 = { }
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
_class_0.__parent.__init(self, element, parent)
|
||||
for _index_0 = 1, #self do
|
||||
local child = self[_index_0]
|
||||
if self.menu.width then
|
||||
if not (child.width) then
|
||||
child.width = self.menu.width
|
||||
end
|
||||
end
|
||||
if self.menu.height then
|
||||
if not (child.height) then
|
||||
child.height = self.menu.height
|
||||
end
|
||||
end
|
||||
if self.menu.align then
|
||||
if not (child.align) then
|
||||
child.align = self.menu.align
|
||||
end
|
||||
end
|
||||
end
|
||||
if #self.menu > 0 then
|
||||
local slab = require(tostring(path:sub(1, -7)) .. "/slab")
|
||||
local _list_0 = self.menu
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local child = _list_0[_index_0]
|
||||
insert(self, slab.make(child, self))
|
||||
end
|
||||
end
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "menu",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
menu = _class_0
|
||||
return _class_0
|
||||
end
|
21
prototypes/scp-clicker/slab/types/menu.moon
Normal file
21
prototypes/scp-clicker/slab/types/menu.moon
Normal file
@ -0,0 +1,21 @@
|
||||
path = (...)\sub 1, -("menu"\len! + 2)
|
||||
generic = require "#{path}/generic"
|
||||
|
||||
import insert from table
|
||||
|
||||
class menu extends generic
|
||||
new: (element={}, parent) =>
|
||||
super element, parent
|
||||
|
||||
for child in *@
|
||||
if @menu.width
|
||||
child.width = @menu.width unless child.width
|
||||
if @menu.height
|
||||
child.height = @menu.height unless child.height
|
||||
if @menu.align
|
||||
child.align = @menu.align unless child.align
|
||||
|
||||
if #@menu > 0
|
||||
slab = require "#{path\sub 1, -7}/slab"
|
||||
for child in *@menu
|
||||
insert(@, slab.make child, @)
|
61
prototypes/scp-clicker/slab/types/spinner.lua
Normal file
61
prototypes/scp-clicker/slab/types/spinner.lua
Normal file
@ -0,0 +1,61 @@
|
||||
local path = (...):sub(1, -(("spinner"):len() + 2))
|
||||
local generic = require(tostring(path) .. "/generic")
|
||||
local graphics
|
||||
graphics = love.graphics
|
||||
local pi, min
|
||||
do
|
||||
local _obj_0 = math
|
||||
pi, min = _obj_0.pi, _obj_0.min
|
||||
end
|
||||
local tau = pi * 2
|
||||
local half_pi = pi / 2
|
||||
local spinner
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = generic
|
||||
local _base_0 = {
|
||||
draw = function(self)
|
||||
return graphics.arc("line", self.x + self.w / 2, self.y + self.h / 2, min(self.w, self.h), self.offset, self.offset + self.value * tau)
|
||||
end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
_class_0.__parent.__init(self, element, parent)
|
||||
if not (self.offset) then
|
||||
self.offset = 0
|
||||
end
|
||||
self.value = 0.1
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "spinner",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
spinner = _class_0
|
||||
return _class_0
|
||||
end
|
21
prototypes/scp-clicker/slab/types/spinner.moon
Normal file
21
prototypes/scp-clicker/slab/types/spinner.moon
Normal file
@ -0,0 +1,21 @@
|
||||
path = (...)\sub 1, -("spinner"\len! + 2)
|
||||
generic = require "#{path}/generic"
|
||||
|
||||
import graphics from love
|
||||
import pi, min from math
|
||||
tau = pi * 2
|
||||
half_pi = pi / 2
|
||||
|
||||
class spinner extends generic
|
||||
new: (element={}, parent) =>
|
||||
super element, parent
|
||||
|
||||
-- @offset = -half_pi
|
||||
@offset = 0 unless @offset
|
||||
@value = 0.1 -- temporary
|
||||
|
||||
-- slab = require "#{path\sub 1, -7}/slab"
|
||||
|
||||
draw: =>
|
||||
-- TODO should be setting color!
|
||||
graphics.arc "line", @x + @w / 2, @y + @h / 2, min(@w, @h), @offset, @offset + @value * tau
|
48
prototypes/scp-clicker/slab/types/text.lua
Normal file
48
prototypes/scp-clicker/slab/types/text.lua
Normal file
@ -0,0 +1,48 @@
|
||||
local path = (...):sub(1, -(("text"):len() + 2))
|
||||
local generic = require(tostring(path) .. "/generic")
|
||||
local graphics
|
||||
graphics = love.graphics
|
||||
local text
|
||||
do
|
||||
local _class_0
|
||||
local _parent_0 = generic
|
||||
local _base_0 = {
|
||||
draw = function(self) end
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
setmetatable(_base_0, _parent_0.__base)
|
||||
_class_0 = setmetatable({
|
||||
__init = function(self, element, parent)
|
||||
if element == nil then
|
||||
element = { }
|
||||
end
|
||||
return _class_0.__parent.__init(self, element, parent)
|
||||
end,
|
||||
__base = _base_0,
|
||||
__name = "text",
|
||||
__parent = _parent_0
|
||||
}, {
|
||||
__index = function(cls, name)
|
||||
local val = rawget(_base_0, name)
|
||||
if val == nil then
|
||||
local parent = rawget(cls, "__parent")
|
||||
if parent then
|
||||
return parent[name]
|
||||
end
|
||||
else
|
||||
return val
|
||||
end
|
||||
end,
|
||||
__call = function(cls, ...)
|
||||
local _self_0 = setmetatable({}, _base_0)
|
||||
cls.__init(_self_0, ...)
|
||||
return _self_0
|
||||
end
|
||||
})
|
||||
_base_0.__class = _class_0
|
||||
if _parent_0.__inherited then
|
||||
_parent_0.__inherited(_parent_0, _class_0)
|
||||
end
|
||||
text = _class_0
|
||||
return _class_0
|
||||
end
|
15
prototypes/scp-clicker/slab/types/text.moon
Normal file
15
prototypes/scp-clicker/slab/types/text.moon
Normal file
@ -0,0 +1,15 @@
|
||||
path = (...)\sub 1, -("text"\len! + 2)
|
||||
generic = require "#{path}/generic"
|
||||
|
||||
import graphics from love
|
||||
|
||||
class text extends generic
|
||||
new: (element={}, parent) =>
|
||||
super element, parent
|
||||
|
||||
-- @font = graphics.newFont 14
|
||||
|
||||
-- slab = require "#{path\sub 1, -7}/slab"
|
||||
|
||||
draw: =>
|
||||
-- TODO
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user