don't update min/max bounding box when drawing with CLEAR polarity
[geda-gerbv.git] / src / amacro.c
blobb0cde0c20e87b0a001827e720078400fd45af409
1 /*
2 * gEDA - GNU Electronic Design Automation
3 * This files is a part of gerbv.
5 * Copyright (C) 2000-2002 Stefan Petersen (spe@stacken.kth.se)
7 * $Id$
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.
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.
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
24 /** \file amacro.c
25 \brief Aperture macro parsing functions
26 \ingroup libgerbv
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
33 #include "gerbv.h"
34 #include "gerb_file.h"
35 #include "amacro.h"
38 * Allocates a new instruction structure
40 static gerbv_instruction_t *
41 new_instruction(void)
43 gerbv_instruction_t *instruction;
45 instruction = (gerbv_instruction_t *)malloc(sizeof(gerbv_instruction_t));
46 if (instruction == NULL) {
47 free(instruction);
48 return NULL;
51 memset(instruction, 0, sizeof(gerbv_instruction_t));
53 return instruction;
54 } /* new_instruction */
58 * Allocates a new amacro structure
60 static gerbv_amacro_t *
61 new_amacro(void)
63 gerbv_amacro_t *amacro;
65 amacro = (gerbv_amacro_t *)malloc(sizeof(gerbv_amacro_t));
66 if (amacro == NULL) {
67 free(amacro);
68 return NULL;
71 memset(amacro, 0, sizeof(gerbv_amacro_t));
73 return amacro;
74 } /* new_amacro */
78 * Defines precedence of operators used in aperture macros
80 static int
81 math_op_prec(gerbv_opcodes_t math_op)
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:
94 return 0;
95 } /* math_op_prec */
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.
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)
112 * Parses the definition of an aperture macro
114 gerbv_amacro_t *
115 parse_aperture_macro(gerb_file_t *fd)
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;
127 amacro = new_amacro();
129 memset(math_op, GERBV_OPCODE_NOP, MATH_OP_STACK_SIZE);
132 * Get macroname
134 amacro->name = gerb_fgetstring(fd, '*');
135 c = gerb_fgetc(fd); /* skip '*' */
136 if (c == EOF) {
137 continueLoop = 0;
141 * Since I'm lazy I have a dummy head. Therefore the first
142 * instruction in all programs will be NOP.
144 amacro->program = new_instruction();
145 ip = amacro->program;
147 while(continueLoop) {
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);
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;
170 * Check is due to some gerber files has spurious empty lines.
171 * (EagleCad of course).
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;
183 equate = 0;
184 primitive = 0;
185 found_primitive = 0;
187 break;
188 case '=':
189 if (equate) {
190 found_primitive = 1;
192 break;
193 case ',':
194 if (!found_primitive) {
195 found_primitive = 1;
196 break;
198 while (!MATH_OP_EMPTY) {
199 ip->next = new_instruction(); /* XXX Check return value */
200 ip = ip->next;
201 ip->opcode = MATH_OP_POP;
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;
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;
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;
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;
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;
247 MATH_OP_PUSH(GERBV_OPCODE_MUL);
248 comma = 1;
249 break;
250 case '0':
252 * Comments in aperture macros are a definition starting with
253 * zero and ends with a '*'
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;
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 '.':
272 * First number in an aperture macro describes the primitive
273 * as a numerical value
275 if (!found_primitive) {
276 primitive = (primitive * 10) + (c - '0');
277 break;
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;
298 if (c == EOF) {
299 continueLoop = 0;
302 free (amacro);
303 return NULL;
307 void
308 free_amacro(gerbv_amacro_t *amacro)
310 gerbv_amacro_t *am1, *am2;
311 gerbv_instruction_t *instr1, *instr2;
313 am1 = amacro;
314 while (am1 != NULL) {
315 free(am1->name);
316 am1->name = NULL;
318 instr1 = am1->program;
319 while (instr1 != NULL) {
320 instr2 = instr1;
321 instr1 = instr1->next;
322 free(instr2);
323 instr2 = NULL;
326 am2 = am1;
327 am1 = am1->next;
328 free(am2);
329 am2 = NULL;
332 return;
333 } /* free_amacro */
336 void
337 print_program(gerbv_amacro_t *amacro)
339 gerbv_instruction_t *ip;
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;
375 fflush(stdout);
377 } /* print_program */