Compare commits

...

5 Commits

Author SHA1 Message Date
yw05 42f7a9e75a Add reference compilers/implementations 2021-04-24 19:09:23 +02:00
yw05 9ce465fc2e .gitignore: ignore local BF testing source files 2021-04-16 08:38:04 +02:00
yw05 f18025f359 Add CL implementations of certain programs 2021-04-16 08:36:33 +02:00
yw05 b7725e22ab Coding style 2021-04-07 15:20:10 +02:00
yw05 28941c7321 Add Brainf@#k to Lua transpiler 2021-04-07 11:32:03 +02:00
10 changed files with 531 additions and 65 deletions

11
.astylerc Normal file
View File

@ -0,0 +1,11 @@
--indent=force-tab=8
--style=attach
--indent-after-parens
--pad-comma
--pad-header
--fill-empty-lines
--align-pointer=name
--align-reference=name
--attach-return-type
--max-code-length=80
--lineend=linux

3
.dir-locals.el Normal file
View File

@ -0,0 +1,3 @@
((nil . ((c-basic-offset . 8)
(indent-tabs-mode . t)
(tab-width . 8))))

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
bin/*
*/*.o
*/*.fasl
bf/*.bf
*/*~

View File

@ -14,39 +14,39 @@ void Ackermann(mpz_t r, mpz_t m, mpz_t n) {
mpz_addmul_ui(r, n, 2);
return;
}
mpz_t t1, t2, ir;
mpz_init(t1);
mpz_sub_ui(t1, m, 1);
mpz_init_set(t2, n);
mpz_init_set_ui(ir, 1);
while(mpz_sgn(t2)>=0) {
while (mpz_sgn(t2)>=0) {
Ackermann(r, t1, ir);
mpz_set(ir, r);
mpz_sub_ui(t2, t2, 1);
}
mpz_clear(t1);
mpz_clear(t2);
mpz_clear(ir);
return;
}
int main(int argc, char** argv) {
int main(int argc, char **argv) {
if (argc != 3)
return 1;
mpz_t m, n, r;
assert(!mpz_init_set_str(m, argv[1], 10));
assert(!mpz_init_set_str(n, argv[2], 10));
mpz_init(r);
Ackermann(r, m, n);
mpz_out_str(stdout, 10, r);
putchar('\n');
mpz_clear(m);
mpz_clear(n);
mpz_clear(r);
}
}

18
Ackermann/Ackermann.lisp Normal file
View File

