LUA

LUA BASICS

READ FILE

file2 = io.open("test2.lua", "r")
print(file2:read())
io.close(file2)

WRITE FILE

file = io.open("test2.lua", "w")
io.input(file)
file:write("hw")
io.close(file)

WRITE BYTE TO FILE

file3 =local stack = require("stack")

stack.push(1)
stack.push(2)
stack.push(3)

assert(stack.pop() == 3)
assert(stack.pop() == 2)
stack.push(72)
assert(stack.pop() == 72)
assert(stack.pop() == 1)
assert(stack.pop() == nil)
 io.open("test3", "wb")
file3:write(string.char(0xFF)) -- OK
io.close(file3)

string to number

local nb = '1465.6574'
assert(tonumber(nb) == 1465.6574)
io.write("hello")

for loop, array of bytes

file = io.open("raw", "wb")
io.input(file)

bytes = {0xFF, 0xAA, 0x03}

for i in pairs(bytes) do
        file:write(string.char(bytes[i]))
end
io.close(file)

repeat until, do while

repeat 
	tok = lex.next_token()
	print(tok.kind.."  "..tok.value)
until tok.kind == "EOF"

function for loop bytes

function writeBytes(filename, bytes)
        file = io.open("raw", "wb")
        io.input(file)
        for i in pairs(bytes) do
                file:write(string.char(bytes[i]))
        end
        io.close(file)
        return 0
end

writeBytes("raw", {0xCC, 0xAB, 0xFF, 0x01})

arrays

local headers = {"names", "city", "job"}
local names = {"Luke", "Leia", "Anakin"}
local city = {"Tatooin", "Aldraan", "Moustafaar"}
local jobs = {"Jedi", "Princess", "Sith"}


local file = io.open("SW.csv", "w")
io.input(file)

for i=1, #headers do
        file:write(headers[i])
        file:write(";")
end

file:write("\n")

for i=1, #names do
        file:write(names[i])
        file:write(";")
        file:write(city[i])
        file:write(";")
        file:write(jobs[i])
        file:write("\n")
end

io.close(file)

bytewise

local pixel = 0xFF9ABCDE
assert( (pixel>>8*3) & 0xFF == 0xFF )
assert( (pixel>>8*2) & 0xFF == 0x9A )
assert( (pixel>>8*1) & 0xFF == 0xBC )
assert( (pixel>>8*0) & 0xFF == 0xDE )

tables

local foo = {"foo", "bar", "foobar"}

assert(foo[1] == "foo")
assert(foo[2] == "bar")
assert(foo[3] == "foobar")

table.insert(foo, "barfoo")

assert(foo[4] == "barfoo")

tables 2

local cart = {}

cart["tomatoes"] = 7
cart["apples"] = 10
cart["citrus"] = 4

assert(cart["tomatoes"] == 7)
assert(cart["apples"] == 10)
assert(cart["citrus"] == 4)

