1
0
mirror of git://projects.qi-hardware.com/eda-tools.git synced 2024-11-23 00:56:15 +02:00
eda-tools/b2/lang.y
Werner Almesberger bca50f009f b2/lang.y (parse_jump): improve diagnostic for code after end/ignore
Before, the error was a rather confusing "unknown keyword", suggesting
a typo.
2012-06-03 21:18:51 -03:00

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 = &param_ops_name;
}
| '<' WORD '>'
{
$$ = alloc_type(struct format);
$$->ops = &param_ops_set;
$$->u.set = find_nameset($2);
if (!$$->u.set)
yyerrorf("unknown name set \"%s\"", $2);
}
| '#' WORD
{
$$ = alloc_type(struct format);
$$->ops = &param_ops_abs;
$$->u.abs = $2;
}
| '#' '#'
{
$$ = alloc_type(struct format);
$$->ops = &param_ops_abs;
$$->u.abs = NULL;
}
| '%' WORD
{
$$ = alloc_type(struct format);
$$->ops = &param_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);
}
;