Browse Source

Major refactoring, added short variables, added thin memory management library!

remotes/origin/HEAD
Seth Stubbs 6 months ago
parent
commit
d8456793f9
  1. 4
      Makefile
  2. 6
      src/main.c
  3. 222
      src/uasm.c
  4. 3
      src/uasm.h
  5. 0
      src/ulex.c
  6. 0
      src/ulex.h
  7. 155
      src/uparse.c
  8. 12
      src/uparse.h

4
Makefile

@ -1,6 +1,6 @@
CC=clang
CFLAGS=-fPIE -Wall -O2 -Isrc -std=c89
LDFLAGS=-lm #-fsanitize=address
CFLAGS=-fPIE -Wall -g -fsanitize=address -O2 -Isrc -std=c89
LDFLAGS=-lm -fsanitize=address
OUT=bin/uxncle
CHDR=\

6
src/main.c

@ -3,9 +3,9 @@
int main() {
UASTNode *tree = UP_parseSource(
"prntint 6 + 2 * 21 + 3 * 6;\n"
"prntint 6 / 2;\n"
"prntint 24;"
"short a = 2;\n"
"short b = 6;\n"
"prntint b / a;"
);
UA_genTal(tree, fopen("bin/out.tal", "w"));

222
src/uasm.c

@ -1,10 +1,26 @@
#include "uasm.h"
#include "uparse.h"
/* compiler state */
typedef struct {
FILE *out;
UScope *scopes[MAX_SCOPES];
int sCount;
int pushed; /* current bytes on the stack */
} UCompState;
static const char preamble[] =
"|10 @Console [ &pad $8 &char $1 &byte $1 &short $2 &string $2 ]\n"
"|0000\n"
"@number [ &started $1 ]\n"
"|0100\n";
"@uxncle [ &heap $2 ]\n"
"|0100\n"
/* setup mem lib */
";uxncle-heap .uxncle/heap STZ2\n";
/* TODO:
- write a thin library to handle heap allocation & deallocation. (we'll need this to store temporary values in scopes)
*/
static const char postamble[] =
"\n"
@ -26,45 +42,200 @@ static const char postamble[] =
"\tPOP JMP2r\n"
"\tLIT '0 ADD .Console/char DEO\n"
"\t#01 .number/started STZ\n"
"JMP2r\n";
"JMP2r\n"
/* start of thin memory library */
"@alloc-uxncle\n" /* this subroutine handles allocating memory on the heap, expects the size (short) */
".uxncle/heap LDZ2\n" /* load the heap pointer onto the stack */
"ADD2\n" /* add the size */
".uxncle/heap STZ2\n" /* store the new heap pointer */
"JMP2r\n" /* return */
"@dealloc-uxncle\n" /* this subroutine handles deallocating memory from the heap, expects the size (short) */
".uxncle/heap LDZ2\n" /* load the heap pointer onto the stack */
"SWP2\n" /* move the heap pointer behind the size, so when we subtract it'll be heap - size, not size - heap */
"SUB2\n" /* sub the size from the address */
".uxncle/heap STZ2\n" /* store the new heap pointer */
"JMP2r\n" /* return */
"@peek-uxncle-short\n" /* this subroutine handles loading a short from the heap and pushing it onto the stack, expects the offset (short) */
".uxncle/heap LDZ2\n" /* load the heap pointer onto the stack */
"SWP2\n" /* move the heap pointer behind the offset */
"SUB2\n"
"LDA2\n" /* loads the short from the heap onto the stack */
"JMP2r\n" /* return */
"@poke-uxncle-short\n" /* this subroutine handles popping a short from the stack and saving it into the heap, expects the value (short) and the offset (short) */
".uxncle/heap LDZ2\n" /* load the heap pointer onto the stack */
"SWP2\n" /* move the heap pointer behind the offset */
"SUB2\n"
"STA2\n" /* stores the value into the address */
"JMP2r\n" /* return */
"@peek-uxncle\n" /* this subroutine handles loading a byte from the heap and pushing it onto the stack, expects the offset (short) */
".uxncle/heap LDZ2\n" /* load the heap pointer onto the stack */
"SWP2\n" /* move the heap pointer behind the offset */
"SUB2\n"
"LDA\n" /* loads the byte from the heap onto the stack */
"JMP2r\n" /* return */
"@poke-uxncle\n" /* this subroutine handles popping a byte from the stack and saving it into the heap, expects the value (byte) and the offset (short) */
".uxncle/heap LDZ2\n" /* load the heap pointer onto the stack */
"SWP2\n" /* move the heap pointer behind the offset */
"SUB2\n"
"STA\n" /* stores the value into the address */
"JMP2r\n" /* return */
"@uxncle-heap\n"
"|ffff &end";
void compileAST(UCompState *state, UASTNode *node);
/* ==================================[[ generic helper functions ]]================================== */
/* throws a compiler error */
void cError(UCompState *state, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
printf("Compiler error!\n\t");
vprintf(fmt, args);
va_end(args);
exit(EXIT_FAILURE);
}
void writeShortLit(UCompState *state, uint16_t lit) {
fprintf(state->out, "#%.4x ", lit);
state->pushed += 2;
}
void writeByteLit(UCompState *state, uint8_t lit) {
fprintf(state->out, "#%.2x ", lit);
state->pushed++;
}
uint16_t getSize(UCompState *state, UVar *var) {
switch(var->type) {
case TYPE_BYTE: return 1;
case TYPE_SHORT: return 2;
default:
cError(state, "unknown type! [%d]", var->type);
}
}
uint16_t getScopeSize(UCompState *state, UScope *scope) {
uint16_t size = 0;
int i;
/* add up all the sizes */
for (i = 0; i < scope->vCount; i++)
size += getSize(state, &scope->vars[i]);
return size;
}
void pushScope(UCompState *state, UScope *scope) {
state->scopes[state->sCount++] = scope;
writeShortLit(state, getScopeSize(state, scope));
fwrite(";alloc-uxncle JSR2\n", 19, 1, state->out);
state->pushed -= 2;
}
void popScope(UCompState *state) {
UScope *scope = state->scopes[--state->sCount];
writeShortLit(state, getScopeSize(state, scope));
fwrite(";dealloc-uxncle JSR2\n", 21, 1, state->out);
state->pushed -= 2;
}
void writeShortLit(FILE *out, int lit) {
fprintf(out, "#%.4x ", lit);
uint16_t getOffset(UCompState *state, int scope, int var) {
uint16_t offsetAddr = 0;
int i, z;
/* sanity check */
if (state->sCount < scope || state->scopes[scope]->vCount < var)
cError(state, "Invalid variable id!");
/* walk scopes adding the size of the data on the heap */
for (i = scope; i >= 0; i--)
for (z = var; z >= 0; z--)
offsetAddr += getSize(state, &state->scopes[i]->vars[z]);
return offsetAddr;
}
void getShortVar(UCompState *state, int scope, int var) {
uint16_t offsetAddr = getOffset(state, scope, var);
writeShortLit(state, offsetAddr); /* write the offset */
fprintf(state->out, ";peek-uxncle-short JSR2\n"); /* call the mem lib */
}
void setShortVar(UCompState *state, int scope, int var) {
uint16_t offsetAddr = getOffset(state, scope, var);
writeShortLit(state, offsetAddr); /* write the offset */
fprintf(state->out, ";poke-uxncle-short JSR2\n"); /* call the mem lib */
state->pushed -= 4; /* pops the offset (short) & the value (short) */
}
void compileVar(UCompState *state, UASTNode *node) {
UASTVarNode *var = (UASTVarNode*)node;
getShortVar(state, var->scope, var->var);
}
void compileExpression(FILE *out, UASTNode *node) {
/* ==================================[[ arithmetic ]]================================== */
void cShortArith(UCompState *state, const char *instr) {
fprintf(state->out, "%s2\n", instr);
state->pushed -= 2;
}
void compileExpression(UCompState *state, UASTNode *node) {
/* first, traverse down the AST recusively */
if (node->left)
compileExpression(out, node->left);
compileExpression(state, node->left);
if (node->right)
compileExpression(out, node->right);
compileExpression(state, node->right);
switch(node->type) {
case NODE_ADD: fwrite("ADD2\n", 5, 1, out); break;
case NODE_SUB: fwrite("SUB2\n", 5, 1, out); break;
case NODE_MUL: fwrite("MUL2\n", 5, 1, out); break;
case NODE_DIV: fwrite("DIV2\n", 5, 1, out); break;
case NODE_INTLIT: writeShortLit(out, ((UASTIntNode*)node)->num); break;
case NODE_ADD: cShortArith(state, "ADD"); break;
case NODE_SUB: cShortArith(state, "SUB"); break;
case NODE_MUL: cShortArith(state, "MUL"); break;
case NODE_DIV: cShortArith(state, "DIV"); break;
case NODE_SHORTLIT: writeShortLit(state, ((UASTIntNode*)node)->num); break;
case NODE_VAR: compileVar(state, node); break;
default:
printf("Compiler error! unknown AST node!! [%d]\n", node->type);
exit(EXIT_FAILURE);
cError(state, "unknown AST node!! [%d]\n", node->type);
}
}
void compilePrintInt(FILE *out, UASTNode *node) {
compileExpression(out, node->left);
fwrite(";print-decimal JSR2 #20 .Console/char DEO\n", 42, 1, out);
void compilePrintInt(UCompState *state, UASTNode *node) {
compileExpression(state, node->left);
fwrite(";print-decimal JSR2 #20 .Console/char DEO\n", 42, 1, state->out);
}
void compileShort(UCompState *state, UASTNode *node) {
UASTVarNode *var = (UASTVarNode*)node;
/* if there's no assignment, the default value will be scary undefined memory :O */
if (node->left) {
compileExpression(state, node->left);
setShortVar(state, var->scope, var->var);
}
}
void compileAST(FILE *out, UASTNode *node) {
void compileScope(UCompState *state, UASTNode *node) {
UASTScopeNode *nScope = (UASTScopeNode*)node;
pushScope(state, &nScope->scope);
/* compile the statements in the scope */
compileAST(state, node->left);
popScope(state);
}
void compileAST(UCompState *state, UASTNode *node) {
/* STATE nodes hold the expression in node->left, and the next expression in node->right */
while (node) {
switch(node->type) {
case NODE_STATE_PRNT: compilePrintInt(out, node); break;
case NODE_STATE_EXPR: compileExpression(out, node->left); break;
case NODE_STATE_PRNT: compilePrintInt(state, node); break;
case NODE_STATE_SHORT: compileShort(state, node); break;
case NODE_STATE_EXPR: compileExpression(state, node->left); break;
case NODE_STATE_SCOPE: compileScope(state, node); break;
default:
printf("Compiler error! unknown Statement node!! [%d]\n", node->type);
exit(EXIT_FAILURE);
cError(state, "unknown statement node!! [%d]\n", node->type);
}
/* move to the next statement */
@ -73,11 +244,16 @@ void compileAST(FILE *out, UASTNode *node) {
}
void UA_genTal(UASTNode *tree, FILE *out) {
UCompState state;
state.sCount = 0;
state.pushed = 0;
state.out = out;
/* first, write the preamble */
fwrite(preamble, sizeof(preamble)-1, 1, out);
/* now parse the whole AST */
compileAST(out, tree);
compileAST(&state, tree);
/* finally, write the postamble */
fwrite(postamble, sizeof(postamble)-1, 1, out);

3
src/uasm.h

@ -4,6 +4,9 @@
#include "uxncle.h"
#include "uparse.h"
/* default heap space to hold temporary values */
#define HEAP_SPACE 0x1800
#include <stdio.h>
/* takes a syntax tree and spits out the generated asm into the provided file stream */

0
src/ulex.c

0
src/ulex.h

155
src/uparse.c

@ -20,6 +20,7 @@ typedef struct {
UASTNode* parsePrecedence(UParseState *state, UASTNode *left, Precedence prec);
UASTNode* expression(UParseState *state);
UASTNode* statement(UParseState *state);
ParseRule ruleTable[];
int str2int(char *str, int len) {
@ -63,7 +64,7 @@ UASTNode *newNode(UParseState *state, UASTNodeType type, UASTNode *left, UASTNod
}
UASTNode *newNumNode(UParseState *state, UASTNode *left, UASTNode *right, int num) {
UASTIntNode *node = (UASTIntNode*)newBaseNode(state, sizeof(UASTIntNode), NODE_INTLIT, left, right);
UASTIntNode *node = (UASTIntNode*)newBaseNode(state, sizeof(UASTIntNode), NODE_SHORTLIT, left, right);
node->num = num;
return (UASTNode*)node;
}
@ -77,19 +78,43 @@ UASTNode *newScopeNode(UParseState *state, UASTNode *left, UASTNode *right, USco
UScope* newScope(UParseState *state) {
UScope *scope = &state->scopes[state->sCount++];
/* set the scope */
/* sanity check */
if (state->sCount >= MAX_SCOPES)
error(state, "Max scope limit reached!");
scope->vCount = 0;
return scope;
}
void endScope(UParseState *state) {
state->sCount--;
}
UScope* getScope(UParseState *state) {
return &state->scopes[state->sCount-1];
}
UVar* findVar(UParseState *state, char *name, int length) {
int i, z;
/* walk the scopes and variables */
for (i = state->sCount-1; i >= 0; i--)
for (z = state->scopes[i].vCount-1; z >= 0; z--)
if (state->scopes[i].vars[z].len == length && !memcmp(state->scopes[i].vars[z].name, name, length))
return &state->scopes[i].vars[z];
/* var wasn't found */
return NULL;
}
int newVar(UParseState *state, UVarType type, char *name, int length) {
UScope *scope = getScope(state);
UVar *var = &scope->vars[scope->vCount++];
/* make sure the variable name wasn't already in use */
if (findVar(state, name, length) != NULL)
error(state, "Variable '%.*s' already declared!", length, name);
/* sanity check */
if (scope->vCount >= MAX_LOCALS)
error(state, "Max local limit reached, too many locals declared in scope!");
@ -98,6 +123,8 @@ int newVar(UParseState *state, UVarType type, char *name, int length) {
var->type = type;
var->name = name;
var->len = length;
var->scope = state->sCount-1;
var->var = scope->vCount-1;
var->declared = 0;
return scope->vCount-1;
}
@ -161,6 +188,20 @@ UASTNode* binOperator(UParseState *state, UASTNode *left, Precedence currPrec) {
return newNode(state, type, left, right);
}
UASTNode* identifer(UParseState *state, UASTNode *left, Precedence currPrec) {
UASTVarNode *nVar;
UVar *var = findVar(state, state->previous.str, state->previous.len);
if (var == NULL)
error(state, "Identifer '%.*s' not found!", state->previous.len, state->previous.str);
/* finally, create the Var node */
nVar = (UASTVarNode*)newBaseNode(state, sizeof(UASTVarNode), NODE_VAR, NULL, NULL);
nVar->var = var->var;
nVar->scope = var->scope;
return (UASTNode*)nVar;
}
ParseRule ruleTable[] = {
/* keywords */
{NULL, NULL, PREC_NONE}, /* TOKEN_BYTE */
@ -169,7 +210,7 @@ ParseRule ruleTable[] = {
{NULL, NULL, PREC_NONE}, /* TOKEN_PRINTINT */
/* literals */
{NULL, NULL, PREC_NONE}, /* TOKEN_IDENT */
{identifer, NULL, PREC_LITERAL}, /* TOKEN_IDENT */
{number, NULL, PREC_LITERAL}, /* TOKEN_NUMBER */
{NULL, NULL, PREC_NONE}, /* TOKEN_LEFT_BRACE */
@ -190,8 +231,52 @@ ParseRule ruleTable[] = {
{NULL, NULL, PREC_NONE}, /* TOKEN_ERR */
};
UASTNode* parsePrecedence(UParseState *state, UASTNode *left, Precedence prec) {
ParseFunc func;
/* grab the prefix function */
advance(state);
func = getRule(state->previous.type)->prefix;
if (func == NULL) {
error(state, "Illegal syntax! [prefix]");
return NULL;
}
left = func(state, left, prec);
while (prec <= getRule(state->current.type)->level) {
func = getRule(state->current.type)->infix;
if (func == NULL) {
error(state, "Illegal syntax! [infix]");
return NULL;
}
advance(state);
left = func(state, left, getRule(state->previous.type)->level);
}
return left;
}
/* ==================================[[ parse statement functions ]]================================== */
UASTNode* parseScope(UParseState *state, int expectBrace) {
UASTNode *root = NULL, *current = NULL;
do {
if (root == NULL) {
root = statement(state);
current = root;
} else {
current->right = statement(state);
current = current->right;
}
} while(!isPEnd(state) && (!expectBrace || !check(state, TOKEN_RIGHT_BRACE)));
if (expectBrace && !match(state, TOKEN_RIGHT_BRACE))
error(state, "Expected '}' to end scope!");
return root;
}
UASTNode* printStatement(UParseState *state) {
/* make our statement node & return */
return newNode(state, NODE_STATE_PRNT, expression(state), NULL);
@ -211,32 +296,21 @@ UASTNode* shortStatement(UParseState *state) {
/* if it's assigned a value, evaluate the expression & set the left node, if not set it to NULL */
node = (UASTVarNode*)newBaseNode(state, sizeof(UASTVarNode), NODE_STATE_SHORT, (match(state, TOKEN_EQUAL)) ? expression(state) : NULL, NULL);
node->var = var;
node->scope = state->sCount-1;
return (UASTNode*)node;
}
UASTNode* parsePrecedence(UParseState *state, UASTNode *left, Precedence prec) {
ParseFunc func;
UASTNode* scopeStatement(UParseState *state) {
UASTScopeNode *node;
UScope *scope = newScope(state);
/* grab the prefix function */
advance(state);
func = getRule(state->previous.type)->prefix;
if (func == NULL) {
error(state, "Illegal syntax! [prefix]");
return NULL;
}
/* create scope node and copy the finished scope struct */
node = (UASTScopeNode*)newBaseNode(state, sizeof(UASTScopeNode), NODE_STATE_SCOPE, parseScope(state, 1), NULL);
node->scope = *scope;
left = func(state, left, prec);
while (prec <= getRule(state->current.type)->level) {
func = getRule(state->current.type)->infix;
if (func == NULL) {
error(state, "Illegal syntax! [infix]");
return NULL;
}
advance(state);
left = func(state, left, getRule(state->previous.type)->level);
}
endScope(state);
return left;
return (UASTNode*)node;
}
UASTNode* expression(UParseState *state) {
@ -254,6 +328,10 @@ UASTNode* statement(UParseState *state) {
/* find a statement match */
if (match(state, TOKEN_PRINTINT)) {
node = printStatement(state);
} else if (match(state, TOKEN_SHORT)) {
node = shortStatement(state);
} else if (match(state, TOKEN_LEFT_BRACE)) {
node = scopeStatement(state);
} else {
/* no statement match was found, just parse the expression */
node = expression(state);
@ -272,11 +350,11 @@ void printNode(UASTNode *node) {
case NODE_SUB: printf("SUB"); break;
case NODE_MUL: printf("MUL"); break;
case NODE_DIV: printf("DIV"); break;
case NODE_INTLIT: printf("[%d]", ((UASTIntNode*)node)->num); break;
case NODE_SHORTLIT: printf("[%d]", ((UASTIntNode*)node)->num); break;
case NODE_STATE_PRNT: printf("PRNT"); break;
case NODE_STATE_SCOPE: printf("SCPE"); break;
case NODE_STATE_SHORT: printf("SHRT"); break;
case NODE_STATE_VAR: printf("VAR[%d]", ((UASTVarNode*)node)->var); break;
case NODE_VAR: printf("VAR[%d]", ((UASTVarNode*)node)->var); break;
case NODE_STATE_EXPR: printf("EXPR"); break;
default: break;
}
@ -295,27 +373,22 @@ void printTree(UASTNode *node, int indent) {
UASTNode *UP_parseSource(const char *src) {
UParseState state;
UASTNode *root = NULL, *current = NULL;
int treeIndent = 8;
state.sCount = 1;
UASTScopeNode *root = NULL;
UScope *scope;
int treeIndent = 16;
UL_initLexState(&state.lstate, src);
advance(&state);
state.sCount = 0;
scope = newScope(&state);
do {
if (root == NULL) {
root = statement(&state);
current = root;
} else {
current->right = statement(&state);
current = current->right;
treeIndent += 4;
}
} while(!isPEnd(&state));
/* create scope node and copy the finished scope struct */
root = (UASTScopeNode*)newBaseNode(&state, sizeof(UASTScopeNode), NODE_STATE_SCOPE, parseScope(&state, 0), NULL);
root->scope = *scope;
printTree(root, treeIndent);
return root;
endScope(&state);
printTree((UASTNode*)root, treeIndent);
return (UASTNode*)root;
}
void UP_freeTree(UASTNode *tree) {

12
src/uparse.h

@ -13,16 +13,17 @@ typedef enum {
NODE_SUB,
NODE_MUL,
NODE_DIV,
NODE_INTLIT,
NODE_SHORTLIT,
NODE_VAR,
/*
statement nodes below
node->left holds expression tree, node->right holds the next statement
*/
NODE_STATE_PRNT,
NODE_STATE_SCOPE,
NODE_STATE_SHORT,
NODE_STATE_VAR,
NODE_STATE_EXPR,
/* scopes are different, node->left holds the statement tree for the scope, node->right holds the next statement */
NODE_STATE_SCOPE,
} UASTNodeType;
typedef enum {
@ -34,6 +35,8 @@ typedef struct {
UVarType type;
char *name;
int len;
int scope;
int var;
int declared; /* if the variable can be used yet */
} UVar;
@ -50,7 +53,8 @@ typedef struct s_UASTNode {
typedef struct {
COMMON_NODE_HEADER;
int var; /* index of the UVar in the closest scope in the tree */
int var; /* index of the UVar */
int scope; /* index of the scope */
} UASTVarNode;
typedef struct {

Loading…
Cancel
Save