mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Got the compiler partially working
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -21,6 +21,7 @@ out/
|
||||
*.meta
|
||||
*.log
|
||||
out
|
||||
*.stackdump
|
||||
|
||||
#Shell files
|
||||
*.bat
|
||||
|
||||
@@ -10,7 +10,7 @@ Special thanks to http://craftinginterpreters.com/ for their fantastic book that
|
||||
|
||||
## Building
|
||||
|
||||
TODO
|
||||
Simply run make in the rood directory.
|
||||
|
||||
# License
|
||||
|
||||
|
||||
@@ -2,3 +2,7 @@ print "hello world";
|
||||
print null;
|
||||
print true;
|
||||
print false;
|
||||
print 42;
|
||||
print 3.14;
|
||||
print -69;
|
||||
print -4.20;
|
||||
@@ -7,5 +7,5 @@
|
||||
#define TOY_VERSION_MAJOR 0
|
||||
#define TOY_VERSION_MINOR 6
|
||||
#define TOY_VERSION_PATCH 0
|
||||
#define TOY_VERSION_BUILD __DATE__
|
||||
#define TOY_VERSION_BUILD __DATE__ ";" __TIME__
|
||||
|
||||
|
||||
208
source/compiler.c
Normal file
208
source/compiler.c
Normal file
@@ -0,0 +1,208 @@
|
||||
#include "compiler.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
void initCompiler(Compiler* compiler) {
|
||||
initLiteralArray(&compiler->literalCache);
|
||||
compiler->bytecode = NULL;
|
||||
compiler->capacity = 0;
|
||||
compiler->count = 0;
|
||||
|
||||
//default atomic literals
|
||||
Literal n = TO_NULL_LITERAL;
|
||||
Literal t = TO_BOOLEAN_LITERAL(true);
|
||||
Literal f = TO_BOOLEAN_LITERAL(false);
|
||||
|
||||
writeLiteralArray(&compiler->literalCache, n);
|
||||
writeLiteralArray(&compiler->literalCache, t);
|
||||
writeLiteralArray(&compiler->literalCache, f);
|
||||
}
|
||||
|
||||
void writeCompiler(Compiler* compiler, Node* node) {
|
||||
//grow if the bytecode space is too small
|
||||
if (compiler->capacity < compiler->count + 8) { //assume 8 is the maximum space needed by each instruction (can change later)
|
||||
int oldCapacity = compiler->capacity;
|
||||
|
||||
compiler->capacity = GROW_CAPACITY(oldCapacity);
|
||||
compiler->bytecode = GROW_ARRAY(unsigned char, compiler->bytecode, oldCapacity, compiler->capacity);
|
||||
}
|
||||
|
||||
//determine node type
|
||||
switch(node->type) {
|
||||
case NODE_LITERAL: {
|
||||
//ensure the literal is in the cache
|
||||
int index = findLiteralIndex(&compiler->literalCache, node->atomic.literal);
|
||||
if (index < 0) {
|
||||
index = writeLiteralArray(&compiler->literalCache, node->atomic.literal);
|
||||
}
|
||||
|
||||
//push the node opcode to the bytecode
|
||||
if (index >= 256) {
|
||||
//push a "long" index
|
||||
compiler->bytecode[compiler->count++] = OP_LITERAL_LONG; //1 byte
|
||||
*((unsigned short*)(compiler->bytecode + compiler->count)) = (unsigned short)index; //2 bytes
|
||||
|
||||
compiler->count += sizeof(unsigned short);
|
||||
}
|
||||
else {
|
||||
//push the index
|
||||
compiler->bytecode[compiler->count++] = OP_LITERAL; //1 byte
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)index; //1 byte
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NODE_UNARY:
|
||||
//pass to the child node, then embed the unary command (print, negate, etc.)
|
||||
writeCompiler(compiler, node->unary.child);
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)node->unary.opcode; //1 byte
|
||||
break;
|
||||
|
||||
case NODE_BINARY:
|
||||
//pass to the child nodes, then embed the binary command (math, etc.)
|
||||
writeCompiler(compiler, node->binary.left);
|
||||
writeCompiler(compiler, node->binary.right);
|
||||
compiler->bytecode[compiler->count++] = (unsigned char)node->binary.opcode; //1 byte
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void freeCompiler(Compiler* compiler) {
|
||||
freeLiteralArray(&compiler->literalCache);
|
||||
FREE(unsigned char, compiler->bytecode);
|
||||
compiler->bytecode = NULL;
|
||||
compiler->capacity = 0;
|
||||
compiler->count = 0;
|
||||
}
|
||||
|
||||
static void emitByte(char** collationPtr, int* capacityPtr, int* countPtr, unsigned char byte) {
|
||||
//grow the array
|
||||
if (*countPtr + 1 > *capacityPtr) {
|
||||
int oldCapacity = *capacityPtr;
|
||||
*capacityPtr = GROW_CAPACITY(*capacityPtr);
|
||||
*collationPtr = GROW_ARRAY(char, *collationPtr, oldCapacity, *capacityPtr);
|
||||
}
|
||||
|
||||
//append to the collation
|
||||
(*collationPtr)[(*countPtr)++] = byte;
|
||||
}
|
||||
|
||||
static void emitShort(char** collationPtr, int* capacityPtr, int* countPtr, unsigned short bytes) {
|
||||
char* ptr = (char*)&bytes;
|
||||
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
ptr++;
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
}
|
||||
|
||||
static void emitInt(char** collationPtr, int* capacityPtr, int* countPtr, int bytes) {
|
||||
char* ptr = (char*)&bytes;
|
||||
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
ptr++;
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
ptr++;
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
ptr++;
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
}
|
||||
|
||||
static void emitFloat(char** collationPtr, int* capacityPtr, int* countPtr, float bytes) {
|
||||
char* ptr = (char*)&bytes;
|
||||
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
ptr++;
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
ptr++;
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
ptr++;
|
||||
emitByte(collationPtr, capacityPtr, countPtr, *ptr);
|
||||
}
|
||||
|
||||
//return the result
|
||||
char* collateCompiler(Compiler* compiler, int* size) {
|
||||
int capacity = GROW_CAPACITY(0);
|
||||
int count = 0;
|
||||
char* collation = ALLOCATE(char, capacity);
|
||||
|
||||
//embed the header with version information
|
||||
emitByte(&collation, &capacity, &count, TOY_VERSION_MAJOR);
|
||||
emitByte(&collation, &capacity, &count, TOY_VERSION_MINOR);
|
||||
emitByte(&collation, &capacity, &count, TOY_VERSION_PATCH);
|
||||
|
||||
//embed the build info
|
||||
if (strlen(TOY_VERSION_BUILD) + count + 1 > capacity) {
|
||||
int oldCapacity = capacity;
|
||||
capacity = strlen(TOY_VERSION_BUILD) + count + 1; //full header size
|
||||
collation = GROW_ARRAY(char, collation, oldCapacity, capacity);
|
||||
}
|
||||
|
||||
memcpy(&collation[count], TOY_VERSION_BUILD, strlen(TOY_VERSION_BUILD));
|
||||
count += strlen(TOY_VERSION_BUILD);
|
||||
collation[count++] = '\0'; //terminate the build string
|
||||
|
||||
emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate header
|
||||
|
||||
//embed the data section (first short is the number of literals)
|
||||
emitShort(&collation, &capacity, &count, compiler->literalCache.count);
|
||||
|
||||
//emit each literal by type
|
||||
for (int i = 0; i < compiler->literalCache.count; i++) {
|
||||
//literal Opcode
|
||||
// emitShort(&collation, &capacity, &count, OP_LITERAL); //This isn't needed
|
||||
|
||||
//literal type, followed by literal value
|
||||
switch(compiler->literalCache.literals[i].type) {
|
||||
case LITERAL_NULL:
|
||||
emitByte(&collation, &capacity, &count, LITERAL_NULL);
|
||||
//null has no following value
|
||||
break;
|
||||
|
||||
case LITERAL_BOOLEAN:
|
||||
emitByte(&collation, &capacity, &count, LITERAL_BOOLEAN);
|
||||
emitByte(&collation, &capacity, &count, AS_BOOLEAN(compiler->literalCache.literals[i]));
|
||||
break;
|
||||
|
||||
case LITERAL_INTEGER:
|
||||
emitByte(&collation, &capacity, &count, LITERAL_INTEGER);
|
||||
emitInt(&collation, &capacity, &count, AS_INTEGER(compiler->literalCache.literals[i]));
|
||||
break;
|
||||
|
||||
case LITERAL_FLOAT:
|
||||
emitByte(&collation, &capacity, &count, LITERAL_FLOAT);
|
||||
emitFloat(&collation, &capacity, &count, AS_FLOAT(compiler->literalCache.literals[i]));
|
||||
break;
|
||||
|
||||
case LITERAL_STRING: {
|
||||
emitByte(&collation, &capacity, &count, LITERAL_STRING);
|
||||
|
||||
Literal str = compiler->literalCache.literals[i];
|
||||
|
||||
for (int c = 0; c < STRLEN(str); c++) {
|
||||
emitByte(&collation, &capacity, &count, AS_STRING(str)[c]);
|
||||
}
|
||||
|
||||
emitByte(&collation, &capacity, &count, '\0'); //terminate the string
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate data
|
||||
|
||||
//code section
|
||||
for (int i = 0; i < compiler->count; i++) {
|
||||
emitByte(&collation, &capacity, &count, compiler->bytecode[i]);
|
||||
}
|
||||
|
||||
emitByte(&collation, &capacity, &count, OP_SECTION_END); //terminate code
|
||||
|
||||
emitByte(&collation, &capacity, &count, OP_EOF); //terminate bytecode
|
||||
|
||||
//finalize
|
||||
SHRINK_ARRAY(char, collation, capacity, count);
|
||||
|
||||
*size = count;
|
||||
|
||||
return collation;
|
||||
}
|
||||
21
source/compiler.h
Normal file
21
source/compiler.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "opcodes.h"
|
||||
|
||||
#include "node.h"
|
||||
#include "literal_array.h"
|
||||
|
||||
//the compiler takes the nodes, and turns them into sequential chunks of bytecode, saving literals to an external array
|
||||
typedef struct Compiler {
|
||||
LiteralArray literalCache;
|
||||
unsigned char* bytecode;
|
||||
int capacity;
|
||||
int count;
|
||||
} Compiler;
|
||||
|
||||
void initCompiler(Compiler* compiler);
|
||||
void writeCompiler(Compiler* compiler, Node* node);
|
||||
void freeCompiler(Compiler* compiler);
|
||||
|
||||
//embed the header with version information, data section, code section, etc.
|
||||
char* collateCompiler(Compiler* compiler, int* size);
|
||||
150
source/debug.c
150
source/debug.c
@@ -1,6 +1,9 @@
|
||||
#include "debug.h"
|
||||
|
||||
#include "keyword_types.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -77,4 +80,151 @@ void copyrightCommand(int argc, const char* argv[]) {
|
||||
printf("1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n\n");
|
||||
printf("2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n\n");
|
||||
printf("3. This notice may not be removed or altered from any source distribution.\n\n");
|
||||
}
|
||||
|
||||
//utils
|
||||
static unsigned char printByte(const char* tb, int* count) {
|
||||
unsigned char ret = *(unsigned char*)(tb + *count);
|
||||
printf("%u ", ret);
|
||||
*count += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned short printShort(const char* tb, int* count) {
|
||||
unsigned short ret = *(unsigned short*)(tb + *count);
|
||||
printf("%d ", ret);
|
||||
*count += 2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int printInt(const char* tb, int* count) {
|
||||
int ret = *(int*)(tb + *count);
|
||||
printf("%d ", ret);
|
||||
*count += 4;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static float printFloat(const char* tb, int* count) {
|
||||
float ret = *(float*)(tb + *count);
|
||||
printf("%f ", ret);
|
||||
*count += 4;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char* printString(const char* tb, int* count) {
|
||||
const char* ret = tb + *count;
|
||||
*count += printf("%s ", ret); //return includes the space, but not the null terminator
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void consumeByte(unsigned char byte, const char* str, int* count) {
|
||||
if (byte != str[*count]) {
|
||||
printf("Failed to consume the correct byte");
|
||||
}
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
static void consumeShort(unsigned short bytes, const char* str, int* count) {
|
||||
if (bytes != *(unsigned short*)(str + *count)) {
|
||||
printf("Failed to consume the correct byte");
|
||||
}
|
||||
*count += 2;
|
||||
}
|
||||
|
||||
void dissectBytecode(const char* tb, int size) {
|
||||
int count = 0;
|
||||
|
||||
//header
|
||||
printf("--header--\n");
|
||||
printByte(tb, &count);
|
||||
printByte(tb, &count);
|
||||
printByte(tb, &count);
|
||||
printString(tb, &count);
|
||||
consumeByte(OP_SECTION_END, tb, &count);
|
||||
|
||||
printf("\n");
|
||||
|
||||
//data
|
||||
printf("--data--\n");
|
||||
const short literalCount = printShort(tb, &count);
|
||||
|
||||
for (int i = 0; i < literalCount; i++) {
|
||||
const unsigned char literalType = printByte(tb, &count);
|
||||
|
||||
switch(literalType) {
|
||||
case LITERAL_NULL:
|
||||
//NO-OP
|
||||
printf("(null)");
|
||||
break;
|
||||
|
||||
case LITERAL_BOOLEAN: {
|
||||
const bool b = printByte(tb, &count);
|
||||
printf("(boolean %s)", b ? "true" : "false");
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_INTEGER: {
|
||||
const int d = printInt(tb, &count);
|
||||
printf("(integer %d)", d);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_FLOAT: {
|
||||
const float f = printFloat(tb, &count);
|
||||
printf("(float %f)", f);
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_STRING: {
|
||||
const s = printString(tb, &count);
|
||||
printf("(string)");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
consumeByte(OP_SECTION_END, tb, &count);
|
||||
|
||||
//code
|
||||
printf("--bytecode--\n");
|
||||
while(tb[count] != OP_EOF) {
|
||||
const opcode = printByte(tb, &count);
|
||||
|
||||
switch (opcode) {
|
||||
case OP_PRINT:
|
||||
printf("print:\n");
|
||||
break;
|
||||
|
||||
case OP_LITERAL: {
|
||||
printf("literal ");
|
||||
printByte(tb, &count);
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_LITERAL_LONG: {
|
||||
printf("long literal ");
|
||||
printByte(tb, &count);
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_NEGATE: {
|
||||
printf("negate\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_SECTION_END: {
|
||||
printf("--SECTION END--");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown opcode found\n");
|
||||
}
|
||||
}
|
||||
|
||||
consumeByte(OP_EOF, tb, &count);
|
||||
}
|
||||
@@ -19,3 +19,5 @@ void initCommand(int argc, const char* argv[]);
|
||||
void usageCommand(int argc, const char* argv[]);
|
||||
void helpCommand(int argc, const char* argv[]);
|
||||
void copyrightCommand(int argc, const char* argv[]);
|
||||
|
||||
void dissectBytecode(const char* tb, int size);
|
||||
86
source/literal_array.c
Normal file
86
source/literal_array.c
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "literal_array.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//exposed functions
|
||||
void initLiteralArray(LiteralArray* array) {
|
||||
array->capacity = 0;
|
||||
array->count = 0;
|
||||
array->literals = NULL;
|
||||
}
|
||||
|
||||
int writeLiteralArray(LiteralArray* array, Literal literal) {
|
||||
if (array->capacity < array->count + 1) {
|
||||
int oldCapacity = array->capacity;
|
||||
|
||||
array->capacity = GROW_CAPACITY(oldCapacity);
|
||||
array->literals = GROW_ARRAY(Literal, array->literals, oldCapacity, array->capacity);
|
||||
}
|
||||
|
||||
//if it's a string, make a local copy
|
||||
if (IS_STRING(literal)) {
|
||||
literal = TO_STRING_LITERAL(copyString(AS_STRING(literal), STRLEN(literal)));
|
||||
}
|
||||
|
||||
array->literals[array->count] = literal;
|
||||
return array->count++;
|
||||
}
|
||||
|
||||
void freeLiteralArray(LiteralArray* array) {
|
||||
//clean up memory
|
||||
for(int i = 0; i < array->count; i++) {
|
||||
freeLiteral(array->literals[i]);
|
||||
}
|
||||
|
||||
FREE_ARRAY(Literal, array->literals, array->capacity);
|
||||
initLiteralArray(array);
|
||||
}
|
||||
|
||||
//find a literal in the array that matches the "literal" argument
|
||||
int findLiteralIndex(LiteralArray* array, Literal literal) {
|
||||
for (int i = 0; i < array->count; i++) {
|
||||
//not the same type
|
||||
if (array->literals[i].type != literal.type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//matching type, compare values
|
||||
switch(array->literals[i].type) {
|
||||
case LITERAL_NULL:
|
||||
return i;
|
||||
|
||||
case LITERAL_BOOLEAN:
|
||||
if (AS_BOOLEAN(array->literals[i]) == AS_BOOLEAN(literal)) {
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_INTEGER:
|
||||
if (AS_INTEGER(array->literals[i]) == AS_INTEGER(literal)) {
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_FLOAT:
|
||||
if (AS_FLOAT(array->literals[i]) == AS_FLOAT(literal)) {
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
|
||||
case LITERAL_STRING:
|
||||
if (strcmp(AS_STRING(array->literals[i]), AS_STRING(literal)) == 0) {
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "[Internal] Unexpected literal type in findLiteralIndex(): %d\n", literal.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
16
source/literal_array.h
Normal file
16
source/literal_array.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "literal.h"
|
||||
|
||||
typedef struct LiteralArray {
|
||||
int capacity;
|
||||
int count;
|
||||
Literal* literals;
|
||||
} LiteralArray;
|
||||
|
||||
void initLiteralArray(LiteralArray* array);
|
||||
int writeLiteralArray(LiteralArray* array, Literal literal);
|
||||
void freeLiteralArray(LiteralArray* array);
|
||||
|
||||
int findLiteralIndex(LiteralArray* array, Literal literal);
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
#define ALLOCATE(type, count) ((type*)reallocate(NULL, 0, sizeof(type) * (count)))
|
||||
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
|
||||
#define GROW_CAPACITY(capacity) ((capacity) < 8 ? 8 : (capacity) * 2)
|
||||
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate(pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define FREE_ARRAY(type, pointer, oldCount) reallocate(pointer, sizeof(type) * (oldCount), 0)
|
||||
#define GROW_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define SHRINK_ARRAY(type, pointer, oldCount, count) (type*)reallocate((type*)pointer, sizeof(type) * (oldCount), sizeof(type) * (count))
|
||||
#define FREE_ARRAY(type, pointer, oldCount) reallocate((type*)pointer, sizeof(type) * (oldCount), 0)
|
||||
|
||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
void freeNode(Node* node) {
|
||||
switch(node->type) {
|
||||
case NODE_ATOMIC:
|
||||
case NODE_LITERAL:
|
||||
freeLiteral(node->atomic.literal);
|
||||
break;
|
||||
|
||||
@@ -23,18 +23,27 @@ void freeNode(Node* node) {
|
||||
FREE(Node, node);
|
||||
}
|
||||
|
||||
void emitAtomicLiteral(Node** nodeHandle, Literal literal) {
|
||||
void emitNodeLiteral(Node** nodeHandle, Literal literal) {
|
||||
//allocate a new node
|
||||
*nodeHandle = ALLOCATE(Node, 1);
|
||||
|
||||
(*nodeHandle)->type = NODE_ATOMIC;
|
||||
(*nodeHandle)->type = NODE_LITERAL;
|
||||
(*nodeHandle)->atomic.literal = literal;
|
||||
}
|
||||
|
||||
void emitNodeUnary(Node** nodeHandle, Opcode opcode) {
|
||||
//allocate a new node
|
||||
*nodeHandle = ALLOCATE(Node, 1);
|
||||
|
||||
(*nodeHandle)->type = NODE_UNARY;
|
||||
(*nodeHandle)->unary.opcode = opcode;
|
||||
(*nodeHandle)->unary.child = NULL;
|
||||
}
|
||||
|
||||
void printNode(Node* node) {
|
||||
switch(node->type) {
|
||||
case NODE_ATOMIC:
|
||||
printf("atomic:");
|
||||
case NODE_LITERAL:
|
||||
printf("literal:");
|
||||
printLiteral(node->atomic.literal);
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,43 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "opcodes.h"
|
||||
#include "literal.h"
|
||||
#include "opcodes.h"
|
||||
|
||||
//nodes are the intermediaries between parsers and compilers
|
||||
typedef union _node Node;
|
||||
|
||||
typedef enum NodeType {
|
||||
NODE_ATOMIC, //a simple value
|
||||
NODE_LITERAL, //a simple value
|
||||
NODE_UNARY, //one child
|
||||
NODE_BINARY, //two children, left and right
|
||||
// NODE_GROUPING,
|
||||
} NodeType;
|
||||
|
||||
typedef struct NodeAtomic {
|
||||
typedef struct NodeLiteral {
|
||||
NodeType type;
|
||||
Literal literal;
|
||||
} NodeAtomic;
|
||||
} NodeLiteral;
|
||||
|
||||
typedef struct NodeUnary {
|
||||
NodeType type;
|
||||
Opcode opcode;
|
||||
Node* child;
|
||||
} NodeUnary;
|
||||
|
||||
typedef struct NodeBinary {
|
||||
NodeType type;
|
||||
Opcode opcode;
|
||||
Node* left;
|
||||
Node* right;
|
||||
} NodeBinary;
|
||||
|
||||
union _node {
|
||||
NodeType type;
|
||||
NodeAtomic atomic;
|
||||
NodeLiteral atomic;
|
||||
NodeUnary unary;
|
||||
NodeBinary binary;
|
||||
};
|
||||
|
||||
void freeNode(Node* node);
|
||||
void emitAtomicLiteral(Node** nodeHandle, Literal literal);
|
||||
void emitNodeLiteral(Node** nodeHandle, Literal literal);
|
||||
void emitNodeUnary(Node** nodeHandle, Opcode opcode);
|
||||
|
||||
void printNode(Node* node);
|
||||
|
||||
|
||||
@@ -8,7 +8,13 @@ typedef enum Opcode {
|
||||
|
||||
//data
|
||||
OP_LITERAL,
|
||||
OP_LITERAL_LONG, //for more than 256 literals in a chunk
|
||||
|
||||
//operators
|
||||
OP_NEGATE,
|
||||
|
||||
//meta
|
||||
OP_SECTION_END,
|
||||
//TODO: add more
|
||||
} Opcode;
|
||||
|
||||
|
||||
@@ -110,12 +110,12 @@ ParseRule parseRules[];
|
||||
//forward declarations
|
||||
static void parsePrecedence(Parser* parser, Node** nodeHandle, PrecedenceRule rule);
|
||||
|
||||
//the atomic expression rules
|
||||
//the expression rules
|
||||
static void string(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
|
||||
//handle strings
|
||||
switch(parser->previous.type) {
|
||||
case TOKEN_LITERAL_STRING:
|
||||
emitAtomicLiteral(nodeHandle, TO_STRING_LITERAL(copyString(parser->previous.lexeme, parser->previous.length)));
|
||||
emitNodeLiteral(nodeHandle, TO_STRING_LITERAL(copyString(parser->previous.lexeme, parser->previous.length)));
|
||||
break;
|
||||
|
||||
//TODO: interpolated strings
|
||||
@@ -130,21 +130,67 @@ static void binary(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
|
||||
}
|
||||
|
||||
static void unary(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
|
||||
//TODO
|
||||
switch(parser->previous.type) {
|
||||
case TOKEN_MINUS:
|
||||
//temp handle to potentially negate values
|
||||
Node* tmpNode = NULL;
|
||||
parsePrecedence(parser, &tmpNode, PREC_TERNARY);
|
||||
|
||||
//check for literals
|
||||
if (tmpNode->type == NODE_LITERAL) {
|
||||
//negate directly, if int or float
|
||||
Literal lit = tmpNode->atomic.literal;
|
||||
|
||||
if (IS_INTEGER(lit)) {
|
||||
lit = TO_INTEGER_LITERAL( -AS_INTEGER(lit) );
|
||||
}
|
||||
|
||||
if (IS_FLOAT(lit)) {
|
||||
lit = TO_FLOAT_LITERAL( -AS_FLOAT(lit) );
|
||||
}
|
||||
|
||||
tmpNode->atomic.literal = lit;
|
||||
*nodeHandle = tmpNode;
|
||||
}
|
||||
else {
|
||||
//process normally
|
||||
emitNodeUnary(nodeHandle, OP_NEGATE);
|
||||
parsePrecedence(parser, nodeHandle, PREC_TERNARY);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
error(parser, parser->previous, "Unexpected token passed to unary precedence rule");
|
||||
}
|
||||
}
|
||||
|
||||
static void atomic(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
|
||||
switch(parser->previous.type) {
|
||||
case TOKEN_NULL:
|
||||
emitAtomicLiteral(nodeHandle, TO_NULL_LITERAL);
|
||||
emitNodeLiteral(nodeHandle, TO_NULL_LITERAL);
|
||||
break;
|
||||
|
||||
case TOKEN_LITERAL_TRUE:
|
||||
emitAtomicLiteral(nodeHandle, TO_BOOLEAN_LITERAL(true));
|
||||
emitNodeLiteral(nodeHandle, TO_BOOLEAN_LITERAL(true));
|
||||
break;
|
||||
|
||||
case TOKEN_LITERAL_FALSE:
|
||||
emitAtomicLiteral(nodeHandle, TO_BOOLEAN_LITERAL(false));
|
||||
emitNodeLiteral(nodeHandle, TO_BOOLEAN_LITERAL(false));
|
||||
break;
|
||||
|
||||
case TOKEN_LITERAL_INTEGER: {
|
||||
int value = 0;
|
||||
sscanf(parser->previous.lexeme, "%d", &value);
|
||||
emitNodeLiteral(nodeHandle, TO_INTEGER_LITERAL(value));
|
||||
}
|
||||
break;
|
||||
|
||||
case TOKEN_LITERAL_FLOAT: {
|
||||
float value = 0;
|
||||
sscanf(parser->previous.lexeme, "%f", &value);
|
||||
emitNodeLiteral(nodeHandle, TO_FLOAT_LITERAL(value));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -190,13 +236,13 @@ ParseRule parseRules[] = { //must match the token types
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_IDENTIFIER,
|
||||
{atomic, NULL, PREC_NONE},// TOKEN_LITERAL_TRUE,
|
||||
{atomic, NULL, PREC_NONE},// TOKEN_LITERAL_FALSE,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_LITERAL_INTEGER,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_LITERAL_FLOAT,
|
||||
{atomic, NULL, PREC_NONE},// TOKEN_LITERAL_INTEGER,
|
||||
{atomic, NULL, PREC_NONE},// TOKEN_LITERAL_FLOAT,
|
||||
{string, NULL, PREC_PRIMARY},// TOKEN_LITERAL_STRING,
|
||||
|
||||
//math operators
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_PLUS,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_MINUS,
|
||||
{unary, NULL, PREC_NONE},// TOKEN_MINUS,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_MULTIPLY,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_DIVIDE,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_MODULO,
|
||||
@@ -287,6 +333,7 @@ static void printStmt(Parser* parser, Node* node) {
|
||||
|
||||
//set the node info
|
||||
node->type = NODE_UNARY;
|
||||
node->unary.opcode = OP_PRINT;
|
||||
node->unary.child = ALLOCATE(Node, 1);
|
||||
expression(parser, &(node->unary.child));
|
||||
|
||||
@@ -329,7 +376,12 @@ void initParser(Parser* parser, Lexer* lexer) {
|
||||
}
|
||||
|
||||
void freeParser(Parser* parser) {
|
||||
initParser(parser, NULL);
|
||||
parser->lexer = NULL;
|
||||
parser->error = false;
|
||||
parser->panic = false;
|
||||
|
||||
parser->previous.type = TOKEN_NULL;
|
||||
parser->current.type = TOKEN_NULL;
|
||||
}
|
||||
|
||||
Node* scanParser(Parser* parser) {
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
//#include "toy.h"
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -131,21 +134,34 @@ void repl() {
|
||||
void debug() {
|
||||
Lexer lexer;
|
||||
Parser parser;
|
||||
Compiler compiler;
|
||||
|
||||
char* source = readFile(command.filename);
|
||||
|
||||
initLexer(&lexer, source);
|
||||
initParser(&parser, &lexer);
|
||||
initCompiler(&compiler);
|
||||
|
||||
//run the parser until the end of the source
|
||||
Node* node = scanParser(&parser);
|
||||
while(node != NULL) {
|
||||
printNode(node);
|
||||
writeCompiler(&compiler, node);
|
||||
|
||||
freeNode(node);
|
||||
|
||||
node = scanParser(&parser);
|
||||
}
|
||||
|
||||
//get the data dump
|
||||
int size = 0;
|
||||
const char* tb = collateCompiler(&compiler, &size);
|
||||
|
||||
dissectBytecode(tb, size);
|
||||
|
||||
//cleanup
|
||||
FREE_ARRAY(char, tb, size);
|
||||
freeCompiler(&compiler);
|
||||
freeParser(&parser);
|
||||
}
|
||||
|
||||
//entry point
|
||||
|
||||
0
source/token.h
Normal file
0
source/token.h
Normal file
Reference in New Issue
Block a user