gerbv  2.6A
amacro.c
Go to the documentation of this file.
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 
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 *
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 *
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
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 *
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, MATH_OP_STACK_SIZE);
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
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
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 */