From 13c5ca77833139a418c084fca3ef2ec79a22802c Mon Sep 17 00:00:00 2001 From: bx Date: Wed, 15 Dec 2021 16:07:44 +0000 Subject: [PATCH] putting this in a git so i can post it --- env.lua | 51 +++++ eval.lua | 222 +++++++++++++++++++++ lib.lua | 183 ++++++++++++++++++ lisp.p8 | 580 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1036 insertions(+) create mode 100644 env.lua create mode 100644 eval.lua create mode 100644 lib.lua create mode 100644 lisp.p8 diff --git a/env.lua b/env.lua new file mode 100644 index 0000000..d514dac --- /dev/null +++ b/env.lua @@ -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 + + diff --git a/eval.lua b/eval.lua new file mode 100644 index 0000000..14b96e6 --- /dev/null +++ b/eval.lua @@ -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 + + + + + diff --git a/lib.lua b/lib.lua new file mode 100644 index 0000000..ecfa9a2 --- /dev/null +++ b/lib.lua @@ -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 + diff --git a/lisp.p8 b/lisp.p8 new file mode 100644 index 0000000..58de7c0 --- /dev/null +++ b/lisp.p8 @@ -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