mirror of
git://projects.qi-hardware.com/eda-tools.git
synced 2024-11-30 06:40:41 +02:00
bca50f009f
Before, the error was a rather confusing "unknown keyword", suggesting a typo.
711 lines
9.8 KiB
Plaintext
711 lines
9.8 KiB
Plaintext
%{
|
|
/*
|
|
* lang.y - BOOM grammar
|
|
*
|
|
* Copyright 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 <assert.h>
|
|
|
|
#include "util.h"
|
|
#include "relop.h"
|
|
#include "param.h"
|
|
#include "chr.h"
|
|
#include "db.h"
|
|
#include "subst.h"
|
|
#include "lang.h"
|
|
|
|
#include "y.tab.h"
|
|
|
|
|
|
const char *dollar;
|
|
|
|
struct action hierarchy;
|
|
struct subst *substitutions = NULL;
|
|
|
|
static struct currency *curr;
|
|
|
|
|
|
static struct field_stack {
|
|
const struct field *field;
|
|
struct field_stack *next;
|
|
} *field_stack = NULL;
|
|
|
|
|
|
static void push_field(const struct field *field)
|
|
{
|
|
struct field_stack *entry;
|
|
|
|
entry = alloc_type(struct field_stack);
|
|
entry->field = field;
|
|
entry->next = field_stack;
|
|
field_stack = entry;
|
|
}
|
|
|
|
|
|
static void pop_fields(const struct field *last)
|
|
{
|
|
struct field_stack *entry;
|
|
const struct field *popped;
|
|
|
|
do {
|
|
assert(field_stack);
|
|
entry = field_stack;
|
|
popped = entry->field;
|
|
field_stack = entry->next;
|
|
free(entry);
|
|
} while (popped != last);
|
|
}
|
|
|
|
|
|
static const struct field *top_field(void)
|
|
{
|
|
return field_stack ? field_stack->field : NULL;
|
|
}
|
|
|
|
|
|
static struct subst *parse_jump(const char *keyword, const char *target)
|
|
{
|
|
if (!strcmp(keyword, "break"))
|
|
return subst_break(target);
|
|
if (!strcmp(keyword, "continue"))
|
|
return subst_continue(target);
|
|
if (!strcmp(keyword, "end") || !strcmp(keyword, "ignore"))
|
|
yyerror("unreachable code");
|
|
yyerrorf("unknown keyword \"%s\"", keyword);
|
|
}
|
|
|
|
%}
|
|
|
|
|
|
%union {
|
|
const char *s;
|
|
int num;
|
|
double fnum;
|
|
struct equiv *equiv;
|
|
struct names *names;
|
|
struct format *format;
|
|
enum relop relop;
|
|
struct field *field;
|
|
struct selector *sel;
|
|
struct condition *cond;
|
|
struct action act;
|
|
struct part *part;
|
|
struct param *param;
|
|
struct price *price;
|
|
struct stock *stock;
|
|
struct provider *prov;
|
|
struct subst *subst;
|
|
};
|
|
|
|
|
|
%token START_HIERARCHY START_CHAR START_INVENTORY
|
|
%token START_EXCHANGE START_PROVIDERS START_SUBST START_BOM
|
|
%token START_SYMBOLS
|
|
%token BOM_EESCHEMA
|
|
%token TOK_LE TOK_GE TOK_NL
|
|
%token <s> WORD PATTERN
|
|
|
|
%type <num> int
|
|
%type <fnum> float
|
|
%type <equiv> nameqs
|
|
%type <names> names nameq
|
|
%type <format> format
|
|
%type <relop> relop opt_relop
|
|
%type <field> /*rules*/ opt_rule rule opt_fields fields field
|
|
%type <sel> selectors
|
|
%type <cond> conditions condition
|
|
%type <act> hierarchy opt_wildcard action
|
|
%type <part> part inventory_item
|
|
%type <param> param params
|
|
%type <price> prices price
|
|
%type <stock> stock
|
|
%type <prov> providers provider
|
|
%type <subst> substitutions block opt_block
|
|
%type <s> word_or_dollar
|
|
|
|
%%
|
|
|
|
all:
|
|
START_HIERARCHY hierarchy
|
|
{
|
|
hierarchy = $2;
|
|
}
|
|
| START_CHAR characteristics
|
|
| START_INVENTORY inventory
|
|
| START_EXCHANGE exchange
|
|
| START_PROVIDERS providers
|
|
| START_SUBST substitutions
|
|
{
|
|
substitutions = $2;
|
|
}
|
|
| START_BOM bom
|
|
| START_SYMBOLS symbols
|
|
;
|
|
|
|
|
|
/* ----- Characteristics hierarchy ----------------------------------------- */
|
|
|
|
|
|
hierarchy:
|
|
nameset hierarchy
|
|
{
|
|
$$ = $2;
|
|
}
|
|
| action
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
nameset:
|
|
'<' WORD '>' '=' names ';'
|
|
{
|
|
register_nameset($2, $5);
|
|
}
|
|
;
|
|
|
|
names:
|
|
nameq
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| nameq '<' names
|
|
{
|
|
$$ = $1;
|
|
$$->next = $3;
|
|
}
|
|
;
|
|
|
|
nameq:
|
|
nameqs
|
|
{
|
|
$$ = alloc_type(struct names);
|
|
$$->equiv = $1;
|
|
$$->next = NULL;
|
|
}
|
|
;
|
|
|
|
nameqs:
|
|
WORD
|
|
{
|
|
$$ = alloc_type(struct equiv);
|
|
$$->name = $1;
|
|
$$->next = NULL;
|
|
}
|
|
| WORD '=' nameqs
|
|
{
|
|
$$ = alloc_type(struct equiv);
|
|
$$->name = $1;
|
|
$$->next = $3;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* @@@ for now, we can't select on a collective of fields. maybe later, with
|
|
* a syntax change.
|
|
*
|
|
rules:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| rule rules
|
|
{
|
|
$$ = $1;
|
|
$$->next = $2;
|
|
}
|
|
;
|
|
*/
|
|
|
|
opt_rule:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| rule
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
rule:
|
|
field
|
|
{
|
|
$1->prev = top_field();
|
|
push_field($1);
|
|
}
|
|
'{' selectors opt_wildcard '}'
|
|
{
|
|
$$ = $1;
|
|
$$->sel = $4;
|
|
$$->any = $5;
|
|
$$->next = NULL;
|
|
field_finalize($$);
|
|
pop_fields($$);
|
|
}
|
|
;
|
|
|
|
selectors:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| conditions ':' action selectors
|
|
{
|
|
$$ = new_selector();
|
|
$$->cond = $1;
|
|
$$->act = $3;
|
|
$$->next = $4;
|
|
}
|
|
;
|
|
|
|
conditions:
|
|
condition
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| condition '|' conditions
|
|
{
|
|
$$ = $1;
|
|
$$->next = $3;
|
|
}
|
|
;
|
|
|
|
condition:
|
|
opt_relop WORD
|
|
{
|
|
$$ = new_condition($1, $2);
|
|
}
|
|
;
|
|
|
|
opt_wildcard:
|
|
{
|
|
$$.fields = NULL;
|
|
$$.rules = NULL;
|
|
}
|
|
| '*' ':' action
|
|
{
|
|
$$ = $3;
|
|
}
|
|
;
|
|
|
|
action:
|
|
{
|
|
$<field>$ = (struct field *) top_field();
|
|
push_field(top_field());
|
|
}
|
|
opt_fields opt_rule ';'
|
|
{
|
|
$$.fields = $2;
|
|
$$.rules = $3;
|
|
pop_fields($<field>1);
|
|
}
|
|
;
|
|
|
|
opt_relop:
|
|
{
|
|
$$ = rel_eq;
|
|
}
|
|
| relop
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
relop:
|
|
'='
|
|
{
|
|
$$ = rel_eq;
|
|
}
|
|
| '<'
|
|
{
|
|
$$ = rel_lt;
|
|
}
|
|
| '>'
|
|
{
|
|
$$ = rel_gt;
|
|
}
|
|
| TOK_LE
|
|
{
|
|
$$ = rel_le;
|
|
}
|
|
| TOK_GE
|
|
{
|
|
$$ = rel_ge;
|
|
}
|
|
;
|
|
|
|
opt_fields:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| '{' fields '}'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
fields:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| field
|
|
{
|
|
$1->prev = top_field();
|
|
push_field($1);
|
|
}
|
|
fields
|
|
{
|
|
$$ = $1;
|
|
$$->next = $3;
|
|
if ($3)
|
|
$3->prev = $1;
|
|
}
|
|
;
|
|
|
|
field:
|
|
WORD '=' format
|
|
{
|
|
$$ = new_field($1, $3, top_field());
|
|
}
|
|
;
|
|
|
|
format:
|
|
'*'
|
|
{
|
|
$$ = alloc_type(struct format);
|
|
$$->ops = ¶m_ops_name;
|
|
}
|
|
| '<' WORD '>'
|
|
{
|
|
$$ = alloc_type(struct format);
|
|
$$->ops = ¶m_ops_set;
|
|
$$->u.set = find_nameset($2);
|
|
if (!$$->u.set)
|
|
yyerrorf("unknown name set \"%s\"", $2);
|
|
}
|
|
| '#' WORD
|
|
{
|
|
$$ = alloc_type(struct format);
|
|
$$->ops = ¶m_ops_abs;
|
|
$$->u.abs = $2;
|
|
}
|
|
| '#' '#'
|
|
{
|
|
$$ = alloc_type(struct format);
|
|
$$->ops = ¶m_ops_abs;
|
|
$$->u.abs = NULL;
|
|
}
|
|
| '%' WORD
|
|
{
|
|
$$ = alloc_type(struct format);
|
|
$$->ops = ¶m_ops_rel;
|
|
$$->u.rel = field_find($2, top_field());
|
|
if (!$$->u.rel)
|
|
yyerrorf("unknown field \"%s\"", $2);
|
|
}
|
|
;
|
|
|
|
|
|
/* ----- Part characteristics --------------------------------------------- */
|
|
|
|
|
|
characteristics:
|
|
| TOK_NL
|
|
| part characteristics
|
|
{
|
|
part_finalize($1, &hierarchy);
|
|
}
|
|
;
|
|
|
|
part:
|
|
WORD WORD params TOK_NL
|
|
{
|
|
$$ = part_add($1, $2);
|
|
if ($$->param)
|
|
yyerrorf("%s %s parameters already defined",
|
|
$1, $2);
|
|
$$->param = $3;
|
|
}
|
|
;
|
|
|
|
params:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| param params
|
|
{
|
|
$$ = $1;
|
|
$$->next = $2;
|
|
}
|
|
;
|
|
|
|
param:
|
|
WORD '=' WORD
|
|
{
|
|
$$ = alloc_type(struct param);
|
|
$$->u.name = $1;
|
|
$$->op = rel_eq;
|
|
$$->value.u.s = $3;
|
|
}
|
|
;
|
|
|
|
|
|
/* ----- Part inventory ---------------------------------------------------- */
|
|
|
|
|
|
inventory:
|
|
| TOK_NL
|
|
| inventory_item inventory
|
|
{
|
|
part_dump(stderr, $1);
|
|
}
|
|
;
|
|
|
|
inventory_item:
|
|
WORD WORD stock TOK_NL
|
|
{
|
|
$$ = part_add($1, $2);
|
|
$3->provider = provider_add($1);
|
|
part_add_stock($$, $3);
|
|
}
|
|
;
|
|
|
|
stock:
|
|
WORD int int WORD float prices
|
|
{
|
|
$$ = alloc_type(struct stock);
|
|
$$->provider = NULL;
|
|
$$->cat = $1;
|
|
$$->avail = $2;
|
|
$$->package = $3;
|
|
$$->curr = currency_lookup($4);
|
|
if (!$$->curr)
|
|
yyerrorf("unknown currency %s", $4);
|
|
$$->add = $5;
|
|
$$->price = $6;
|
|
}
|
|
;
|
|
|
|
prices:
|
|
price
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| price prices
|
|
{
|
|
$$ = $1;
|
|
$$->next = $2;
|
|
}
|
|
;
|
|
|
|
price:
|
|
int float
|
|
{
|
|
$$ = alloc_type(struct price);
|
|
$$->qty = $1;
|
|
$$->value = $2;
|
|
$$->next = NULL;
|
|
}
|
|
;
|
|
|
|
int:
|
|
WORD
|
|
{
|
|
char *end;
|
|
|
|
$$ = strtol($1, &end, 10);
|
|
if (*end)
|
|
yyerrorf("\"%s\" is not an integer", $1);
|
|
}
|
|
;
|
|
|
|
float:
|
|
WORD
|
|
{
|
|
char *end;
|
|
|
|
$$ = strtod($1, &end);
|
|
if (*end)
|
|
yyerrorf("\"%s\" is not a number", $1);
|
|
}
|
|
;
|
|
|
|
|
|
/* ----- Currency exchange ------------------------------------------------- */
|
|
|
|
|
|
exchange:
|
|
| TOK_NL
|
|
| currency exchange
|
|
;
|
|
|
|
currency:
|
|
WORD
|
|
{
|
|
curr = currency_add($1);
|
|
}
|
|
rates TOK_NL
|
|
;
|
|
|
|
rates:
|
|
| rate rates
|
|
;
|
|
|
|
rate:
|
|
WORD float
|
|
{
|
|
const struct currency *to;
|
|
|
|
/* add and not lookup, since we just introduce the
|
|
currency here */
|
|
to = currency_add($1);
|
|
currency_exchange(curr, to, $2);
|
|
}
|
|
;
|
|
|
|
|
|
/* ----- Providers --------------------------------------------------------- */
|
|
|
|
|
|
providers:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| TOK_NL
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| provider providers
|
|
{
|
|
$$ = $1;
|
|
$$->next = $2;
|
|
}
|
|
;
|
|
|
|
provider:
|
|
WORD WORD float float TOK_NL
|
|
{
|
|
$$ = provider_add($1);
|
|
if ($$->curr)
|
|
yyerrorf("provider %s is already defined", $1);
|
|
$$->curr = currency_add($2);
|
|
$$->shipping = $3;
|
|
$$->minimum = $4;
|
|
}
|
|
;
|
|
|
|
|
|
/* ----- Substitutions ----------------------------------------------------- */
|
|
|
|
|
|
substitutions:
|
|
block
|
|
{
|
|
$$ = $1;
|
|
subst_finalize($$);
|
|
}
|
|
;
|
|
|
|
block:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| word_or_dollar relop PATTERN opt_block block
|
|
{
|
|
if ($4) {
|
|
if ($2 != rel_eq)
|
|
yyerror("only = allow for matching");
|
|
$$ = subst_match($1, $3, NULL);
|
|
$$->u.match.block = $4;
|
|
} else {
|
|
$$ = subst_assign($1, $2, $3);
|
|
}
|
|
free((void *) $3);
|
|
$$->next = $5;
|
|
}
|
|
| WORD WORD block
|
|
{
|
|
if (!strcmp($1, "print")) {
|
|
$$ = subst_print($2);
|
|
$$->next = $3;
|
|
} else {
|
|
$$ = parse_jump($1, $2);
|
|
if ($3)
|
|
yyerror("unreachable code");
|
|
}
|
|
}
|
|
| WORD '$'
|
|
{
|
|
$$ = parse_jump($1, NULL);
|
|
}
|
|
| WORD
|
|
{
|
|
if (!strcmp($1, "end"))
|
|
$$ = subst_end();
|
|
else if (!strcmp($1, "ignore"))
|
|
$$ = subst_ignore();
|
|
else
|
|
$$ = parse_jump($1, NULL);
|
|
}
|
|
| WORD PATTERN
|
|
{
|
|
}
|
|
;
|
|
|
|
word_or_dollar:
|
|
'$'
|
|
{
|
|
$$ = dollar;
|
|
}
|
|
| WORD
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
opt_block:
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
| '{' block '}'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
|
|
/* ----- KiCad BOM (from eeschema) ----------------------------------------- */
|
|
|
|
|
|
bom:
|
|
BOM_EESCHEMA
|
|
/*
|
|
* The KiCad BOM syntax is quite alien, so we just check the
|
|
* overall structure here, leave extracting the lines with
|
|
* actual content to the lexer, and do all the detail work in
|
|
* bom.c
|
|
*/
|
|
;
|
|
|
|
|
|
/* ----- Symbols (BOM supplement) ------------------------------------------ */
|
|
|
|
|
|
symbols:
|
|
| TOK_NL
|
|
| symbol symbols
|
|
;
|
|
|
|
symbol:
|
|
WORD WORD TOK_NL
|
|
{
|
|
bom_set_sym($1, $2);
|
|
}
|
|
;
|