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