@ -0,0 +1,18 @@
;;;; This is a Common Lisp implementation of the Ackermann function
(declaim (ftype (function (fixnum integer) integer) Ackermann))
(defun Ackermann (m n)
(declare (type fixnum m) (type integer n))
(case m
((0) (1+ n))
((1) (+ 2 n))
((2) (+ n n 3))
((3) (+ (expt 2 (+ 3 n)) -3))
(t (let ((ir 1) (m-1 (- m 1)))
(dotimes (i (1+ n) ir)
(setf ir (Ackermann m-1 ir)))))))
(defmacro Ackermann-test (m n)
(let ((i (gensym)) (j (gensym)))
`(dotimes (,i ,m nil) (dotimes (,j ,n) (format t "A(~d,~d)=~d~%" ,i ,j (Ackermann ,i ,j))))))

8
README
View File

@ -5,6 +5,14 @@ The build.pl script is used to compile the programs.
* If the names of the subdirectories are provided, then the script compiles the files in the given subdirectories of this repository if possible.
* If only "clean" is provided, then the script cleans the bin/ directory and all object files.
* If no argument is provided, then the script compiles everything in this repository that can be compiled.
Additionally, these programs are used as references for this project:
* C: GCC
* Common Lisp: SBCL
* Lua: LuaJIT
C coding style
* The coding style should correspond to the coding style defined in the .astylerc file.
* Long lines (>80 characters) should usually be broken in a way that makes sense to readers.
License
The code here are licensed under GNU GPLv3 only. You may not choose a different license.

157
bf/bf2cl.lisp Normal file
View File

@ -0,0 +1,157 @@
;;;; This is a simple BF-Common Lisp transpiler
(defpackage :bf (:use :cl) (:export :tocl :tocl-string :tocl-file
:tofile :tofile-string :tofile-file))
(in-package :bf)
(defgeneric token-tocl (token &key list pointer input output &allow-other-keys))
(defstruct (operation)
(optype nil :type (or null symbol)))
(defstruct (address)
(absolute nil :type boolean)
(offset 0 :type number))
(defmethod token-tocl ((token address) &key list pointer &allow-other-keys)
(copy-list (if (address-absolute token)
`(aref ,list ,(mod (address-offset token) 65536))
(if (eql 0 (address-offset token))
`(aref ,list ,pointer)
`(aref ,list (mod (+ ,pointer ,(address-offset token)) 65536))))))
(defstruct (op-inc
(:include operation (optype 'inc :type symbol :read-only t)))
(position (make-address :absolute nil :offset 0) :type address)
(delta 0 :type fixnum))
(defmethod token-tocl ((token op-inc) &key list pointer &allow-other-keys)
(copy-list `(setf ,(token-tocl (op-inc-position token) :list list :pointer pointer)
(mod (+ ,(token-tocl (op-inc-position token) :list list :pointer pointer)
,(op-inc-delta token))
256))))
(defstruct (op-offset
(:include operation (optype 'offset :type symbol :read-only t)))
(delta 0 :type fixnum))
(defmethod token-tocl ((token op-offset) &key pointer &allow-other-keys)
(copy-list `(setf ,pointer (mod (+ ,pointer ,(op-offset-delta token)) 65536))))
(defstruct (op-read
(:include operation (optype 'read :type symbol :read-only t)))
(position (make-address :absolute nil :offset 0) :type address))
(defmethod token-tocl ((token op-read) &key list pointer input &allow-other-keys)
(copy-list `(setf ,(token-tocl (op-read-position token) :list list :pointer pointer)
(char-code (read-byte ,input nil #\Null)))))
(defstruct (op-write
(:include operation (optype 'write :type symbol :read-only t)))
(position (make-address :absolute nil :offset 0) :type address))
(defmethod token-tocl ((token op-write) &key list pointer output &allow-other-keys)
(copy-list `(write-char
(code-char ,(token-tocl (op-write-position token) :list list :pointer pointer))
,output)))
(defstruct (op-loop
(:include operation (optype 'loop :type symbol :read-only t)))
(position (make-address :absolute nil :offset 0) :type address)
(body nil :type list))
(defmethod token-tocl ((token op-loop) &key list pointer input output &allow-other-keys)
(copy-list `(do () ((eql 0 ,(token-tocl (op-loop-position token)
:list list
:pointer pointer
:input input
:output output)))
(progn . ,(transpile (op-loop-body token) list pointer input output t)))))
(defun tokenize-char (char &optional (stream *standard-input*) (recursive-p nil))
(case char
(#\+ (make-op-inc :delta 1))
(#\- (make-op-inc :delta -1))
(#\< (make-op-offset :delta -1))
(#\> (make-op-offset :delta 1))
(#\, (make-op-read))
(#\. (make-op-write))
(#\[ (make-op-loop :body (tokenize stream t)))
(#\] (if recursive-p t (error "unbalanced ]")))
(t nil)))
(defun tokenize (stream &optional (recursive-p nil))
(do* ((c t (read-char stream nil))
(v nil (tokenize-char c stream recursive-p))
(l nil (if (typep v 'operation) (cons v l) l)))
((or (null c) (eql t v)) l)))
(defmacro deref-pos (absolute-p offset)
(let ((a (gensym)) (o (gensym)))
`(copy (let ((,a ,absolute-p) (,o ,offset))
(cond
(,a (mod ,o 65536))
((eql 0 ,o) 'pos)
(t `(mod (+ pos ,,o) 65536)))))))
(defun let-wrapper (body list pointer input output)
(declare (ignore input output))
(copy-list `(let ((,list (make-array 65536 :element-type 'fixnum :initial-element 0
:adjustable nil))
(,pointer 0))
(declare (type fixnum ,pointer))
,@body)))
(defun transpile (elist list pointer input output &optional (recursive-p nil))
(let ((l (nreverse (cons nil (loop for i in elist collect (token-tocl i
:list list
:pointer pointer
:input input
:output output))))))
(if recursive-p l (let-wrapper l list pointer input output))))
(defun combine-tokens (elist)
(do* ((r elist (cdr r)) (i (car r) (car r))) ((null i) elist)
(typecase i
(op-inc (loop for j on (cdr r) for e = (car j)
while (and (typep e 'op-inc)
(equalp (op-inc-position e) (op-inc-position i)))
do (incf (op-inc-delta i) (op-inc-delta e))
finally (setf (cdr r) j)))
(op-offset (loop for j on (cdr r) for e = (car j)
while (typep e 'op-offset)
do (incf (op-offset-delta i) (op-offset-delta e))
finally (setf (cdr r) j)))
(op-loop (setf (op-loop-body i) (combine-tokens (op-loop-body i))))
(t i))))
(defmacro optimize-token-list (elist)
`(combine-tokens ,elist))
(defmacro toformlist (stream input output)
`(transpile (optimize-token-list (tokenize ,stream)) (gensym) (gensym) ,input ,output))
(defmacro tocl (stream)
`(compile nil `(lambda (&optional (is *standard-input*) (os *standard-output*))
(declare (ignorable is os) (optimize speed))
,(toformlist ,stream 'is 'os))))
(defmacro tocl-string (string)
(let ((str (gensym)))
`(with-input-from-string (,str ,string) (tocl ,str))))
(defmacro tocl-file (file)
(let ((str (gensym)))
`(with-open-file (,str ,file) (tocl ,str))))
(defmacro tofile (istream ostream)
(let ((is (gensym)))
`(let ((,is ,istream))
(prin1 (toformlist ,is '*standard-input* '*standard-output*) ,ostream))))
(defmacro tofile-string (string ostream)
(let ((str (gensym)))
`(with-input-from-string (,str ,string) (tofile ,str ,ostream))))
(defmacro tofile-file (file ostream)
(let ((str (gensym)))
`(with-open-file (,str ,file) (tofile ,str ,ostream))))

253
bf/bf2lua.lua Normal file
View File

@ -0,0 +1,253 @@
local fn = tostring(arg[1]) or error("No filename supplied")
local f = io.open(fn,"r") or error("Cannot open file")
local str = f:read("*all")
f:close()
local tokenize, t2str
local ops = {
cval = 1, vset = 2, mpos = 3, writ = 4, wstr = 5, read = 6, loop = 7
}
-- Matching patterns
local ptns = {
["^([%+%-]+)"] = function(_, str)
local diff = 0
for i = 1, #str do
diff = diff + (string.sub(str,i,i) == "+" and 1 or -1)
end
return {ops.cval, diff}
end,
["^%[%-%]([%+%-]*)"] = function(_, str)
local diff = 0
for i = 1, #str do
diff = diff + (string.sub(str,i,i) == "+" and 1 or -1)
end
return {ops.vset, diff}
end,
["^([<>]+)"] = function(_, str)
local diff = 0
for i = 1, #str do
diff = diff + (string.sub(str,i,i) == "<" and -1 or 1)
end
return {ops.mpos, diff}
end,
["^(%.+)"] = function(_, str)
return {ops.writ, string.len(str)}
end,
["^(,+)"] = function(_, str)
return {ops.read, string.len(str)}
end,
["^(%b[])"] = function(_, str)
return {ops.loop, tokenize(string.sub(str, 2, -2))}
end,
}
tokenize = function(str, pos, cont)
if not pos then pos = 1 end
local t = {}
local endp = false
while pos <= #str do
if string.match(str, "^%s+$", pos) then break end
local _, e = string.find(str, "^%s+", pos)
if e then pos = e+1 end
local tout = nil
for p, f in pairs(ptns) do
local match = {string.find(str, p, pos)}
if match[1] then
pos = match[2]+1
match[2] = string.sub(str, pos+1) .. (cont or "")
tout, endp = f(unpack(match, 2))
break
end
end
if not tout then
return nil, (endp or "Invalid command: " .. string.sub(str, pos))
end
t[#t+1] = tout
if endp then break end
end
return t, pos
end
local function getbalance(t)
local ct = {}
local cl = {}
local o = 0
for i = 1, #t, 1 do
local op = t[i][1]
if op == ops.loop or op == ops.read or op == ops.writ or op == ops.vset then return nil end
if op == ops.mpos then
o = o + t[i][2]
ct.min = ct.min and math.min(ct.min, o) or o
ct.max = ct.max and math.max(ct.max, o) or o
elseif op == ops.cval then
if not ct[o] then
ct[o] = {ops.cval, 0}
cl[#cl+1] = o
end
ct[o][2] = ct[o][2] + t[i][2]
end
end
table.sort(cl)
return o == 0, ct, cl
end
local function isbalanced(t)
local o = 0
for i = 1, #t, 1 do
local op = t[i][1]
if op == ops.loop or op == ops.read or op == ops.writ or op == ops.vset then return nil end
if op == ops.mpos then o = o + t[i][2] end
end
return o == 0
end
local function optimize(t)
-- Pass 1: combine instructions
local i = 1
local j = 1
local p1 = {}
while (t[i]) do
local op = t[i][1]
p1[j] = {}
for k, v in pairs(t[i]) do p1[j][k] = v end
if op == ops.loop then
p1[j][2] = optimize(p1[j][2])
elseif op == ops.vset then
while t[i+1] and t[i+1][1] == op do
p1[j][2] = t[t+1][2]
i = i+1
end
else
while t[i+1] and t[i+1][1] == op do
p1[j][2] = p1[j][2] + t[i+1][2]
i = i+1
end
end
i = i+1
j = j+1
end
-- Pass 2: compute constants
local p2
if t.st then
local st = {}
for i = 0, 32767 do st[i] = 0 end
p2 = {}
p2.st = st
p2.p = 0
local opt = true
local j = 1
local tbuf = ""
for i = 1, #p1, 1 do
if opt then
local op = p1[i][1]
if op == ops.mpos then
p2.p = p2.p + p1[i][2]
j = j-1
elseif op == ops.cval then
st[p2.p] = st[p2.p] + p1[i][2]
j = j-1
elseif op == ops.writ then
tbuf = tbuf .. string.char(st[p2.p]):rep(p1[i][2])
j = j-1
elseif op == ops.vset then
st[p2.p] = p1[i][2]
j = j-1
elseif op == ops.loop and isbalanced(p1[i][2]) then
local _, ct, cl = getbalance(p1[i][2])
local rep = nil
if ct[0] then
ct[0][2] = (-ct[0][2])%256
for k = 0, 255 do
if (st[p2.p]+256*k)%ct[0][2] == 0 then
rep = k
break
end
end
end
if rep then
rep = (st[p2.p]+256*rep)/ct[0][2]
ct[0][2] = -ct[0][2]
for k = 1, #cl do
local o = cl[k]
local v = ct[o]
local p = (p2.p+o)%32768
st[p] = (st[p] + v[2]*rep)%256
end
else
if #tbuf > 0 then
p2[#p2+1] = {ops.wstr, tbuf}
j = j+1
end
p2[j] = p1[i]
return p2 -- Infinite loop
end
j = j-1
else
if #tbuf>0 then
p2[j] = {ops.wstr,tbuf}
tbuf = ""
j = j+1
end
p2[j] = p1[i]
opt = false
end
j = j+1
else
p2[j] = p1[i]
j = j+1
end
end
if #tbuf>0 then p2[#p2+1] = {ops.wstr, tbuf} end
else
p2 = p1
end
-- Return result of final pass
local t = p2
return t
end
local sct = {
[ops.cval] = function(o) return string.format("tape[pos]=(tape[pos]%+d)%%256", o) end,
[ops.vset] = function(v) return string.format("tape[pos]=%d", v%256) end,
[ops.mpos] = function(o) return string.format("pos=(pos%+d)%%32768",o) end,
[ops.writ] = function(n) return string.format("iowrite(srep(schar(tape[pos]),%d))", n) end,
[ops.wstr] = function(s) return string.format("iowrite(%q)", s) end,
[ops.read] = function(n) return string.format("tape[pos]=(string.byte(ioread(%d) or '',%d) or 0)", n, n) end,
[ops.loop] = function(b) return string.format("while tape[pos]~=0 do\n%s\nend", t2str(b)) end,
}
t2str = function(t)
local r = {}
for i = 1, #t do
a = t[i]
if not sct[a[1]] then error("Random transpiler bug: "..tostring(a[1])) end
r[i] = sct[a[1]](unpack(a, 2))
end
return table.concat(r, "\n"), t.st and table.concat(t.st, ",", 0), t.p, nil
end
local s, e = tokenize(str)
if not s then error(e) end
s.st = true
s, e = optimize(s)
if not s then error(e) end
local c, p
s, c, p, e = t2str(s)
if not s then error(e) end
print(string.format([[
local tape = {[0]=%s}
local pos = %d
local ioread = io.read
local iowrite = io.write
local sbyte = string.byte
local schar = string.char
local srep = string.rep
%s
]], c, p, s))

View File

@ -65,4 +65,4 @@ if (! @ARGV) {
foreach my $i (keys %defs) {
compile $i;
}
}
}

View File

@ -35,9 +35,12 @@ static void gend() {
static void ginit() {
S_ERRIF(SDL, SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO), gend());
S_ERRIF(SDL, !(window = SDL_CreateWindow("Minesweeper", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 270, 300, 0)), gend());
S_ERRIF(SDL, !(window = SDL_CreateWindow("Minesweeper",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
270, 300, 0)), gend());
windowid = SDL_GetWindowID(window);
S_ERRIF(SDL, !(renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC)), gend());
S_ERRIF(SDL, !(renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC)), gend());
SDL_RenderClear(renderer);
//int iiflags = IMG_INIT_PNG;
@ -60,7 +63,9 @@ static void redraw_board() {
int v;
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
r = (SDL_Rect){30*j, 30*i, 30, 30};
r = (SDL_Rect) {
30*j, 30*i, 30, 30
};
v = board[i][j];
if (v&TILE_OPENED)
v%=16;
@ -72,10 +77,15 @@ static void redraw_board() {
SDL_SetRenderDrawColor(renderer, 0xbb, 0xbb, 0xbb, 0xff);
SDL_RenderDrawLine(renderer, 0, 270, 269, 270);
SDL_RenderDrawLine(renderer, 0, 271, 269, 271);
r = (SDL_Rect){0, 272, 270, 28};
r = (SDL_Rect) {
0, 272, 270, 28
};
SDL_SetRenderDrawColor(renderer, 0x7f, 0x7f, 0x7f, 0xff);
SDL_RenderFillRect(renderer, &r);
SDL_SetRenderDrawColor(renderer, endgame==2?0xff:0x00, endgame==1?0x80:0x00, endgame==0?0xff:0x00, 0xff);
SDL_SetRenderDrawColor(renderer,
endgame==2?0xff:0x00,
endgame==1?0x80:0x00,
endgame==0?0xff:0x00, 0xff);
r.w = (uint16_t)270*nrevealed/(81-nmines);
SDL_RenderFillRect(renderer, &r);
SDL_RenderPresent(renderer);
@ -133,7 +143,7 @@ static void newboard() {
for (unsigned int i = 0; i < nmines; i++) {
int x = rand()%9;
int y = rand()%9;
while(board[y][x] == 9||(x==0&&y==0)) {
while (board[y][x] == 9||(x==0&&y==0)) {
x--;
if (x<0) {
y = (y+8)%9;
@ -157,55 +167,58 @@ static int mainloop() {
SDL_PollEvent(&event);
if (event.type == SDL_QUIT)
return 0;
switch(event.type) {
case SDL_MOUSEBUTTONDOWN: {
SDL_MouseButtonEvent mbevent = event.button;
if (mbevent.clicks != 1)
break;
if (mbevent.windowID != windowid)
break;
if (mbevent.x < 0 || mbevent.x > 270)
break;
if ((nrevealed>0 || endgame) && mbevent.y >= 270 && mbevent.y < 300) {
endgame = 0;
newboard();
break;
}
if (endgame)
break;
if (mbevent.y < 0 || mbevent.y >= 270)
break;
int x = mbevent.x/30;
int y = mbevent.y/30;
switch(mbevent.button) {
case SDL_BUTTON_LEFT: {
reveal_tile(x, y);
break;
}
case SDL_BUTTON_RIGHT: {
rmbevent(x, y);
break;
}
case SDL_BUTTON_MIDDLE: {
unflag_tile(x, y);
break;
}
default: break;
}
switch (event.type) {
case SDL_MOUSEBUTTONDOWN: {
SDL_MouseButtonEvent mbevent = event.button;
if (mbevent.clicks != 1)
break;
if (mbevent.windowID != windowid)
break;
if (mbevent.x < 0 || mbevent.x > 270)
break;
if ((nrevealed>0 || endgame) && mbevent.y >= 270 && mbevent.y < 300) {
endgame = 0;
newboard();
break;
}
if (endgame)
break;
if (mbevent.y < 0 || mbevent.y >= 270)
break;
int x = mbevent.x/30;
int y = mbevent.y/30;
switch (mbevent.button) {
case SDL_BUTTON_LEFT: {
reveal_tile(x, y);
break;
}
case SDL_BUTTON_RIGHT: {
rmbevent(x, y);
break;
}
case SDL_BUTTON_MIDDLE: {
unflag_tile(x, y);
break;
}
default:
break;
}
redraw_board();
break;
};
case SDL_WINDOWEVENT: {
SDL_WindowEvent wevent = event.window;
switch (wevent.event) {
case SDL_WINDOWEVENT_EXPOSED: {
redraw_board();
break;
};
case SDL_WINDOWEVENT: {
SDL_WindowEvent wevent = event.window;
switch (wevent.event) {
case SDL_WINDOWEVENT_EXPOSED:{
redraw_board();
break;
}
default: break;
}
}
default: break;
default:
break;
}
}
default:
break;
}
SDL_Delay(10);
return 1;
@ -216,13 +229,13 @@ int main(int argc, char **argv) {
nmines = strtoul(argv[1], NULL, 10);
if (nmines < 9 || nmines > 72)
nmines = 18;
ginit();
srand(time(NULL));
newboard();
while(mainloop());
while (mainloop());
gend();
return 0;
}
}