xref: /netbsd-src/sys/external/bsd/sljit/dist/doc/tutorial/brainfuck.c (revision 99e10043c2d890154986b9f0cfb10c84949ba483)
1*99e10043Salnsn /*
2*99e10043Salnsn  *    Brainfuck interpreter with SLJIT
3*99e10043Salnsn  *
4*99e10043Salnsn  *    Copyright 2015 Wen Xichang (wenxichang@163.com). All rights reserved.
5*99e10043Salnsn  *
6*99e10043Salnsn  * Redistribution and use in source and binary forms, with or without modification, are
7*99e10043Salnsn  * permitted provided that the following conditions are met:
8*99e10043Salnsn  *
9*99e10043Salnsn  *   1. Redistributions of source code must retain the above copyright notice, this list of
10*99e10043Salnsn  *      conditions and the following disclaimer.
11*99e10043Salnsn  *
12*99e10043Salnsn  *   2. Redistributions in binary form must reproduce the above copyright notice, this list
13*99e10043Salnsn  *      of conditions and the following disclaimer in the documentation and/or other materials
14*99e10043Salnsn  *      provided with the distribution.
15*99e10043Salnsn  *
16*99e10043Salnsn  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
17*99e10043Salnsn  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18*99e10043Salnsn  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19*99e10043Salnsn  * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20*99e10043Salnsn  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21*99e10043Salnsn  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22*99e10043Salnsn  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23*99e10043Salnsn  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24*99e10043Salnsn  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*99e10043Salnsn  */
26*99e10043Salnsn 
27*99e10043Salnsn #include "sljitLir.h"
28*99e10043Salnsn 
29*99e10043Salnsn #include <stdio.h>
30*99e10043Salnsn #include <stdlib.h>
31*99e10043Salnsn 
32*99e10043Salnsn #define BF_CELL_SIZE		3000
33*99e10043Salnsn #define BF_LOOP_LEVEL		256
34*99e10043Salnsn 
readvalid(FILE * src)35*99e10043Salnsn static int readvalid(FILE *src)
36*99e10043Salnsn {
37*99e10043Salnsn 	int chr;
38*99e10043Salnsn 
39*99e10043Salnsn 	while ((chr = fgetc(src)) != EOF) {
40*99e10043Salnsn 		switch (chr) {
41*99e10043Salnsn 		case '+':
42*99e10043Salnsn 		case '-':
43*99e10043Salnsn 		case '>':
44*99e10043Salnsn 		case '<':
45*99e10043Salnsn 		case '.':
46*99e10043Salnsn 		case ',':
47*99e10043Salnsn 		case '[':
48*99e10043Salnsn 		case ']':
49*99e10043Salnsn 			return chr;
50*99e10043Salnsn 		}
51*99e10043Salnsn 	}
52*99e10043Salnsn 
53*99e10043Salnsn 	return chr;
54*99e10043Salnsn }
55*99e10043Salnsn 
56*99e10043Salnsn /* reading same instruction, and count, for optimization */
57*99e10043Salnsn /* ++++  -> '+', 4 */
gettoken(FILE * src,int * ntok)58*99e10043Salnsn static int gettoken(FILE *src, int *ntok)
59*99e10043Salnsn {
60*99e10043Salnsn 	int chr = readvalid(src);
61*99e10043Salnsn 	int chr2;
62*99e10043Salnsn 	int cnt = 1;
63*99e10043Salnsn 
64*99e10043Salnsn 	if (chr == EOF)
65*99e10043Salnsn 		return EOF;
66*99e10043Salnsn 
67*99e10043Salnsn 	if (chr == '.' || chr == ',' || chr == '[' || chr == ']') {
68*99e10043Salnsn 		*ntok = 1;
69*99e10043Salnsn 		return chr;
70*99e10043Salnsn 	}
71*99e10043Salnsn 
72*99e10043Salnsn 	while ((chr2 = readvalid(src)) == chr)
73*99e10043Salnsn 		cnt++;
74*99e10043Salnsn 
75*99e10043Salnsn 	if (chr2 != EOF)
76*99e10043Salnsn 		ungetc(chr2, src);
77*99e10043Salnsn 
78*99e10043Salnsn 	*ntok = cnt;
79*99e10043Salnsn 	return chr;
80*99e10043Salnsn }
81*99e10043Salnsn 
82*99e10043Salnsn /* maintaining loop matched [] */
83*99e10043Salnsn struct loop_node_st {
84*99e10043Salnsn 	struct sljit_label *loop_start;
85*99e10043Salnsn 	struct sljit_jump *loop_end;
86*99e10043Salnsn };
87*99e10043Salnsn 
88*99e10043Salnsn /* stack of loops */
89*99e10043Salnsn static struct loop_node_st loop_stack[BF_LOOP_LEVEL];
90*99e10043Salnsn static int loop_sp;
91*99e10043Salnsn 
loop_push(struct sljit_label * loop_start,struct sljit_jump * loop_end)92*99e10043Salnsn static int loop_push(struct sljit_label *loop_start, struct sljit_jump *loop_end)
93*99e10043Salnsn {
94*99e10043Salnsn 	if (loop_sp >= BF_LOOP_LEVEL)
95*99e10043Salnsn 		return -1;
96*99e10043Salnsn 
97*99e10043Salnsn 	loop_stack[loop_sp].loop_start = loop_start;
98*99e10043Salnsn 	loop_stack[loop_sp].loop_end = loop_end;
99*99e10043Salnsn 	loop_sp++;
100*99e10043Salnsn 	return 0;
101*99e10043Salnsn }
102*99e10043Salnsn 
loop_pop(struct sljit_label ** loop_start,struct sljit_jump ** loop_end)103*99e10043Salnsn static int loop_pop(struct sljit_label **loop_start, struct sljit_jump **loop_end)
104*99e10043Salnsn {
105*99e10043Salnsn 	if (loop_sp <= 0)
106*99e10043Salnsn 		return -1;
107*99e10043Salnsn 
108*99e10043Salnsn 	loop_sp--;
109*99e10043Salnsn 	*loop_start = loop_stack[loop_sp].loop_start;
110*99e10043Salnsn 	*loop_end = loop_stack[loop_sp].loop_end;
111*99e10043Salnsn 	return 0;
112*99e10043Salnsn }
113*99e10043Salnsn 
my_alloc(long size,long n)114*99e10043Salnsn static SLJIT_CALL void *my_alloc(long size, long n)
115*99e10043Salnsn {
116*99e10043Salnsn 	return calloc(size, n);
117*99e10043Salnsn }
118*99e10043Salnsn 
my_putchar(long c)119*99e10043Salnsn static SLJIT_CALL void my_putchar(long c)
120*99e10043Salnsn {
121*99e10043Salnsn 	putchar(c);
122*99e10043Salnsn }
123*99e10043Salnsn 
my_getchar(void)124*99e10043Salnsn static SLJIT_CALL long my_getchar(void)
125*99e10043Salnsn {
126*99e10043Salnsn 	return getchar();
127*99e10043Salnsn }
128*99e10043Salnsn 
my_free(void * mem)129*99e10043Salnsn static SLJIT_CALL void my_free(void *mem)
130*99e10043Salnsn {
131*99e10043Salnsn 	free(mem);
132*99e10043Salnsn }
133*99e10043Salnsn 
134*99e10043Salnsn #define loop_empty()		(loop_sp == 0)
135*99e10043Salnsn 
136*99e10043Salnsn /* compile bf source to a void func() */
compile(FILE * src,unsigned long * lcode)137*99e10043Salnsn static void *compile(FILE *src, unsigned long *lcode)
138*99e10043Salnsn {
139*99e10043Salnsn 	void *code = NULL;
140*99e10043Salnsn 	int chr;
141*99e10043Salnsn 	int nchr;
142*99e10043Salnsn 
143*99e10043Salnsn 	struct sljit_compiler *C = sljit_create_compiler();
144*99e10043Salnsn 	struct sljit_jump *end;
145*99e10043Salnsn 	struct sljit_label *loop_start;
146*99e10043Salnsn 	struct sljit_jump *loop_end;
147*99e10043Salnsn 
148*99e10043Salnsn 	int SP = SLJIT_S0;			/* bf SP */
149*99e10043Salnsn 	int CELLS = SLJIT_S1;		/* bf array */
150*99e10043Salnsn 
151*99e10043Salnsn 	sljit_emit_enter(C, 0,  0,  2, 2, 0, 0, 0);								/* opt arg R  S  FR FS local_size */
152*99e10043Salnsn 
153*99e10043Salnsn 	sljit_emit_op2(C, SLJIT_XOR, SP, 0, SP, 0, SP, 0);						/* SP = 0 */
154*99e10043Salnsn 
155*99e10043Salnsn 	sljit_emit_op1(C, SLJIT_MOV, SLJIT_R0, 0, SLJIT_IMM, BF_CELL_SIZE);
156*99e10043Salnsn 	sljit_emit_op1(C, SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, 1);
157*99e10043Salnsn 	sljit_emit_ijump(C, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(my_alloc));/* calloc(BF_CELL_SIZE, 1) => R0 */
158*99e10043Salnsn 
159*99e10043Salnsn 	end = sljit_emit_cmp(C, SLJIT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0);		/* R0 == 0 --> jump end */
160*99e10043Salnsn 
161*99e10043Salnsn 	sljit_emit_op1(C, SLJIT_MOV, CELLS, 0, SLJIT_R0, 0);					/* CELLS = R0 */
162*99e10043Salnsn 
163*99e10043Salnsn 	while ((chr = gettoken(src, &nchr)) != EOF) {
164*99e10043Salnsn 		switch (chr) {
165*99e10043Salnsn 		case '+':
166*99e10043Salnsn 		case '-':
167*99e10043Salnsn 			sljit_emit_op1(C, SLJIT_MOV_UB, SLJIT_R0, 0, SLJIT_MEM2(CELLS, SP), 0);		/* R0 = CELLS[SP] */
168*99e10043Salnsn 			sljit_emit_op2(C, chr == '+' ? SLJIT_ADD : SLJIT_SUB,
169*99e10043Salnsn 						   SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, nchr);					/* R0 ?= nchr */
170*99e10043Salnsn 			sljit_emit_op1(C, SLJIT_MOV_UB, SLJIT_MEM2(CELLS, SP), 0, SLJIT_R0, 0);		/* CELLS[SP] = R0 */
171*99e10043Salnsn 			break;
172*99e10043Salnsn 		case '>':
173*99e10043Salnsn 		case '<':
174*99e10043Salnsn 			sljit_emit_op2(C, chr == '>' ? SLJIT_ADD : SLJIT_SUB,
175*99e10043Salnsn 						   SP, 0, SP, 0, SLJIT_IMM, nchr);								/* SP ?= nchr */
176*99e10043Salnsn 			break;
177*99e10043Salnsn 		case '.':
178*99e10043Salnsn 			sljit_emit_op1(C, SLJIT_MOV_UB, SLJIT_R0, 0, SLJIT_MEM2(CELLS, SP), 0);		/* R0 = CELLS[SP] */
179*99e10043Salnsn 			sljit_emit_ijump(C, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(my_putchar));	/* putchar(R0) */
180*99e10043Salnsn 			break;
181*99e10043Salnsn 		case ',':
182*99e10043Salnsn 			sljit_emit_ijump(C, SLJIT_CALL0, SLJIT_IMM, SLJIT_FUNC_OFFSET(my_getchar));	/* R0 = getchar() */
183*99e10043Salnsn 			sljit_emit_op1(C, SLJIT_MOV_UB, SLJIT_MEM2(CELLS, SP), 0, SLJIT_R0, 0);		/* CELLS[SP] = R0 */
184*99e10043Salnsn 			break;
185*99e10043Salnsn 		case '[':
186*99e10043Salnsn 			loop_start = sljit_emit_label(C);											/* loop_start: */
187*99e10043Salnsn 			sljit_emit_op1(C, SLJIT_MOV_UB, SLJIT_R0, 0, SLJIT_MEM2(CELLS, SP), 0);		/* R0 = CELLS[SP] */
188*99e10043Salnsn 			loop_end = sljit_emit_cmp(C, SLJIT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0);		/* IF R0 == 0 goto loop_end */
189*99e10043Salnsn 
190*99e10043Salnsn 			if (loop_push(loop_start, loop_end)) {
191*99e10043Salnsn 				fprintf(stderr, "Too many loop level\n");
192*99e10043Salnsn 				goto compile_failed;
193*99e10043Salnsn 			}
194*99e10043Salnsn 			break;
195*99e10043Salnsn 		case ']':
196*99e10043Salnsn 			if (loop_pop(&loop_start, &loop_end)) {
197*99e10043Salnsn 				fprintf(stderr, "Unmatch loop ]\n");
198*99e10043Salnsn 				goto compile_failed;
199*99e10043Salnsn 			}
200*99e10043Salnsn 
201*99e10043Salnsn 			sljit_set_label(sljit_emit_jump(C, SLJIT_JUMP), loop_start);				/* goto loop_start */
202*99e10043Salnsn 			sljit_set_label(loop_end, sljit_emit_label(C));								/* loop_end: */
203*99e10043Salnsn 			break;
204*99e10043Salnsn 		}
205*99e10043Salnsn 	}
206*99e10043Salnsn 
207*99e10043Salnsn 	if (!loop_empty()) {
208*99e10043Salnsn 		fprintf(stderr, "Unmatch loop [\n");
209*99e10043Salnsn 		goto compile_failed;
210*99e10043Salnsn 	}
211*99e10043Salnsn 
212*99e10043Salnsn 	sljit_emit_op1(C, SLJIT_MOV, SLJIT_R0, 0, CELLS, 0);
213*99e10043Salnsn 	sljit_emit_ijump(C, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(my_free));	/* free(CELLS) */
214*99e10043Salnsn 
215*99e10043Salnsn 	sljit_set_label(end, sljit_emit_label(C));
216*99e10043Salnsn 	sljit_emit_return(C, SLJIT_UNUSED, 0, 0);
217*99e10043Salnsn 
218*99e10043Salnsn 	code = sljit_generate_code(C);
219*99e10043Salnsn 	if (lcode)
220*99e10043Salnsn 		*lcode = sljit_get_generated_code_size(C);
221*99e10043Salnsn 
222*99e10043Salnsn compile_failed:
223*99e10043Salnsn 	sljit_free_compiler(C);
224*99e10043Salnsn 	return code;
225*99e10043Salnsn }
226*99e10043Salnsn 
227*99e10043Salnsn /* function prototype of bf compiled code */
228*99e10043Salnsn typedef void (*bf_entry_t)(void);
229*99e10043Salnsn 
main(int argc,char ** argv)230*99e10043Salnsn int main(int argc, char **argv)
231*99e10043Salnsn {
232*99e10043Salnsn 	void *code;
233*99e10043Salnsn 	bf_entry_t entry;
234*99e10043Salnsn 	FILE *fp;
235*99e10043Salnsn 
236*99e10043Salnsn 	if (argc < 2) {
237*99e10043Salnsn 		fprintf(stderr, "Usage: %s <brainfuck program>\n", argv[0]);
238*99e10043Salnsn 		return -1;
239*99e10043Salnsn 	}
240*99e10043Salnsn 
241*99e10043Salnsn 	fp = fopen(argv[1], "rb");
242*99e10043Salnsn 	if (!fp) {
243*99e10043Salnsn 		perror("open");
244*99e10043Salnsn 		return -1;
245*99e10043Salnsn 	}
246*99e10043Salnsn 
247*99e10043Salnsn 	code = compile(fp, NULL);
248*99e10043Salnsn 	fclose(fp);
249*99e10043Salnsn 
250*99e10043Salnsn 	if (!code) {
251*99e10043Salnsn 		fprintf(stderr, "[Fatal]: Compile failed\n");
252*99e10043Salnsn 		return -1;
253*99e10043Salnsn 	}
254*99e10043Salnsn 
255*99e10043Salnsn 	entry = (bf_entry_t)code;
256*99e10043Salnsn 	entry();
257*99e10043Salnsn 
258*99e10043Salnsn 	sljit_free_code(code);
259*99e10043Salnsn 	return 0;
260*99e10043Salnsn }
261