mirror of
git://projects.qi-hardware.com/fped.git
synced 2024-12-22 22:52:25 +02:00
bb055fa209
This is for thermal vias and the unusual grounding method NXP use in their HVQFN33 footprints.
1303 lines
22 KiB
Plaintext
1303 lines
22 KiB
Plaintext
%{
|
|
/*
|
|
* fpd.y - FootPrint Definition language
|
|
*
|
|
* Written 2009-2012 by Werner Almesberger
|
|
* Copyright 2009-2012 by Werner Almesberger
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "util.h"
|
|
#include "error.h"
|
|
#include "coord.h"
|
|
#include "expr.h"
|
|
#include "obj.h"
|
|
#include "meas.h"
|
|
#include "gui_status.h"
|
|
#include "gui_inst.h" /* for %meas */
|
|
#include "dump.h"
|
|
#include "tsort.h"
|
|
#include "fpd.h"
|
|
|
|
#include "y.tab.h"
|
|
|
|
|
|
struct expr *expr_result;
|
|
const char *var_id;
|
|
struct value *var_value_list;
|
|
|
|
|
|
static struct frame *curr_frame;
|
|
static struct table *curr_table;
|
|
static struct row *curr_row;
|
|
|
|
static struct vec *last_vec = NULL;
|
|
|
|
static struct table **next_table;
|
|
static struct loop **next_loop;
|
|
static struct vec **next_vec;
|
|
static struct obj **next_obj;
|
|
|
|
static int n_vars, n_values;
|
|
|
|
static const char *id_sin, *id_cos, *id_sqrt, *id_floor;
|
|
|
|
static struct tsort *tsort;
|
|
|
|
|
|
/* ----- lookup functions -------------------------------------------------- */
|
|
|
|
|
|
static struct frame *find_frame(const char *name)
|
|
{
|
|
struct frame *f;
|
|
|
|
for (f = frames->next; f; f = f->next)
|
|
if (f->name == name)
|
|
return f;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static struct vec *find_vec(const struct frame *frame, const char *name)
|
|
{
|
|
struct vec *v;
|
|
|
|
for (v = frame->vecs; v; v = v->next)
|
|
if (v->name == name)
|
|
return v;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static struct obj *find_obj(const struct frame *frame, const char *name)
|
|
{
|
|
struct obj *obj;
|
|
|
|
for (obj = frame->objs; obj; obj = obj->next)
|
|
if (obj->name == name)
|
|
return obj;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int find_label(const struct frame *frame, const char *name)
|
|
{
|
|
if (find_vec(frame, name))
|
|
return 1;
|
|
if (find_obj(frame, name))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct var *find_var(const struct frame *frame, const char *name)
|
|
{
|
|
const struct table *table;
|
|
struct var *var;
|
|
struct loop *loop;
|
|
|
|
for (table = frame->tables; table; table = table->next)
|
|
for (var = table->vars; var; var = var->next)
|
|
if (!var->key && var->name == name)
|
|
return var;
|
|
for (loop = frame->loops; loop; loop = loop->next)
|
|
if (loop->var.name == name)
|
|
return &loop->var;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* ----- item creation ----------------------------------------------------- */
|
|
|
|
|
|
static void set_frame(struct frame *frame)
|
|
{
|
|
curr_frame = frame;
|
|
next_table = &frame->tables;
|
|
next_loop = &frame->loops;
|
|
next_vec = &frame->vecs;
|
|
next_obj = &frame->objs;
|
|
last_vec = NULL;
|
|
}
|
|
|
|
|
|
static void make_var(const char *id, int key, struct expr *expr)
|
|
{
|
|
struct table *table;
|
|
|
|
table = zalloc_type(struct table);
|
|
table->vars = zalloc_type(struct var);
|
|
table->vars->name = id;
|
|
table->vars->frame = curr_frame;
|
|
table->vars->table = table;
|
|
table->vars->key = key;
|
|
table->rows = zalloc_type(struct row);
|
|
table->rows->table = table;
|
|
table->rows->values = zalloc_type(struct value);
|
|
table->rows->values->expr = expr;
|
|
table->rows->values->row = table->rows;
|
|
table->active_row = table->rows;
|
|
*next_table = table;
|
|
next_table = &table->next;
|
|
}
|
|
|
|
|
|
static void make_loop(const char *id, struct expr *from, struct expr *to)
|
|
{
|
|
struct loop *loop;
|
|
|
|
loop = alloc_type(struct loop);
|
|
loop->var.name = id;
|
|
loop->var.next = NULL;
|
|
loop->var.frame = curr_frame;
|
|
loop->var.table = NULL;
|
|
loop->from.expr = from;
|
|
loop->from.row = NULL;
|
|
loop->from.next = NULL;
|
|
loop->to.expr = to;
|
|
loop->to.row = NULL;
|
|
loop->to.next = NULL;
|
|
loop->next = NULL;
|
|
loop->active = 0;
|
|
loop->initialized = 0;
|
|
*next_loop = loop;
|
|
next_loop = &loop->next;
|
|
}
|
|
|
|
|
|
static struct obj *new_obj(enum obj_type type)
|
|
{
|
|
struct obj *obj;
|
|
|
|
obj = alloc_type(struct obj);
|
|
obj->type = type;
|
|
obj->name = NULL;
|
|
obj->frame = curr_frame;
|
|
obj->next = NULL;
|
|
obj->lineno = lineno;
|
|
return obj;
|
|
}
|
|
|
|
|
|
/* ---- frame qualifiers --------------------------------------------------- */
|
|
|
|
|
|
static int duplicate_qualifier(const struct frame_qual *a,
|
|
const struct frame_qual *b)
|
|
{
|
|
if (!b)
|
|
return 0;
|
|
if (a != b && a->frame == b->frame) {
|
|
yyerrorf("duplicate qualifier \"%s\"", a->frame->name);
|
|
return 1;
|
|
}
|
|
if (b && duplicate_qualifier(a, b->next))
|
|
return 1;
|
|
return duplicate_qualifier(a->next, a->next);
|
|
}
|
|
|
|
|
|
static int can_reach(const struct frame *curr, const struct frame_qual *qual,
|
|
const struct frame *end)
|
|
{
|
|
const struct obj *obj;
|
|
|
|
if (curr == end)
|
|
return !qual;
|
|
|
|
/*
|
|
* Don't recurse for removing the qualifier alone. We require a frame
|
|
* reference step as well, so that things like foo.foo.foo.bar.vec
|
|
* aren't allowed.
|
|
*
|
|
* Since a duplicate qualifier can never work, we test for this error
|
|
* explicitly in "duplicate_qualifier".
|
|
*/
|
|
if (qual && curr == qual->frame)
|
|
qual = qual->next;
|
|
|
|
for (obj = curr->objs; obj; obj = obj->next)
|
|
if (obj->type == ot_frame)
|
|
if (can_reach(obj->u.frame.ref, qual, end))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int check_qbase(struct qbase *qbase)
|
|
{
|
|
if (duplicate_qualifier(qbase->qualifiers, qbase->qualifiers))
|
|
return 0;
|
|
|
|
if (!can_reach(frames, qbase->qualifiers, qbase->vec->frame))
|
|
yywarn("not all qualifiers can be reached");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* ----- debugging directives ---------------------------------------------- */
|
|
|
|
|
|
static int dbg_delete(const char *frame_name, const char *name)
|
|
{
|
|
struct vec *vec;
|
|
struct obj *obj;
|
|
struct frame *frame;
|
|
|
|
if (!frame_name) {
|
|
frame = curr_frame;
|
|
} else {
|
|
frame = find_frame(frame_name);
|
|
if (!frame) {
|
|
yyerrorf("unknown frame \"%s\"", frame_name);
|
|
return 0;
|
|
}
|
|
}
|
|
vec = find_vec(frame, name);
|
|
if (vec) {
|
|
delete_vec(vec);
|
|
return 1;
|
|
}
|
|
obj = find_obj(frame, name);
|
|
if (obj) {
|
|
delete_obj(obj);
|
|
return 1;
|
|
}
|
|
if (!frame_name) {
|
|
frame = find_frame(name);
|
|
if (frame) {
|
|
if (curr_frame == frame) {
|
|
yyerrorf("a frame can't delete itself");
|
|
return 0;
|
|
}
|
|
delete_frame(frame);
|
|
return 1;
|
|
}
|
|
}
|
|
if (frame_name)
|
|
yyerrorf("unknown item \"%s.%s\"", frame_name, name);
|
|
else
|
|
yyerrorf("unknown item \"%s\"", name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int dbg_move(const char *name, int anchor, const char *dest)
|
|
{
|
|
struct vec *to, *vec;
|
|
struct obj *obj;
|
|
struct vec **anchors[3];
|
|
int n_anchors;
|
|
|
|
to = find_vec(curr_frame, dest);
|
|
if (!to) {
|
|
yyerrorf("unknown vector \"%s\"", dest);
|
|
return 0;
|
|
}
|
|
vec = find_vec(curr_frame, name);
|
|
if (vec) {
|
|
if (anchor) {
|
|
yyerrorf("invalid anchor (%d > 0)", anchor);
|
|
return 0;
|
|
}
|
|
vec->base = to;
|
|
return 1;
|
|
}
|
|
obj = find_obj(curr_frame, name);
|
|
if (!obj) {
|
|
yyerrorf("unknown item \"%s\"", name);
|
|
return 0;
|
|
}
|
|
n_anchors = obj_anchors(obj, anchors);
|
|
if (anchor >= n_anchors) {
|
|
yyerrorf("invalid anchor (%d > %d)", anchor, n_anchors-1);
|
|
return 0;
|
|
}
|
|
*anchors[anchor] = to;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* @@@ This is very similar to what we do in rule "obj". Consider merging.
|
|
*/
|
|
|
|
/*
|
|
* We need to pass base_frame and base_vec, not just the vector (with the
|
|
* frame implied) since we can also reference the frame's origin, whose
|
|
* "vector" is NULL.
|
|
*/
|
|
|
|
static int dbg_link_frame(const char *frame_name,
|
|
struct frame *base_frame, struct vec *base_vec)
|
|
{
|
|
struct frame *frame;
|
|
struct obj *obj;
|
|
|
|
assert(!base_vec || base_vec->frame == base_frame);
|
|
frame = find_frame(frame_name);
|
|
if (!frame) {
|
|
yyerrorf("unknown frame \"%s\"", frame_name);
|
|
return 0;
|
|
}
|
|
/* this can only fail in %frame */
|
|
if (is_parent_of(frame, base_frame)) {
|
|
yyerrorf("frame \"%s\" is a parent of \"%s\"",
|
|
frame->name, base_frame->name);
|
|
return 0;
|
|
}
|
|
obj = new_obj(ot_frame);
|
|
obj->base = base_vec;
|
|
obj->frame = base_frame;
|
|
obj->u.frame.ref = frame;
|
|
connect_obj(base_frame, obj);
|
|
if (!frame->active_ref)
|
|
frame->active_ref = obj;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int dbg_print(const struct expr *expr, const struct frame *frame)
|
|
{
|
|
const char *s;
|
|
struct num num;
|
|
|
|
s = eval_str(expr, frame);
|
|
if (s) {
|
|
printf("%s\n", s);
|
|
return 1;
|
|
}
|
|
num = eval_num(expr, frame);
|
|
if (is_undef(num))
|
|
return 0;
|
|
printf("%lg%s\n", num.n, str_unit(num));
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int dbg_meas(const char *name)
|
|
{
|
|
const struct obj *obj;
|
|
const struct inst *inst;
|
|
struct coord a1, b1;
|
|
char *s;
|
|
|
|
obj = find_obj(frames, name);
|
|
if (!obj) {
|
|
yyerrorf("unknown object \"%s\"", name);
|
|
return 0;
|
|
}
|
|
|
|
/* from fped.c:main */
|
|
|
|
if (!pkg_name)
|
|
pkg_name = stralloc("_");
|
|
reporter = report_to_stderr;
|
|
if (!instantiate())
|
|
return 0;
|
|
|
|
inst = find_meas_hint(obj);
|
|
if (!inst) {
|
|
yyerrorf("measurement \"%s\" was not instantiated", name);
|
|
return 0;
|
|
}
|
|
|
|
project_meas(inst, &a1, &b1);
|
|
s = format_len(obj->u.meas.label ? obj->u.meas.label : "",
|
|
dist_point(a1, b1), curr_unit);
|
|
printf("%s\n", s);
|
|
free(s);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
%}
|
|
|
|
|
|
%union {
|
|
struct num num;
|
|
int flag;
|
|
char *str;
|
|
const char *id;
|
|
struct expr *expr;
|
|
struct frame *frame;
|
|
struct table *table;
|
|
struct var *var;
|
|
struct row *row;
|
|
struct value *value;
|
|
struct vec *vec;
|
|
struct obj *obj;
|
|
enum pad_type pt;
|
|
enum meas_type mt;
|
|
struct {
|
|
int inverted;
|
|
int max;
|
|
} mo;
|
|
struct {
|
|
struct frame *frame;
|
|
struct vec *vec;
|
|
} qvec;
|
|
struct qbase {
|
|
struct vec *vec;
|
|
struct frame_qual *qualifiers;
|
|
} qbase;
|
|
};
|
|
|
|
|
|
%token START_FPD START_EXPR START_VAR START_VALUES
|
|
%token TOK_SET TOK_LOOP TOK_PACKAGE TOK_FRAME TOK_TABLE TOK_VEC
|
|
%token TOK_PAD TOK_RPAD TOK_HOLE TOK_RECT TOK_LINE TOK_CIRC TOK_ARC
|
|
%token TOK_MEAS TOK_MEASX TOK_MEASY TOK_UNIT
|
|
%token TOK_NEXT TOK_NEXT_INVERTED TOK_MAX TOK_MAX_INVERTED
|
|
%token TOK_DBG_DEL TOK_DBG_MOVE TOK_DBG_FRAME
|
|
%token TOK_DBG_PRINT TOK_DBG_IPRINT
|
|
%token TOK_DBG_DUMP TOK_DBG_EXIT TOK_DBG_TSORT TOK_DBG_MEAS
|
|
%token TOK_ALLOW_HOLES TOK_ALLOW_OVERLAP TOK_ALLOW_TOUCH
|
|
|
|
%token <num> NUMBER
|
|
%token <str> STRING
|
|
%token <id> ID LABEL
|
|
|
|
%type <table> table
|
|
%type <var> vars var
|
|
%type <row> rows
|
|
%type <value> row value opt_value_list
|
|
%type <vec> vec base
|
|
%type <obj> object obj meas unlabeled_meas
|
|
%type <expr> expr opt_expr add_expr mult_expr unary_expr primary_expr
|
|
%type <num> opt_num
|
|
%type <flag> opt_key
|
|
%type <frame> frame_qualifier
|
|
%type <str> opt_string
|
|
%type <pt> pad_type
|
|
%type <mt> meas_type
|
|
%type <mo> meas_op
|
|
%type <qvec> qualified_base
|
|
%type <qbase> qbase qbase_unchecked
|
|
|
|
%%
|
|
|
|
all:
|
|
START_FPD
|
|
{
|
|
frames = zalloc_type(struct frame);
|
|
set_frame(frames);
|
|
id_sin = unique("sin");
|
|
id_cos = unique("cos");
|
|
id_sqrt = unique("sqrt");
|
|
id_floor = unique("floor");
|
|
}
|
|
fpd
|
|
| START_EXPR expr
|
|
{
|
|
expr_result = $2;
|
|
}
|
|
| START_VAR ID opt_value_list
|
|
{
|
|
var_id = $2;
|
|
var_value_list = $3;
|
|
}
|
|
| START_VALUES row
|
|
{
|
|
var_value_list = $2;
|
|
}
|
|
;
|
|
|
|
fpd:
|
|
frame_defs part_name opt_setup opt_frame_items opt_measurements
|
|
| frame_defs setup opt_frame_items opt_measurements
|
|
| frame_defs frame_items opt_measurements
|
|
| frame_defs opt_measurements
|
|
;
|
|
|
|
part_name:
|
|
TOK_PACKAGE STRING
|
|
{
|
|
const char *p;
|
|
|
|
if (!*$2) {
|
|
yyerrorf("invalid package name");
|
|
YYABORT;
|
|
}
|
|
for (p = $2; *p; *p++)
|
|
if (*p < 32 || *p > 126) {
|
|
yyerrorf("invalid package name");
|
|
YYABORT;
|
|
}
|
|
pkg_name = $2;
|
|
}
|
|
;
|
|
|
|
opt_setup:
|
|
| setup
|
|
;
|
|
|
|
setup:
|
|
unit
|
|
| allow_pads
|
|
| allow_holes
|
|
| unit allow_pads
|
|
| unit allow_pads allow_holes
|
|
| unit allow_holes
|
|
| unit allow_holes allow_pads
|
|
| allow_pads unit
|
|
| allow_pads unit allow_holes
|
|
| allow_holes unit
|
|
| allow_holes unit allow_pads
|
|
;
|
|
|
|
unit:
|
|
TOK_UNIT ID
|
|
{
|
|
if (!strcmp($2, "mm"))
|
|
curr_unit = curr_unit_mm;
|
|
else if (!strcmp($2, "mil"))
|
|
curr_unit = curr_unit_mil;
|
|
else if (!strcmp($2, "auto"))
|
|
curr_unit = curr_unit_auto;
|
|
else {
|
|
yyerrorf("unrecognized unit \"%s\"", $2);
|
|
YYABORT;
|
|
}
|
|
}
|
|
;
|
|
|
|
allow_pads:
|
|
TOK_ALLOW_TOUCH
|
|
{
|
|
allow_overlap = ao_touch;
|
|
}
|
|
| TOK_ALLOW_OVERLAP
|
|
{
|
|
allow_overlap = ao_any;
|
|
}
|
|
;
|
|
|
|
allow_holes:
|
|
TOK_ALLOW_HOLES
|
|
{
|
|
holes_linked = 0;
|
|
}
|
|
;
|
|
|
|
frame_defs:
|
|
| frame_defs frame_def
|
|
;
|
|
|
|
frame_def:
|
|
TOK_FRAME ID '{'
|
|
{
|
|
if (find_frame($2)) {
|
|
yyerrorf("duplicate frame \"%s\"", $2);
|
|
YYABORT;
|
|
}
|
|
curr_frame = zalloc_type(struct frame);
|
|
curr_frame->name = $2;
|
|
set_frame(curr_frame);
|
|
curr_frame->next = frames->next;
|
|
frames->next = curr_frame;
|
|
}
|
|
opt_frame_items '}'
|
|
{
|
|
set_frame(frames);
|
|
}
|
|
;
|
|
|
|
opt_frame_items:
|
|
| frame_items
|
|
;
|
|
|
|
frame_items:
|
|
frame_item
|
|
| frame_item frame_items
|
|
;
|
|
|
|
frame_item:
|
|
table
|
|
| TOK_SET opt_key ID '=' expr
|
|
{
|
|
if (!$2 && find_var(curr_frame, $3)) {
|
|
yyerrorf("duplicate variable \"%s\"", $3);
|
|
YYABORT;
|
|
}
|
|
make_var($3, $2, $5);
|
|
}
|
|
| TOK_LOOP ID '=' expr ',' expr
|
|
{
|
|
if (find_var(curr_frame, $2)) {
|
|
yyerrorf("duplicate variable \"%s\"", $2);
|
|
YYABORT;
|
|
}
|
|
make_loop($2, $4, $6);
|
|
}
|
|
| vec
|
|
| LABEL vec
|
|
{
|
|
if (find_label(curr_frame, $1)) {
|
|
yyerrorf("duplicate label \"%s\"", $1);
|
|
YYABORT;
|
|
}
|
|
$2->name = $1;
|
|
}
|
|
| object
|
|
| LABEL object
|
|
{
|
|
if (find_label(curr_frame, $1)) {
|
|
yyerrorf("duplicate label \"%s\"", $1);
|
|
YYABORT;
|
|
}
|
|
$2->name = $1;
|
|
}
|
|
| debug_item
|
|
;
|
|
|
|
debug_item:
|
|
TOK_DBG_DEL ID
|
|
{
|
|
if (!dbg_delete(NULL, $2))
|
|
YYABORT;
|
|
}
|
|
| TOK_DBG_DEL ID '.' ID
|
|
{
|
|
if (!dbg_delete($2, $4))
|
|
YYABORT;
|
|
}
|
|
| TOK_DBG_MOVE ID opt_num ID
|
|
{
|
|
if (!dbg_move($2, $3.n, $4))
|
|
YYABORT;
|
|
}
|
|
| TOK_DBG_FRAME ID qualified_base
|
|
{
|
|
if (!dbg_link_frame($2, $3.frame, $3.vec))
|
|
YYABORT;
|
|
}
|
|
| TOK_DBG_PRINT expr
|
|
{
|
|
if (!dbg_print($2, curr_frame))
|
|
YYABORT;
|
|
}
|
|
| TOK_DBG_MEAS ID
|
|
{
|
|
if (!dbg_meas($2))
|
|
YYABORT;
|
|
}
|
|
| TOK_DBG_DUMP
|
|
{
|
|
if (!dump(stdout, NULL)) {
|
|
perror("stdout");
|
|
exit(1);
|
|
}
|
|
}
|
|
| TOK_DBG_EXIT
|
|
{
|
|
exit(0);
|
|
}
|
|
| TOK_DBG_TSORT '{'
|
|
{
|
|
tsort = begin_tsort();
|
|
}
|
|
sort_items '}'
|
|
{
|
|
void **sort, **walk;
|
|
|
|
sort = end_tsort(tsort);
|
|
for (walk = sort; *walk; walk++)
|
|
printf("%s\n", (char *) *walk);
|
|
free(sort);
|
|
}
|
|
;
|
|
|
|
sort_items:
|
|
| sort_items '+' ID
|
|
{
|
|
add_node(tsort, (void *) $3, 0);
|
|
}
|
|
| sort_items '-' ID
|
|
{
|
|
add_node(tsort, (void *) $3, 1);
|
|
}
|
|
| sort_items ID ID opt_num
|
|
{
|
|
struct node *a, *b;
|
|
|
|
/* order is important here ! */
|
|
a = add_node(tsort, (void *) $2, 0);
|
|
b = add_node(tsort, (void *) $3, 0);
|
|
add_edge(a, b, $4.n);
|
|
}
|
|
;
|
|
|
|
table:
|
|
TOK_TABLE
|
|
{
|
|
$<table>$ = zalloc_type(struct table);
|
|
*next_table = $<table>$;
|
|
curr_table = $<table>$;
|
|
n_vars = 0;
|
|
}
|
|
'{' vars '}' rows
|
|
{
|
|
$$ = $<table>2;
|
|
$$->vars = $4;
|
|
$$->rows = $6;
|
|
$$->active_row = $6;
|
|
next_table = &$$->next;
|
|
}
|
|
;
|
|
|
|
vars:
|
|
var
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| vars ',' var
|
|
{
|
|
struct var **walk;
|
|
|
|
$$ = $1;
|
|
for (walk = &$$; *walk; walk = &(*walk)->next);
|
|
*walk = $3;
|
|
}
|
|
;
|
|
|
|
var:
|
|
opt_key ID
|
|
{
|
|
if (!$1 && find_var(curr_frame, $2)) {
|
|
yyerrorf("duplicate variable \"%s\"", $2);
|
|
YYABORT;
|
|
}
|
|
$$ = zalloc_type(struct var);
|
|
$$->name = $2;
|
|
$$->frame = curr_frame;
|
|
$$->table = curr_table;
|
|
$$->key = $1;
|
|
$$->next = NULL;
|
|
n_vars++;
|
|
}
|
|
;
|
|
|
|
opt_key:
|
|
{
|
|
$$ = 0;
|
|
}
|
|
| '?'
|
|
{
|
|
$$ = 1;
|
|
}
|
|
;
|
|
|
|
rows:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| '{'
|
|
{
|
|
$<row>$ = alloc_type(struct row);
|
|
$<row>$->table = curr_table;
|
|
curr_row = $<row>$;;
|
|
n_values = 0;
|
|
}
|
|
row '}'
|
|
{
|
|
if (n_vars != n_values) {
|
|
yyerrorf("table has %d variables but row has "
|
|
"%d values", n_vars, n_values);
|
|
YYABORT;
|
|
}
|
|
$<row>2->values = $3;
|
|
}
|
|
rows
|
|
{
|
|
$$ = $<row>2;
|
|
$$->next = $6;
|
|
}
|
|
;
|
|
|
|
row:
|
|
value
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| row ',' value
|
|
{
|
|
struct value **walk;
|
|
|
|
$$ = $1;
|
|
for (walk = &$$; *walk; walk = &(*walk)->next);
|
|
*walk = $3;
|
|
}
|
|
;
|
|
|
|
value:
|
|
expr
|
|
{
|
|
$$ = alloc_type(struct value);
|
|
$$->expr = $1;
|
|
$$->row = curr_row;
|
|
$$->next = NULL;
|
|
n_values++;
|
|
}
|
|
;
|
|
|
|
vec:
|
|
TOK_VEC base '(' expr ',' expr ')'
|
|
{
|
|
$$ = alloc_type(struct vec);
|
|
$$->nul_tag = 0;
|
|
$$->name = NULL;
|
|
$$->base = $2;
|
|
$$->x = $4;
|
|
$$->y = $6;
|
|
$$->frame = curr_frame;
|
|
$$->next = NULL;
|
|
last_vec = $$;
|
|
*next_vec = $$;
|
|
next_vec = &$$->next;
|
|
}
|
|
;
|
|
|
|
base:
|
|
'@'
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| '.'
|
|
{
|
|
$$ = last_vec;
|
|
if (!$$) {
|
|
yyerrorf(". without predecessor");
|
|
YYABORT;
|
|
}
|
|
}
|
|
| ID
|
|
{
|
|
$$ = find_vec(curr_frame, $1);
|
|
if (!$$)
|
|
$$ = (struct vec *) $1;
|
|
}
|
|
;
|
|
|
|
qualified_base:
|
|
base
|
|
{
|
|
$$.frame = curr_frame;
|
|
$$.vec = $1;
|
|
}
|
|
| frame_qualifier '@'
|
|
{
|
|
$$.frame = $1;
|
|
$$.vec = NULL;
|
|
}
|
|
| frame_qualifier ID
|
|
{
|
|
$$.frame = $1;
|
|
$$.vec = find_vec($1, $2);
|
|
if (!$$.vec) {
|
|
yyerrorf("unknown vector \"%s.%s\"",
|
|
$1->name, $2);
|
|
YYABORT;
|
|
}
|
|
}
|
|
;
|
|
|
|
frame_qualifier:
|
|
ID '.'
|
|
{
|
|
$$ = find_frame($1);
|
|
if (!$$) {
|
|
yyerrorf("unknown frame \"%s\"", $1);
|
|
YYABORT;
|
|
}
|
|
}
|
|
;
|
|
|
|
object:
|
|
obj
|
|
{
|
|
$$ = $1;
|
|
*next_obj = $1;
|
|
next_obj = &$1->next;
|
|
}
|
|
;
|
|
|
|
obj:
|
|
TOK_PAD STRING base base pad_type
|
|
{
|
|
$$ = new_obj(ot_pad);
|
|
$$->base = $3;
|
|
$$->u.pad.name = $2;
|
|
$$->u.pad.other = $4;
|
|
$$->u.pad.rounded = 0;
|
|
$$->u.pad.type = $5;
|
|
}
|
|
| TOK_RPAD STRING base base pad_type
|
|
{
|
|
$$ = new_obj(ot_pad);
|
|
$$->base = $3;
|
|
$$->u.pad.name = $2;
|
|
$$->u.pad.other = $4;
|
|
$$->u.pad.rounded = 1;
|
|
$$->u.pad.type = $5;
|
|
}
|
|
| TOK_HOLE base base
|
|
{
|
|
$$ = new_obj(ot_hole);
|
|
$$->base = $2;
|
|
$$->u.hole.other = $3;
|
|
}
|
|
| TOK_RECT base base opt_expr
|
|
{
|
|
$$ = new_obj(ot_rect);
|
|
$$->base = $2;
|
|
$$->u.rect.other = $3;
|
|
$$->u.rect.width = $4;
|
|
}
|
|
| TOK_LINE base base opt_expr
|
|
{
|
|
$$ = new_obj(ot_line);
|
|
$$->base = $2;
|
|
$$->u.line.other = $3;
|
|
$$->u.line.width = $4;
|
|
}
|
|
| TOK_CIRC base base opt_expr
|
|
{
|
|
$$ = new_obj(ot_arc);
|
|
$$->base = $2;
|
|
$$->u.arc.start = $3;
|
|
$$->u.arc.end = $3;
|
|
$$->u.arc.width = $4;
|
|
}
|
|
| TOK_ARC base base base opt_expr
|
|
{
|
|
$$ = new_obj(ot_arc);
|
|
$$->base = $2;
|
|
$$->u.arc.start = $3;
|
|
$$->u.arc.end = $4;
|
|
$$->u.arc.width = $5;
|
|
}
|
|
| TOK_FRAME ID
|
|
{
|
|
$<num>$.n = lineno;
|
|
}
|
|
base
|
|
{
|
|
$$ = new_obj(ot_frame);
|
|
$$->base = $4;
|
|
$$->u.frame.ref = find_frame($2);
|
|
if (!$$->u.frame.ref) {
|
|
yyerrorf("unknown frame \"%s\"", $2);
|
|
YYABORT;
|
|
}
|
|
if (!$$->u.frame.ref->active_ref)
|
|
$$->u.frame.ref->active_ref = $$;
|
|
$$->u.frame.lineno = $<num>3.n;
|
|
}
|
|
| TOK_DBG_IPRINT expr
|
|
{
|
|
$$ = new_obj(ot_iprint);
|
|
$$->base = NULL;
|
|
$$->u.iprint.expr = $2;
|
|
}
|
|
;
|
|
|
|
pad_type:
|
|
{
|
|
$$ = pt_normal;
|
|
}
|
|
| ID
|
|
{
|
|
if (!strcmp($1, "bare"))
|
|
$$ = pt_bare;
|
|
else if (!strcmp($1, "trace"))
|
|
$$ = pt_trace;
|
|
else if (!strcmp($1, "paste"))
|
|
$$ = pt_paste;
|
|
else if (!strcmp($1, "mask"))
|
|
$$ = pt_mask;
|
|
else {
|
|
yyerrorf("unknown pad type \"%s\"", $1);
|
|
YYABORT;
|
|
}
|
|
}
|
|
;
|
|
|
|
opt_measurements:
|
|
| measurements
|
|
;
|
|
|
|
measurements:
|
|
unlabeled_meas /* @@@ hack */
|
|
{
|
|
*next_obj = $1;
|
|
next_obj = &$1->next;
|
|
}
|
|
| measurements meas
|
|
{
|
|
*next_obj = $2;
|
|
next_obj = &$2->next;
|
|
}
|
|
| measurements debug_item
|
|
;
|
|
|
|
meas:
|
|
unlabeled_meas
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| LABEL unlabeled_meas
|
|
{
|
|
$$ = $2;
|
|
if (find_label(curr_frame, $1)) {
|
|
yyerrorf("duplicate label \"%s\"", $1);
|
|
YYABORT;
|
|
}
|
|
$$->name = $1;
|
|
}
|
|
;
|
|
|
|
unlabeled_meas:
|
|
meas_type opt_string qbase meas_op qbase opt_expr
|
|
{
|
|
struct meas *meas;
|
|
|
|
$$ = new_obj(ot_meas);
|
|
meas = &$$->u.meas;
|
|
meas->type = $4.max ? $1+3 : $1;
|
|
meas->label = $2;
|
|
$$->base = $3.vec;
|
|
meas->low_qual = $3.qualifiers;
|
|
meas->inverted = $4.inverted;
|
|
meas->high = $5.vec;
|
|
meas->high_qual = $5.qualifiers;
|
|
meas->offset = $6;
|
|
$$->next = NULL;
|
|
}
|
|
;
|
|
|
|
qbase:
|
|
qbase_unchecked
|
|
{
|
|
$$ = $1;
|
|
if (!check_qbase(&$$))
|
|
YYABORT;
|
|
}
|
|
;
|
|
|
|
qbase_unchecked:
|
|
ID
|
|
{
|
|
$$.vec = find_vec(frames, $1);
|
|
if (!$$.vec) {
|
|
yyerrorf("unknown vector \"%s\"", $1);
|
|
YYABORT;
|
|
}
|
|
$$.qualifiers = NULL;
|
|
}
|
|
| ID '.' ID
|
|
{
|
|
const struct frame *frame;
|
|
|
|
frame = find_frame($1);
|
|
$$.vec = frame ? find_vec(frame, $3) : NULL;
|
|
if (!$$.vec) {
|
|
yyerrorf("unknown vector \"%s.%s\"", $1, $3);
|
|
YYABORT;
|
|
}
|
|
$$.qualifiers = NULL;
|
|
}
|
|
| ID '/' qbase
|
|
{
|
|
const struct frame *frame;
|
|
struct frame_qual *qual;
|
|
|
|
$$ = $3;
|
|
frame = find_frame($1);
|
|
if (!frame) {
|
|
yyerrorf("unknown frame \"%s\"", $1);
|
|
YYABORT;
|
|
} else {
|
|
qual = alloc_type(struct frame_qual);
|
|
qual->frame = frame;
|
|
qual->next = $$.qualifiers;
|
|
$$.qualifiers = qual;
|
|
}
|
|
}
|
|
;
|
|
|
|
meas_type:
|
|
TOK_MEAS
|
|
{
|
|
$$ = mt_xy_next;
|
|
}
|
|
| TOK_MEASX
|
|
{
|
|
$$ = mt_x_next;
|
|
}
|
|
| TOK_MEASY
|
|
{
|
|
$$ = mt_y_next;
|
|
}
|
|
;
|
|
|
|
meas_op:
|
|
TOK_NEXT
|
|
{
|
|
$$.max = 0;
|
|
$$.inverted = 0;
|
|
}
|
|
| TOK_NEXT_INVERTED
|
|
{
|
|
$$.max = 0;
|
|
$$.inverted = 1;
|
|
}
|
|
| TOK_MAX
|
|
{
|
|
$$.max = 1;
|
|
$$.inverted = 0;
|
|
}
|
|
| TOK_MAX_INVERTED
|
|
{
|
|
$$.max = 1;
|
|
$$.inverted = 1;
|
|
}
|
|
;
|
|
|
|
opt_num:
|
|
{
|
|
$$.n = 0;
|
|
}
|
|
| NUMBER
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
opt_string:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| STRING
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
opt_expr:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| expr
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
expr:
|
|
add_expr
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
add_expr:
|
|
mult_expr
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| add_expr '+' mult_expr
|
|
{
|
|
$$ = binary_op(op_add, $1, $3);
|
|
}
|
|
| add_expr '-' mult_expr
|
|
{
|
|
$$ = binary_op(op_sub, $1, $3);
|
|
}
|
|
;
|
|
|
|
mult_expr:
|
|
unary_expr
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| mult_expr '*' unary_expr
|
|
{
|
|
$$ = binary_op(op_mult, $1, $3);
|
|
}
|
|
| mult_expr '/' unary_expr
|
|
{
|
|
$$ = binary_op(op_div, $1, $3);
|
|
}
|
|
;
|
|
|
|
unary_expr:
|
|
primary_expr
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| '-' primary_expr
|
|
{
|
|
$$ = binary_op(op_minus, $2, NULL);
|
|
}
|
|
;
|
|
|
|
primary_expr:
|
|
NUMBER
|
|
{
|
|
$$ = new_op(op_num);
|
|
$$->u.num = $1;
|
|
}
|
|
| ID
|
|
{
|
|
$$ = new_op(op_var);
|
|
$$->u.var = $1;
|
|
}
|
|
| STRING
|
|
{
|
|
$$ = new_op(op_string);
|
|
$$->u.str = $1;
|
|
}
|
|
| '(' expr ')'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
| ID '(' expr ')'
|
|
{
|
|
if ($1 == id_sin)
|
|
$$ = binary_op(op_sin, $3, NULL);
|
|
else if ($1 == id_cos)
|
|
$$ = binary_op(op_cos, $3, NULL);
|
|
else if ($1 == id_sqrt)
|
|
$$ = binary_op(op_sqrt, $3, NULL);
|
|
else if ($1 == id_floor)
|
|
$$ = binary_op(op_floor, $3, NULL);
|
|
else {
|
|
yyerrorf("unknown function \"%s\"", $1);
|
|
YYABORT;
|
|
}
|
|
}
|
|
;
|
|
|
|
/* special sub-grammar */
|
|
|
|
opt_value_list:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| '=' row
|
|
{
|
|
$$ = $2;
|
|
}
|
|
;
|