putting this in a git so i can post it

master
bx 2021-12-15 16:07:44 +00:00
commit 13c5ca7783
4 changed files with 1036 additions and 0 deletions

51
env.lua 100644
View File

@ -0,0 +1,51 @@
--[[
env is a table with
__p the parent env if there is one
__t a table of strings with the same keys as the env
entries in __t can be "special" or "macro"
if there isnt an entry it's assumed to be normal
--]]
-- todo: guard against setting / getting __t and __p ?
function e_create(env, sym, val, type, cont)
if not is_sym(sym) then
return tostr(sym) .. " is not a symbol"
end
env[sym] = none
return e_set(env, sym, val, type, cont)
end
function e_set(env, sym, val, type, cont)
if not is_sym(sym) then
return tostr(sym) .. " is not a symbol"
end
if env[sym] != nil then
env[sym] = val
env.__t[sym] = type
return cont(val, type)
elseif env.__p != nil then
return e_set(env.__p, sym, val, type, cont)
else
return tostr(sym) .. " sym unbound cant set it"
end
end
function e_get(env, sym, cont)
if not is_sym(sym) then
return tostr(sym) .. " is not a symbol"
end
if env[sym] != nil then
return cont(env[sym], env.__t[sym])
elseif env.__p != nil then
return e_get(env.__p, sym, cont)
else
return tostr(sym) .. " sym unbound cant get it"
end
end

222
eval.lua 100644
View File

