commit a865abc08be3321711e894d7ff44d87ad92d600c Author: Martin Felis Date: Sat Mar 6 19:08:48 2021 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65b4681 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.*.swp +*.png +*.dot diff --git a/class.lua b/class.lua new file mode 100644 index 0000000..7d62707 --- /dev/null +++ b/class.lua @@ -0,0 +1,98 @@ +--[[ +Copyright (c) 2010-2013 Matthias Richter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Except as contained in this notice, the name(s) of the above copyright holders +shall not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +]]-- + +local function include_helper(to, from, seen) + if from == nil then + return to + elseif type(from) ~= 'table' then + return from + elseif seen[from] then + return seen[from] + end + + seen[from] = to + for k,v in pairs(from) do + k = include_helper({}, k, seen) -- keys might also be tables + if to[k] == nil then + to[k] = include_helper({}, v, seen) + end + end + return to +end + +-- deeply copies `other' into `class'. keys in `other' that are already +-- defined in `class' are omitted +local function include(class, other) + return include_helper(class, other, {}) +end + +-- returns a deep copy of `other' +local function clone(other) + return setmetatable(include({}, other), getmetatable(other)) +end + +local function new(class) + -- mixins + class = class or {} -- class can be nil + local inc = class.__includes or {} + if getmetatable(inc) then inc = {inc} end + + for _, other in ipairs(inc) do + if type(other) == "string" then + other = _G[other] + end + include(class, other) + end + + -- class implementation + class.__index = class + class.init = class.init or class[1] or function() end + class.include = class.include or include + class.clone = class.clone or clone + + -- constructor call + return setmetatable(class, {__call = function(c, ...) + local o = setmetatable({}, c) + o:init(...) + return o + end}) +end + +-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons). +if class_commons ~= false and not common then + common = {} + function common.class(name, prototype, parent) + return new{__includes = {prototype, parent}} + end + function common.instance(class, ...) + return class(...) + end +end + + +-- the module +return setmetatable({new = new, include = include, clone = clone}, + {__call = function(_,...) return new(...) end}) diff --git a/generate.lua b/generate.lua new file mode 100644 index 0000000..e570acc --- /dev/null +++ b/generate.lua @@ -0,0 +1,128 @@ +#!/usr/bin/lua + +--require 'strict' + +local Class = require 'class' + +Edge = Class { + init = function (self, s_node, e_node, label) + self.s = s_node + self.e = e_node + self.label = label + self.directed = false + end, +} + +Node = Class { + init = function (self, label) + self.label = label + self.edges = {} + end, +} + +Graph = Class { + init = function (self, name) + self.name = name + self.start_node = {} + self.end_node = {} + self.nodes = {} + self.edges = {} + end, + + writeDotFile = function (self, filename) + print ("writing file: " .. filename .. ".png") + local outfile = io.open (filename .. ".dot", "w+") + + outfile:write("digraph dungeon {\n") + + for i,e in ipairs (self.edges) do + outfile:write (e.s.label .. " -> " .. e.e.label) + + local options = {} + + if e.directed == false then + table.insert (options, "dir=none") + end + + if e.label ~= "" then + table.insert (options, "label=" .. e.label) + end + + local have_options = #options > 0 + if have_options then + outfile:write (" [") + end + + for i,v in ipairs(options) do + if i > 1 then + outfile:write (", ") + end + outfile:write (v) + end + + if have_options then + outfile:write(" ]") + end + + outfile:write ("\n") + end + outfile:write("}\n") + + outfile:close() + + os.execute ("dot -Tpng " .. filename .. ".dot -o " .. filename .. ".png") + os.execute ("eog " .. filename .. ".png") + end, + + insertSubGraph = function (self, edge_index, subgraph) + end +} + +function create_simple() + local graph = Graph("simple") + + local start_node = Node("SimpleStart") + local goal_node = Node("SimpleGoal") + local se_edge = Edge(start_node, goal_node, "se") + se_edge.directed = true + local es_edge = Edge(goal_node, start_node, "es") + + graph.start_node = start_node + graph.goal_node = goal_node + table.insert (graph.nodes, start_node) + table.insert (graph.nodes, goal_node) + table.insert (graph.edges, se_edge) + table.insert (graph.edges, es_edge) + + return graph +end + +function create_lock() + local graph = Graph("lock") + + local start_node = Node("LockStart") + local goal_node = Node("LockGoal") + local lock_node = Node("Lock") + local sl_edge = Edge(start_node, lock_node, "sl") + local ls_edge = Edge(lock_node, start_node, "ls_key") + ls_edge.directed = true + local lg_edge = Edge(lock_node, goal_node, "lg") + + graph.start_node = start_node + graph.goal_node = goal_node + + table.insert (graph.nodes, start_node) + table.insert (graph.nodes, lock_node) + table.insert (graph.nodes, goal_node) + table.insert (graph.edges, sl_edge) + table.insert (graph.edges, ls_edge) + table.insert (graph.edges, lg_edge) + + return graph +end + +local simple = create_simple() +simple:writeDotFile("simple") + +local lock = create_lock() +lock:writeDotFile("lock_graph") diff --git a/strict.lua b/strict.lua new file mode 100644 index 0000000..5012e35 --- /dev/null +++ b/strict.lua @@ -0,0 +1,122 @@ +--- Checks uses of undeclared global variables. +-- All global variables must be 'declared' through a regular assignment +-- (even assigning nil will do) in a main chunk before being used +-- anywhere or assigned to inside a function. Existing metatables __newindex and __index +-- metamethods are respected. +-- +-- You can set any table to have strict behaviour using strict.module. Creating a new +-- module with strict.closed_module makes the module immune to monkey-patching, if +-- you don't wish to encourage monkey business. +-- +-- If the global PENLIGHT_NO_GLOBAL_STRICT is defined, then this module won't make the +-- global environment strict - if you just want to explicitly set table strictness. +-- +-- @module pl.strict + +require 'debug' -- for Lua 5.2 +local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget +local strict = {} + +local function what () + local d = getinfo(3, "S") + return d and d.what or "C" +end + +--- make an existing table strict. +-- @string name name of table (optional) +-- @tab[opt] mod table - if nil then we'll return a new table +-- @tab[opt] predeclared - table of variables that are to be considered predeclared. +-- @return the given table, or a new table +function strict.module (name,mod,predeclared) + local mt, old_newindex, old_index, old_index_type, global, closed + if predeclared then + global = predeclared.__global + closed = predeclared.__closed + end + if type(mod) == 'table' then + mt = getmetatable(mod) + if mt and rawget(mt,'__declared') then return end -- already patched... + else + mod = {} + end + if mt == nil then + mt = {} + setmetatable(mod, mt) + else + old_newindex = mt.__newindex + old_index = mt.__index + old_index_type = type(old_index) + end + mt.__declared = predeclared or {} + mt.__newindex = function(t, n, v) + if old_newindex then + old_newindex(t, n, v) + if rawget(t,n)~=nil then return end + end + if not mt.__declared[n] then + if global then + local w = what() + if w ~= "main" and w ~= "C" then + error("assign to undeclared global '"..n.."'", 2) + end + end + mt.__declared[n] = true + end + rawset(t, n, v) + end + mt.__index = function(t,n) + if not mt.__declared[n] and what() ~= "C" then + if old_index then + if old_index_type == "table" then + local fallback = old_index[n] + if fallback ~= nil then + return fallback + end + else + local res = old_index(t, n) + if res then return res end + end + end + local msg = "variable '"..n.."' is not declared" + if name then + msg = msg .. " in '"..name.."'" + end + error(msg, 2) + end + return rawget(t, n) + end + return mod +end + +--- make all tables in a table strict. +-- So strict.make_all_strict(_G) prevents monkey-patching +-- of any global table +-- @tab T +function strict.make_all_strict (T) + for k,v in pairs(T) do + if type(v) == 'table' and v ~= T then + strict.module(k,v) + end + end +end + +--- make a new module table which is closed to further changes. +function strict.closed_module (mod,name) + local M = {} + mod = mod or {} + local mt = getmetatable(mod) + if not mt then + mt = {} + setmetatable(mod,mt) + end + mt.__newindex = function(t,k,v) + M[k] = v + end + return strict.module(name,M) +end + +if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then + strict.module(nil,_G,{_PROMPT=true,__global=true}) +end + +return strict