%{
/* Parser for gpasm
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
James Bowman, Craig Franklin
This file is part of gputils.
gputils 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, or (at your option)
any later version.
gputils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gputils; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "stdhdr.h"
#include "libgputils.h"
#include "gpasm.h"
#include "evaluate.h"
#include "gperror.h"
#include "directive.h"
#include "lst.h"
#include "macro.h"
#include "coff.h"
#include "scan.h"
void yyerror(char *message)
{
gperror(103, message);
}
int yylex(void);
extern int _16bit_core;
/************************************************************************/
/* Some simple functions for building parse trees */
static struct pnode *mk_pnode(enum pnode_tag tag)
{
struct pnode *new = malloc(sizeof(*new));
new->tag = tag;
return new;
}
struct pnode *mk_constant(int value)
{
struct pnode *new = mk_pnode(constant);
new->value.constant = value;
return new;
}
struct pnode *mk_offset(struct pnode *p)
{
struct pnode *new = mk_pnode(offset);
new->value.offset = p;
return new;
}
static struct pnode *mk_symbol(char *value)
{
struct pnode *new = mk_pnode(symbol);
new->value.symbol = value;
return new;
}
static struct pnode *mk_string(char *value)
{
struct pnode *new = mk_pnode(string);
new->value.string = value;
return new;
}
struct pnode *mk_list(struct pnode *head, struct pnode *tail)
{
struct pnode *new = mk_pnode(list);
new->value.list.head = head;
new->value.list.tail = tail;
return new;
}
static struct pnode *mk_2op(int op, struct pnode *p0, struct pnode *p1)
{
struct pnode *new = mk_pnode(binop);
new->value.binop.op = op;
new->value.binop.p0 = p0;
new->value.binop.p1 = p1;
return new;
}
static struct pnode *mk_1op(int op, struct pnode *p0)
{
struct pnode *new = mk_pnode(unop); new->value.unop.op = op;
new->value.unop.p0 = p0;
return new;
}
/************************************************************************/
/* shared functions */
gpasmVal set_label(char *label, struct pnode *parms)
{
gpasmVal value = 0;
if (asm_enabled()) {
value = do_or_append_insn("set", parms);
if (!state.mac_prev) {
set_global(label, value, TEMPORARY, gvt_constant);
}
}
return value;
}
int return_op(int operation)
{
/* returns an operator for the replacement of i+=1 with i=i+1*/
switch(operation) {
case ASSIGN_PLUS: return '+';
case ASSIGN_MINUS: return '-';
case ASSIGN_MULTIPLY: return '*';
case ASSIGN_DIVIDE: return '/';
case ASSIGN_MODULUS: return '%';
case ASSIGN_LSH: return LSH;
case ASSIGN_RSH: return RSH;
case ASSIGN_AND: return '&';
case ASSIGN_OR: return '|';
case ASSIGN_XOR: return '^';
default:
assert(0); /* Unhandled operator */
}
return 0;
}
void next_line(int value)
{
char l[BUFSIZ];
char *e = l;
if ((state.src->type == src_macro) ||
(state.src->type == src_while)) {
/* while loops can be defined inside a macro or nested */
if (state.mac_prev) {
state.lst.line.linetype = none;
if (state.mac_body)
state.mac_body->src_line = strdup(state.src->lst.m->src_line);
}
if (((state.src->type == src_while) || (state.lst.expand)) &&
(state.pass == 2)) {
assert(state.src->lst.m->src_line != NULL);
lst_format_line(state.src->lst.m->src_line, value);
}
if (state.src->lst.m->next) {
state.src->lst.m = state.src->lst.m->next;
}
} else if ((state.src->type == src_file) &&
(state.src->lst.f != NULL)) {
fgets(l, BUFSIZ, state.src->lst.f);
l[strlen(l) - 1] = '\0'; /* Eat the trailing newline */
if (state.mac_prev) {
state.lst.line.linetype = none;
if (state.mac_body)
state.mac_body->src_line = strdup(l);
}
if (state.pass == 2) {
lst_format_line(e, value);
}
}
state.src->line_number++;
switch (state.next_state) {
case state_exitmacro:
execute_exitm();
break;
case state_include:
open_src(state.next_buffer.file, 1);
free(state.next_buffer.file);
break;
case state_macro:
/* push the label for local directive */
state.stTop = push_macro_symbol_table(state.stTop);
execute_macro(state.next_buffer.macro, 0);
break;
case state_section:
/* create a new coff section */
coff_new_section(state.obj.new_sec_name,
state.obj.new_sec_addr,
state.obj.new_sec_flags);
break;
case state_while:
execute_macro(state.next_buffer.macro, 1);
break;
default:
break;
}
}
/************************************************************************/
%}
/* Bison declarations */
%union {
gpasmVal i;
char *s;
struct pnode *p;
}
%token LABEL
%token IDENTIFIER
%token CBLOCK
%token DEBUG_LINE
%token ENDC
%token ERRORLEVEL
%token FILL
%token LIST
%token NUMBER
%token PROCESSOR
%token STRING
%token INCLUDE
%token UPPER
%token HIGH
%token LOW
%token LSH
%token RSH
%token GREATER_EQUAL
%token LESS_EQUAL
%token EQUAL
%token NOT_EQUAL
%token '<'
%token '>'
%token '&'
%token '|'
%token '^'
%token LOGICAL_AND
%token LOGICAL_OR
%token '='
%token ASSIGN_PLUS
%token ASSIGN_MINUS
%token ASSIGN_MULTIPLY
%token ASSIGN_DIVIDE
%token ASSIGN_MODULUS
%token ASSIGN_LSH
%token ASSIGN_RSH
%token ASSIGN_AND
%token ASSIGN_OR
%token ASSIGN_XOR
%token INCREMENT
%token DECREMENT
%token TBL_NO_CHANGE
%token TBL_POST_INC
%token TBL_POST_DEC
%token TBL_PRE_INC
%token CONCAT
%token VAR
%token VARLAB_BEGIN
%token VAR_BEGIN
%token VAR_END
%token '['
%token ']'
%type '+'
%type '-'
%type '*'
%type '/'
%type '%'
%type '!'
%type '~'
%type line
%type label_concat
%type decimal_ops
%type statement
%type
parameter_list %type
expr %type
e0 %type
e1 %type
e2 %type
e3 %type
e4 %type
e5 %type
e6 %type
e7 %type
e8 %type
e9 %type
cidentifier %type e1op %type e2op %type e3op %type e4op %type e5op %type e6op %type e7op %type e8op %type e9op %type assign_equal_ops %type
list_block %type
list_expr %% /* Grammar rules */ program: /* can be nothing */ | program { state.lst.line.was_org = state.org; state.lst.line.linetype = none; state.next_state = state_nochange; } line | program error '\n' { next_line(0); } ; line: label_concat assign_equal_ops expr '\n' { struct pnode *parms; int exp_result; exp_result = do_insn("set", mk_list($3, NULL)); parms = mk_list(mk_2op(return_op($2), mk_symbol($1), mk_constant(exp_result)), NULL); next_line(set_label($1, parms)); } | label_concat '=' expr '\n' { struct pnode *parms; /* implements i = 6 + 1 */ parms = mk_list($3, NULL); next_line(set_label($1, parms)); } | label_concat DECREMENT '\n' { struct pnode *parms; /* implements i-- */ parms = mk_list(mk_1op(DECREMENT, mk_symbol($1)), NULL); next_line(set_label($1, parms)); } | label_concat INCREMENT '\n' { struct pnode *parms; /* implements i++ */ parms = mk_list(mk_1op(INCREMENT, mk_symbol($1)), NULL); next_line(set_label($1, parms)); } | label_concat statement { if (asm_enabled() && (state.lst.line.linetype == none)) state.lst.line.linetype = insn; if (asm_enabled()) { if (state.mac_head) { /* This is a macro definition. Set it up */ struct symbol *mac; struct macro_head *h = NULL; mac = get_symbol(state.stMacros, $1); if (mac) h = get_symbol_annotation(mac); /* It's not an error if macro was defined on pass 1 and we're in pass 2. */ if (h && !((h->pass == 1) && (state.pass == 2))) { gperror(GPE_DUPLICATE_MACRO, NULL); } else { if (!mac) mac = add_symbol(state.stMacros, $1); annotate_symbol(mac, state.mac_head); h = state.mac_head; h->line_number = state.src->line_number; h->file_symbol = state.src->file_symbol; } h->pass = state.pass; /* The macro is defined so allow calls. */ if (state.pass == 2) h->defined = 1; state.mac_head = NULL; } else if (!state.mac_prev) { /* Outside a macro, just define the label. */ switch (state.lst.line.linetype) { case sec: strncpy(state.obj.new_sec_name, $1, 78); break; case set: set_global($1, $2, TEMPORARY, gvt_constant); break; case org: case equ: set_global($1, $2, PERMANENT, gvt_constant); break; case insn: set_global($1, $2 << _16bit_core, PERMANENT, gvt_address); break; case res: set_global($1, $2, PERMANENT, gvt_static); break; case dir: gperror(GPE_ILLEGAL_LABEL, NULL); break; default: break; } } } next_line($2); } | statement { if (state.mac_head) { /* This is a macro definition, but the label was missing */ state.mac_head = NULL; gperror(GPE_NO_MACRO_NAME, NULL); } else { next_line(0); } } ; decimal_ops: ERRORLEVEL | DEBUG_LINE; statement: '\n' { if (!state.mac_prev) { $$ = state.org; } else { macro_append(); } } | INCLUDE '\n' { $$ = do_or_append_insn("include", mk_list(mk_string($1), NULL)); } | PROCESSOR { force_ident = 1; } IDENTIFIER '\n' { $$ = do_or_append_insn($1, mk_list(mk_symbol($3), NULL)); force_ident = 0; } | LIST '\n' { $$ = do_or_append_insn($1, NULL); } | LIST { force_decimal = 1; } list_block '\n' { $$ = do_or_append_insn($1, $3); force_decimal = 0; } | decimal_ops { force_decimal = 1; } parameter_list '\n' { $$ = do_or_append_insn($1, $3); force_decimal = 0; } | IDENTIFIER '\n' { $$ = do_or_append_insn($1, NULL); } | IDENTIFIER parameter_list '\n' { $$ = do_or_append_insn($1, $2); } | FILL IDENTIFIER ')' ',' expr '\n' { int number; int i; if (!state.mac_prev) { number = eval_fill_number($5); for (i = 0; i < number; i++) { $$ = do_insn($2, NULL); } } else { macro_append(); } } | FILL IDENTIFIER parameter_list ')' ',' expr '\n' { int number; int i; if (!state.mac_prev) { number = eval_fill_number($6); for (i = 0; i < number; i++) { $$ = do_insn($2, $3); } } else { macro_append(); } } | CBLOCK expr '\n' { if (!state.mac_prev) { begin_cblock($2); } else { macro_append(); } next_line(0); } const_block ENDC '\n' { if (state.mac_prev) { macro_append(); } $$ = 0; } | CBLOCK '\n' { if (state.mac_prev) { macro_append(); } next_line(0); } const_block ENDC '\n' { if (state.mac_prev) { macro_append(); } $$ = 0; } | CBLOCK error ENDC '\n' { $$ = 0; } ; const_block: | const_block const_line { next_line(0); } ; const_line: '\n' | const_def_list '\n' { if (state.mac_prev) { macro_append(); } } | label_concat '\n' { if (!state.mac_prev) { cblock_expr(mk_symbol($1)); } else { macro_append(); } } | label_concat expr '\n' { if (!state.mac_prev) { cblock_expr_incr(mk_symbol($1), $2); } else { macro_append(); } } ; const_def_list: const_def | const_def_list ',' const_def ; const_def: cidentifier { if (!state.mac_prev) { cblock_expr($1); } } | cidentifier ':' expr { if (!state.mac_prev) { cblock_expr_incr($1, $3); } } ; assign_equal_ops: ASSIGN_PLUS | ASSIGN_MINUS | ASSIGN_MULTIPLY | ASSIGN_DIVIDE | ASSIGN_MODULUS | ASSIGN_LSH | ASSIGN_RSH | ASSIGN_AND | ASSIGN_OR | ASSIGN_XOR; parameter_list: expr { $$ = mk_list($1, NULL); } | expr ',' { gperror(GPE_BADCHAR, "Illegal Character (,)"); $$ = mk_list($1, NULL); } | expr ',' parameter_list { $$ = mk_list($1, $3); } ; expr: e9 | STRING { $$ = mk_string($1); } ; e9: e8 | e9 e9op e8 { $$ = mk_2op($2, $1, $3); } ; e9op: '=' ; e8: e7 | e8 e8op e7 { $$ = mk_2op($2, $1, $3); } ; e8op: LOGICAL_OR ; e7: e6 | e7 e7op e6 { $$ = mk_2op($2, $1, $3); } ; e7op: LOGICAL_AND; e6: e5 | e6 e6op e5 { $$ = mk_2op($2, $1, $3); } ; e6op: '&' | '|' | '^' ; e5: e4 | e5 e5op e4 { $$ = mk_2op($2, $1, $3); } ; e5op: '<' | '>' | EQUAL | NOT_EQUAL | GREATER_EQUAL | LESS_EQUAL ; e4: e3 | e4 e4op e3 { $$ = mk_2op($2, $1, $3); } ; e4op: LSH | RSH ; e3: e2 | e3 e3op e2 { $$ = mk_2op($2, $1, $3); } ; e3op: '+' | '-' ; e2: e1 | e2 e2op e1 { $$ = mk_2op($2, $1, $3); } ; e2op: '*' | '/' | '%'; e1: e0 | e1op e0 { $$ = mk_1op($1, $2); } ; e1op: UPPER | HIGH | LOW | '-' | '!' | '~' | '+'; e0: cidentifier { $$ = $1; } | NUMBER { $$ = mk_constant($1); } | '$' { $$ = mk_symbol("$"); } | '(' expr ')' { $$ = $2; } | '[' expr ']' { $$ = mk_offset($2); } | '*' { $$ = mk_constant(TBL_NO_CHANGE); } | TBL_POST_INC { $$ = mk_constant($1); } | TBL_POST_DEC { $$ = mk_constant($1); } | TBL_PRE_INC { $$ = mk_constant($1); } ; cidentifier: IDENTIFIER { $$ = mk_symbol($1); } | VAR_BEGIN expr ')' { $$ = mk_2op(CONCAT, mk_symbol($1), mk_1op(VAR, $2)); } | VAR_BEGIN expr VAR_END { $$ = mk_2op(CONCAT, mk_symbol($1), mk_2op(CONCAT, mk_1op(VAR, $2), mk_symbol($3))); } ; label_concat: LABEL { $$ = $1; } | VARLAB_BEGIN expr ')' { if (asm_enabled() && !state.mac_prev) { $$ = evaluate_concatenation(mk_2op(CONCAT, mk_symbol($1), mk_1op(VAR, $2))); } } | VARLAB_BEGIN expr VAR_END { if (asm_enabled() && !state.mac_prev) { $$ = evaluate_concatenation(mk_2op(CONCAT, mk_symbol($1), mk_2op(CONCAT, mk_1op(VAR, $2), mk_symbol($3)))); } } ; list_block: list_expr { $$ = mk_list($1, NULL); } | list_expr ',' list_block { $$ = mk_list($1, $3); } ; list_expr: IDENTIFIER { if ((strcasecmp($1, "p") == 0) || (strcasecmp($1, "pe") == 0)) { force_ident = 1; } } e9op e8 { $$ = mk_2op($3, mk_symbol($1), $4); force_ident = 0; } | e8 { $$ = $1; } ; %%