Add licence
[ilari-esolangs.git] / parser.c
blob725e1dba2ef5f7b46ed5b41b2214907d0c1242bc
1 #include "parser.h"
2 #include <stdio.h>
3 #include "sane-ctype.h"
4 #include "class.h"
8 /* on line, no non-whitespace yet. */
9 #define PS_INITIAL 0
10 /* Last was non-whitespace. */
11 #define PS_LAST_NOT_WS 1
12 /* Last was whitespace, non-ws seen. */
13 #define PS_WHITESPACE 2
14 /* Last was newline. */
15 #define PS_NEWLINE 3
16 /* Last was Carriage return. */
17 #define PS_LAST_CR 4
18 /* Start of line. */
19 #define PS2_INITIAL 0
20 /* On non-comment line. */
21 #define PS2_NOT_COMMENT 1
22 /* On comment line. */
23 #define PS2_COMMENT 2
25 /* Parse context. */
26 struct parser
28 /* Line number. */
29 unsigned p_line;
30 /* Main parser status. */
31 unsigned p_status;
32 /* Secondary parser status. */
33 unsigned p_status2;
34 /* Backslash flag. */
35 unsigned p_backslash;
36 /* Number of class to parse. */
37 size_t p_classnum;
38 /* Number of fields. */
39 size_t p_fields;
40 /* Number of locals. */
41 size_t p_locals;
42 /* Max number of locals. */
43 size_t p_maxlocals;
44 /* Allocated size of instructions. */
45 size_t p_ins_allocated;
46 /* Real size of instructions. */
47 size_t p_ins_size;
48 /* Instructions. */
49 unsigned char* p_ins;
50 /* Field number. */
51 unsigned p_field;
52 /* Have value for field. 0 = no, 1 = zero, 2 = nonzero. */
53 unsigned p_have_field_value;
56 #define CARRIAGE_RETURN 13
57 #define SPACE 32
58 #define LINEFEED 10
59 #define BACKSLASH 92
60 #define HASH 35
61 #define ZERO 48
62 #define NINE 57
65 /*****************************************************************************/
66 struct parser* parser_create()
68 struct parser* parser;
69 parser = malloc(sizeof(struct parser));
70 if(!parser) {
71 fprintf(stderr, "Out of memory!\n");
72 exit(1);
75 parser->p_line = 1;
76 parser->p_status = PS_INITIAL;
77 parser->p_status2 = PS2_INITIAL;
78 parser->p_backslash = 0;
79 parser->p_classnum = 0;
80 parser->p_fields = 0;
81 parser->p_locals = 0;
82 parser->p_maxlocals = 0;
83 parser->p_ins_allocated = 0;
84 parser->p_ins_size = 0;
85 parser->p_ins = NULL;
86 parser->p_field = 0;
87 parser->p_have_field_value = 0;
89 return parser;
92 /*****************************************************************************/
93 void parser_destroy(struct parser* parser)
95 free(parser->p_ins);
96 free(parser);
99 /*****************************************************************************/
100 static void parser_input3(struct parser* parser, char input)
102 if(input == SPACE) {
103 if(parser->p_field == 2) {
104 fprintf(stderr, "Line %zu: Illegal character <%02X> "
105 "in class body.\n", parser->p_line, input);
106 exit(1);
108 parser->p_field++;
109 parser->p_have_field_value = 0;
110 } else if(input == LINEFEED) {
111 if(parser->p_field < 1) {
112 fprintf(stderr, "Line %zu: Truncated class "
113 "description.\n", parser->p_line);
114 exit(1);
116 /* Parsed class. */
117 if(parser->p_locals > parser->p_maxlocals)
118 parser->p_maxlocals = parser->p_locals;
120 struct class* class;
121 class = class_make(parser->p_fields, parser->p_locals,
122 parser->p_ins, parser->p_ins_size);
123 class_register(class);
125 parser->p_ins = NULL;
126 parser->p_ins_allocated = 0;
127 parser->p_ins_size = 0;
128 parser->p_have_field_value = 0;
129 parser->p_field = 0;
130 parser->p_fields = 0;
131 parser->p_locals = 0;
132 } else if(parser->p_field < 2) {
133 if(input < ZERO && input > NINE) {
134 fprintf(stderr, "Line %zu: Illegal character <%02X> "
135 "in numeric value.\n", parser->p_line,
136 input);
137 exit(1);
139 if(input == ZERO) {
140 if(parser->p_have_field_value == 1) {
141 fprintf(stderr, "Line %zu: Invalid numeric "
142 "value.\n", parser->p_line);
143 exit(1);
148 if(parser->p_field == 0)
149 parser->p_fields = 10 * parser->p_fields +
150 (input - ZERO);
151 else
152 parser->p_locals = 10 * parser->p_locals +
153 (input - ZERO);
155 } else {
156 switch(input) {
157 case '+':
158 case '-':
159 case 'E':
160 case 'f':
161 case 'F':
162 case 'l':
163 case 'L':
164 case 'p':
165 case 'P':
166 case 's':
167 case 'S':
168 case 'N':
169 break;
170 default:
171 fprintf(stderr, "Line %zu: Invalid instruction "
172 "<%02X>.\n", parser->p_line, input);
173 exit(1);
175 if(parser->p_ins_size == parser->p_ins_allocated) {
176 if(parser->p_ins_allocated == 0)
177 parser->p_ins_allocated = 2;
178 parser->p_ins_allocated = parser->p_ins_allocated *
179 3 / 2;
180 parser->p_ins = realloc(parser->p_ins,
181 parser->p_ins_allocated);
182 if(!parser->p_ins) {
183 fprintf(stderr, "Out of memory.\n");
184 exit(1);
187 parser->p_ins[parser->p_ins_size++] = input;
191 /*****************************************************************************/
192 static void parser_input2(struct parser* parser, char input)
194 /* Here, we got to handle comments and backslashes. */
195 if(parser->p_backslash && (input == LINEFEED || input == SPACE))
196 return;
197 parser->p_backslash = (input == BACKSLASH);
198 if(input == BACKSLASH)
199 return;
201 switch(parser->p_status2) {
202 case PS2_INITIAL:
203 if(input == HASH)
204 parser->p_status2 = PS2_COMMENT;
205 else {
206 parser->p_status2 = PS2_NOT_COMMENT;
207 parser_input3(parser, input);
209 break;
210 case PS2_NOT_COMMENT:
211 parser_input3(parser, input);
212 /* Fallthrough. */
213 case PS2_COMMENT:
214 if(input == LINEFEED)
215 parser->p_status2 = PS2_INITIAL;
216 break;
220 /*****************************************************************************/
221 void parser_input(struct parser* parser, char input)
223 /* We need to clean up input so that whitespaces are normalized. */
224 unsigned is_space = sane_isspace((unsigned char)input);
225 unsigned is_newline = sane_isnewline((unsigned char)input);
227 switch(parser->p_status) {
228 case PS_INITIAL:
229 if(is_newline) {
230 parser->p_status = ((input == CARRIAGE_RETURN) ?
231 PS_LAST_CR : PS_NEWLINE);
232 parser->p_line++;
233 } else if(is_space) {
234 /* Keep state. */
235 } else {
236 parser->p_status = PS_LAST_NOT_WS;
237 parser_input2(parser, input);
239 break;
240 case PS_LAST_NOT_WS:
241 if(is_newline) {
242 parser_input2(parser, LINEFEED);
243 parser->p_status = ((input == CARRIAGE_RETURN) ?
244 PS_LAST_CR : PS_NEWLINE);
245 parser->p_line++;
246 } else if(is_space) {
247 parser->p_status = PS_WHITESPACE;
248 } else {
249 parser_input2(parser, input);
251 break;
252 case PS_WHITESPACE:
253 if(is_newline) {
254 parser_input2(parser, LINEFEED);
255 parser->p_status = ((input == CARRIAGE_RETURN) ?
256 PS_LAST_CR : PS_NEWLINE);
257 parser->p_line++;
258 } else if(is_space) {
259 /* Keep state. */
260 } else {
261 parser->p_status = PS_LAST_NOT_WS;
262 parser_input2(parser, SPACE);
263 parser_input2(parser, input);
265 break;
266 case PS_NEWLINE:
267 if(is_newline) {
268 parser->p_status = ((input == CARRIAGE_RETURN) ?
269 PS_LAST_CR : PS_NEWLINE);
270 parser->p_line++;
271 } else if(is_space) {
272 parser->p_status = PS_INITIAL;
273 } else {
274 parser->p_status = PS_LAST_NOT_WS;
275 parser_input2(parser, input);
277 break;
278 case PS_LAST_CR:
279 if(is_newline) {
280 parser->p_status = ((input == CARRIAGE_RETURN) ?
281 PS_LAST_CR : PS_NEWLINE);
282 if(input != LINEFEED)
283 parser->p_line++;
284 } else if(is_space) {
285 parser->p_status = PS_INITIAL;
286 } else {
287 parser->p_status = PS_LAST_NOT_WS;
288 parser_input2(parser, input);
290 break;
294 /*****************************************************************************/
295 void parser_eof(struct parser* parser)
297 if(parser->p_status == PS_LAST_NOT_WS ||
298 parser->p_status == PS_WHITESPACE)
299 parser_input2(parser, LINEFEED);
302 /*****************************************************************************/
303 size_t parser_maxlocals(struct parser* parser)
305 return parser->p_maxlocals;