Merge pull request #95 from Yonaba/master

Adds #91 - Added Binary Heaps Lua module
master
rjpcomputing 2015-09-21 13:21:04 -04:00
commit e7960051e3
11 changed files with 1656 additions and 0 deletions

View File

@ -0,0 +1,20 @@
Copyright (c) 2012-2013 Roland Yonaba
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.
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.

View File

@ -0,0 +1,98 @@
#Binary-Heaps#
Implementation of *binary heaps* data structure in pure Lua
##Usage##
Add 'binary_heap.lua' file inside your project.
Call it using __require__ function.
It will return a table containing a set of functions, acting as a class.
##API##
* __heap:new()__ : Returns a new heap ( a Min-Heap by default).
* __heap()__ : Same as heap:new()
* __heap:empty()__ : Checks if a heap is empty.
* __heap:getSize()__ : Returns the size of the heap.
* __heap:clear()__ : Clears a heap
* __heap:leftChildIndex(index)__ : Returns the left child index of element at position index in the heap
* __heap:rightChildIndex(index)__ : Returns the right child index of element at position index in the heap
* __heap:parentIndex(index)__ : Returns the parent index of element at position index in the heap
* __heap:insert(value,linkedData)__ : Inserts value with linked data in the heap and percolates it up at its proper place.
* __heap:add(value, linkedData)__ : Alias to <tt>heap.insert</tt>
* __heap:replace(value,linkedData)__ : Saves the top of the heap, adds a new element at the top and reorders the heap.
* __heap:pop()__ : Pops the top element, reorders the heap and returns this element unpacked : value first then data linked
* __heap:checkIndex()__ : checks existence of an element at position index in the heap.
* __heap:reset(function)__ : Reorders the current heap regards to the new comparison function given as argument
* __heap:merge(other)__ : merge the current heap with another
* __heap:isValid()__ : Checks if a heap is valid
* __heap:heap(item)__ : Restores the heap property (in case the heap was earlier found non-valid)
##Additionnal features##
```lua
h1+h2 : Returns a new heap with all data stored inside h1 and h2 heaps
tostring(h) : Returns a string representation of heap h
print(h) : Prints current heap h as a string
```
By default, you create Min-heaps. If you do need __Max-heaps__, you can easily create them this way:
```lua
local comp = function(a,b) return a>b end
local myHeap = heap(comp)
```
##Chaining##
Some functions can be chained together, as they return the heap itself:
```lua
heap:clear()
heap:add() or heap:insert()
heap:reset()
heap:merge()
heap:heap()
```
Example:
```lua
h = Heap()
h:add(1):add(2):heap():clear():add(3):add(4):merge(Heap()):reset()
print(h)
```
#Documentation used#
* [Algolist.net data structure course][]
* [Victor S.Adamchik's Lecture on Cs.cmu.edu][]
* [RPerrot's Article on Developpez.com][]
##License##
This work is under MIT-LICENSE
Copyright (c) 2012 Roland Yonaba
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.
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.
[Algolist.net data structure course]: http://www.algolist.net/Data_structures/Binary_heap/Array-based_int_repr
[Victor S.Adamchik's Lecture on Cs.cmu.edu]: http://www.cs.cmu.edu/~adamchik/15-121/lectures/Binary%20Heaps/heaps.html
[RPerrot's Article on Developpez.com]: http://rperrot.developpez.com/articles/algo/structures/arbres/
[Lua Class System]: http://yonaba.github.com/Lua-Class-System/
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Yonaba/binary-heaps/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

View File

@ -0,0 +1,35 @@
#Version history#
##1.5.1 (03/27/2013)
* `heap()` handles an optional arg `item`
* `heap()` now returns in case it wa found empty.
##1.5 (08/27/12)
* Added chaining
* Added <tt>Heap:add()</tt> as alias to <tt>Heap:insert()</tt>
* Buxfix with <tt>Heap:reset()</tt>
* Deleted unused <tt>Heap:init()</tt>
* Code cleaning, Indentation Fixed
##1.4 (08/01/2012)
* Made the current module independant from [LuaClassSystem][]
##1.3 (06/13/2012)
* Name clashing fixed : size() was renamed getSize()
##1.2 (05/28/12)
* Updated third-party library (Lua Class System)
* Added version_history.md
##1.1 (05/25/12)
* Converted to module
##1.0 (05/21/12)
* Heap class and instances now managed with Lua Class System
* Internal class structure modified, items now stored in a private "_heap" field
* Added heap:init(), heap:top(), heap:replace(), heap:heap()
##0.3 (05/14/12)
* Initial Release
[LuaClassSystem]: https://github.com/Yonaba/Lua-Class-System

View File

@ -0,0 +1,20 @@
CHANGELOG
=========
###0.2.0 (06/11/14)
* `strictness` no longer create globals. It returns a local table of library functions.
* `strictness` can now create (or convert) strict/unstrict tables (or environnements).
* Strict/unstrict rules are now applied per table (or environnement).
* Strict mode enforces variable declarations and complain on undefined fields access/assignment.
* Tables (or environnements) already existing metatables are preserved, including `__index` and `__newindex` fields.
* Added `strictness.strict` to convert a normal table (or environnement) to a strict one.
* Added `strictness.unstrict` to convert a strict table (or environnement) to a normal one.
* Added `strictness.is_strict` to check if a table (or environnement) is strict.
* Added `strictness.strictf` to wrap a normal function into a non strict function.
* Added `strictness.unstrictf` to wrap a normal function into a strict function.
* Added `strictness.run_strict` to run a normal function in strict mode.
* Added `strictness.run_unstrict` to run a normal function in non strict mode.
* Made compliant with Lua 5.2 new _ENV lexical scoping (although strictly speaking there are no globals in Lua 5.2).
###0.1.0 (10/05/13)
* Initial release

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013-2014 Roland Y.
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.
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.

View File

@ -0,0 +1,63 @@
strictness
===========
[![Build Status](https://travis-ci.org/Yonaba/strictness.png)](https://travis-ci.org/Yonaba/strictness)
[![Coverage Status](https://coveralls.io/repos/Yonaba/strictness/badge.png?branch=master)](https://coveralls.io/r/Yonaba/strictness?branch=master)
[![License](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE)
With the __Lua__ programming language, undeclared variables are not detected until runtime, as Lua will not complain when loading code.
This is releated to the convention that Lua uses : [global by default](http://www.lua.org/pil/1.2.html). In other words, when a variable is not recognized as *local*, it will be
interpreted as a *global* one, and will involve a lookup in the global environment `_G` (for Lua 5.1). Note that this behaviour has been addressed
in Lua 5.2, which strictly speaking has no globals, because of its [lexical scoping](http://www.luafaq.org/#T8.2.1).
*strictness* is a module to track *access and assignment* to undefined variables in your code. It *enforces* to declare globals and modules variables before
assigning them values. As such, it helps having a better control on the scope of variables across the code.
*strictness* is mostly meant to work with Lua [5.1](http://www.lua.org/versions.html#5.1), but it is compatible with Lua [5.2](http://www.lua.org/versions.html#5.2).
##Installation
####Git
git clone git://github.com/Yonaba/strictness
####Download
* See [releases](https://github.com/Yonaba/strictness/releases)
####LuaRocks
luarocks install strictness
####MoonRocks
moonrocks install strictness
or
luarocks install strictness --server=http://rocks.moonscript.org strictness
## Documentation
See [tutorial.md](doc/tutorial.md).
##Tests
This project has specification tests. To run these tests, execute the following command from the project root folder:
lua spec/tests.lua
##Similar projects
Feel free to check those alternate implementations, from with *strictness* takes some inspiration:
* [strict.lua](http://rtfc.googlecode.com/svn-history/r2/trunk/lua-5.1/etc/strict.lua) which is included in the official Lua 5.1 distribution,
* [pl.strict](https://github.com/stevedonovan/Penlight/blob/master/lua/pl/strict.lua) which is part of [Penlight](https://github.com/stevedonovan/Penlight),
##License
This work is under [MIT-LICENSE](http://www.opensource.org/licenses/mit-license.php)<br/>
*Copyright (c) 2013-2014 Roland Yonaba*.
See [LICENSE](LICENSE).
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Yonaba/strictness/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

View File

@ -0,0 +1,306 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>strictness documentation</title>
<link rel="stylesheet" href="ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>strictness</h1>
<h2>Contents</h2>
<ul>
<li><a href="#Functions">Functions</a></li>
</ul>
<h2>Modules</h2>
<ul class="$(kind=='Topics' and '' or 'nowrap'">
<li><strong>strictness</strong></li>
</ul>
</div>
<div id="content">
<h1>Module <code>strictness</code></h1>
<p><em>strictness, a "strict" mode for Lua</em>.</p>
<p> Source on <a href="http://github.com/Yonaba/strictness">Github</a></p>
<h3>Info:</h3>
<ul>
<li><strong>Copyright</strong>: 2013-2014</li>
<li><strong>License</strong>: MIT</li>
<li><strong>Author</strong>: Roland Yonaba</li>
</ul>
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#strict">strict ([t[, ...]])</a></td>
<td class="summary">Makes a given table strict.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#is_strict">is_strict (t)</a></td>
<td class="summary">Checks if a given table is strict.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unstrict">unstrict (t)</a></td>
<td class="summary">Makes a given table non-strict.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#strictf">strictf (f)</a></td>
<td class="summary">Creates a strict function.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#unstrictf">unstrictf (f)</a></td>
<td class="summary">Creates a non-strict function.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#run_strict">run_strict (f[, ...])</a></td>
<td class="summary">Returns the result of a function call in strict mode.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#run_unstrict">run_unstrict (f[, ...])</a></td>
<td class="summary">Returns the result of a function call in non-strict mode.</td>
</tr>
</table>
<br/>
<br/>
<h2><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "strict"></a>
<strong>strict ([t[, ...]])</strong>
</dt>
<dd>
Makes a given table strict. It mutates the passed-in table (or creates a
new table) and returns it. The returned table is strict, indexing or
assigning undefined fields will raise an error.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">t</span>
a table
</li>
<li><span class="parameter">...</span>
a vararg list of allowed fields in the table.
</li>
</ul>
<h3>Returns:</h3>
<ol>
the passed-in table <code>t</code> or a new table, patched to be strict.
</ol>
<h3>Usage:</h3>
<ul>
<pre class="example">
<span class="keyword">local</span> t = strictness.strict()
<span class="keyword">local</span> t2 = strictness.strict({})
<span class="keyword">local</span> t3 = strictness.strict({}, <span class="string">'field1'</span>, <span class="string">'field2'</span>)</pre>
</ul>
</dd>
<dt>
<a name = "is_strict"></a>
<strong>is_strict (t)</strong>
</dt>
<dd>
Checks if a given table is strict.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">t</span>
a table
</li>
</ul>
<h3>Returns:</h3>
<ol>
<code>true</code> if the table is strict, <code>false</code> otherwise.
</ol>
<h3>Usage:</h3>
<ul>
<pre class="example"><span class="keyword">local</span> is_strict = strictness.is_strict(a_table)</pre>
</ul>
</dd>
<dt>
<a name = "unstrict"></a>
<strong>unstrict (t)</strong>
</dt>
<dd>
Makes a given table non-strict. It mutates the passed-in table and
returns it. The returned table is non-strict.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">t</span>
a table
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example"><span class="keyword">local</span> unstrict_table = strictness.unstrict(trict_table)</pre>
</ul>
</dd>
<dt>
<a name = "strictf"></a>
<strong>strictf (f)</strong>
</dt>
<dd>
Creates a strict function. Wraps the given function and returns the wrapper.
The new function will always run in strict mode in its environment, whether
or not this environment is strict.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">f</span>
a function, or a callable value.
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example">
<span class="keyword">local</span> strict_f = strictness.strictf(a_function)
<span class="keyword">local</span> result = strict_f(...)</pre>
</ul>
</dd>
<dt>
<a name = "unstrictf"></a>
<strong>unstrictf (f)</strong>
</dt>
<dd>
Creates a non-strict function. Wraps the given function and returns the wrapper.
The new function will always run in non-strict mode in its environment, whether
or not this environment is strict.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">f</span>
a function, or a callable value.
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example">
<span class="keyword">local</span> unstrict_f = strictness.unstrictf(a_function)
<span class="keyword">local</span> result = unstrict_f(...)</pre>
</ul>
</dd>
<dt>
<a name = "run_strict"></a>
<strong>run_strict (f[, ...])</strong>
</dt>
<dd>
Returns the result of a function call in strict mode.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">f</span>
a function, or a callable value.
</li>
<li><span class="parameter">...</span>
a vararg list of arguments to function <code>f</code>.
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example"><span class="keyword">local</span> result = strictness.run_strict(a_function, arg1, arg2)</pre>
</ul>
</dd>
<dt>
<a name = "run_unstrict"></a>
<strong>run_unstrict (f[, ...])</strong>
</dt>
<dd>
Returns the result of a function call in non-strict mode.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">f</span>
a function, or a callable value.
</li>
<li><span class="parameter">...</span>
a vararg list of arguments to function <code>f</code>.
</li>
</ul>
<h3>Usage:</h3>
<ul>
<pre class="example"><span class="keyword">local</span> result = strictness.run_unstrict(a_function, arg1, arg2)</pre>
</ul>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.2</a></i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,302 @@
/* BEGIN RESET
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.8.2r1
*/
html {
color: #000;
background: #FFF;
}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset,img {
border: 0;
}
address,caption,cite,code,dfn,em,strong,th,var,optgroup {
font-style: inherit;
font-weight: inherit;
}
del,ins {
text-decoration: none;
}
li {
list-style: disc;
margin-left: 20px;
}
caption,th {
text-align: left;
}
h1,h2,h3,h4,h5,h6 {
font-size: 100%;
font-weight: bold;
}
q:before,q:after {
content: '';
}
abbr,acronym {
border: 0;
font-variant: normal;
}
sup {
vertical-align: baseline;
}
sub {
vertical-align: baseline;
}
legend {
color: #000;
}
input,button,textarea,select,optgroup,option {
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-weight: inherit;
}
input,button,textarea,select {*font-size:100%;
}
/* END RESET */
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color: #ffffff; margin: 0px;
}
code, tt { font-family: monospace; }
span.parameter { font-family:monospace; }
span.parameter:after { content:":"; }
span.types:before { content:"("; }
span.types:after { content:")"; }
.type { font-weight: bold; font-style:italic }
body, p, td, th { font-size: .95em; line-height: 1.2em;}
p, ul { margin: 10px 0 0 0px;}
strong { font-weight: bold;}
em { font-style: italic;}
h1 {
font-size: 1.5em;
margin: 0 0 20px 0;
}
h2, h3, h4 { margin: 15px 0 10px 0; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.06em; }
a:link { font-weight: bold; color: #004080; text-decoration: none; }
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
a:link:hover { text-decoration: underline; }
hr {
color:#cccccc;
background: #00007f;
height: 1px;
}
blockquote { margin-left: 3em; }
ul { list-style-type: disc; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
}
pre.example {
background-color: rgb(245, 245, 245);
border: 1px solid silver;
padding: 10px;
margin: 10px 0 10px 0;
font-family: "Andale Mono", monospace;
font-size: .85em;
}
pre {
background-color: rgb(245, 245, 245);
border: 1px solid silver;
padding: 10px;
margin: 10px 0 10px 0;
overflow: auto;
font-family: "Andale Mono", monospace;
}
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
#container {
margin-left: 1em;
margin-right: 1em;
background-color: #f0f0f0;
}
#product {
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#main {
background-color: #f0f0f0;
border-left: 2px solid #cccccc;
}
#navigation {
float: left;
width: 18em;
vertical-align: top;
background-color: #f0f0f0;
overflow: visible;
}
#navigation h2 {
background-color:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align: left;
padding:0.2em;
border-top:1px solid #dddddd;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 18em;
padding: 1em;
width: 700px;
border-left: 2px solid #cccccc;
border-right: 2px solid #cccccc;
background-color: #ffffff;
}
#about {
clear: both;
padding: 5px;
border-top: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight: bold; color: #004080; text-decoration: underline; }
#main {
background-color: #ffffff;
border-left: 0px;
}
#container {
margin-left: 2%;
margin-right: 2%;
background-color: #ffffff;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#navigation {
display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.module_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f0f0f0; min-width: 200px; }
table.module_list td.summary { width: 100%; }
table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name { background-color: #f0f0f0; min-width: 200px; }
table.function_list td.summary { width: 100%; }
ul.nowrap {
overflow:auto;
white-space:nowrap;
}
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
dl.table h3, dl.function h3 {font-size: .95em;}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* styles for prettification of source */
pre .comment { color: #558817; }
pre .constant { color: #a8660d; }
pre .escape { color: #844631; }
pre .keyword { color: #2239a8; font-weight: bold; }
pre .library { color: #0e7c6b; }
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
pre .string { color: #a8660d; }
pre .number { color: #f8660d; }
pre .operator { color: #2239a8; font-weight: bold; }
pre .preprocessor, pre .prepro { color: #a33243; }
pre .global { color: #800080; }
pre .prompt { color: #558817; }
pre .url { color: #272fc2; text-decoration: underline; }

View File

@ -0,0 +1,277 @@
strictness tutorial
===================
# <a name='TOC'>Table of Contents</a>
* [What is *strictness* ?](#what)
* [Tutorial](#tuto)
* [Adding *strictness* to your project](#adding)
* [The *strictness* module](#module)
* [Strict tables](#stricttables)
* [Non-strict tables](#unstricttables)
* [Checking strictness](#checking)
* [Strict functions](#strictf)
* [Non-strict functions](#unstrictf)
* [Combo functions](#combo)
* [License](#license)
# <a name='what'>What is *strictness* ?</a>
*strictness* is a Lua module for tracking accesses and assignements to indefined variables in Lua code. It is actually known that [undefined variables](http://lua-users.org/wiki/DetectingUndefinedVariables) and global variables as well can be very problematic especially when working on large projects and maintaining code that spans across several files.
*strictness* aims to address this problem by providing a solution similar to [strict structs](http://lua-users.org/wiki/StrictStructs), so that accessing undefined fields will always throw an error.
**[[⬆]](#TOC)**
# <a name='tuto'>Tutorial</a>
## <a name='adding'>Adding *strictness* to your project</a>
Place the file [strictness.lua](strictness.lua) in your Lua project and call it with [require](http://pgl.yoyo.org/luai/i/require). *strictness* does not write anything in the global (or the current) environnement. It rather returns a local module of functions.
```lua
local strictness = require "strictness"
````
**[[⬆]](#TOC)**
## <a name='module'>The *strictness* module</a>
### <a name='stricttables'>Strict tables</a>
*strictness* provides the function `strictness.strict` that patches a given table, so that we can no longer access to undefined keys in this table.
Let us apply appy this on the global environnement:
```lua
strictness.strict(_G)
print(x) --> this line produces an error
````
The statement `print(x)`produces the following error:
````
...\test.lua:2: Attempt to access undeclared variable "x" in <table: 0x00321328>.
```
To avoid this, we now have to __declare explitely__ our globals. Assigning `nil` will do:
```lua
strictness.strict(_G)
x = nil
print(x) --> nil
x = 3
print(x) --> 3
````
A table can be made strict with allowed varnames.
```lua
strictness.strict(_G, 'x', 'y', 'z') -- "varnames x, y and z are allowed"
print(x, y, z) --> nil, nil, nil
x, y, z = 1, 2, 3
print(x, y, z) --> 1, 2, 3
````
Also, in case nothing is passed to `strictness.strict`, it will return a new table:
```lua
local t = strictness.strict()
print(t.k) --> produces an error
t.k = nil --> declare a field "k"
print(t.k) --> nil
````
`strictness.strict` preserves the metatable of the passed-in table.
```lua
local t = setmetatable({}, {
__call = function() return 'call' end,
__tostring = function() return t.name end
})
t.name = 'table t'
strictness.strict(t)
print(t()) --> "call"
print(t) --> "table t"
````
In case a table was already made strict, passing it again to `strictness.strict` will raise an error:
```lua
local t = {}
strictness.strict(t)
strictness.strict(t) --> this will produce an error
````
````
...\test.lua:3: <table: 0x0032c110> was already made strict.
````
**[[⬆]](#TOC)**
### <a name='unstricttables'>Non-Strict (or normal) tables</a>
A strict table can be converted back to a normal one via `strictness.unstrict`:
```lua
local t = strictness.strict()
strictness.unstrict(t)
t.k = 5
print(t.k) --> 5
````
**[[⬆]](#TOC)**
### <a name='checking'>Checking strictness</a>
`strictness.is_strict` checks if a given table was patched via `strictness.strict`:
```lua
local strict_table = strictness.strict()
local normal_table = {}
print(strictness.is_strict(strict_table)) --> true
print(strictness.is_strict(normal_table)) --> false
````
**[[⬆]](#TOC)**
### <a name='strictf'>Strict functions</a>
`strictness.strictf` returns a wrapper function that runs the original function in strict mode. The returned function is not allowed to write or access undefined fields in its environment. Let us draw an example:
```lua
local env = {} -- a blank environment for our functions
-- A function that writes a varname and assigns it a value
local function normal_f(varname, value)
env[varname] = value
end
-- Convert the original function to a strict one
local strict_f = strictness.strictf(normal_f)
-- set environments for functions
setfenv(normal_f, env)
setfenv(strict_f, env)
-- Call the normal function, no error
normal_f("var1", "hello")
print(env.var1) --> "hello"
strict_f("var2", "hello") --> produces an error
````
````
...\test.lua:5: Attempt to assign value to an undeclared variable "var2" in <table: 0x0032c440>.
````
Notice that here, the strict function always run in strict mode whether its environment is strict or not.
**[[⬆]](#TOC)**
### <a name='unstrictf'>Non-strict functions</a>
Similarly, `strictness.unstrictf` creates a wrapper function that runs in non-strict mode in its environment. In other terms, the returned function is allowed to access and assign values in its environments, whether or not this environment is strict.
```lua
local env = strictness.strict() -- a blank and strict environment for our functions
-- A function that assigns a value to a variable named "some_var"
local function normal_f(value)
some_var = value
end
-- Converts the original function to a non-strict one
local unstrict_f = strictness.unstrictf(normal_f)
-- set environments for functions
setfenv(normal_f, env)
setfenv(unstrict_f, env)
-- Call the normal function, it should err because its env is strict
normal_f("hello") --> produces an error
-- Call the non-strict function, no error
unstrict_f("hello")
print(env.some_var) --> "hello
````
Here is an example with Lua 5.2:
```lua
local new_env = {print = print} -- a new env
do
local _ENV = strictness.strict(new_env) -- sets a new strict env for the do..end scope
local function normal_f(value) some_var = value end -- our normal function
normal_f(5) --> produces an error, since normal_f cannot write in the strict _ENV
end
````
```lua
local new_env = {print = print} -- a new env
do
local _ENV = strictness.strict(new_env) -- sets a new strict env for the do..end scope
local function normal_f(value) some_var = value end -- our normal function
local unstrict_f = strictness.unstrictf(normal_f) -- the non-strict version of our normal function
unstrict_f(5) -- no longer produces error
print(some_var) --> 5
end
````
**[[⬆]](#TOC)**
### <a name='combo'>Combo functions</a>
*strictness* also provides two combo functions, `strictness.run_strict` and `strictness.run_unstrict`. Those functions takes a function `f` plus an optional vararg `...` and return the result of the call `f(...)` in strict and non-strict mode respectively.
Syntactically speaking, `strictnes.run_strict` is the equivalent to this:
```lua
local strict_f = strictness.strictf(f)
strict_f(...)
````
While `strictness.run_unstrict` is a short for:
```lua
local unstrict_f = strictness.unstrictf(f)
unstrict_f(...)
````
Here is an example for `strictness.run_strict`:
```lua
local strictness = require 'strictness'
local env = {} -- an environment
-- A function that assigns a value to a variable named "some_var"
local function normal_f(value) some_var = value end
setfenv(normal_f, env) -- defines an env for normal_f
strictness.run_strict(normal_f, 3) --> produces an error
````
And another example with `strictness.run_unstrict``:
```lua
local strictness = require 'strictness'
local env = strictness.strict() -- a strict environment
-- A function that assigns a value to a variable named "some_var"
local function normal_f(value) some_var = value end
setfenv(normal_f, env) -- defines an env for normal_f
strictness.run_unstrict`(normal_f, 3) -- no error!
print(env.some_var, some_var) --> 3, nil
````
**[[⬆]](#TOC)**
# <a name='license'>LICENSE</a>
This work is under [MIT-LICENSE](http://www.opensource.org/licenses/mit-license.php)<br/>
*Copyright (c) 2013-2014 Roland Yonaba*.<br/>
See [LICENSE](http://github.com/Yonaba/strictness/blob/master/LICENSE).
**[[⬆]](#TOC)**

256
files/lua/binary_heap.lua Normal file
View File

@ -0,0 +1,256 @@
-- Copyright (c) 2012-2013 Roland Yonaba
-- An implementation of Binary Heaps data structure in pure Lua
--[[
Documentation :
- http://www.algolist.net/Data_structures/Binary_heap/Array-based_int_repr
- http://www.cs.cmu.edu/~adamchik/15-121/lectures/Binary%20Heaps/heaps.html
- http://rperrot.developpez.com/articles/algo/structures/arbres/
--]]
local require = require
local assert = assert
local ipairs = ipairs
local pairs = pairs
local floor = math.floor
local tostring = tostring
local setmetatable = setmetatable
-- Default sorting function.
-- Used for Min-Heaps creation.
local function f_min(a,b) return a < b end
-- Value lookup in a table
local indexOf = function(t,v)
for i = 1,#t do
if t[i] == v then return i end
end
return nil
end
-- Percolates up datum in the heap recursively
local function percolate_up(self,index)
local pIndex
if index > 1 then
pIndex = floor(index/2)
if self._heap[pIndex] then
if not (self.sort(self._heap[pIndex].value,self._heap[index].value)) then
self._heap[pIndex],self._heap[index] = self._heap[index],self._heap[pIndex]
percolate_up(self,pIndex) -- Recursive call from the parent index
end
else
return
end
end
end
-- Percolates down datum in the heap recursively
local function percolate_down(self,index)
local lfIndex,rtIndex,minIndex
lfIndex = 2*index
rtIndex = lfIndex + 1
if rtIndex > self.size then
if lfIndex > self.size then return
else minIndex = lfIndex end
else
if self.sort(self._heap[lfIndex].value,self._heap[rtIndex].value) then
minIndex = lfIndex
else
minIndex = rtIndex
end
end
if not self.sort(self._heap[index].value,self._heap[minIndex].value) then
self._heap[index],self._heap[minIndex] = self._heap[minIndex],self._heap[index]
percolate_down(self,minIndex) -- Recursive call from the newly shifted index
end
end
-- Minimalistic heap class constructor
local function newHeap(class,comp)
return setmetatable({_heap = {},sort = comp or f_min, size = 0},class)
end
-- The heap class
local heap = setmetatable({}, {__call = function(self,...) return newHeap(self,...) end})
heap.__index = heap
-- Checks if a heap is empty
-- Return true or false [boolean]
function heap:empty()
return (self.size==0)
end
-- Gets heap size (the very number of elements stored in the heap)
-- Returns the heap size [number]
function heap:getSize()
return self.size
end
-- Clears the heap
-- Returns nothing [nil]
function heap:clear()
self._heap = {}
self.size = 0
return self
end
-- Returns the left child index of the current index
-- Returned index may not be a valid index in the heap
-- Returns this index [number]
function heap:leftChildIndex(index)
return (2*index)
end
-- Returns the right child index of the current index
-- Returned index may not be a valid index in the heap
-- Returns this index [number]
function heap:rightChildIndex(index)
return 2*index+1
end
-- Returns the parent index of the current index
-- Returned index may not be a valid index in the heap
-- Returns this index [number]
function heap:parentIndex(index)
return floor(index/2)
end
-- Returns the top element in the heap
-- Does not pop the heap
function heap:top()
assert(not self:empty(),'Heap is empty')
return self._heap[1].value,self._heap[1].data
end
-- Inserts a value in the heap as a table {value = value, data = data}
-- <data> Argument is optional and may represent extra information linked to <value> argument.
-- Returns nothing [nil]
function heap:insert(value,data)
self.size = self.size + 1
self._heap[self.size] = {value = value, data = data}
percolate_up(self,self.size)
return self
end
heap.add = heap.insert
-- Pops the first element in the heap
-- Returns this element unpacked: value first then data linked
function heap:pop()
assert(not self:empty(), 'Heap is empty.')
local root = self._heap[1]
self._heap[1] = self._heap[self.size]
self._heap[self.size] = nil
self.size = self.size-1
if self.size>1 then
percolate_down(self,1)
end
return root.value,root.data
end
-- Checks if the given index is valid in the heap
-- Returns the element stored in the heap at that very index [table], otherwise nil. [nil]
function heap:checkIndex(index)
return self._heap[index] or nil
end
-- Pops the first element in the heap
-- Replaces it with the given element and reorders the heap
-- The size of the heap is preserved
function heap:replace(value,data)
assert(not self:empty(), 'heap is empty, use insert()')
local root = self._heap[1]
self._heap[1] = {value = value,data = data}
percolate_down(self,1)
return root.value,root.data
end
-- Resets the heap property regards to the comparison function given as argument (Optional)
-- Returns nothing [nil]
function heap:reset(comp)
self.sort = comp or self.sort
local _heap = self._heap
self._heap = {}
self.size = 0
for i in pairs(_heap) do
self:insert(_heap[i].value,_heap[i].data)
end
return self
end
-- Appends a heap contents to the current one
-- Returns nothing [nil]
function heap:merge(other)
assert(self:isValid(),'Self heap is not a valid heap')
assert(other:isValid(),'Argument is not a valid heap')
assert(self.sort(1,2) == other.sort(1,2),'Heaps must have the same sort functions')
for i,node in ipairs(other._heap) do
self:insert(node.value,node.data)
end
return self
end
-- Shortcut for merging heaps with '+' operator
-- Returns a new heap based on h1+h2 [table]
function heap.__add(h1,h2)
local h = heap()
h:merge(h1)
h:merge(h2)
return h
end
-- Tests if each element stored in a heap is located at the right place
-- Returns true on success, false on error. [boolean]
function heap:isValid()
if self.size <= 1 then return true end
local i = 1
local lfIndex,rtIndex
for i = 1,(floor(self.size/2)) do
lfIndex = 2*i
rtIndex = lfIndex+1
if self:checkIndex(lfIndex) then
if not self.sort(self._heap[i].value,self._heap[lfIndex].value) then
return false
end
end
if self:checkIndex(rtIndex) then
if not self.sort(self._heap[i].value,self._heap[rtIndex].value) then
return false
end
end
end
return true
end
-- Restores the heap property
-- Should be used when a heap was found non-valid
function heap:heap(item)
if (self.size == 0) then return end
if item then
local i = indexOf(self.__heap,item)
if i then
percolate_down(self, i)
percolate_up(self, i)
end
return
end
for i = floor(self.size/2),1,-1 do
percolate_down(self,i)
end
return self
end
-- (Debug utility) Create a string representation of the current
-- Returns this string to be used with print() or tostring() [string]
function heap.__tostring(self)
local out = ''
for k in ipairs(self._heap) do
out = out.. (('Element %d - Value : %s\n'):format(k,tostring(self._heap[k].value)))
end
return out
end
return heap

259
files/lua/strictness.lua Normal file
View File

@ -0,0 +1,259 @@
#!/usr/bin/env lua
------------------
-- *strictness, a "strict" mode for Lua*.
-- Source on [Github](http://github.com/Yonaba/strictness)
-- @author Roland Yonaba
-- @copyright 2013-2014
-- @license MIT
local _LUA52 = _VERSION:match('Lua 5.2')
local setmetatable, getmetatable = setmetatable, getmetatable
local pairs, ipairs = pairs, ipairs
local rawget, rawget = rawget, rawget
local unpack = _LUA52 and table.unpack or unpack
local tostring, select, error = tostring, select, error
local getfenv = getfenv
local _MODULEVERSION = '0.2.0'
----------------------------- Private definitions -----------------------------
if _LUA52 then
-- Provide a replacement for getfenv in Lua 5.2, using the debug library
-- Taken from: http://lua-users.org/lists/lua-l/2010-06/msg00313.html
-- Slightly modified to handle f being nil and return _ENV if f is global.
getfenv = function(f)
f = (type(f) == 'function' and f or debug.getinfo((f or 0) + 1, 'f').func)
local name, val
local up = 0
repeat
up = up + 1
name, val = debug.getupvalue(f, up)
until name == '_ENV' or name == nil
return val~=nil and val or _ENV
end
end
-- Lua reserved keywords
local is_reserved_keyword = {
['and'] = true, ['break'] = true, ['do'] = true, ['else'] = true,
['elseif'] = true, ['end'] = true, ['false'] = true, ['for'] = true,
['function'] = true, ['if'] = true, ['in'] = true, ['local'] = true,
['nil'] = true, ['not'] = true, ['or'] = true, ['repeat'] = true,
['return'] = true, ['then'] = true, ['true'] = true, ['until'] = true,
['while'] = true,
}; if _LUA52 then is_reserved_keyword['goto'] = true end
-- Throws an error if cond
local function complain_if(cond, msg, level)
return cond and error(msg, level or 3)
end
-- Checks if iden match an valid Lua identifier syntax
local function is_identifier(iden)
return tostring(iden):match('^[%a_]+[%w_]*$') and
not is_reserved_keyword[iden]
end
-- Checks if all elements of vararg are valid Lua identifiers
local function validate_identifiers(...)
local arg, varnames= {...}, {}
for i, iden in ipairs(arg) do
complain_if(not is_identifier(iden),
('varname #%d "<%s>" is not a valid Lua identifier.')
:format(i, tostring(iden)),4)
varnames[iden] = true
end
return varnames
end
-- add true keys in register all keys in t
local function add_allowed_keys(t,register)
for key in pairs(t) do
if is_identifier(key) then register[key] = true end
end
return register
end
-- Checks if the given arg is callable
local function callable(f)
return type(f) == 'function' or (getmetatable(f) and getmetatable(f).__call)
end
------------------------------- Module functions ------------------------------
--- Makes a given table strict. It mutates the passed-in table (or creates a
-- new table) and returns it. The returned table is strict, indexing or
-- assigning undefined fields will raise an error.
-- @function strictness.strict
-- @param[opt] t a table
-- @param[opt] ... a vararg list of allowed fields in the table.
-- @return the passed-in table `t` or a new table, patched to be strict.
-- @usage
-- local t = strictness.strict()
-- local t2 = strictness.strict({})
-- local t3 = strictness.strict({}, 'field1', 'field2')
local function make_table_strict(t, ...)
t = t or {}
local has_mt = getmetatable(t)
complain_if(type(t) ~= 'table',
('Argument #1 should be a table, not %s.'):format(type(t)),3)
local mt = getmetatable(t) or {}
complain_if(mt.__strict,
('<%s> was already made strict.'):format(tostring(t)),3)
local varnames = v
mt.__allowed = add_allowed_keys(t, validate_identifiers(...))
mt.__predefined_index = mt.__index
mt.__predefined_newindex = mt.__newindex
mt.__index = function(tbl, key)
if not mt.__allowed[key] then
if mt.__predefined_index then
local expected_result = mt.__predefined_index(tbl, key)
if expected_result then return expected_result end
end
complain_if(true,
('Attempt to access undeclared variable "%s" in <%s>.')
:format(key, tostring(tbl)),3)
end
return rawget(tbl, key)
end
mt.__newindex = function(tbl, key, val)
if mt.__predefined_newindex then
mt.__predefined_newindex(tbl, key, val)
if rawget(tbl, key) ~= nil then return end
end
if not mt.__allowed[key] then
if val == nil then
mt.__allowed[key] = true
return
end
complain_if(not mt.__allowed[key],
('Attempt to assign value to an undeclared variable "%s" in <%s>.')
:format(key,tostring(tbl)),3)
mt.__allowed[key] = true
end
rawset(tbl, key, val)
end
mt.__strict = true
mt.__has_mt = has_mt
return setmetatable(t, mt)
end
--- Checks if a given table is strict.
-- @function strictness.is_strict
-- @param t a table
-- @return `true` if the table is strict, `false` otherwise.
-- @usage
-- local is_strict = strictness.is_strict(a_table)
local function is_table_strict(t)
complain_if(type(t) ~= 'table',
('Argument #1 should be a table, not %s.'):format(type(t)),3)
return not not (getmetatable(t) and getmetatable(t).__strict)
end
--- Makes a given table non-strict. It mutates the passed-in table and
-- returns it. The returned table is non-strict.
-- @function strictness.unstrict
-- @param t a table
-- @usage
-- local unstrict_table = strictness.unstrict(trict_table)
local function make_table_unstrict(t)
complain_if(type(t) ~= 'table',
('Argument #1 should be a table, not %s.'):format(type(t)),3)
if is_table_strict(t) then
local mt = getmetatable(t)
if not mt.__has_mt then
setmetatable(t, nil)
else
mt.__index, mt.__newindex = mt.__predefined_index, mt.__predefined_newindex
mt.__strict, mt.__allowed, mt.__has_mt = nil, nil, nil
mt.__predefined_index, mt.__predefined_newindex = nil, nil
end
end
return t
end
--- Creates a strict function. Wraps the given function and returns the wrapper.
-- The new function will always run in strict mode in its environment, whether
-- or not this environment is strict.
-- @function strictness.strictf
-- @param f a function, or a callable value.
-- @usage
-- local strict_f = strictness.strictf(a_function)
-- local result = strict_f(...)
local function make_function_strict(f)
complain_if(not callable(f),
('Argument #1 should be a callable, not %s.'):format(type(f)),3)
return function(...)
local ENV = getfenv(f)
local was_strict = is_table_strict(ENV)
if not was_strict then make_table_strict(ENV) end
local results = {f(...)}
if not was_strict then make_table_unstrict(ENV) end
return unpack(results)
end
end
--- Creates a non-strict function. Wraps the given function and returns the wrapper.
-- The new function will always run in non-strict mode in its environment, whether
-- or not this environment is strict.
-- @function strictness.unstrictf
-- @param f a function, or a callable value.
-- @usage
-- local unstrict_f = strictness.unstrictf(a_function)
-- local result = unstrict_f(...)
local function make_function_unstrict(f)
complain_if(not callable(f),
('Argument #1 should be a callable, not %s.'):format(type(f)),3)
return function(...)
local ENV = getfenv(f)
local was_strict = is_table_strict(ENV)
make_table_unstrict(ENV)
local results = {f(...)}
if was_strict then make_table_strict(ENV) end
return unpack(results)
end
end
--- Returns the result of a function call in strict mode.
-- @function strictness.run_strict
-- @param f a function, or a callable value.
-- @param[opt] ... a vararg list of arguments to function `f`.
-- @usage
-- local result = strictness.run_strict(a_function, arg1, arg2)
local function run_strict(f,...)
complain_if(not callable(f),
('Argument #1 should be a callable, not %s.'):format(type(f)),3)
return make_function_strict(f)(...)
end
--- Returns the result of a function call in non-strict mode.
-- @function strictness.run_unstrict
-- @param f a function, or a callable value.
-- @param[opt] ... a vararg list of arguments to function `f`.
-- @usage
-- local result = strictness.run_unstrict(a_function, arg1, arg2)
local function run_unstrict(f,...)
complain_if(not callable(f),
('Argument #1 should be a callable, not %s.'):format(type(f)),3)
return make_function_unstrict(f)(...)
end
return {
strict = make_table_strict,
unstrict = make_table_unstrict,
is_strict = is_table_strict,
strictf = make_function_strict,
unstrictf = make_function_unstrict,
run_strict = run_strict,
run_unstrict = run_unstrict,
_VERSION = 'strictness v'.._MODULEVERSION,
_URL = 'http://github.com/Yonaba/strictness',
_LICENSE = 'MIT <http://raw.githubusercontent.com/Yonaba/strictness/master/LICENSE>',
_DESCRIPTION = 'Tracking accesses and assignments to undefined variables in Lua code'
}