@ -0,0 +1,222 @@
-- eval.lua --
e_create(lib, "defmacro!", function(ast, env, cont)
-- (defmacro! name normal-function)
if #ast < 2 then
return "macro needs 2 args"
end
return eval(ast[2], env, function(val)
return e_create(env, ast[1], val, "macro", cont)
end)
end, "special", id)
e_create(lib, "quote", function(ast, env, cont)
return cont(deep_copy(ast[1])) -- no mutate
end, "special", id)
e_create(lib, "comma", function(ast, env, cont)
return "comma outside of backquote"
end, "special", id)
e_create(lib, "backquote", function(ast, env, cont)
if #ast < 1 then
return "backquote need arg"
end
ast = ast[1]
--todo: implement backquote, where we iterate on the quoted thing
--and look for invokations of (comma thing) in which we eval
--thing and replace comma thing with it
--todo: could i define backquote from within lisp now that i have
--macros ?????
return "nope lol oiuhre4swiuhsrgiuhjswegfoij oijerws"
end, "special", id)
e_create(lib, "set!", function(ast, env, cont)
-- (set! symbol value)
if #ast < 2 then
return "too few args for set!"
end
return eval(ast[2], env, function(val, type)
return e_set(env, ast[1], val, type, cont)
end)
end, "special", id)
e_create(lib, "def!", function(ast, env, cont)
-- (def! symbol value)
if #ast < 2 then
return "too few args for def!"
end
return eval(ast[2], env, function(val, type)
return e_create(env, ast[1], val, type, cont)
end)
end, "special", id)
e_create(lib, "let", function(ast, env, cont)
-- (let ((a 2) (b 4) (c 8)) ...)
if #ast < 2 then
return "let needs binds and body"
end
-- todo: check args to make sure it's a list of (bind expr)
if not is_tab(ast[1]) then
return "first args must be a list"
end
local new_env = { __p = env, __t = {}, }
local binds, body = ast[1], rest(ast)
local function do_body(idx, val)
if idx >= #body then
return cont(val)
end
return eval(body[idx + 1], new_env, bind(do_body, idx + 1))
end
local function do_binds(idx, val)
return e_create(new_env, binds[idx][1], val, nil, function()
if idx == #binds then
return do_body(0, nil)
end
return eval(binds[idx + 1][2], env, bind(do_binds, idx + 1))
end)
end
if #binds > 0 then
return eval(binds[1][2], env, bind(do_binds, 1))
end
return do_body(0, nil)
end, "special", id)
--todo: cond is more generic, replace this with cond ?
e_create(lib, "if", function(ast, env, cont)
-- (if cond a b)
if #ast < 2 then
return "if requires pred and body"
end
return eval(ast[1], env, function(pred)
if pred == false or pred == none then
if ast[3] then
return eval(ast[3], env, cont)
end
return cont(none)
end
return eval(ast[2], env, cont)
end)
end, "special", id)
e_create(lib, "\\", function(ast, env, cont)
-- (lam (a b c...) body...)
if #ast < 2 then
return "lam needs args and body"
end
if not is_tab(ast[1]) then
return "lam binds have to be list"
end
local binds, body = ast[1], rest(ast)
return cont(function(args, _, cont)
local lam_env = { __p = env, __t = {}, }
if #args < #binds then
return "lambda needs more args"
end
local function do_body(idx, val)
if idx - 1 >= #body then
return cont(val)
end
return eval(body[idx], lam_env, bind(do_body, idx + 1))
end
local function do_binds(idx)
if idx - 1 >= #binds then
return do_body(1, nil)
end
return e_create(lam_env, binds[idx], args[idx], nil, bind(do_binds, idx + 1))
end
return do_binds(1)
end)
end, "special", id)
function eval_fun_call(ast, env, cont)
return eval(ast[1], env, function(fn)
if not is_fun(fn) then
return tostr(ast[1]) .. " is not a function"
end
-- todo: if we can avoid cloning args
-- and just bind args_length instead
-- this should be faster
local ast_args, zeroth = rest(ast), {}
local function eval_args(args, val)
if val != zeroth then
args = pack(unpack(args)) -- no mutate
if val == nil then
args[#args + 1] = none
else
args[#args + 1] = val
end
end
if #args == #ast_args then
return fn(args, env, cont)
end
return eval(ast_args[#args + 1], env, bind(eval_args, args))
end
return eval_args({}, zeroth)
end)
end
-- eval :: error : string or nil (on success)
function eval(ast, env, cont)
if ast == nil then
return "error ast is nil"
end
if is_num(ast) or is_bool(ast) or is_str(ast)
or ast == none then
return cont(ast)
end
if is_sym(ast) then
return e_get(env, ast, cont)
end
if is_tab(ast) and #ast == 0 then
return "cannot eval empty list"
end
if is_tab(ast) then
if is_tab(ast[1]) then
return eval_fun_call(ast, env, cont)
end
return e_get(env, ast[1], function(val, type)
if type == "special" then
return val(rest(ast), env, cont)
elseif type == "macro" then
--todo: write macro-expand function ?
--todo: can we write this back into the ast ? (for SPEED)
return val(rest(ast), env, function(new_ast)
return eval(new_ast, env, cont)
end)
else
return eval_fun_call(ast, env, cont)
end
end)
end
return "unimplemented ast: " .. tostr(ast)
end

183
lib.lua 100644
View File

@ -0,0 +1,183 @@
-- lib.lua --
local lib = { __p = nil, __t = {}, }
-- todo: we can set these directly when we know that e_create works right
function id(a) return a end
e_create(lib, "call/cc", function(args, env, cont)
if not is_fun(args[1]) then
return "call/cc takes a function"
end
local function escape(args, env, _cont)
if args[1] == nil then
return cont("no value passed")
end
return cont(args[1])
end
return args[1]({escape}, env, cont)
end, nil, id)
e_create(lib, "do", function(args, env, cont)
return cont(args[#args])
end, nil, id)
-- todo: error on wrong arg type
e_create(lib, "+", function(args, env, cont)
local acc = 0
for i = 1, #args do
acc += args[i]
end
return cont(acc)
end, nil, id)
e_create(lib, "*", function(args, env, cont)
local acc = args[1]
for i = 2, #args do
acc *= args[i]
end
return cont(acc)
end, nil, id)
e_create(lib, "-", function(args, env, cont)
-- todo: return errors for bad types
-- todo: unary minus
local acc = args[1] or 0
for i = 2, #args do
acc -= args[i]
end
return cont(acc)
end, nil, id)
e_create(lib, "/", function(args, env, cont)
local acc = args[1] or 0
for i = 2, #args do
acc /= args[i]
end
return cont(acc)
end, nil, id)
e_create(lib, "even?", function(args, env, cont)
return cont(args[1] % 2 == 0)
end, nil, id)
e_create(lib, "?!", function(args, env, cont)
local s = ""
for i = 1, #args do
if is_str(args[i]) then
s = s .. sub(args[i], 2) .. " "
elseif args[i] == none then
s = s .. "[none] "
else
s = s .. tostr(args[i]) .. " "
end
end
print(s)
return cont(";" .. s)
end, nil, id)
e_create(lib, "<", function(args, env, cont)
local acc = -32768 -- lowest pico-8 value i think
for i = 1, #args do
if not (args[i] > acc) then
return cont(false)
end
acc = args[i]
end
return cont(true)
end, nil, id)
e_create(lib, "=", function(args, env, cont)
local acc = args[1]
for i = 2, #args do
if args[i] != acc then
return cont(false)
end
end
return cont(true)
end, nil, id)
e_create(lib, "list", function(args, env, cont)
return cont(args)
end, nil, id)
e_create(lib, "[]", function(args, env, cont)
if not is_tab(args[1]) then
return "[] 1st arg must be table"
end
return cont(args[1][args[2]])
end, nil, id)
e_create(lib, "len", function(args, env, cont)
if not is_tab(args[1]) then
return "len arg must be table"
end
return cont(#(args[1]))
end, nil, id)
e_create(lib, "join", function(args, env, cont)
local new, len = {}, 0
for i = 1, #args do
local arg = args[i]
if not is_tab(arg) then
len += 1
new[len] = arg
goto next
end
for j = 1, #arg do
new[len + j] = arg[j]
end
len += #arg
::next::
end
return cont(new)
end, nil, id)
e_create(lib, "fold", function(args, env, cont)
if not is_fun(args[1]) then
return "1st arg should be func"
elseif args[2] == nil then
return "2nd arg should be start val"
elseif not is_tab(args[3]) then
return "3rd arg should be table"
end
local fn, acc, list = args[1], args[2], args[3]
local len = #list
if len < 1 then
return "cant fold empty list"
end
local function do_fold(idx, val)
if idx == len then
return cont(val)
end
return fn({val, list[idx + 1]}, env, bind(do_fold, idx + 1))
end
return fn({acc, list[1]}, env, bind(do_fold, 1))
end, nil, id)
function wrap_fn(fn, name)
e_create(lib, name, function(args, env, cont)
return cont(fn(unpack(args)))
end, nil, id)
end
for i = 1, #wraps do
-- todo: should i / do i need to prefix these ??
-- todo: post these with ! since almost all of them can mutate
wrap_fn(wraps[i].v, wraps[i].k)
end
wraps = nil -- let there be garbage collection
for k, v in pairs({
["sym?"] = is_sym,
["num?"] = is_num,
["fun?"] = is_fun,
["tab?"] = is_tab,
["str?"] = is_str,
["bool?"] = is_bool,
}) do
wrap_fn(v, k)
end

580
lisp.p8 100644
View File

@ -0,0 +1,580 @@
pico-8 cartridge // http://www.pico-8.com
version 32
__lua__
-- utils / globals
local none = {}
--have to do this before i make
--any functions
local wraps = {}
for k,v in pairs(_ENV) do
if type(v)=="function" then
wraps[#wraps + 1]={k=k, v=v,}
end
end
function is_sym(s)
return type(s) == "string"
and sub(s,1,1) != ";"
end
function is_num(n)
return type(n) == "number"
end
function is_fun(f)
return type(f) == "function"
end
function is_tab(t)
return type(t) == "table"
and t != none
end
function is_str(s)
return type(s) == "string"
and sub(s,1,1) == ";"
end
function is_bool(b)
return type(b) == "boolean"
end
--todo:
--can this be made faster ?
function rest(t)
local function r_1(fst, ...)
return {...}
end
return r_1(unpack(t))
end
function deep_copy(v)
if not is_tab(v) then
return v
end
local c = {}
for key = 1, #v do
--for key in pairs(v) do
c[key] = deep_copy(v[key])
end
return c
end
--todo: this is limitted to 2
--args, it's kinda missleading
--ig, tho i doubt anyone else
--will be using this func
--todo: see if this needs to /
--can be spead up
function bind(func, ...)
local args = {...}
local function copy(t)
return pack(unpack(t))
end
return function(a, b)
local args_2 = copy(args)
args_2[#args + 1] = a
args_2[#args + 2] = b
return func(unpack(args_2))
end
end
function list_tostr(l, s)
if l == none then
return s .. "none"
end
if type(l)=="table" then
s = s .. "("
for i = 1, #l do
if(i > 1) s = s .. " "
s = list_tostr(l[i], s)
end
s = s .. ")"
return s
end
if is_str(l) then
return
s.."\""..sub(l,2).."\""
end
return s .. tostr(l)
end
function chr_size(c)
return ord(c) < 128 and 4 or 8
end
function replace(s, a, b)
local r = ""
for i = 1, #s do
local c = sub(s, i, i)
if c == a then
c = b
end
r = r .. c
end
return r
end
-->8
-- tokenizer
--[[ todo:
generisize token_str + com ?
--]]
--::tok,rst,type
--[[
"pun" = puncuation
"ws" = whitespace
"sym" = symbol
"num" = number
"end" = end of input
"str" = string
"com" = comment
--]]
function token_str(s, hk)
for i = 2, #s do
if(hk) hk(sub(s,i,i))
if sub(s,i,i) == "\"" then
return sub(s, 1, i),
sub(s,i + 1), "str"
end
end
return s, "", "str" --no end
end
function token_com(s, hk)
for i = 2, #s do
if(hk) hk(sub(s,i,i))
if sub(s,i,i) == "\n" then
return sub(s, 1, i),
sub(s,i + 1), "com"
end
end
return s, "", "com" --eof
end
function token_1(s, hk)
local fst,rst = sub(s,1,1),
sub(s,2)
if(hk) hk(fst)
if(s=="")return "","","end"
if fst == "\"" then
return token_str(s, hk)
end
if fst == ";" then
return token_com(s, hk)
end
if fst == " "
or fst == "\n"
or fst == "\t" then
local t,r,tp = token_1(rst,hk)
if tp == "ws" then
return fst..t, r, "ws"
end
return fst, rst, "ws"
end
if fst == "("
or fst == ")"
or fst == "'"
or fst == "`"
or fst == ","
or fst == "@" then
return fst, rst, "pun"
end
local t,r,tp = token_1(rst,hk)
if tp == "sym" then
return fst..t, r, "sym"
end
return fst,rst,"sym"
end
function token(s, hk)
local t,r,tp = token_1(s, hk)
if tonum(t) != nil then
return tonum(t),r,"num"
end
return t,r,tp
end
-->8
-- parser
--[[ todo:
collect line numbers ?
--]]
function parse(src)
local t, rst, tp = token(src)
if tp=="ws"
or tp=="com" then
return parse(rst) end
if t == "'" then
t, rst, tp = parse(rst)
return {"quote",t}, rst, tp
end
if t == "`" then
t, rst, tp = parse(rst)
return {"backquote",t}, rst, tp
end
if t == "," then
t, rst, tp = parse(rst)
return {"comma",t}, rst, tp
end
if tp == "str" then
return ";" .. sub(t,2,#t-1),
rst, tp
end
if t == "(" then
local list = {}
while true do
t, rst, tp = parse(rst)
if(t == ")") break
if tp == "end" then
print("err, unclosed paren")
break
end
add(list, t)
end
return list, rst, tp
end
return t, rst, tp
end
-->8
-- env
#include env.lua
-->8
-- lib
#include lib.lua
-->8
-- eval
#include eval.lua
eval(parse([[
(do
(def! push (\ (l a)
(join l (list a))))
(def! bq-fold (\ (acc ast)
(push acc
(if (tab? ast)
(if (= ([] ast 1) 'comma)
([] ast 2)
(fold bq-fold '(list) ast))
(list 'quote ast)))))
(defmacro! backquote (\ (ast)
(fold bq-fold '(list) ast))))
]]), lib, function(val)
--
end)
-->8
cls()
lisp_src = [[
;; edit example
(?! 69 "steamed hams") ;comment
(do
(def! add-or-sub (\ (func)
(func 10 18)))
(add-or-sub (\ (a b)
(?! a b)
(= a b))))
(do
(def! add-or-sub (\ (func)
(func 10 18)))
(add-or-sub (\ (a b)
(?! a b)
(= a b))))
(do
(def! farts (list 69 420 666))
(def! combo (\ (acc l)
(?! "combo:" acc l)
(+ acc l)))
(fold combo 0 farts))
(do
(def! thing 1)
(def! adder (\ (num)
; unamed second function
(\ (a)
; unamed third function
(\ (b)
(+ a b num)))
))
(def! crap (adder 4))
(def! dung (crap 2))
(dung 1)
;((adder 4) 2) -> dung
(dung 63))
(do
(def! cownter (let ((c 0))
(\ (a)
(set! c (+ c a))
(?! c))))
(cownter 1)
(cownter 5)
(cownter 2))
(do
(def! uwu (\ (x y)
(set! x (- 0 x))
(set! y (- 0 y))
(camera x y)
(color 5)
(line 10 98 10 88)
(line 10 98 50 98)
(line 50 88 50 98)
(line 50 88 40 88)
(line 20 88 10 88)
(line 20 88 20 60)
(line 40 88 40 60)
(line 40 60 20 60)
(line 40 70 20 70)
(line 30 65 30 60)))
(cls 1)
(uwu 50 0)
(uwu 0 0)
(uwu 25 -48))
(do
(defmacro! bee
(let ((bees 0)) (\ (val)
(set! bees (+ 1 bees))
`(?! ,bees ,val))))
(def! a 6)
(def! b "trees")
(bee b)
(bee "yeet")
(bee ((\() "yeet")))
(bee "tank")
(bee "yank")
(bee "gank"))
(def! draw (let ()
(def! l (\ (c)
(def! x (rnd 128))
(def! y (rnd 128))
(def! u (rnd 128))
(def! v (rnd 128))
(line x y u v c)
(spr 1 (- x 4) (- y 4))
(circ u v 4)))
(def! jump 0)
(\ ()
(call/cc (\ (c)
(set! jump c)))
(cls 15)
(cursor 1 1)
(color 0)
(?! (stat 0))
(map 0 0)
(if (< 0 (btn))
(?! "hi"))
(l 1) (l 0)
(l 1) (l 0)
(l 1) (l 0)
(l 1) (l 0))))
]]
lisp_src = replace(lisp_src,
"\t", " ")
list,rst = parse(lisp_src)
print(list_tostr(list, ""))
local enviro = {
__p = lib,
__t = {},
["true"] = true,
["false"] = false,
["none"] = none,
}
local err = eval(list, enviro,
function(v)
print(list_tostr(v, "-> "))
end)
if(err) print("err: " .. tostr(err))
-->8
-- edit
--[[
todo:
--]]
function draw_line(s, y)
if y > 128 or y < -6 then
return
end
local x = 0
local cols = {
str=3, com=13, num=12, sym=6,
bound=14, lib=11,
}
local t, rst, tp = token(s)
while tp != "end" do
color(7)
if cols[tp] then
color(cols[tp])
end
if lib[t] then
color(cols.lib)
end
if enviro[t] then
color(cols.bound)
end
x = print(t, x, y)
t, rst, tp = token(rst)
end
end
function draw_code(y)
cls(0)
local src=split(lisp_src, "\n")
for i = 1, #src do
draw_line(src[i], y+ i * 6 - 6)
--print(src[i])
end
end
-->8
-- draw
if enviro.draw then
function _draw()
local err = enviro.draw({},
enviro, function(v)end)
if err then
color(7)
?err
stop()
end
end
else
local scroll = 0
function _draw()
draw_code(scroll)
if(btn(⬆️))scroll += 6
if(btn(⬇️))scroll -= 6
end
end
__gfx__
00000000008888000033330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000002200000000220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700002200000000220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000088aa8000036633000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00077000a88aa8a00636633600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700700a88aa8a00636633600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000088888000033333000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000080080000003003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
03333330333330003333330300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333333333333332233300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
03332223332223233333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
03333332333233333333232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333333332333333233300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33332333323333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333233322333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
03333323333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
03333333333333333332333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33233333333222332333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333223333333333333000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
32323233233333333332333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333333322323333333000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33323333333332233333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00333333333333333332333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333333333333322333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333332332333333323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
32323322223333333332333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33223333323333233233333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
33333333333333333332333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
03332333333333333333333000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
30333303330033300333333000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__map__
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000002021220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000003031320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000004041420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000202121212200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000404141414200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000