Initial commit of files related to NED architecture.
[ned1] / nedasm / nedasm_parser.c
/*
* © 2018 Aaron Taylor <ataylor at subgeniuskitty dot com>
* See LICENSE.txt file for copyright and license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <inttypes.h>
#include "nedasm_structures.h"
#include "nedasm_misc.h"
#include "nedasm_parser_extensions.h"
void
seek_newline(uint32_t * linenum, FILE * input)
{
int c;
while ((c = fgetc(input)) != EOF) {
if (c == '\n') {
(*linenum)++;
return;
}
}
ungetc(EOF, input);
}
struct instruction *
parse_mnemonic(uint32_t * linenum, FILE * input, struct instruction * instructions)
{
int c;
char mnemonic[MAX_MNEMONIC_LEN];
size_t mnemonic_index = 0;
char data[MAX_DATA_LEN];
size_t data_index = 0;
char label[MAX_LABEL_LEN];
size_t label_index = 0;
while ((c = fgetc(input)) != EOF) {
if (c == '_' || c == '+' || c == '>') {
continue;
} else if (isupper(c)) {
if (mnemonic_index >= MAX_MNEMONIC_LEN-2) { /* -2 to reserve space for '\0'. */
fprintf(stderr, "ERROR: Violating MAX_MNEMONIC_LEN on line %d.\n", *linenum);
exit(EXIT_FAILURE);
}
mnemonic[mnemonic_index++] = c;
} else if (isdigit(c)) {
if (data_index >= MAX_DATA_LEN-2) { /* -2 to reserve space for '\0'. */
fprintf(stderr, "ERROR: Violating MAX_DATA_LEN on line %d.\n", *linenum);
exit(EXIT_FAILURE);
}
data[data_index++] = c;
} else if (islower(c)) {
if (label_index >= MAX_LABEL_LEN-2) { /* -2 to reserve space for '\0'. */
fprintf(stderr, "ERROR: Violating MAX_LABEL_LEN on line %d.\n", *linenum);
exit(EXIT_FAILURE);
}
label[label_index++] = c;
} else {
ungetc(c, input);
break;
}
}
mnemonic[mnemonic_index] = '\0';
data[data_index] = '\0';
label[label_index] = '\0';
if (c == EOF) ungetc(EOF, input);
struct instruction * new_instruction = create_instruction_struct();
new_instruction->linenum = *linenum;
if (strncmp(mnemonic, "WORD", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = WORD;
new_instruction->data = strtoimax(data, NULL, 10);
} else if (strncmp(mnemonic, "IM", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = IM;
new_instruction->data = strtoimax(data, NULL, 10);
} else if (strncmp(mnemonic, "LDSP", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = LDSP;
new_instruction->data = strtoimax(data, NULL, 10);
} else if (strncmp(mnemonic, "STSP", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = STSP;
new_instruction->data = strtoimax(data, NULL, 10);
} else if (strncmp(mnemonic, "MVSTCK", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = MVSTCK;
} else if (strncmp(mnemonic, "ADD", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = ADD;
} else if (strncmp(mnemonic, "XOR", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = XOR;
} else if (strncmp(mnemonic, "NOT", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = NOT;
} else if (strncmp(mnemonic, "OR", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = OR;
} else if (strncmp(mnemonic, "AND", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = AND;
} else if (strncmp(mnemonic, "BRZ", MAX_MNEMONIC_LEN) == 0) {
if (label_index) {
new_instruction->syllable = WORD;
new_instruction->target = malloc(label_index+1);
strncpy(new_instruction->target, label, label_index+1);
new_instruction->next = create_instruction_struct();
new_instruction->next->prev = new_instruction;
new_instruction->next->syllable = BRZ;
new_instruction->linenum = *linenum;
} else {
new_instruction->syllable = BRZ;
}
} else if (strncmp(mnemonic, "TEST", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = TEST;
} else if (strncmp(mnemonic, "CMPSWP", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = CMPSWP;
} else if (strncmp(mnemonic, "SHIFT", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = SHIFT;
} else if (strncmp(mnemonic, "STORE", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = STORE;
} else if (strncmp(mnemonic, "LOAD", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = LOAD;
} else if (strncmp(mnemonic, "NOP", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = NOP;
} else if (strncmp(mnemonic, "HALT", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = HALT;
} else if (strncmp(mnemonic, "JMP", MAX_MNEMONIC_LEN) == 0) {
if (label_index) {
new_instruction->syllable = WORD;
new_instruction->target = malloc(label_index+1);
strncpy(new_instruction->target, label, label_index+1);
new_instruction->next = create_instruction_struct();
new_instruction->next->prev = new_instruction;
new_instruction->next->syllable = JMP;
new_instruction->linenum = *linenum;
} else {
new_instruction->syllable = JMP;
}
} else if (strncmp(mnemonic, "SWAP", MAX_MNEMONIC_LEN) == 0) {
new_instruction->syllable = SWAP;
} else {
/* Fake/composite mnemonics. Not part of the NED ISA. */
new_instruction = parse_pseudo_mnemonic(mnemonic, mnemonic_index, data, data_index,
label, label_index, new_instruction);
}
if (new_instruction->syllable == 0) {
fprintf(stderr, "ERROR: Unable to match mnemonic on line %d.\n", *linenum);
exit(EXIT_FAILURE);
}
if (instructions != NULL) {
instructions = seek_instruction_list_end(instructions);
new_instruction = seek_instruction_list_start(new_instruction);
new_instruction->prev = instructions;
instructions->next = new_instruction;
} else {
instructions = new_instruction;
}
instructions = seek_instruction_list_end(instructions);
return instructions;
}
struct instruction *
parse_dest_label(uint32_t * linenum, FILE * input, struct instruction * instructions)
{
instructions = seek_instruction_list_end(instructions);
char word[MAX_LABEL_LEN];
size_t i = 0;
int c;
while ((c = fgetc(input)) != EOF) {
/* TODO: Accept underscores or hyphens inside labels. */
if (!islower(c)) break;
word[i++] = c;
if (i >= MAX_LABEL_LEN) {
fprintf(stderr, "ERROR: Exceeded max label length on line %d.\n", *linenum);
exit(EXIT_FAILURE);
}
}
ungetc(c, input);
word[i] = '\0';
struct instruction * new_instruction = create_instruction_struct();
new_instruction->syllable = LABEL;
new_instruction->linenum = *linenum;
new_instruction->label = malloc(i+1);
strncpy(new_instruction->label, word, i+1);
if (instructions != NULL) {
instructions->next = new_instruction;
new_instruction->prev = instructions;
} else {
instructions = new_instruction;
}
return instructions;
}
void
parse_assembly(struct instruction ** instructions, FILE * input)
{
int c;
uint32_t linenum = 1;
while ((c = fgetc(input)) != EOF) {
if (c == '#') {
seek_newline(&linenum, input);
} else if (c == ' ' || c == '\t') {
/* Intentionally empty in order to skip whitespace. */
} else if (c == '\n') {
linenum++;
} else if (isupper(c)) { /* Mnemonics start with uppercase alpha char. */
ungetc(c, input);
*instructions = parse_mnemonic(&linenum, input, *instructions);
} else if (islower(c)) { /* Labels start with lowercase alpha char. */
ungetc(c, input);
*instructions = parse_dest_label(&linenum, input, *instructions);
} else {
fprintf(stderr, "ERROR: Invalid syntax on line %d\n", linenum);
exit(EXIT_FAILURE);
}
}
}