cart["tomatoes"] = cart["tomatoes"] + 1
assert(cart["tomatoes"] == 8

iterate over string

local str = "Hello world"
for i = 1, #str do  
        print(str:sub(i,i))
end

Recursion, power, factorial

-- FACTORIAL
function factorial(n)
	if n == 0 then return 1 end
	return n*factorial(n-1)
end

local tests = {
  [0] = 1,
  [1] = 1,
  [2] = 2,
  [3] = 6,
  [4] = 24,
  [5] = 120,
}

for input, expected in pairs(tests) do
	assert(factorial(input) == expected, ("factorial(%d) failed"):format(input))
end


-- POWER
function power(x, n)
	if n == 0 then return 1 end
	return x*power(x, n-1)
end

for i=1, 8 do
	assert(2^i == power(2, i))
end

picture, module

picture.pixels = {}

function picture.fill()
        for i=1,picture.width*picture.height do
                table.insert(picture.pixels, 0xFFFF0000)
        end
end

function picture.exportPpm(filename)
        local ppm_header = "P6\n"..picture.width.." "..picture.height.." 255\n"
        local file = io.open(filename, "wb")

        io.input(file)
        for i = 1, #ppm_header do 
                file:write(ppm_header:sub(i,i))
        end

        for i=1, #picture.pixels do
                file:write(string.char((picture.pixels[i]>>8*2) & 0xFF))
                file:write(string.char((picture.pixels[i]>>8*1) & 0xFF))
                file:write(string.char((picture.pixels[i]>>8*0) & 0xFF))
        end
        io.close(file)
end

return picture

-- 

local pic = require("picture")
pic.width = 100local foo = {"foo", "bar", "foobar"}
print(foo[1])
print(foo[2])
print(foo[3])
table.insert(foo, "barfoo")
print(foo[4])

stack = {}

function stack.push(item)
        table.insert(stack, 1, item)
end

function stack.pop()
        local value = stack[1]
        table.remove(stack, 1)
        return value            jj
end

return stack

--

local stack = require("stack")

stack.push(1)
stack.push(2)
stack.push(3)

assert(stack.pop() == 3)
assert(stack.pop() == 2)
stack.push(72)
assert(stack.pop() == 72)
assert(stack.pop() == 1)
assert(stack.pop() == nil)

node

function browse_tree(n)
	print(n.data)
	if n.next then
		return browse_tree(n.next)	
	end
end



local n1 = {}
n1["data"] = 1
n1["next"] = nil

browse_tree(n1)


print("---")


local n2 = {}
n2["data"] = 2

n1["next"] = n2
n2["next"] = nil

browse_tree(n1)

nooop

local Canvas = {}

function Canvas.new(width, height)
        local self = {}
        self.width = width
        self.height = height
        return self
end

return Canvas

-- 

local Canvas = require("Canvas")
local c = Canvas.new(80, 60)

local c2 = Canvas.new(40, 30)


assert(c.width == 80)
assert(c2.width == 40)

c2.width = 60
assert(c2.width == 60)
assert(c.width == 80)

better noop

function Token(kind, value)
	return {
      kind = kind,
	  value = value
	}
end

------

function Lexer(text)
  local index = 1
  local current = text:sub(index, index)

  local function advance()
    index = index + 1

    if index > #text then
	    current = nil
    else 
    	current = text:sub(index, index)
    end
  end


  local function skip_whitespace()
	  while current ~= nil and current:sub(index, index) == ' ' do 
		  self.advance()
	  end
  end


  return {
    advance = advance,
    skip_whitespace = skip_whitespace,
    get_current = function() return current end,
    get_index = function() return index end
  }
end


anonymous function nooop

function Node(value, op, left, right)
    return {
        value = value,
        op = op,
        left = left,
        right = right,
        isLeaf = function(node)
            return node.value ~= nil
        end
    }
end

Picture.lua

local self = {}

self.width = 8 
self.height = 6

self.pixels = {}

for i=1, self.width*self.height do
	table.insert(self.pixels, 0xFFFFFFFF)
end

function self.fill(color)
	for i=1, self.width*self.height do
		self.pixels[i] = color 
	end
end

function self.setPixel(x, y, color)
	self.pixels[x+self.width*(y-1)] = color
end

function self.exportPpm(filename)
	local ppm_header = "P6\n"..self.width.." "..self.height.." 255\n"
	local file = io.open(filename, "wb")

	io.input(file)
	for i = 1, #ppm_header do 
		file:write(ppm_header:sub(i,i))
	end

	for i=1, #self.pixels do
		file:write(string.char((self.pixels[i]>>8*2) & 0xFF))
		file:write(string.char((self.pixels[i]>>8*1) & 0xFF))
		file:write(string.char((self.pixels[i]>>8*0) & 0xFF))
	end
	io.close(file)
end

function self.flip()
end

function self.flop()
end

return self

call c function from lua

foo.c

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

static int cland_add(lua_State *L)
{
  int a = luaL_checknumber(L, 1);
  int b = luaL_checknumber(L, 2);

  lua_pushnumber(L, a + b);

  return 1;    
}

luaL_Reg clandlib[] = {
  {"cland_add", cland_add},
  {NULL, NULL}
};

int luaopen_cland(lua_State *L)
{
  luaL_newlib(L, clandlib);
  return 1;
}

main.lua

local cland = require "cland"
assert(cland.cland_add(10, 11) == 21)

compile

gcc -shared -fpic `pkg-config lua5.4 --cflags` -o cland.so cland.c `pkg-config lua5.4 --libs`

run :

lua main.lua

notes

  • sudo apt install liblua5.4-dev

SORTING ALGORITHM

local lst = {37, 82, 14, 69, 45, 91, 58, 23, 76, 3, 88, 55, 12, 97, 41, 60, 27, 74, 8, 95, 32, 50, 19, 79, 6, 99, 43, 66, 25, 71, 10, 83, 30, 54, 17, 90, 39, 62, 21, 84, 5, 93, 48, 35, 80, 15, 57, 68, 24, 96}


function printTable(lst)
  for i=1, #lst do
    io.write(lst[i])
    if i ~= #lst then
      io.write(',')
    else 
      io.write('\n')
    end
  end
end

-- ~~~~~~~~ BUBBLESORT ~~~~~~~~
function bubblesort(lst)
  local x = #lst
  for j=x, 2, -1 do
    for i=2, j do
      if lst[i-1] > lst[i] then
        local tmp = lst[i-1]
        lst[i-1] = lst[i]
        lst[i] = tmp
      end
    end
  end
end

-- printTable(lst)
-- bubblesort(lst)
-- printTable(lst)



-- ~~~~~~~~ QUICKSORT ~~~~~~~~
function quicksort(lst)
  if #lst <= 1 then return lst end
  local pivot = lst[#lst//2]

  local left = {}
  for i=1, #lst do
    if lst[i] < pivot then
      table.insert(left, lst[i])
    end
  end

  local middle = {}
  for i=1, #lst do
    if lst[i] == pivot then
      table.insert(middle, lst[i])
    end
  end

  local right = {}
  for i=1, #lst do
    if lst[i] > pivot then
      table.insert(right, lst[i])
    end
  end

  local ret = {}
  local left2 = quicksort(left)
  local right2 = quicksort(right)

  for i=1, #left2 do
    table.insert(ret, left2[i])
  end

  for i=1, #middle do
    table.insert(ret, middle[i])
  end

  for i=1, #right2 do
    table.insert(ret, right2[i])
  end

  return ret
end


printTable(lst)
local sorted = quicksort(lst)
printTable(sorted)

ed.lua

-- minimalist text editor, inspired by : https://www.youtube.com/watch?v=gnvDPCXktWQ

function main()
	local file = io.open(arg[1], "r")

	local file_content = {}
	for line in file:lines() do table.insert(file_content, line) end
	io.close(file)

	for i in pairs(file_content) do print(file_content[i]) end

	local current_line = 0
	current_line = io.read("*n")

	io.read("*l") -- flush \n from buffer

	file_content[current_line] = io.read("*l")
	
	local file2 = io.open(arg[1], "w")

	for i in pairs(file_content) do 
		file2:write(file_content[i])
		file2:write("\n")
	end

	io.close(file2)
end


main()

bf.lua

local Stack = {}

function Stack.new()
  local self = {}

  function self.push(x)
    table.insert(self, x)
  end
  
  function self.pop()
    if #self == 0 then return nil end
    local head = self[#self]
    table.remove(self, #self)
    return head
  end

  return self
end






function lex(filename) 
  local file = io.open(filename, "r")
  local code = file:read("*a")
  file:close()
  return code
end


function run(code)
  local stack = Stack.new()
  local tape = {}

  -- FIXME: unlimited cells
  for i=1, 5000 do
    tape[i] = 0
  end

  -- TODO : naive version (no jump table) 
  local jmp = {} 

  for i=1, #code do
    local c = code:sub(i,i)
    if c=='[' then
      stack.push(i)
    elseif c==']' then
      local matching_bracket = stack.pop()
      if not matching_bracket then error("unmatched ']'") end
      jmp[i] = matching_bracket
      jmp[matching_bracket] = i
    end
  end
  -- TODO : naive version (no jump table) 

  -- if stack not empty at the end, there is unmatched "[" : 
  if stack.pop() then error("unmatched '['") end


  local pointer_loc = 1
  local i = 1
  while i<=#code do 
    local c = code:sub(i,i)
    if c == '+' then
      tape[pointer_loc] = (tape[pointer_loc]+1) % 256
    elseif c == '-' then
      tape[pointer_loc] = (tape[pointer_loc]-1) % 256
    elseif c == '>' then
      pointer_loc = pointer_loc + 1
      if pointer_loc > #tape then table.insert(tape, 0) end
    elseif c == '<' then
      pointer_loc = pointer_loc - 1
      if pointer_loc < 1 then error("Pointer OOB") end
    elseif c == '[' and tape[pointer_loc] == 0 then
      i = jmp[i]
    elseif c == ']' and tape[pointer_loc] ~= 0 then 
      i = jmp[i]
    elseif c == ',' then 
      -- read 1 byte input
      local input = string.byte(io.read(1))
      tape[pointer_loc] = input

    elseif c == '.' then 
      io.write(string.char(tape[pointer_loc]))
      -- io.write(tape[pointer_loc])
    else
	-- any anyrecognized character, just ignore
    end
    i = i+1
  end
end

code = lex("foo.bf")
run(code)

string.byte()

assert(string.byte('a') == 97)

assert(string.byte('abcd', 1) == 97)
assert(string.byte('abcd', 2) == 98)
assert(string.byte('abcd', 5) == nil)

local a,b,c,d = string.byte('abcd', 1, 4)
assert(
	a==97 and
	b==98 and
	c==99 and
	d==100
)

BITWISE

function tobin(str)
	return tonumber(str,2)
end


assert(5 == tobin('101'))

assert(tobin('101')<<2 == 20)
assert(tobin('101')<<2 == tobin('10100'))

assert(tobin('101')>>2 == 1)

assert(tobin('1010') | tobin('0110') == tobin('1110'))
assert(tobin('1010') & tobin('0110') == tobin('0010'))

STRONG RNG

local random_buffer = {}  -- Persistent buffer
local buffer_index = 0    -- Tracks buffer usage

-- generate numbers
function random_urandom(a, b)
    if buffer_index <= 0 then
        local f = io.open("/dev/urandom", "rb")
        if not f then error("Failed to open /dev/urandom") end
        random_buffer = {f:read(1024):byte(1, 1024)}
		-- limit syscalls to /dev/random
		
        f:close()
        buffer_index = 1024 -- Reset buffer index
    end

    local num = 0
    for i = 1, 4 do
        num = (num << 8) + random_buffer[buffer_index]
        buffer_index = buffer_index - 1
    end

    return a + (num % (b - a + 1))
end


for i=1, 100 do
	table.insert(nbs, random_urandom(0, 2))
end