1 /*
2 * gEDA - GNU Electronic Design Automation
3 * This files is a part of gerbv.
4 *
5 * Copyright (C) 2000-2002 Stefan Petersen (spe@stacken.kth.se)
6 *
7 * $Id$
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
22 */
23
24 /** \file amacro.c
25 \brief Aperture macro parsing functions
26 \ingroup libgerbv
27 */
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #include "gerbv.h"
34 #include "gerb_file.h"
35 #include "amacro.h"
36
37 /*
38 * Allocates a new instruction structure
39 */
40 static gerbv_instruction_t *
new_instruction(void)41 new_instruction(void)
42 {
43 gerbv_instruction_t *instruction;
44
45 instruction = (gerbv_instruction_t *)malloc(sizeof(gerbv_instruction_t));
46 if (instruction == NULL) {
47 free(instruction);
48 return NULL;
49 }
50
51 memset(instruction, 0, sizeof(gerbv_instruction_t));
52
53 return instruction;
54 } /* new_instruction */
55
56
57 /*
58 * Allocates a new amacro structure
59 */
60 static gerbv_amacro_t *
new_amacro(void)61 new_amacro(void)
62 {
63 gerbv_amacro_t *amacro;
64
65 amacro = (gerbv_amacro_t *)malloc(sizeof(gerbv_amacro_t));
66 if (amacro == NULL) {
67 free(amacro);
68 return NULL;
69 }
70
71 memset(amacro, 0, sizeof(gerbv_amacro_t));
72
73 return amacro;
74 } /* new_amacro */
75
76
77 /*
78 * Defines precedence of operators used in aperture macros
79 */
80 static int
math_op_prec(gerbv_opcodes_t math_op)81 math_op_prec(gerbv_opcodes_t math_op)
82 {
83 switch (math_op) {
84 case GERBV_OPCODE_ADD:
85 case GERBV_OPCODE_SUB:
86 return 1;
87 case GERBV_OPCODE_MUL:
88 case GERBV_OPCODE_DIV:
89 return 2;
90 default:
91 ;
92 }
93
94 return 0;
95 } /* math_op_prec */
96
97
98 /*
99 * Operations on the operator stack. The operator stack is used in the
100 * "shunting yard algorithm" to achive precedence.
101 * Aperture macros has a very limited set of operators and matching precedence,
102 * so it is solved by a small array and an index to that array.
103 */
104 #define MATH_OP_STACK_SIZE 2
105 #define MATH_OP_PUSH(val) math_op[math_op_idx++] = val
106 #define MATH_OP_POP math_op[--math_op_idx]
107 #define MATH_OP_TOP (math_op_idx > 0)?math_op[math_op_idx - 1]:GERBV_OPCODE_NOP
108 #define MATH_OP_EMPTY (math_op_idx == 0)
109
110
111 /*
112 * Parses the definition of an aperture macro
113 */
114 gerbv_amacro_t *
parse_aperture_macro(gerb_file_t * fd)115 parse_aperture_macro(gerb_file_t *fd)
116 {
117 gerbv_amacro_t *amacro;
118 gerbv_instruction_t *ip = NULL;
119 int primitive = 0, c, found_primitive = 0;
120 gerbv_opcodes_t math_op[MATH_OP_STACK_SIZE];
121 int math_op_idx = 0;
122 int comma = 0; /* Just read an operator (one of '*+X/) */
123 int neg = 0; /* negative numbers succeding , */
124 unsigned char continueLoop = 1;
125 int equate = 0;
126
127 amacro = new_amacro();
128
129 memset(math_op, GERBV_OPCODE_NOP, sizeof(math_op));
130
131 /*
132 * Get macroname
133 */
134 amacro->name = gerb_fgetstring(fd, '*');
135 c = gerb_fgetc(fd); /* skip '*' */
136 if (c == EOF) {
137 continueLoop = 0;
138 }
139
140 /*
141 * Since I'm lazy I have a dummy head. Therefore the first
142 * instruction in all programs will be NOP.
143 */
144 amacro->program = new_instruction();
145 ip = amacro->program;
146
147 while(continueLoop) {
148
149 c = gerb_fgetc(fd);
150 switch (c) {
151 case '$':
152 if (found_primitive) {
153 ip->next = new_instruction(); /* XXX Check return value */
154 ip = ip->next;
155 ip->opcode = GERBV_OPCODE_PPUSH;
156 amacro->nuf_push++;
157 ip->data.ival = gerb_fgetint(fd, NULL);
158 comma = 0;
159 } else {
160 equate = gerb_fgetint(fd, NULL);
161 }
162 break;
163 case '*':
164 while (!MATH_OP_EMPTY) {
165 ip->next = new_instruction(); /* XXX Check return value */
166 ip = ip->next;
167 ip->opcode = MATH_OP_POP;
168 }
169 /*
170 * Check is due to some gerber files has spurious empty lines.
171 * (EagleCad of course).
172 */
173 if (found_primitive) {
174 ip->next = new_instruction(); /* XXX Check return value */
175 ip = ip->next;
176 if (equate) {
177 ip->opcode = GERBV_OPCODE_PPOP;
178 ip->data.ival = equate;
179 } else {
180 ip->opcode = GERBV_OPCODE_PRIM;
181 ip->data.ival = primitive;
182 }
183 equate = 0;
184 primitive = 0;
185 found_primitive = 0;
186 }
187 break;
188 case '=':
189 if (equate) {
190 found_primitive = 1;
191 }
192 break;
193 case ',':
194 if (!found_primitive) {
195 found_primitive = 1;
196 break;
197 }
198 while (!MATH_OP_EMPTY) {
199 ip->next = new_instruction(); /* XXX Check return value */
200 ip = ip->next;
201 ip->opcode = MATH_OP_POP;
202 }
203 comma = 1;
204 break;
205 case '+':
206 while ((!MATH_OP_EMPTY) &&
207 (math_op_prec(MATH_OP_TOP) >= math_op_prec(GERBV_OPCODE_ADD))) {
208 ip->next = new_instruction(); /* XXX Check return value */
209 ip = ip->next;
210 ip->opcode = MATH_OP_POP;
211 }
212 MATH_OP_PUSH(GERBV_OPCODE_ADD);
213 comma = 1;
214 break;
215 case '-':
216 if (comma) {
217 neg = 1;
218 comma = 0;
219 break;
220 }
221 while((!MATH_OP_EMPTY) &&
222 (math_op_prec(MATH_OP_TOP) >= math_op_prec(GERBV_OPCODE_SUB))) {
223 ip->next = new_instruction(); /* XXX Check return value */
224 ip = ip->next;
225 ip->opcode = MATH_OP_POP;
226 }
227 MATH_OP_PUSH(GERBV_OPCODE_SUB);
228 break;
229 case '/':
230 while ((!MATH_OP_EMPTY) &&
231 (math_op_prec(MATH_OP_TOP) >= math_op_prec(GERBV_OPCODE_DIV))) {
232 ip->next = new_instruction(); /* XXX Check return value */
233 ip = ip->next;
234 ip->opcode = MATH_OP_POP;
235 }
236 MATH_OP_PUSH(GERBV_OPCODE_DIV);
237 comma = 1;
238 break;
239 case 'X':
240 case 'x':
241 while ((!MATH_OP_EMPTY) &&
242 (math_op_prec(MATH_OP_TOP) >= math_op_prec(GERBV_OPCODE_MUL))) {
243 ip->next = new_instruction(); /* XXX Check return value */
244 ip = ip->next;
245 ip->opcode = MATH_OP_POP;
246 }
247 MATH_OP_PUSH(GERBV_OPCODE_MUL);
248 comma = 1;
249 break;
250 case '0':
251 /*
252 * Comments in aperture macros are a definition starting with
253 * zero and ends with a '*'
254 */
255 if (!found_primitive && (primitive == 0)) {
256 /* Comment continues 'til next *, just throw it away */
257 gerb_fgetstring(fd, '*');
258 c = gerb_fgetc(fd); /* Read the '*' */
259 break;
260 }
261 case '1':
262 case '2':
263 case '3':
264 case '4':
265 case '5':
266 case '6':
267 case '7':
268 case '8':
269 case '9':
270 case '.':
271 /*
272 * First number in an aperture macro describes the primitive
273 * as a numerical value
274 */
275 if (!found_primitive) {
276 primitive = (primitive * 10) + (c - '0');
277 break;
278 }
279 (void)gerb_ungetc(fd);
280 ip->next = new_instruction(); /* XXX Check return value */
281 ip = ip->next;
282 ip->opcode = GERBV_OPCODE_PUSH;
283 amacro->nuf_push++;
284 ip->data.fval = gerb_fgetdouble(fd);
285 if (neg)
286 ip->data.fval = -ip->data.fval;
287 neg = 0;
288 comma = 0;
289 break;
290 case '%':
291 gerb_ungetc(fd); /* Must return with % first in string
292 since the main parser needs it */
293 return amacro;
294 default :
295 /* Whitespace */
296 break;
297 }
298 if (c == EOF) {
299 continueLoop = 0;
300 }
301 }
302 free (amacro);
303 return NULL;
304 }
305
306
307 void
free_amacro(gerbv_amacro_t * amacro)308 free_amacro(gerbv_amacro_t *amacro)
309 {
310 gerbv_amacro_t *am1, *am2;
311 gerbv_instruction_t *instr1, *instr2;
312
313 am1 = amacro;
314 while (am1 != NULL) {
315 free(am1->name);
316 am1->name = NULL;
317
318 instr1 = am1->program;
319 while (instr1 != NULL) {
320 instr2 = instr1;
321 instr1 = instr1->next;
322 free(instr2);
323 instr2 = NULL;
324 }
325
326 am2 = am1;
327 am1 = am1->next;
328 free(am2);
329 am2 = NULL;
330 }
331
332 return;
333 } /* free_amacro */
334
335
336 void
print_program(gerbv_amacro_t * amacro)337 print_program(gerbv_amacro_t *amacro)
338 {
339 gerbv_instruction_t *ip;
340
341 printf("Macroname [%s] :\n", amacro->name);
342 for (ip = amacro->program ; ip != NULL; ip = ip->next) {
343 switch(ip->opcode) {
344 case GERBV_OPCODE_NOP:
345 printf(" NOP\n");
346 break;
347 case GERBV_OPCODE_PUSH:
348 printf(" PUSH %f\n", ip->data.fval);
349 break;
350 case GERBV_OPCODE_PPOP:
351 printf(" PPOP %d\n", ip->data.ival);
352 break;
353 case GERBV_OPCODE_PPUSH:
354 printf(" PPUSH %d\n", ip->data.ival);
355 break;
356 case GERBV_OPCODE_ADD:
357 printf(" ADD\n");
358 break;
359 case GERBV_OPCODE_SUB:
360 printf(" SUB\n");
361 break;
362 case GERBV_OPCODE_MUL:
363 printf(" MUL\n");
364 break;
365 case GERBV_OPCODE_DIV:
366 printf(" DIV\n");
367 break;
368 case GERBV_OPCODE_PRIM:
369 printf(" PRIM %d\n", ip->data.ival);
370 break;
371 default :
372 printf(" ERROR!\n");
373 break;
374 }
375 fflush(stdout);
376 }
377 } /* print_program */
378