Asm

/* $Id$ */
/* Copyright (c) 2011-2022 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Devel Asm */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <System.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "common.h"
#include "token.h"
#include "parser.h"
#ifndef PROGNAME_ASM
# define PROGNAME_ASM "asm"
#endif
/* private */
/* types */
typedef struct _State
{
Cpp * cpp;
Token * token;
unsigned int error_cnt;
unsigned int warning_cnt;
AsmCode * code;
/* directive */
char * directive;
char ** args;
size_t args_cnt;
/* instruction */
AsmArchInstructionCall call;
/* operands */
int address;
int negative;
} State;
/* prototypes */
static int _parser_check(State * state, TokenCode code);
static int _parser_defines(State * state, AsmPrefs * ap);
static int _parser_error(State * state, char const * format, ...);
static int _parser_is_code(State * state, TokenCode code);
static int _parser_in_set(State * state, TokenSet set);
static int _parser_recover(State * state, TokenCode code, char const * name);
static int _parser_scan(State * state);
static int _parser_warning(State * state, char const * format, ...);
/* grammar */
static int _program(State * state);
static int _directive(State * state);
static int _directive_arg(State * state);
static int _directive_args(State * state);
static int _directive_name(State * state);
static int _section_args(State * state);
static int _section_name(State * state);
static int _newline(State * state);
static int _space(State * state);
static int _statement(State * state);
static int _function(State * state);
static int _function_name(State * state);
static int _instruction(State * state);
static int _instruction_name(State * state);
static int _operand_list(State * state);
static int _operand(State * state);
static int _prefix(State * state);
static int _value(State * state);
static int _symbol(State * state);
static int _register(State * state);
static int _immediate(State * state);
static int _address(State * state);
static int _sign(State * state);
static int _offset(State * state);
/* functions */
/* parser_recover */
static int _parser_recover(State * state, TokenCode code, char const * name)
{
while(!_parser_is_code(state, code))
_parser_scan(state);
return _parser_error(state, "%s%s", name, " expected");
}
/* parser_scan */
static int _scan_skip_meta(State * state);
static int _parser_scan(State * state)
{
int ret;
TokenCode code;
char const * string;
if(state->token != NULL)
token_delete(state->token);
if((ret = _scan_skip_meta(state)) != 0
|| state->token == NULL)
return ret;
code = token_get_code(state->token);
string = token_get_string(state->token);
if(code == AS_CODE_WORD)
{
if(string != NULL && string[0] == '$')
token_set_code(state->token, AS_CODE_IMMEDIATE);
}
else if(code == AS_CODE_OPERATOR_MODULO)
{
/* FIXME ugly workaround */
if((ret = _scan_skip_meta(state)) != 0)
return ret;
if(_parser_is_code(state, AS_CODE_WORD))
token_set_code(state->token, AS_CODE_REGISTER);
}
return 0;
}
static int _scan_skip_meta(State * state)
{
int ret = 0;
TokenCode code;
while(cpp_scan(state->cpp, &state->token) == 0)
{
if(state->token == NULL)
return ret;
if((code = token_get_code(state->token)) < AS_CODE_META_FIRST
|| code > AS_CODE_META_LAST)
return ret;
if(code == AS_CODE_META_ERROR)
ret |= _parser_error(state, "%s", token_get_string(
state->token));
else if(code == AS_CODE_META_WARNING)
_parser_warning(state, "%s", token_get_string(
state->token));
token_delete(state->token);
}
return 1;
}
/* parser_check */
static int _parser_check(State * state, TokenCode code)
{
int ret = 0;
if(!_parser_is_code(state, code))
/* FIXME convert the code to a string */
ret = _parser_error(state, "%s%u", "Parse error: expected ",
code);
ret |= _parser_scan(state);
return ret;
}
/* parser_defines */
static int _parser_defines(State * state, AsmPrefs * ap)
{
int ret = 0;
char const * p;
char * q;
size_t len;
size_t i;
if((p = asmcode_get_arch(state->code)) != NULL
&& (len = strlen(p)) > 0)
{
if((q = malloc(len + 5)) == NULL)
return -error_set_code(1, "%s", strerror(errno));
snprintf(q, len + 5, "__%s__", p);
ret |= cpp_define_add(state->cpp, q, NULL);
free(q);
}
if(ret == 0 && ap != NULL)
for(i = 0; i < ap->defines_cnt; i++)
ret |= cpp_define_add(state->cpp, ap->defines[i].name,
ap->defines[i].value);
return ret;
}
/* parser_is_code */
static int _parser_is_code(State * state, TokenCode code)
{
if(state->token == NULL)
return 0;
return token_get_code(state->token) == code;
}
/* parser_in_set */
static int _parser_in_set(State * state, TokenSet set)
{
if(state->token == NULL)
return 0;
return token_in_set(state->token, set);
}
/* parser_error */
static int _parser_error(State * state, char const * format, ...)
{
va_list ap;
fputs(PROGNAME_ASM ": ", stderr);
if(state->cpp != NULL && state->token != NULL)
/* FIXME will be wrong when string-based input is supported */
fprintf(stderr, "%s%s%u: ", cpp_get_filename(state->cpp),
", line ", token_get_line(state->token));
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputc('\n', stderr);
return ++state->error_cnt;
}
/* parser_warning */
static int _parser_warning(State * state, char const * format, ...)
{
va_list ap;
fputs(PROGNAME_ASM ": ", stderr);
if(state->cpp != NULL && state->token != NULL)
/* FIXME will be wrong when string-based input is supported */
fprintf(stderr, "%s%s%u: ", cpp_get_filename(state->cpp),
", line ", token_get_line(state->token));
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputc('\n', stderr);
return ++state->warning_cnt;
}
/* protected */
/* functions */
/* parser */
int parser(AsmPrefs * ap, AsmCode * code, char const * infile)
{
CppPrefs prefs;
State state;
memset(&prefs, 0, sizeof(prefs));
prefs.filename = infile;
prefs.filters = CPP_FILTER_COMMENT;
memset(&state, 0, sizeof(state));
state.code = code;
if((state.cpp = cpp_new(&prefs)) == NULL)
return -1;
if(_parser_defines(&state, ap) != 0)
{
cpp_delete(state.cpp);
return -1;
}
if(_parser_scan(&state) != 0)
{
cpp_delete(state.cpp);
return _parser_error(&state, "%s", error_get(NULL));
}
if(_program(&state) != 0)
error_set_code(1, "%s%s%u%s%u%s", infile,
": Compilation failed with ", state.error_cnt,
" error(s) and ", state.warning_cnt,
" warning(s)");
if(state.token != NULL)
token_delete(state.token);
return state.error_cnt;
}
/* parser_string */
int parser_string(AsmPrefs * ap, AsmCode * code, char const * string)
{
CppPrefs prefs;
State state;
size_t i;
memset(&prefs, 0, sizeof(prefs));
#if 0
prefs.filename = infile;
#endif
prefs.filters = CPP_FILTER_COMMENT;
memset(&state, 0, sizeof(state));
state.code = code;
if((state.cpp = cpp_new_string(&prefs, string)) == NULL)
return -1;
if(ap != NULL)
for(i = 0; i < ap->defines_cnt; i++)
if(cpp_define_add(state.cpp, ap->defines[i].name,
ap->defines[i].value) != 0)
{
cpp_delete(state.cpp);
return -1;
}
if(_parser_scan(&state) != 0)
return _parser_error(&state, "%s", error_get(NULL));
if(_program(&state) != 0)
error_set_code(1, "%s%u%s%u%s", "Compilation failed with ",
state.error_cnt, " error(s) and ",
state.warning_cnt, " warning(s)");
if(state.token != NULL)
token_delete(state.token);
return state.error_cnt;
}
/* grammar */
/* program */
static int _program(State * state)
/* { directive | statement } */
{
int ret = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
for(;;)
{
if(_parser_in_set(state, TS_DIRECTIVE))
/* directive */
ret |= _directive(state);
else if(_parser_in_set(state, TS_STATEMENT))
/* statement */
ret |= _statement(state);
else
/* end of input (FIXME really check) */
break;
}
return ret;
}
/* directive */
static int _directive(State * state)
/* "." directive_name [ space [ directive_args ] ] newline */
{
int ret;
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* "." */
ret = _parser_check(state, AS_CODE_OPERATOR_DOT);
/* directive_name */
if(!_parser_in_set(state, TS_DIRECTIVE_NAME))
return _parser_recover(state, AS_CODE_NEWLINE,
"Directive name");
ret |= _directive_name(state);
/* [ space [ directive_args ] ] */
if(_parser_in_set(state, TS_SPACE))
{
ret |= _space(state);
if(_parser_in_set(state, TS_DIRECTIVE_ARGS))
{
/* XXX hack to allow dots in section names */
if(string_compare(state->directive, "section") == 0)
ret |= _section_args(state);
else
ret |= _directive_args(state);
}
}
/* execute the directive */
if(string_compare(state->directive, "section") == 0)
{
if(state->args_cnt != 1)
ret |= _parser_error(state, "%s",
"Sections expect a name");
else if(asmcode_section(state->code, state->args[0]) != 0)
ret |= _parser_error(state, "%s", error_get(NULL));
}
else if(asmcode_directive(state->code, state->directive,
(char const **)state->args,
state->args_cnt) != 0)
ret |= _parser_error(state, "%s", error_get(NULL));
free(state->directive);
state->directive = NULL;
for(i = 0; i < state->args_cnt; i++)
free(state->args[i]);
free(state->args);
state->args = NULL;
state->args_cnt = 0;
/* newline */
if(!_parser_in_set(state, TS_NEWLINE))
return _parser_recover(state, AS_CODE_NEWLINE, "New line");
ret |= _newline(state);
return ret;
}
/* directive_arg */
static int _directive_arg(State * state)
/* WORD | NUMBER | "." */
{
char const * string;
char ** p;
if(state->token == NULL
|| (string = token_get_string(state->token)) == NULL
|| strlen(token_get_string(state->token)) == 0)
return error_set_code(1, "%s",
"Empty directive arguments are not allowed");
if((p = realloc(state->args, sizeof(*p) * (state->args_cnt + 1)))
== NULL)
return error_set_code(1, "%s", strerror(errno));
state->args = p;
p = &state->args[state->args_cnt];
if((*p = strdup(string)) == NULL)
return error_set_code(1, "%s", strerror(errno));
state->args_cnt++;
return _parser_scan(state);
}
/* directive_args */
static int _directive_args(State * state)
/* directive_arg { space [ directive_arg ] } */
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
ret = _directive_arg(state);
while(_parser_in_set(state, TS_SPACE))
{
/* space */
ret |= _space(state);
/* [ directive_arg ] */
if(_parser_in_set(state, TS_DIRECTIVE_ARGS))
ret |= _directive_arg(state);
}
return ret;
}
/* directive_name */
static int _directive_name(State * state)
/* WORD */
{
int ret = 0;
char const * string;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(state->token == NULL
|| (string = token_get_string(state->token)) == NULL
|| strlen(token_get_string(state->token)) == 0)
return error_set_code(1, "%s",
"Directives with empty names are not allowed");
if((state->directive = strdup(string)) == NULL)
return ret | error_set_code(1, "%s", strerror(errno));
ret |= _parser_scan(state);
#ifdef DEBUG
fprintf(stderr, "%s\"%s\"\n", "DEBUG: directive ", state->directive);
#endif
return ret;
}
/* section_args */
static int _section_args(State * state)
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
ret = _section_name(state);
while(_parser_in_set(state, TS_DIRECTIVE_ARGS))
/* section_name */
ret |= _section_name(state);
return ret;
}
/* section_name */
static int _section_name(State * state)
/* WORD | NUMBER | "." */
{
char const * string;
char * p;
size_t len;
if(state->token == NULL
|| (string = token_get_string(state->token)) == NULL
|| (len = strlen(token_get_string(state->token))) == 0)
return error_set_code(1, "%s",
"Empty section arguments are not allowed");
if(state->args == NULL)
{
if((state->args = malloc(sizeof(string))) == NULL
|| (state->args[0] = strdup(string)) == NULL)
return error_set_code(1, "%s", strerror(errno));
state->args_cnt = 1;
return _parser_scan(state);
}
if((p = realloc(state->args[0], strlen(state->args[0]) + len + 1))
== NULL)
return error_set_code(1, "%s", strerror(errno));
state->args[0] = p;
strcat(state->args[0], string);
return _parser_scan(state);
}
/* newline */
static int _newline(State * state)
/* [ space ] NEWLINE */
{
int ret = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* [ space ] */
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
/* NEWLINE */
ret |= _parser_check(state, AS_CODE_NEWLINE);
return ret;
}
/* space */
static int _space(State * state)
/* SPACE { SPACE } */
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
ret = _parser_check(state, AS_CODE_WHITESPACE);
while(_parser_is_code(state, AS_CODE_WHITESPACE))
ret |= _parser_scan(state);
return ret;
}
/* statement */
static int _statement(State * state)
/* ( function | [ space [ instruction ] ] ) newline */
{
int ret = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(_parser_in_set(state, TS_FUNCTION))
/* function */
ret = _function(state);
else if(_parser_in_set(state, TS_SPACE))
{
/* space [ instruction ] */
ret = _space(state);
if(_parser_in_set(state, TS_INSTRUCTION))
ret |= _instruction(state);
}
else if(!_parser_in_set(state, TS_NEWLINE))
return _parser_recover(state, AS_CODE_NEWLINE, "Statement");
ret |= _newline(state);
return ret;
}
/* function */
static int _function(State * state)
/* function_name ":" */
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
ret = _function_name(state);
ret |= _parser_check(state, AS_CODE_OPERATOR_COLON);
return ret;
}
/* function_name */
static int _function_name(State * state)
/* WORD */
{
int ret;
char const * string;
char * function = NULL;
if((string = token_get_string(state->token)) == NULL)
return _parser_error(state, "%s", "Empty function names are"
" not allowed");
if((function = strdup(string)) == NULL)
return error_set_code(1, "%s", strerror(errno));
ret = _parser_check(state, AS_CODE_WORD);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s \"%s\"\n", "function", function);
#endif
if(asmcode_function(state->code, function) != 0)
ret |= _parser_error(state, "%s", error_get(NULL));
free(function);
return ret;
}
/* instruction */
static int _instruction(State * state)
/* [ prefix space ] instruction_name [ space [ operand_list ] ] */
{
int ret = 0;
char const * string;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* reset the current instruction */
memset(&state->call, 0, sizeof(state->call));
/* [ prefix space ] */
if(_parser_in_set(state, TS_PREFIX)
&& (string = token_get_string(state->token)) != NULL
&& asmcode_get_arch_prefix_by_name(state->code,
string) != NULL)
{
ret |= _prefix(state);
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
else
ret |= _parser_error(state, "%s", "Expected a space");
}
/* instruction_name */
ret |= _instruction_name(state);
if(_parser_in_set(state, TS_SPACE))
{
/* [ space */
ret |= _space(state);
/* [ operand_list ] ] */
if(_parser_in_set(state, TS_OPERAND_LIST))
ret |= _operand_list(state);
}
/* call the current instruction */
if(state->call.name != NULL)
{
if(asmcode_instruction(state->code, &state->call) != 0)
ret |= _parser_error(state, "%s", error_get(NULL));
}
/* FIXME memory leak (register names...) */
memset(&state->call, 0, sizeof(state->call));
return ret;
}
/* instruction_name */
static int _instruction_name(State * state)
/* WORD */
{
char const * string;
AsmArchInstruction const * ai;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if((string = token_get_string(state->token)) == NULL)
{
_parser_scan(state);
return _parser_error(state, "%s", "Empty instructions are not"
" allowed");
}
if((ai = asmcode_get_arch_instruction_by_name(state->code, string))
== NULL)
return _parser_error(state, "%s: %s", string,
"Unknown instruction");
state->call.name = ai->name;
/* optimized free(state->operator); out */
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %s \"%s\"\n", __func__, "instruction ",
string);
#endif
return _parser_scan(state);
}
/* operand_list */
static int _operand_list(State * state)
/* operand [ space ] { "," [ space ] operand [ space ] } */
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
ret = _operand(state);
state->call.operands_cnt++;
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
while(_parser_is_code(state, AS_CODE_COMMA))
{
/* "," */
ret |= _parser_scan(state);
/* [ space ] */
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
/* operand */
ret |= _operand(state);
/* [ space ] */
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
state->call.operands_cnt++;
}
return ret;
}
/* operand */
static int _operand(State * state)
/* value | address */
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(_parser_in_set(state, TS_VALUE))
return _value(state);
else if(_parser_in_set(state, TS_ADDRESS))
return _address(state);
return _parser_error(state, "%s", "Expected value or address");
}
/* prefix */
static int _prefix(State * state)
/* WORD */
{
AsmArchPrefix const * prefix;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* this is not supposed to fail */
prefix = asmcode_get_arch_prefix_by_name(state->code,
token_get_string(state->token));
state->call.prefix = prefix->name;
return _parser_scan(state);
}
/* value */
static int _value(State * state)
/* symbol | register | immediate */
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(_parser_in_set(state, TS_SYMBOL))
return _symbol(state);
else if(_parser_in_set(state, TS_REGISTER))
return _register(state);
else if(_parser_in_set(state, TS_IMMEDIATE))
return _immediate(state);
return _parser_error(state, "%s", "Expected symbol, register or"
" immediate value");
}
/* symbol */
static int _symbol(State * state)
/* WORD */
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* FIXME really implement */
return _parser_scan(state);
}
/* register */
static int _register(State * state)
/* "%" WORD */
{
AsmArchOperand * p;
char const * string;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%%%s) %d\n", __func__, token_get_string(
state->token), state->address);
#endif
p = &state->call.operands[state->call.operands_cnt];
string = token_get_string(state->token); /* XXX may be null or empty? */
if(state->address == 2)
{
p->definition = AO_DREGISTER2(0, 0, 0, 0);
p->value.dregister2.name = p->value.dregister.name;
p->value.dregister2.name2 = strdup(string);
}
else if(state->address == 1)
{
p->definition = AO_DREGISTER(0, 0, 0, 0);
p->value.dregister.name = strdup(string);
}
else if(state->address == 0)
{
p->definition = AO_REGISTER(0, 0, 0);
p->value._register.name = strdup(string);
}
return _parser_scan(state);
}
/* immediate */
static int _immediate(State * state)
/* [ sign ] "$" NUMBER */
{
int ret = 0;
AsmArchOperand * p;
char const * string;
uint64_t value = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* [ sign ] */
if(_parser_in_set(state, TS_SIGN))
ret |= _sign(state);
else
state->negative = 0;
/* "$" NUMBER */
p = &state->call.operands[state->call.operands_cnt];
if((string = token_get_string(state->token)) != NULL
|| strlen(string) == 0)
value = strtoul(string + 1, NULL, 0);
else
ret = _parser_error(state, "%s", "Empty values are not"
" allowed");
if(state->address > 0)
p->value.dregister.offset = value;
else
{
p->value.immediate.value = strtoul(string + 1, NULL, 0);
p->definition = AO_IMMEDIATE(0, 0, 0);
p->value.immediate.negative = state->negative;
}
return ret | _parser_scan(state);
}
/* address */
static int _address(State * state)
/* "[" [ space ] [ sign [ space ] ] value [ space ] [ offset [ space ] ] "]" */
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
state->address = 1;
/* "[" */
ret = _parser_scan(state);
/* [ space ] */
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
/* [ sign [ space ] ] */
if(_parser_in_set(state, TS_SIGN))
{
ret |= _sign(state);
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
}
else
state->negative = 0;
/* value */
if(_parser_in_set(state, TS_VALUE))
ret |= _value(state);
else
ret |= _parser_error(state, "%s", "Expected value");
/* [ space ] */
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
/* [ offset [ space ] ] */
if(_parser_in_set(state, TS_OFFSET))
{
state->address = 2;
ret |= _offset(state);
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
}
state->address = 0;
/* "]" */
return ret | _parser_check(state, AS_CODE_OPERATOR_RBRACKET);
}
/* offset */
static int _offset(State * state)
/* sign [ space ] value */
{
int ret;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
/* sign */
ret = _sign(state);
/* [ space ] */
if(_parser_in_set(state, TS_SPACE))
ret |= _space(state);
/* value */
if(_parser_in_set(state, TS_VALUE))
ret |= _value(state);
else
ret |= _parser_error(state, "%s", "Expected a value");
return ret;
}
/* sign */
static int _sign(State * state)
/* ( "+" | "-" ) */
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
state->negative = (token_get_code(state->token)
== AS_CODE_OPERATOR_MINUS) ? 1 : 0;
return _parser_scan(state);
}