Модуль:Luagravity/expr

Материал из свободной русской энциклопедии «Традиция»
Перейти к навигации Перейти к поиску

Для документации этого модуля может быть создана страница Модуль:Luagravity/expr/doc

-- https://github.com/fsantanna/luagravity/blob/master/expr.lua
local _G  = _G
local gvt = require 'Module:Luagravity' -- corrected for wiki.

local setmetatable, assert, type, ipairs, getmetatable, unpack =
      setmetatable, assert, type, ipairs, getmetatable, unpack

-- module (...) -- adapted for wiki.
local p = {}
-- BEHAVIOR

p.mt_expr = {}

local function expr_set (self, value)
    if self.value == value then
        return gvt.cancel
    end
	self.value = value
    return value
end

function p.expr_get (self)
    return self.value
end

function p.is (expr)
    return getmetatable(expr) == p.mt_expr
end

function p.new (v)
    local self = setmetatable({
        value = v,
    }, p.mt_expr)
    self._set = gvt.create(expr_set, {name='set',obj=self,zero=true})
    return self
end

-- DELAY

local function delay_start (self, v)
    gvt.spawn(function()
        gvt.await(self.msecs.value)
        gvt.call(self._set, v)
    end)
end

function p.delay (expr, msecs)
    if not p.is(expr) then
        expr = p.new(expr)
    end
    if not p.is(msecs) then
        msecs = p.new(msecs or 0)
    end
    local self = p.new()
    self.msecs = msecs
    self._start = gvt.create(delay_start, {name='start',obj=self,zero=true})
    gvt.link(expr._set, self._start)
    gvt.call(self._start, expr.value)
    return self
end

-- BOOLEAN

local function cond_set (self, v)
    self.old = self.value
    return expr_set(self, v)
end
 
local function cond_true (self, v)
    local old, new = not(not self.old), not(not self.value)
    if (old ~= new) and new then
        return self.value
    else
        return gvt.cancel
    end
end

local function cond_false (self, v)
    local old, new = not(not self.old), not(not self.value)
    if (old ~= new) and (not new) then
        return self.value
    else
        return gvt.cancel
    end
end

function p.condition (expr)
    if not p.is(expr) then
        expr = p.new(expr)
    end
    local self  = p.new(expr.value)
    self._set   = gvt.create(cond_set,   {name='set',  obj=self,zero=true})
    self._true  = gvt.create(cond_true,  {name='true', obj=self,zero=true})
    self._false = gvt.create(cond_false, {name='false',obj=self,zero=true})
    gvt.link(expr._set,  self._set)
    gvt.link(self._set, self._true)
    gvt.link(self._set, self._false)
    return self
end

-- LIFT

local params = {}
local function lift_set (self, _)
	local srcs = self.srcs
	for i, src in ipairs(srcs) do
		if p.is(src) then
            params[i] = src.value
        else
            params[i] = src
        end
	end
    return expr_set(self, self.fun(unpack(params, 1, #srcs)))
end

function p.lift (fun)
    return function (...)
        local self = p.new(nil)
	    self.fun  = fun    ; assert(type(fun) == 'function')
	    self.srcs = {...}
        self._set = gvt.create(lift_set, {name='set',obj=self,zero=true})
        local rev = {}  -- avoids two equal links
	    for i, src in ipairs(self.srcs) do
            if not rev[src] then
                rev[src] = true
		        if p.is(src) then
                    gvt.link(src._set, self._set)
                end
		    end
	    end
        gvt.call(self._set)
        return self
    end
end

-- VAR

local function attr (self, src)
	if self.src == src then return end
	if p.is(self.src) then
		gvt.unlink(self.src._set, self._set)
	end
	self.src = src
	if p.is(src) then
		gvt.link(src._set, self._set)
        gvt.call(self._set, src.value)
	else
        gvt.call(self._set, src)
	end
end

function p.var (v)
    local self = p.new(v)
	self.src  = nil
    self.attr = attr
    return self
end

-- INTEGRAL / DERIVATIVE

local function integral_set (self, _)
    local value = self.value + self.expr.value*gvt.S ().dt
    self.value = value
    return value
end

function p.integral (expr)
    if not p.is(expr) then
        expr = p.new(expr)
    end
    local self = p.new(0)
    self.expr  = expr
    self._set = gvt.create(integral_set, {name='set',obj=self,zero=true})
    gvt.link('dt', self._set)
    gvt.link(expr._set, self._set)
    return self
end

local function derivative_set (self, _)
    local cur  = self.expr.value
    local last = self.last
    local value = (last and (cur-last)/gvt.S().dt) or nil
    self.last = cur
    self.value = value
    return value
end

function p.derivative (expr)
    if not p.is(expr) then
        expr = p.new(expr)
    end
    local self = p.new(nil)
    self.expr  = expr
    self.last = nil
    self._set = gvt.create(derivative_set, {name='set',obj=self,zero=true})
    gvt.link('dt', self._set)
    gvt.link(expr._set, self._set)
    return self
end

return p