mirror of https://github.com/dschmenk/VM02.git
1466 lines
50 KiB
C
Executable File
1466 lines
50 KiB
C
Executable File
#include <stdio.h>
|
|
#include "tokens.h"
|
|
#include "lex.h"
|
|
#include "codegen.h"
|
|
|
|
int infunc = 0, retfunc_tag = 0, break_tag = 0, stack_loop = 0;
|
|
t_token prevstmnt;
|
|
|
|
t_token binary_ops_table[] = {
|
|
/* Highest precedence */
|
|
MUL_TOKEN, DIV_TOKEN, DIVMOD_TOKEN,
|
|
ADD_TOKEN, SUB_TOKEN,
|
|
SHR_TOKEN, SHL_TOKEN,
|
|
AND_TOKEN,
|
|
EOR_TOKEN,
|
|
OR_TOKEN,
|
|
GT_TOKEN, GE_TOKEN, LT_TOKEN, LE_TOKEN,
|
|
EQ_TOKEN, NE_TOKEN,
|
|
LOGIC_AND_TOKEN,
|
|
LOGIC_OR_TOKEN,
|
|
COMMA_TOKEN
|
|
/* Lowest precedence */
|
|
};
|
|
t_token binary_ops_precedence[] = {
|
|
/* Highest precedence */
|
|
1, 1, 1,
|
|
2, 2,
|
|
3, 3,
|
|
4,
|
|
5,
|
|
6,
|
|
7, 7, 7, 7,
|
|
8, 8,
|
|
9,
|
|
10,
|
|
11
|
|
/* Lowest precedence */
|
|
};
|
|
|
|
t_token opstack[16];
|
|
int precstack[16];
|
|
int opsptr = -1;
|
|
void push_op(t_token op, int prec)
|
|
{
|
|
if (++opsptr == 16)
|
|
{
|
|
parse_error("Stack overflow\n");
|
|
return;
|
|
}
|
|
opstack[opsptr] = op;
|
|
precstack[opsptr] = prec;
|
|
}
|
|
t_token pop_op(void)
|
|
{
|
|
if (opsptr < 0)
|
|
{
|
|
parse_error("Stack underflow\n");
|
|
return (0);
|
|
}
|
|
return opstack[opsptr--];
|
|
}
|
|
t_token tos_op(void)
|
|
{
|
|
return opsptr < 0 ? 0 : opstack[opsptr];
|
|
}
|
|
int tos_op_prec(int tos)
|
|
{
|
|
return opsptr <= tos ? 100 : precstack[opsptr];
|
|
}
|
|
int consts = 0;
|
|
char idconst_name[1024][17];
|
|
int idconst_value[1024];
|
|
int globals = 0;
|
|
int globalsize = 0;
|
|
char idglobal_name[1024][17];
|
|
int idglobal_type[1024];
|
|
int idglobal_tag[1024];
|
|
int locals = 0;
|
|
int localsize = 0;
|
|
char idlocal_name[128][17];
|
|
int idlocal_type[128];
|
|
int idlocal_offset[128];
|
|
int id_match(char *name, int len, char *id)
|
|
{
|
|
if (len == id[0])
|
|
{
|
|
if (len > 16) len = 16;
|
|
while (len--)
|
|
{
|
|
if (name[len] != id[1 + len])
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
int idlocal_lookup(char *name, int len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < locals; i++)
|
|
if (id_match(name, len, &(idlocal_name[i][0])))
|
|
return (i);
|
|
return (-1);
|
|
}
|
|
int idglobal_lookup(char *name, int len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < globals; i++)
|
|
if (id_match(name, len, &(idglobal_name[i][0])))
|
|
return (i);
|
|
return (-1);
|
|
}
|
|
int idconst_lookup(char *name, int len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < consts; i++)
|
|
if (id_match(name, len, &(idconst_name[i][0])))
|
|
return (i);
|
|
return (-1);
|
|
}
|
|
int idlocal_add(char *name, int len, int type, int size)
|
|
{
|
|
if (localsize > 255)
|
|
{
|
|
printf("Local variable size overflow\n");
|
|
return (0);
|
|
}
|
|
char c = name[len];
|
|
name[len] = '\0';
|
|
emit_idlocal(name, localsize);
|
|
name[len] = c;
|
|
idlocal_name[locals][0] = len;
|
|
if (len > 16) len = 16;
|
|
while (len--)
|
|
idlocal_name[locals][1 + len] = name[len];
|
|
idlocal_type[locals] = type;
|
|
idlocal_offset[locals] = localsize;
|
|
localsize += size;
|
|
locals++;
|
|
return (1);
|
|
}
|
|
int idglobal_add(char *name, int len, int type, int size)
|
|
{
|
|
if (globals > 1024)
|
|
{
|
|
printf("Global variable count overflow\n");
|
|
return (0);
|
|
}
|
|
char c = name[len];
|
|
name[len] = '\0';
|
|
emit_idglobal(globalsize, size, name);
|
|
name[len] = c;
|
|
idglobal_name[globals][0] = len;
|
|
if (len > 16) len = 16;
|
|
while (len--)
|
|
idglobal_name[globals][1 + len] = name[len];
|
|
idglobal_type[globals] = type;
|
|
idglobal_tag[globals] = globalsize;
|
|
globalsize += size;
|
|
globals++;
|
|
return (1);
|
|
}
|
|
void idglobal_size(int type, int size, int constsize)
|
|
{
|
|
if (size > constsize)
|
|
globalsize += emit_data(0, 0, 0, size - constsize);
|
|
else
|
|
globalsize += constsize;
|
|
}
|
|
int idfunc_add(char *name, int len, int tag)
|
|
{
|
|
if (globals > 1024)
|
|
{
|
|
printf("Global variable count overflow\n");
|
|
return (0);
|
|
}
|
|
idglobal_name[globals][0] = len;
|
|
if (len > 16) len = 16;
|
|
while (len--)
|
|
idglobal_name[globals][1 + len] = name[len];
|
|
idglobal_type[globals] = FUNC_TYPE;
|
|
idglobal_tag[globals] = tag;
|
|
globals++;
|
|
return (1);
|
|
}
|
|
int idconst_add(char *name, int len, int value)
|
|
{
|
|
if (consts > 1024)
|
|
{
|
|
printf("Constant count overflow\n");
|
|
return (0);
|
|
}
|
|
char c = name[len];
|
|
name[len] = '\0';
|
|
emit_idconst(name, value);
|
|
name[len] = c;
|
|
idconst_name[consts][0] = len;
|
|
if (len > 16) len = 16;
|
|
while (len--)
|
|
idconst_name[consts][1 + len] = name[len];
|
|
idconst_value[consts] = value;
|
|
consts++;
|
|
return (1);
|
|
}
|
|
int id_addr(char *name, int len)
|
|
{
|
|
int i;
|
|
if ((i = idlocal_lookup(name, len)) >= 0)
|
|
return (idlocal_offset[i]);
|
|
if ((i = idglobal_lookup(name, len)) >= 0)
|
|
return (idglobal_tag[i]);
|
|
parse_error("Undeclared identifier");
|
|
return (-1);
|
|
}
|
|
int id_const(char *name, int len)
|
|
{
|
|
int i;
|
|
if ((i = idconst_lookup(name, len)) >= 0)
|
|
return (idconst_value[i]);
|
|
parse_error("Undeclared constant");
|
|
return (0);
|
|
}
|
|
int id_type(char *name, int len)
|
|
{
|
|
int i;
|
|
if ((i = idconst_lookup(name, len)) >= 0)
|
|
return (CONST_TYPE);
|
|
if ((i = idlocal_lookup(name, len)) >= 0)
|
|
return (idlocal_type[i] | LOCAL_TYPE);
|
|
if ((i = idglobal_lookup(name, len)) >= 0)
|
|
return (idglobal_type[i]);
|
|
parse_error("Undeclared identifier");
|
|
return (0);
|
|
}
|
|
void idlocal_reset(void)
|
|
{
|
|
locals = localsize = 0;
|
|
}
|
|
int tag_new(void)
|
|
{
|
|
static int codetag = 0;
|
|
return (codetag++);
|
|
}
|
|
int parse_expr(void);
|
|
int parse_term(void)
|
|
{
|
|
/*
|
|
* Parse terminal tokens.
|
|
*/
|
|
switch (scan())
|
|
{
|
|
case CHAR_TOKEN:
|
|
case INT_TOKEN:
|
|
case FLOAT_TOKEN:
|
|
case ID_TOKEN:
|
|
case STRING_TOKEN:
|
|
break;
|
|
case OPEN_PAREN_TOKEN:
|
|
if (!parse_expr())
|
|
{
|
|
parse_error("Bad expression in parenthesis");
|
|
return (0);
|
|
}
|
|
if (scantoken != CLOSE_PAREN_TOKEN)
|
|
{
|
|
parse_error("Missing closing parenthesis");
|
|
return (0);
|
|
}
|
|
break;
|
|
default:
|
|
/*
|
|
* Non-terminal token.
|
|
*/
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
int parse_constval(long *value, int *size)
|
|
{
|
|
int mod = 0, type = 0;
|
|
*value = 0;
|
|
while (!parse_term())
|
|
{
|
|
switch (scantoken)
|
|
{
|
|
case ADD_TOKEN:
|
|
/*
|
|
* Just ignore unary plus, it is a no-op.
|
|
*/
|
|
break;
|
|
case SUB_TOKEN: // Really NEG_TOKEN
|
|
mod |= 1;
|
|
break;
|
|
case COMP_TOKEN:
|
|
mod |= 2;
|
|
break;
|
|
case LOGIC_NOT_TOKEN:
|
|
mod |= 4;
|
|
break;
|
|
case AT_TOKEN:
|
|
mod |= 8;
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
/*
|
|
* Determine which terminal type.
|
|
*/
|
|
if (scantoken == STRING_TOKEN)
|
|
{
|
|
*value = constval;
|
|
*size = tokenlen - 1;
|
|
type = STRING_TYPE;
|
|
if (mod)
|
|
{
|
|
parse_error("Invalid string modifiers");
|
|
return (0);
|
|
}
|
|
}
|
|
else if (scantoken == CHAR_TOKEN)
|
|
{
|
|
*value = constval;
|
|
*size = 1;
|
|
type = BYTE_TYPE;
|
|
}
|
|
else if (scantoken == INT_TOKEN)
|
|
{
|
|
*value = constval;
|
|
*size = 2;
|
|
type = WORD_TYPE;
|
|
}
|
|
else if (scantoken == ID_TOKEN)
|
|
{
|
|
type = id_type(tokenstr, tokenlen);
|
|
if (type & CONST_TYPE)
|
|
*value = id_const(tokenstr, tokenlen);
|
|
else if ((type & VAR_TYPE) && (mod & 8))
|
|
{
|
|
type = TAG_TYPE;
|
|
*value = id_addr(tokenstr, tokenlen);
|
|
}
|
|
else if (type & FUNC_TYPE)
|
|
{
|
|
type = TAG_TYPE;
|
|
*value = id_addr(tokenstr, tokenlen) | 0x8000;
|
|
}
|
|
else
|
|
{
|
|
parse_error("Invalid constant");
|
|
return (0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parse_error("Invalid constant");
|
|
return (0);
|
|
}
|
|
if (mod & 1)
|
|
*value = -*value;
|
|
if (mod & 2)
|
|
*value = ~*value;
|
|
if (mod & 4)
|
|
*value = *value ? 0 : -1;
|
|
return (type);
|
|
}
|
|
int parse_value(int rvalue)
|
|
{
|
|
int cparams;
|
|
int deref = rvalue;
|
|
int optos = opsptr;
|
|
int type = 0, value = 0, emit_value = 0;
|
|
/*
|
|
* Parse pre operand operators.
|
|
*/
|
|
while (!parse_term())
|
|
{
|
|
switch (scantoken)
|
|
{
|
|
case ADD_TOKEN:
|
|
/*
|
|
* Just ignore unary plus, it is a no-op.
|
|
*/
|
|
break;
|
|
case BPTR_TOKEN:
|
|
if (deref)
|
|
push_op(scantoken, 0);
|
|
else
|
|
{
|
|
type |= BPTR_TYPE;
|
|
deref++;
|
|
}
|
|
break;
|
|
case WPTR_TOKEN:
|
|
if (deref)
|
|
push_op(scantoken, 0);
|
|
else
|
|
{
|
|
type |= WPTR_TYPE;
|
|
deref++;
|
|
}
|
|
break;
|
|
case AT_TOKEN:
|
|
deref--;
|
|
break;
|
|
case SUB_TOKEN: // Really NEG_TOKEN
|
|
case COMP_TOKEN:
|
|
case LOGIC_NOT_TOKEN:
|
|
push_op(scantoken, 0);
|
|
break;
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
/*
|
|
* Determine which terminal type.
|
|
*/
|
|
if (scantoken == INT_TOKEN || scantoken == CHAR_TOKEN)
|
|
{
|
|
value = constval;
|
|
type |= CONST_TYPE;
|
|
}
|
|
else if (scantoken == ID_TOKEN)
|
|
{
|
|
if ((type |= id_type(tokenstr, tokenlen)) & CONST_TYPE)
|
|
value = id_const(tokenstr, tokenlen);
|
|
else if (type & VAR_TYPE)
|
|
value = id_addr(tokenstr, tokenlen);
|
|
else if (type & FUNC_TYPE)
|
|
value = id_addr(tokenstr, tokenlen);
|
|
else
|
|
{
|
|
printf("Bad ID type\n");
|
|
return (0);
|
|
}
|
|
}
|
|
else if (scantoken == CLOSE_PAREN_TOKEN)
|
|
{
|
|
// type |= WORD_TYPE;
|
|
emit_value = 1;
|
|
}
|
|
else
|
|
return (0);
|
|
if (type & CONST_TYPE)
|
|
{
|
|
/*
|
|
* Quick optimizations
|
|
*/
|
|
while ((optos < opsptr)
|
|
&& ((tos_op() == NEG_TOKEN) || (tos_op() == COMP_TOKEN) || (tos_op() == LOGIC_NOT_TOKEN)))
|
|
{
|
|
switch (pop_op())
|
|
{
|
|
case NEG_TOKEN:
|
|
value = -value;
|
|
break;
|
|
case COMP_TOKEN:
|
|
value = ~value;
|
|
break;
|
|
case LOGIC_NOT_TOKEN:
|
|
value = value ? 0 : -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Parse post operand operators.
|
|
*/
|
|
scan();
|
|
while (scantoken == OPEN_PAREN_TOKEN
|
|
|| scantoken == OPEN_BRACKET_TOKEN
|
|
|| scantoken == DOT_TOKEN
|
|
|| scantoken == COLON_TOKEN)
|
|
{
|
|
/*
|
|
* Parse post operand operators.
|
|
*/
|
|
if (scantoken == OPEN_BRACKET_TOKEN)
|
|
{
|
|
/*
|
|
* Array
|
|
*/
|
|
if (!emit_value)
|
|
{
|
|
if (type & ADDR_TYPE)
|
|
{
|
|
if (type & LOCAL_TYPE)
|
|
emit_localaddr(value);
|
|
else
|
|
emit_globaladdr(value, type);
|
|
}
|
|
else if (type & CONST_TYPE)
|
|
{
|
|
emit_const(value);
|
|
}
|
|
emit_value = 1;
|
|
}
|
|
if (type & PTR_TYPE)
|
|
emit_lw();
|
|
if (!parse_expr())
|
|
{
|
|
parse_error("Bad expression");
|
|
return (0);
|
|
}
|
|
if (scantoken != CLOSE_BRACKET_TOKEN)
|
|
{
|
|
parse_error("Missing closing bracket");
|
|
return (0);
|
|
}
|
|
if (type & WORD_TYPE)
|
|
{
|
|
//type |= WPTR_TYPE;
|
|
type = WPTR_TYPE;
|
|
emit_indexword();
|
|
}
|
|
else
|
|
{
|
|
//type |= BPTR_TYPE;
|
|
type = BPTR_TYPE;
|
|
emit_indexbyte();
|
|
}
|
|
//type &= ~(ADDR_TYPE | CONST_TYPE);
|
|
scan();
|
|
}
|
|
else if (scantoken == DOT_TOKEN || scantoken == COLON_TOKEN)
|
|
{
|
|
/*
|
|
* Structure member offset or array of arrays
|
|
*/
|
|
int elem_size;
|
|
int elem_type = (scantoken == DOT_TOKEN) ? BPTR_TYPE : WPTR_TYPE;
|
|
long elem_offset = 0;
|
|
if (parse_constval(&elem_offset, &elem_size))
|
|
{
|
|
/*
|
|
* Constant member offset
|
|
*/
|
|
if (!emit_value)
|
|
{
|
|
if (type & VAR_TYPE)
|
|
{
|
|
if (type & LOCAL_TYPE)
|
|
emit_localaddr(value + elem_offset);
|
|
else
|
|
emit_globaladdrofst(value, elem_offset, type);
|
|
}
|
|
else if (type & CONST_TYPE)
|
|
{
|
|
value += elem_offset;
|
|
emit_const(value);
|
|
}
|
|
else // FUNC_TYPE
|
|
{
|
|
emit_globaladdr(value, type);
|
|
emit_const(elem_offset);
|
|
emit_op(ADD_TOKEN);
|
|
}
|
|
emit_value = 1;
|
|
}
|
|
else
|
|
{
|
|
if (elem_offset != 0)
|
|
{
|
|
emit_const(elem_offset);
|
|
emit_op(ADD_TOKEN);
|
|
}
|
|
}
|
|
scan();
|
|
}
|
|
else if (scantoken == OPEN_BRACKET_TOKEN)
|
|
{
|
|
/*
|
|
* Array of arrays
|
|
*/
|
|
if (!emit_value)
|
|
{
|
|
if (type & ADDR_TYPE)
|
|
{
|
|
if (type & LOCAL_TYPE)
|
|
emit_localaddr(value);
|
|
else
|
|
emit_globaladdr(value, type);
|
|
}
|
|
else if (type & CONST_TYPE)
|
|
{
|
|
emit_const(value);
|
|
}
|
|
emit_value = 1;
|
|
}
|
|
do
|
|
{
|
|
if (emit_value++ > 1)
|
|
{
|
|
emit_indexword();
|
|
emit_lw();
|
|
}
|
|
if (!parse_expr())
|
|
{
|
|
parse_error("Bad expression");
|
|
return (0);
|
|
}
|
|
if (scantoken != CLOSE_BRACKET_TOKEN)
|
|
{
|
|
parse_error("Missing closing bracket");
|
|
return (0);
|
|
}
|
|
} while (scan() == OPEN_BRACKET_TOKEN);
|
|
if (elem_type & WPTR_TYPE)
|
|
emit_indexword();
|
|
else
|
|
emit_indexbyte();
|
|
}
|
|
else
|
|
{
|
|
parse_error("Invalid member offset");
|
|
return (0);
|
|
}
|
|
type = elem_type; //(type & ~(ADDR_TYPE | CONST_TYPE)) | elem_type;
|
|
}
|
|
else if (scantoken == OPEN_PAREN_TOKEN)
|
|
{
|
|
/*
|
|
* Function call
|
|
*/
|
|
if (!emit_value && (type & VAR_TYPE))
|
|
{
|
|
if (type & LOCAL_TYPE)
|
|
emit_localaddr(value);
|
|
else
|
|
emit_globaladdr(value, type);
|
|
}
|
|
if (type & (VAR_TYPE | PTR_TYPE))
|
|
emit_lw();
|
|
if (!(type & (FUNC_TYPE | CONST_TYPE)))
|
|
emit_push();
|
|
parse_expr();
|
|
if (scantoken != CLOSE_PAREN_TOKEN)
|
|
{
|
|
parse_error("Missing closing parenthesis");
|
|
return (0);
|
|
}
|
|
if (type & (FUNC_TYPE | CONST_TYPE))
|
|
emit_call(value);
|
|
else
|
|
{
|
|
emit_pull();
|
|
emit_ical();
|
|
}
|
|
emit_value = 1;
|
|
type = WORD_TYPE; //(type & ~(FUNC_TYPE | CONST_TYPE)) | WORD_TYPE;
|
|
scan();
|
|
}
|
|
}
|
|
if (emit_value)
|
|
{
|
|
if (rvalue && deref && (type & PTR_TYPE))
|
|
(type & BPTR_TYPE) ? emit_lb() : emit_lw();
|
|
}
|
|
else
|
|
{
|
|
if (type & CONST_TYPE)
|
|
emit_const(value);
|
|
else if (deref)
|
|
{
|
|
if (type & FUNC_TYPE)
|
|
emit_call(value);
|
|
else if (type & VAR_TYPE)
|
|
{
|
|
if (type & LOCAL_TYPE)
|
|
(type & BYTE_TYPE) ? emit_llb(value) : emit_llw(value);
|
|
else
|
|
(type & BYTE_TYPE) ? emit_lab(value) : emit_law(value);
|
|
}
|
|
else if (type & PTR_TYPE)
|
|
(type & BPTR_TYPE) ? emit_lb() : emit_lw();
|
|
}
|
|
else
|
|
{
|
|
if (type & LOCAL_TYPE)
|
|
emit_localaddr(value);
|
|
else
|
|
emit_globaladdr(value, type);
|
|
}
|
|
}
|
|
while (optos < opsptr)
|
|
{
|
|
if (!emit_unaryop(pop_op()))
|
|
{
|
|
parse_error(": Invalid unary operation");
|
|
return (0);
|
|
}
|
|
}
|
|
return (type ? type : WORD_TYPE);
|
|
}
|
|
int parse_constexpr(long *value, int *size)
|
|
{
|
|
long val1, val2;
|
|
int type, size1, size2 = 0;
|
|
|
|
if (!(type = parse_constval(&val1, &size1)))
|
|
return (0);
|
|
if (scan() == ADD_TOKEN)
|
|
{
|
|
if (!parse_constval(&val2, &size2))
|
|
return (0);
|
|
*value = val1 + val2;
|
|
}
|
|
else if (scantoken == SUB_TOKEN)
|
|
{
|
|
if (!parse_constval(&val2, &size2))
|
|
return (0);
|
|
*value = val1 - val2;
|
|
}
|
|
else if (scantoken == MUL_TOKEN)
|
|
{
|
|
if (!parse_constval(&val2, &size2))
|
|
return (0);
|
|
*value = val1 * val2;
|
|
}
|
|
else if (scantoken == DIV_TOKEN)
|
|
{
|
|
if (!parse_constval(&val2, &size2))
|
|
return (0);
|
|
*value = val1 / val2;
|
|
}
|
|
else if (scantoken == AND_TOKEN)
|
|
{
|
|
if (!parse_constval(&val2, &size2))
|
|
return (0);
|
|
*value = val1 & val2;
|
|
}
|
|
else if (scantoken == OR_TOKEN)
|
|
{
|
|
if (!parse_constval(&val2, &size2))
|
|
return (0);
|
|
*value = val1 | val2;
|
|
}
|
|
else if (scantoken == EOR_TOKEN)
|
|
{
|
|
if (!parse_constval(&val2, &size2))
|
|
return (0);
|
|
*value = val1 ^ val2;
|
|
}
|
|
else
|
|
*value = val1;
|
|
*size = size1 > size2 ? size1 : size2;
|
|
return (type);
|
|
}
|
|
int parse_expr()
|
|
{
|
|
int prevmatch;
|
|
int matchop = 0;
|
|
int optos = opsptr;
|
|
int i;
|
|
int prevtype, type = 0;
|
|
do
|
|
{
|
|
/*
|
|
* Parse sequence of double operand operations.
|
|
*/
|
|
prevmatch = matchop;
|
|
matchop = 0;
|
|
if (parse_value(1))
|
|
{
|
|
matchop = 1;
|
|
for (i = 0; i < sizeof(binary_ops_table); i++)
|
|
if (scantoken == binary_ops_table[i])
|
|
{
|
|
matchop = 2;
|
|
if (binary_ops_precedence[i] >= tos_op_prec(optos))
|
|
if (!emit_op(pop_op()))
|
|
{
|
|
parse_error(": Invalid binary operation");
|
|
return (0);
|
|
}
|
|
push_op(scantoken, binary_ops_precedence[i]);
|
|
break;
|
|
}
|
|
}
|
|
} while (matchop == 2);
|
|
if (matchop == 0 && prevmatch == 2)
|
|
{
|
|
parse_error("Missing operand");
|
|
return (0);
|
|
}
|
|
while (optos < opsptr)
|
|
if (!emit_op(pop_op()))
|
|
{
|
|
parse_error(": Invalid binary operation");
|
|
return (0);
|
|
}
|
|
return (matchop || prevmatch);
|
|
}
|
|
int parse_setlist(int addr, int type)
|
|
{
|
|
int nexttype, nextaddr;
|
|
char *idptr;
|
|
|
|
if (!(type & VAR_TYPE))
|
|
emit_push();
|
|
if (scan() == ID_TOKEN)
|
|
{
|
|
nexttype = id_type(tokenstr, tokenlen);
|
|
nextaddr = id_addr(tokenstr, tokenlen);
|
|
}
|
|
else
|
|
{
|
|
nexttype = 0;
|
|
nextaddr = 0;
|
|
}
|
|
idptr = tokenstr;
|
|
scan();
|
|
if (nexttype & VAR_TYPE && scantoken == SET_TOKEN)
|
|
{
|
|
parse_expr();
|
|
if (type & LOCAL_TYPE)
|
|
(type & BYTE_TYPE) ? emit_slb(nextaddr) : emit_slw(nextaddr);
|
|
else
|
|
(type & BYTE_TYPE) ? emit_sab(nextaddr) : emit_saw(nextaddr);
|
|
}
|
|
else if (nexttype & VAR_TYPE && scantoken == SETLIST_TOKEN)
|
|
{
|
|
parse_setlist(nextaddr, nexttype);
|
|
}
|
|
else
|
|
{
|
|
tokenstr = idptr;
|
|
scan_rewind(tokenstr);
|
|
if ((nexttype = parse_value(0)) != 0)
|
|
{
|
|
if (scantoken == SET_TOKEN)
|
|
{
|
|
emit_push();
|
|
parse_expr();
|
|
emit_pull();
|
|
emit_swap();
|
|
(nexttype & (BYTE_TYPE | BPTR_TYPE)) ? emit_sb() : emit_sw();
|
|
}
|
|
else if (scantoken == SETLIST_TOKEN)
|
|
parse_setlist(0, nexttype);
|
|
}
|
|
else
|
|
{
|
|
parse_error("Syntax error");
|
|
return (0);
|
|
}
|
|
}
|
|
if (type & VAR_TYPE)
|
|
{
|
|
if (type & LOCAL_TYPE)
|
|
(type & BYTE_TYPE) ? emit_slb(addr) : emit_slw(addr);
|
|
else
|
|
(type & BYTE_TYPE) ? emit_sab(addr) : emit_saw(addr);
|
|
}
|
|
else
|
|
{
|
|
emit_pull();
|
|
emit_swap();
|
|
(type & (BYTE_TYPE | BPTR_TYPE)) ? emit_sb() : emit_sw();
|
|
}
|
|
return (1);
|
|
}
|
|
int parse_stmnt(void)
|
|
{
|
|
int tag_prevbrk, tag_else, tag_endif, tag_while, tag_wend, tag_repeat, tag_for, tag_choice, type, addr, step;
|
|
char *idptr;
|
|
|
|
/*
|
|
* Optimizationf for last function LEAVE
|
|
*/
|
|
if (scantoken != END_TOKEN && scantoken != DONE_TOKEN)
|
|
prevstmnt = scantoken;
|
|
|
|
switch (scantoken)
|
|
{
|
|
case IF_TOKEN:
|
|
parse_expr();
|
|
tag_else = tag_new();
|
|
tag_endif = tag_new();
|
|
emit_skpfls(tag_else);
|
|
scan();
|
|
do {
|
|
while (parse_stmnt()) next_line();
|
|
if (scantoken != ELSEIF_TOKEN)
|
|
break;
|
|
emit_skip(tag_endif);
|
|
emit_codetag(tag_else);
|
|
if (!parse_expr())
|
|
{
|
|
parse_error("Bad expression");
|
|
return (0);
|
|
}
|
|
tag_else = tag_new();
|
|
emit_skpfls(tag_else);
|
|
} while (1);
|
|
if (scantoken == ELSE_TOKEN)
|
|
{
|
|
emit_skip(tag_endif);
|
|
emit_codetag(tag_else);
|
|
scan();
|
|
while (parse_stmnt()) next_line();
|
|
emit_codetag(tag_endif);
|
|
}
|
|
else
|
|
{
|
|
emit_codetag(tag_else);
|
|
emit_codetag(tag_endif);
|
|
}
|
|
if (scantoken != FIN_TOKEN)
|
|
{
|
|
parse_error("Missing IF/FIN");
|
|
return (0);
|
|
}
|
|
break;
|
|
case WHILE_TOKEN:
|
|
tag_while = tag_new();
|
|
tag_wend = tag_new();
|
|
tag_prevbrk = break_tag;
|
|
break_tag = tag_wend;
|
|
emit_codetag(tag_while);
|
|
parse_expr();
|
|
emit_skpfls(tag_wend);
|
|
while (parse_stmnt()) next_line();
|
|
if (scantoken != LOOP_TOKEN)
|
|
{
|
|
parse_error("Missing WHILE/END");
|
|
return (0);
|
|
}
|
|
emit_skip(tag_while);
|
|
emit_codetag(tag_wend);
|
|
break_tag = tag_prevbrk;
|
|
break;
|
|
case REPEAT_TOKEN:
|
|
tag_prevbrk = break_tag;
|
|
break_tag = tag_new();
|
|
tag_repeat = tag_new();
|
|
emit_codetag(tag_repeat);
|
|
scan();
|
|
while (parse_stmnt()) next_line();
|
|
if (scantoken != UNTIL_TOKEN)
|
|
{
|
|
parse_error("Missing REPEAT/UNTIL");
|
|
return (0);
|
|
}
|
|
parse_expr();
|
|
emit_skpfls(tag_repeat);
|
|
emit_codetag(break_tag);
|
|
break_tag = tag_prevbrk;
|
|
break;
|
|
case FOR_TOKEN:
|
|
stack_loop++;
|
|
tag_prevbrk = break_tag;
|
|
break_tag = tag_new();
|
|
tag_for = tag_new();
|
|
if (scan() != ID_TOKEN)
|
|
{
|
|
parse_error("Missing FOR variable");
|
|
return (0);
|
|
}
|
|
type = id_type(tokenstr, tokenlen);
|
|
addr = id_addr(tokenstr, tokenlen);
|
|
if (scan() != SET_TOKEN)
|
|
{
|
|
parse_error("Missing FOR =");
|
|
return (0);
|
|
}
|
|
parse_expr();
|
|
emit_codetag(tag_for);
|
|
if (type & LOCAL_TYPE)
|
|
type & BYTE_TYPE ? emit_dlb(addr) : emit_dlw(addr);
|
|
else
|
|
type & BYTE_TYPE ? emit_dab(addr) : emit_daw(addr);
|
|
step = 1;
|
|
if (scantoken == TO_TOKEN)
|
|
{
|
|
parse_expr();
|
|
}
|
|
else if (scantoken == DOWNTO_TOKEN)
|
|
{
|
|
step = -1;
|
|
parse_expr();
|
|
}
|
|
step > 0 ? emit_skpgt(break_tag) : emit_skplt(break_tag);
|
|
if (scantoken == STEP_TOKEN)
|
|
{
|
|
parse_expr();
|
|
emit_op(step > 0 ? ADD_TOKEN : SUB_TOKEN);
|
|
}
|
|
else
|
|
emit_unaryop(step > 0 ? INC_TOKEN : DEC_TOKEN);
|
|
while (parse_stmnt()) next_line();
|
|
if (scantoken != NEXT_TOKEN)
|
|
{
|
|
parse_error("Missing FOR/NEXT ");
|
|
return (0);
|
|
}
|
|
emit_skip(tag_for);
|
|
emit_codetag(break_tag);
|
|
emit_drop();
|
|
break_tag = tag_prevbrk;
|
|
stack_loop--;
|
|
break;
|
|
case CASE_TOKEN:
|
|
stack_loop++;
|
|
tag_prevbrk = break_tag;
|
|
break_tag = tag_new();
|
|
tag_choice = tag_new();
|
|
parse_expr();
|
|
next_line();
|
|
while (scantoken != ENDCASE_TOKEN)
|
|
{
|
|
if (scantoken == OF_TOKEN)
|
|
{
|
|
if (!parse_expr())
|
|
{
|
|
parse_error("Bad CASE OF expression");
|
|
return (0);
|
|
}
|
|
emit_skpne(tag_choice);
|
|
while (parse_stmnt()) next_line();
|
|
emit_skip(break_tag);
|
|
emit_codetag(tag_choice);
|
|
tag_choice = tag_new();
|
|
}
|
|
else if (scantoken == DEFAULT_TOKEN)
|
|
{
|
|
scan();
|
|
while (parse_stmnt()) next_line();
|
|
if (scantoken != ENDCASE_TOKEN)
|
|
{
|
|
parse_error("Bad CASE DEFAULT clause");
|
|
return (0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parse_error("Bad CASE clause");
|
|
return (0);
|
|
}
|
|
}
|
|
emit_codetag(break_tag);
|
|
emit_drop();
|
|
break_tag = tag_prevbrk;
|
|
stack_loop--;
|
|
break;
|
|
case BREAK_TOKEN:
|
|
if (break_tag)
|
|
emit_skip(break_tag);
|
|
else
|
|
{
|
|
parse_error("BREAK without loop");
|
|
return (0);
|
|
}
|
|
break;
|
|
case RETURN_TOKEN:
|
|
if (infunc)
|
|
{
|
|
int i;
|
|
for (i = 0; i < stack_loop; i++)
|
|
emit_drop();
|
|
parse_expr();
|
|
emit_leave(localsize);
|
|
}
|
|
else
|
|
{
|
|
parse_error("RETURN outside of function");
|
|
return (0);
|
|
}
|
|
break;
|
|
case DROP_TOKEN:
|
|
parse_expr();
|
|
emit_drop();
|
|
break;
|
|
case EOL_TOKEN:
|
|
case COMMENT_TOKEN:
|
|
return (1);
|
|
case ELSE_TOKEN:
|
|
case ELSEIF_TOKEN:
|
|
case FIN_TOKEN:
|
|
case LOOP_TOKEN:
|
|
case UNTIL_TOKEN:
|
|
case NEXT_TOKEN:
|
|
case OF_TOKEN:
|
|
case DEFAULT_TOKEN:
|
|
case ENDCASE_TOKEN:
|
|
case END_TOKEN:
|
|
case DONE_TOKEN:
|
|
case IFUNC_TOKEN:
|
|
case NFUNC_TOKEN:
|
|
return (0);
|
|
case ID_TOKEN:
|
|
idptr = tokenstr;
|
|
type = id_type(tokenstr, tokenlen);
|
|
if (type & (VAR_TYPE | FUNC_TYPE))
|
|
{
|
|
addr = id_addr(tokenstr, tokenlen);
|
|
if (scan() == SET_TOKEN)
|
|
{
|
|
if (type & VAR_TYPE)
|
|
{
|
|
parse_expr();
|
|
if (type & LOCAL_TYPE)
|
|
(type & BYTE_TYPE) ? emit_slb(addr) : emit_slw(addr);
|
|
else
|
|
(type & BYTE_TYPE) ? emit_sab(addr) : emit_saw(addr);
|
|
break;
|
|
}
|
|
}
|
|
else if (type & VAR_TYPE && scantoken == SETLIST_TOKEN)
|
|
{
|
|
parse_setlist(addr, type);
|
|
break;
|
|
}
|
|
else if ((scantoken == EOL_TOKEN) && (type & FUNC_TYPE))
|
|
{
|
|
emit_call(addr);
|
|
break;
|
|
}
|
|
}
|
|
tokenstr = idptr;
|
|
default:
|
|
scan_rewind(tokenstr);
|
|
if ((type = parse_value(0)) != 0)
|
|
{
|
|
if (scantoken == SET_TOKEN)
|
|
{
|
|
parse_expr();
|
|
if (type & LOCAL_TYPE)
|
|
(type & (BYTE_TYPE | BPTR_TYPE)) ? emit_sb() : emit_sw();
|
|
else
|
|
(type & (BYTE_TYPE | BPTR_TYPE)) ? emit_sb() : emit_sw();
|
|
}
|
|
else if (scantoken == SETLIST_TOKEN)
|
|
{
|
|
parse_setlist(0, type);
|
|
}
|
|
else
|
|
{
|
|
if (type & BPTR_TYPE)
|
|
emit_lb();
|
|
else if (type & WPTR_TYPE)
|
|
emit_lw();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parse_error("Syntax error");
|
|
return (0);
|
|
}
|
|
}
|
|
if (scan() != EOL_TOKEN && scantoken != COMMENT_TOKEN)
|
|
{
|
|
parse_error("Extraneous characters");
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
int parse_var(int type)
|
|
{
|
|
char *idstr;
|
|
long constval;
|
|
int consttype, constsize, arraysize, idlen = 0;
|
|
long size = 1;
|
|
|
|
if (scan() == ID_TOKEN)
|
|
{
|
|
idstr = tokenstr;
|
|
idlen = tokenlen;
|
|
if (scan() == OPEN_BRACKET_TOKEN)
|
|
{
|
|
size = 0;
|
|
parse_constexpr(&size, &constsize);
|
|
if (scantoken != CLOSE_BRACKET_TOKEN)
|
|
{
|
|
parse_error("Missing closing bracket");
|
|
return (0);
|
|
}
|
|
scan();
|
|
}
|
|
}
|
|
if (type == WORD_TYPE)
|
|
size *= 2;
|
|
if (scantoken == SET_TOKEN)
|
|
{
|
|
if (infunc)
|
|
{
|
|
parse_error("Cannot initiallize local variables");
|
|
return (0);
|
|
}
|
|
if (idlen)
|
|
idglobal_add(idstr, idlen, type, 0);
|
|
if ((consttype = parse_constexpr(&constval, &constsize)))
|
|
{
|
|
/*
|
|
* Variable initialization.
|
|
*/
|
|
arraysize = emit_data(type, consttype, constval, constsize);
|
|
while (scantoken == COMMA_TOKEN)
|
|
{
|
|
if ((consttype = parse_constexpr(&constval, &constsize)))
|
|
arraysize += emit_data(type, consttype, constval, constsize);
|
|
else
|
|
{
|
|
parse_error("Bad array declaration");
|
|
return (0);
|
|
}
|
|
}
|
|
idglobal_size(PTR_TYPE, size, arraysize);
|
|
}
|
|
else
|
|
{
|
|
parse_error("Bad variable initializer");
|
|
return (0);
|
|
}
|
|
}
|
|
else if (idlen)
|
|
{
|
|
infunc ? idlocal_add(idstr, idlen, type, size)
|
|
: idglobal_add(idstr, idlen, type, size);
|
|
}
|
|
return (1);
|
|
}
|
|
int parse_vars(void)
|
|
{
|
|
long value;
|
|
int type, idlen, size;
|
|
char *idstr;
|
|
|
|
switch (scantoken)
|
|
{
|
|
case CONST_TOKEN:
|
|
if (scan() != ID_TOKEN)
|
|
{
|
|
parse_error("Missing variable");
|
|
return (0);
|
|
}
|
|
idstr = tokenstr;
|
|
idlen = tokenlen;
|
|
if (scan() != SET_TOKEN)
|
|
{
|
|
parse_error("Bad LValue");
|
|
return (0);
|
|
}
|
|
if (!parse_constexpr(&value, &size))
|
|
{
|
|
parse_error("Bad constant");
|
|
return (0);
|
|
}
|
|
idconst_add(idstr, idlen, value);
|
|
break;
|
|
case BYTE_TOKEN:
|
|
case WORD_TOKEN:
|
|
type = (scantoken == BYTE_TOKEN) ? BYTE_TYPE : WORD_TYPE;
|
|
if (!parse_var(type))
|
|
return (0);
|
|
while (scantoken == COMMA_TOKEN)
|
|
{
|
|
if (!parse_var(type))
|
|
return (0);
|
|
}
|
|
break;
|
|
case FUNC_TOKEN:
|
|
if (scan() == ID_TOKEN)
|
|
{
|
|
idstr = tokenstr;
|
|
idlen = tokenlen;
|
|
idfunc_add(tokenstr, tokenlen, tag_new());
|
|
while (scan() == COMMA_TOKEN)
|
|
{
|
|
if (scan() == ID_TOKEN)
|
|
{
|
|
idstr = tokenstr;
|
|
idlen = tokenlen;
|
|
idfunc_add(tokenstr, tokenlen, tag_new());
|
|
}
|
|
else
|
|
{
|
|
parse_error("Bad function pre-declaration");
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parse_error("Bad function pre-declaration");
|
|
return (0);
|
|
}
|
|
case EOL_TOKEN:
|
|
case COMMENT_TOKEN:
|
|
return (1);
|
|
default:
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
int parse_func(void)
|
|
{
|
|
char c;
|
|
int func_tag, defopt, cfnparms;
|
|
optimization(0);
|
|
switch (scantoken)
|
|
{
|
|
case IFUNC_TOKEN:
|
|
case NFUNC_TOKEN:
|
|
defopt = scantoken - IFUNC_TOKEN;
|
|
if (scan() != ID_TOKEN)
|
|
{
|
|
parse_error("Missing function name");
|
|
return (0);
|
|
}
|
|
cfnparms = 0;
|
|
infunc = 1;
|
|
if (idglobal_lookup(tokenstr, tokenlen) >= 0)
|
|
func_tag = id_addr(tokenstr, tokenlen);
|
|
else
|
|
{
|
|
func_tag = tag_new();
|
|
idfunc_add(tokenstr, tokenlen, func_tag);
|
|
}
|
|
c = tokenstr[tokenlen];
|
|
tokenstr[tokenlen] = '\0';
|
|
emit_idfunc(func_tag, tokenstr);
|
|
tokenstr[tokenlen] = c;
|
|
retfunc_tag = tag_new();
|
|
idlocal_reset();
|
|
localsize = 2;
|
|
if (scan() == OPEN_PAREN_TOKEN)
|
|
{
|
|
do
|
|
{
|
|
if (scan() == ID_TOKEN)
|
|
{
|
|
cfnparms++;
|
|
idlocal_add(tokenstr, tokenlen, WORD_TYPE, 2);
|
|
scan();
|
|
}
|
|
} while (scantoken == COMMA_TOKEN);
|
|
if (scantoken != CLOSE_PAREN_TOKEN)
|
|
{
|
|
parse_error("Bad function parameter list");
|
|
return (0);
|
|
}
|
|
scan();
|
|
}
|
|
while (parse_vars()) next_line();
|
|
emit_def(defopt);
|
|
emit_enter(localsize, cfnparms);
|
|
prevstmnt = 0;
|
|
while (parse_stmnt()) next_line();
|
|
infunc = 0;
|
|
if (scantoken != END_TOKEN)
|
|
{
|
|
parse_error("Syntax error");
|
|
return (0);
|
|
}
|
|
if (scan() != EOL_TOKEN && scantoken != COMMENT_TOKEN)
|
|
{
|
|
parse_error("Extraneous characters");
|
|
return (0);
|
|
}
|
|
if (prevstmnt != RETURN_TOKEN)
|
|
emit_leave(localsize);
|
|
return (1);
|
|
case ASM_TOKEN:
|
|
if (scan() != ID_TOKEN)
|
|
{
|
|
parse_error("Missing function name");
|
|
return (0);
|
|
}
|
|
cfnparms = 0;
|
|
infunc = 1;
|
|
if (idglobal_lookup(tokenstr, tokenlen) >= 0)
|
|
func_tag = id_addr(tokenstr, tokenlen);
|
|
else
|
|
{
|
|
func_tag = tag_new();
|
|
idfunc_add(tokenstr, tokenlen, func_tag);
|
|
}
|
|
c = tokenstr[tokenlen];
|
|
tokenstr[tokenlen] = '\0';
|
|
emit_idfunc(func_tag, tokenstr);
|
|
tokenstr[tokenlen] = c;
|
|
retfunc_tag = tag_new();
|
|
idlocal_reset();
|
|
localsize = 2;
|
|
if (scan() == OPEN_PAREN_TOKEN)
|
|
{
|
|
do
|
|
{
|
|
if (scan() == ID_TOKEN)
|
|
{
|
|
cfnparms++;
|
|
idlocal_add(tokenstr, tokenlen, WORD_TYPE, 2);
|
|
scan();
|
|
}
|
|
} while (scantoken == COMMA_TOKEN);
|
|
if (scantoken != CLOSE_PAREN_TOKEN)
|
|
{
|
|
parse_error("Bad function parameter list");
|
|
return (0);
|
|
}
|
|
scan();
|
|
}
|
|
while (parse_vars()) next_line();
|
|
emit_def(1);
|
|
emit_enter(localsize, cfnparms);
|
|
prevstmnt = 0;
|
|
do
|
|
{
|
|
if (scantoken == EOL_TOKEN)
|
|
next_line();
|
|
else if (scantoken != END_TOKEN)
|
|
{
|
|
emit_asm(inputline);
|
|
next_line();
|
|
}
|
|
} while (scantoken != END_TOKEN);
|
|
emit_leave(localsize);
|
|
infunc = 0;
|
|
return (1);
|
|
case EOL_TOKEN:
|
|
case COMMENT_TOKEN:
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
int parse_module(void)
|
|
{
|
|
if (next_line())
|
|
{
|
|
while (parse_vars()) next_line();
|
|
while (parse_func()) next_line();
|
|
if (scantoken != DONE_TOKEN && scantoken != EOF_TOKEN)
|
|
{
|
|
optimization(0);
|
|
emit_start();
|
|
prevstmnt = 0;
|
|
while (parse_stmnt()) next_line();
|
|
if (scantoken != DONE_TOKEN)
|
|
parse_error("Missing DONE statement");
|
|
emit_ret();
|
|
}
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
int main(int argc, char **argv)
|
|
{
|
|
int optc = 1;
|
|
if (argc > optc && argv[optc][0] == '-')
|
|
{
|
|
emit_flags(EDASM);
|
|
optc++;
|
|
}
|
|
if (argc > optc && argv[optc][0] == '$')
|
|
optc++;
|
|
else
|
|
emit_header();
|
|
if (parse_module())
|
|
{
|
|
fprintf(stderr, "Compilation complete.\n");
|
|
emit_trailer();
|
|
}
|
|
return (0);
|
|
}
|