%{ /* * 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 #include #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 WORD PATTERN %type int %type float %type nameqs %type names nameq %type format %type relop opt_relop %type /*rules*/ opt_rule rule opt_fields fields field %type selectors %type conditions condition %type hierarchy opt_wildcard action %type part inventory_item %type param params %type prices price %type stock %type providers provider %type substitutions block opt_block %type 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: { $$ = (struct field *) top_field(); push_field(top_field()); } opt_fields opt_rule ';' { $$.fields = $2; $$.rules = $3; pop_fields($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); } ;