#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <orc/orc.h>
#include <orc/orcparse.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/**
* SECTION:orcparse
* @title: Parser
* @short_description: Parse Orc source code
*/
typedef struct _OrcParser OrcParser;
struct _OrcParser {
const char *code;
int code_length;
const char *p;
int line_number;
char *line;
int creg_index;
OrcOpcodeSet *opcode_set;
OrcProgram *program;
OrcProgram *error_program;
OrcProgram **programs;
int n_programs;
int n_programs_alloc;
char *log;
int log_size;
int log_alloc;
};
static void orc_parse_get_line (OrcParser *parser);
static OrcStaticOpcode * get_opcode (OrcParser *parser, const char *opcode);
static void orc_parse_log (OrcParser *parser, const char *format, ...);
static int opcode_n_args (OrcStaticOpcode *opcode);
static int opcode_arg_size (OrcStaticOpcode *opcode, int arg);
static void orc_parse_sanity_check (OrcParser *parser, OrcProgram *program);
int
orc_parse (const char *code, OrcProgram ***programs)
{
return orc_parse_full (code, programs, NULL);
}
int
orc_parse_full (const char *code, OrcProgram ***programs, char **log)
{
OrcParser _parser;
OrcParser *parser = &_parser;
char *init_function = NULL;
memset (parser, 0, sizeof(*parser));
parser->code = code;
parser->code_length = strlen (code);
parser->line_number = 0;
parser->p = code;
parser->opcode_set = orc_opcode_set_get ("sys");
parser->log = malloc(100);
parser->log_alloc = 100;
parser->log_size = 0;
parser->log[0] = 0;
while (parser->p[0] != 0) {
char *p;
char *end;
char *token[10];
int n_tokens;
orc_parse_get_line (parser);
if (parser->program) orc_program_set_line (parser->program, parser->line_number);
p = parser->line;
end = p + strlen (p);
/* printf("%d: %s\n", parser->line_number, parser->line); */
while (p[0] == ' ' || p[0] == '\t') p++;
if (p[0] == 0) {
continue;
}
if (p[0] == '#') {
/* printf("comment: %s\n", p+1); */
continue;
}
n_tokens = 0;
while (p < end) {
while (p[0] != 0 && (p[0] == ' ' || p[0] == '\t')) p++;
if (p[0] == 0 || p[0] == '#') break;
token[n_tokens] = p;
while (p[0] != 0 && p[0] != ' ' && p[0] != '\t' && p[0] != ',') p++;
n_tokens++;
p[0] = 0;
p++;
}
if (n_tokens == 0) {
continue;
}
{
int i;
for(i=0;i<n_tokens;i++){
/* printf("'%s' ", token[i]); */
}
/* printf("\n"); */
}
if (token[0][0] == '.') {
if (strcmp (token[0], ".function") == 0) {
if (parser->program) {
orc_parse_sanity_check (parser, parser->program);
}
parser->program = orc_program_new ();
orc_program_set_name (parser->program, token[1]);
if (parser->n_programs == parser->n_programs_alloc) {
parser->n_programs_alloc += 32;
parser->programs = realloc (parser->programs,
sizeof(OrcProgram *)*parser->n_programs_alloc);
}
parser->programs[parser->n_programs] = parser->program;
parser->n_programs++;
parser->creg_index = 1;
} else if (strcmp (token[0], ".backup") == 0) {
if (n_tokens < 2) {
orc_parse_log (parser, "error: line %d: .backup without function name\n",
parser->line_number);
} else {
orc_program_set_backup_name (parser->program, token[1]);
}
} else if (strcmp (token[0], ".init") == 0) {
free (init_function);
init_function = NULL;
if (n_tokens < 2) {
orc_parse_log (parser, "error: line %d: .init without function name\n",
parser->line_number);
} else {
init_function = strdup (token[1]);
}
} else if (strcmp (token[0], ".flags") == 0) {
int i;
for(i=1;i<n_tokens;i++){
if (!strcmp (token[i], "2d")) {
orc_program_set_2d (parser->program);
}
}
} else if (strcmp (token[0], ".n") == 0) {
int i;
for(i=1;i<n_tokens;i++){
if (strcmp (token[i], "mult") == 0) {
if (i == n_tokens - 1) {
orc_parse_log (parser, "error: line %d: .n mult requires multiple value\n",
parser->line_number);
} else {
orc_program_set_n_multiple (parser->program,
strtol (token[1], NULL, 0));
i++;
}
} else if (strcmp (token[i], "min") == 0) {
if (i == n_tokens - 1) {
orc_parse_log (parser, "error: line %d: .n min requires multiple value\n",
parser->line_number);
} else {
orc_program_set_n_minimum (parser->program,
strtol (token[1], NULL, 0));
i++;
}
} else if (strcmp (token[i], "max") == 0) {
if (i == n_tokens - 1) {
orc_parse_log (parser, "error: line %d: .n max requires multiple value\n",
parser->line_number);
} else {
orc_program_set_n_maximum (parser->program,
strtol (token[1], NULL, 0));
i++;
}
} else if (i == n_tokens - 1) {
orc_program_set_constant_n (parser->program,
strtol (token[1], NULL, 0));
} else {
orc_parse_log (parser, "error: line %d: unknown .n token '%s'\n",
parser->line_number, token[i]);
}
}
} else if (strcmp (token[0], ".m") == 0) {
int size = strtol (token[1], NULL, 0);
orc_program_set_constant_m (parser->program, size);
} else if (strcmp (token[0], ".source") == 0) {
int size = strtol (token[1], NULL, 0);
int var;
int i;
var = orc_program_add_source (parser->program, size, token[2]);
for(i=3;i<n_tokens;i++){
if (strcmp (token[i], "align") == 0) {
if (i == n_tokens - 1) {
orc_parse_log (parser, "error: line %d: .source align requires alignment value\n",
parser->line_number);
} else {
int alignment = strtol (token[i+1], NULL, 0);
orc_program_set_var_alignment (parser->program, var, alignment);
i++;
}
} else if (i == n_tokens - 1) {
orc_program_set_type_name (parser->program, var, token[i]);
} else {
orc_parse_log (parser, "error: line %d: unknown .dest token '%s'\n",
parser->line_number, token[i]);
}
}
} else if (strcmp (token[0], ".dest") == 0) {
int size = strtol (token[1], NULL, 0);
int var;
int i;
var = orc_program_add_destination (parser->program, size, token[2]);
for(i=3;i<n_tokens;i++){
if (strcmp (token[i], "align") == 0) {
if (i == n_tokens - 1) {
orc_parse_log (parser, "error: line %d: .source align requires alignment value\n",
parser->line_number);
} else {
int alignment = strtol (token[i+1], NULL, 0);
orc_program_set_var_alignment (parser->program, var, alignment);
i++;
}
} else if (i == n_tokens - 1) {
orc_program_set_type_name (parser->program, var, token[i]);
} else {
orc_parse_log (parser, "error: line %d: unknown .source token '%s'\n",
parser->line_number, token[i]);
}
}
} else if (strcmp (token[0], ".accumulator") == 0) {
int size = strtol (token[1], NULL, 0);
int var;
var = orc_program_add_accumulator (parser->program, size, token[2]);
if (n_tokens > 3) {
orc_program_set_type_name (parser->program, var, token[3]);
}
} else if (strcmp (token[0], ".temp") == 0) {
int size = strtol (token[1], NULL, 0);
orc_program_add_temporary (parser->program, size, token[2]);
} else if (strcmp (token[0], ".param") == 0) {
int size = strtol (token[1], NULL, 0);
orc_program_add_parameter (parser->program, size, token[2]);
} else if (strcmp (token[0], ".longparam") == 0) {
int size = strtol (token[1], NULL, 0);
orc_program_add_parameter_int64 (parser->program, size, token[2]);
} else if (strcmp (token[0], ".const") == 0) {
int size = strtol (token[1], NULL, 0);
orc_program_add_constant_str (parser->program, size, token[3], token[2]);
} else if (strcmp (token[0], ".floatparam") == 0) {
int size = strtol (token[1], NULL, 0);
orc_program_add_parameter_float (parser->program, size, token[2]);
} else if (strcmp (token[0], ".doubleparam") == 0) {
int size = strtol (token[1], NULL, 0);
orc_program_add_parameter_double (parser->program, size, token[2]);
} else {
orc_parse_log (parser, "error: line %d: unknown directive: %s\n",
parser->line_number, token[0]);
}
} else {
OrcStaticOpcode *o;
unsigned int flags = 0;
int offset = 0;
if (strcmp (token[0], "x4") == 0) {
flags |= ORC_INSTRUCTION_FLAG_X4;
offset = 1;
} else if (strcmp (token[0], "x2") == 0) {
flags |= ORC_INSTRUCTION_FLAG_X2;
offset = 1;
}
o = get_opcode (parser, token[offset]);
if (o) {
int n_args = opcode_n_args (o);
int i, j;
char *args[4] = { NULL };
if (n_tokens != 1 + offset + n_args) {
orc_parse_log (parser, "error: line %d: too %s arguments for %s (expected %d)\n",
parser->line_number, (n_tokens < 1+offset+n_args) ? "few" : "many",
token[offset], n_args);
}
for(i=offset+1,j=0;i<n_tokens;i++,j++){
char *end;
double unused ORC_GNUC_UNUSED;
char varname[80];
args[j] = token[i];
unused = strtod (token[i], &end);
if (end != token[i]) {
int id;
/* make a unique name based on value and size */
snprintf (varname, sizeof (varname), "_%d.%s", opcode_arg_size(o, j), token[i]);
id = orc_program_add_constant_str (parser->program, opcode_arg_size(o, j),
token[i], varname);
/* it's possible we reused an existing variable, get its name so
* that we can refer to it in the opcode */
args[j] = parser->program->vars[id].name;
}
}
orc_program_append_str_2 (parser->program, token[offset], flags,
args[0], args[1], args[2], args[3]);
} else {
orc_parse_log (parser, "error: line %d: unknown opcode: %s\n",
parser->line_number,
token[offset]);
}
}
}
if (parser->program) {
orc_parse_sanity_check (parser, parser->program);
}
if (parser->line) free (parser->line);
if (log) {
*log = parser->log;
} else {
free (parser->log);
}
if (parser->programs && parser->programs[0]) {
parser->programs[0]->init_function = init_function;
} else {
free (init_function);
}
*programs = parser->programs;
return parser->n_programs;
}
static OrcStaticOpcode *
get_opcode (OrcParser *parser, const char *opcode)
{
int i;
for(i=0;i<parser->opcode_set->n_opcodes;i++){
if (strcmp (opcode, parser->opcode_set->opcodes[i].name) == 0) {
return parser->opcode_set->opcodes + i;
}
}
return NULL;
}
static int
opcode_n_args (OrcStaticOpcode *opcode)
{
int i;
int n = 0;
for(i=0;i<ORC_STATIC_OPCODE_N_DEST;i++){
if (opcode->dest_size[i] != 0) n++;
}
for(i=0;i<ORC_STATIC_OPCODE_N_SRC;i++){
if (opcode->src_size[i] != 0) n++;
}
return n;
}
static int
opcode_arg_size (OrcStaticOpcode *opcode, int arg)
{
int i;
for(i=0;i<ORC_STATIC_OPCODE_N_DEST;i++){
if (opcode->dest_size[i] != 0 && arg-- == 0)
return opcode->dest_size[i];
}
for(i=0;i<ORC_STATIC_OPCODE_N_SRC;i++){
if (opcode->src_size[i] != 0 && arg-- == 0)
return opcode->src_size[i];
}
return 0;
}
static void
orc_parse_log_valist (OrcParser *parser, const char *format, va_list args)
{
char s[100];
int len;
if (parser->error_program != parser->program) {
sprintf(s, "In function %s:\n", parser->program->name);
len = strlen(s);
if (parser->log_size + len + 1 >= parser->log_alloc) {
parser->log_alloc += 100;
parser->log = realloc (parser->log, parser->log_alloc);
}
strcpy (parser->log + parser->log_size, s);
parser->log_size += len;
parser->error_program = parser->program;
}
vsprintf(s, format, args);
len = strlen(s);
if (parser->log_size + len + 1 >= parser->log_alloc) {
parser->log_alloc += 100;
parser->log = realloc (parser->log, parser->log_alloc);
}
strcpy (parser->log + parser->log_size, s);
parser->log_size += len;
}
static void
orc_parse_log (OrcParser *parser, const char *format, ...)
{
va_list var_args;
va_start (var_args, format);
orc_parse_log_valist (parser, format, var_args);
va_end (var_args);
}
static void
orc_parse_get_line (OrcParser *parser)
{
const char *end;
int n;
if (parser->line) {
free (parser->line);
parser->line = NULL;
}
end = strchr (parser->p, '\n');
if (end == NULL) {
end = parser->code + parser->code_length;
}
n = end - parser->p;
parser->line = malloc (n + 1);
memcpy (parser->line, parser->p, n);
parser->line[n] = 0;
/* windows text files might have \r\n as line ending */
if (n > 0 && parser->line[n - 1] == '\r')
parser->line[n - 1] = 0;
parser->p = end;
if (parser->p[0] == '\n') {
parser->p++;
}
parser->line_number++;
}
static void
orc_parse_sanity_check (OrcParser *parser, OrcProgram *program)
{
int i;
int j;
for(i=0;i<=ORC_VAR_T15;i++) {
if (program->vars[i].size == 0) continue;
for(j=i+1;j<=ORC_VAR_T15;j++) {
if (program->vars[j].size == 0) continue;
if (strcmp (program->vars[i].name, program->vars[j].name) == 0) {
orc_parse_log (parser, "error: duplicate variable name: %s\n",
program->vars[i].name);
}
}
}
for(i=0;i<program->n_insns;i++){
OrcInstruction *insn = program->insns + i;
OrcStaticOpcode *opcode = insn->opcode;
for(j=0;j<ORC_STATIC_OPCODE_N_DEST;j++){
if (opcode->dest_size[j] == 0) continue;
if (program->vars[insn->dest_args[j]].used &&
program->vars[insn->dest_args[j]].vartype == ORC_VAR_TYPE_DEST) {
orc_parse_log (parser, "error: destination \"%s\" written multiple times\n",
program->vars[insn->dest_args[j]].name);
}
program->vars[insn->dest_args[j]].used = TRUE;
}
for(j=0;j<ORC_STATIC_OPCODE_N_SRC;j++){
if (opcode->src_size[j] == 0) continue;
if (program->vars[insn->src_args[j]].used &&
program->vars[insn->src_args[j]].vartype == ORC_VAR_TYPE_SRC) {
orc_parse_log (parser, "error: source \"%s\" read multiple times\n",
program->vars[insn->src_args[j]].name);
}
if (!program->vars[insn->src_args[j]].used &&
program->vars[insn->src_args[j]].vartype == ORC_VAR_TYPE_TEMP) {
orc_parse_log (parser, "error: variable \"%s\" used before being written\n",
program->vars[insn->src_args[j]].name);
}
}
}
}
const char *
orc_parse_get_init_function (OrcProgram *program)
{
return program->init_function;
}