Added scopes using '{}' symbols, read more

I've also added a new literal type called 'identifier'. This will be
used for variable names, and has a type mask embedded in it.
This commit is contained in:
2022-08-08 15:02:12 +01:00
parent 08ce270e06
commit 6d5549fc8e
15 changed files with 335 additions and 68 deletions

View File

@@ -14,7 +14,16 @@ print -69;
print -4.20;
print 2 + (3 * 3);
assert true, "This won't be seen";
//test scopes
{
print "This statement is within a scope.";
{
print "This is a deeper scope.";
}
}
print "Back to the outer scope.";
//test asserts
assert true, "This won't be seen";
assert false, "This is an error";

View File

@@ -33,7 +33,7 @@ void writeCompiler(Compiler* compiler, Node* node) {
switch(node->type) {
//TODO: more types, like variables, etc.
case NODE_ERROR: {
printf("[internal] NODE_ERROR encountered in writeCompiler()");
fprintf(stderr, "[Internal] NODE_ERROR encountered in writeCompiler()");
compiler->bytecode[compiler->count++] = OP_EOF; //1 byte
}
break;
@@ -80,6 +80,16 @@ void writeCompiler(Compiler* compiler, Node* node) {
compiler->bytecode[compiler->count++] = (unsigned char)OP_GROUPING_END; //1 byte
break;
case NODE_BLOCK:
compiler->bytecode[compiler->count++] = (unsigned char)OP_SCOPE_BEGIN; //1 byte
for (int i = 0; i < node->block.count; i++) {
writeCompiler(compiler, &(node->block.nodes[i]));
}
compiler->bytecode[compiler->count++] = (unsigned char)OP_SCOPE_END; //1 byte
break;
//TODO: conditional
}
}
@@ -202,6 +212,10 @@ unsigned char* collateCompiler(Compiler* compiler, int* size) {
emitByte(&collation, &capacity, &count, '\0'); //terminate the string
}
break;
default:
fprintf(stderr, "[Internal] Unknown literal type encountered within literal cache\n");
return NULL;
}
}

View File

@@ -19,6 +19,7 @@ static void stderrWrapper(const char* output) {
void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int length) {
initLiteralArray(&interpreter->literalCache);
interpreter->scope = pushScope(NULL);
interpreter->bytecode = bytecode;
interpreter->length = length;
interpreter->count = 0;
@@ -31,6 +32,7 @@ void initInterpreter(Interpreter* interpreter, unsigned char* bytecode, int leng
void freeInterpreter(Interpreter* interpreter) {
freeLiteralArray(&interpreter->literalCache);
interpreter->scope = popScope(interpreter->scope);
FREE_ARRAY(char, interpreter->bytecode, interpreter->length);
freeLiteralArray(&interpreter->stack);
}
@@ -283,6 +285,15 @@ static void execInterpreter(Interpreter* interpreter) {
case OP_GROUPING_END:
return;
//scope
case OP_SCOPE_BEGIN:
interpreter->scope = pushScope(interpreter->scope);
break;
case OP_SCOPE_END:
interpreter->scope = popScope(interpreter->scope);
break;
default:
printf("Unknown opcode found %d, terminating\n", opcode);
printLiteralArray(&interpreter->stack, "\n");

View File

@@ -4,12 +4,14 @@
#include "literal_array.h"
#include "literal_dictionary.h"
#include "scope.h"
typedef void (*PrintFn)(const char*);
//the interpreter acts depending on the bytecode instructions
typedef struct Interpreter {
LiteralArray literalCache; //generally doesn't change after initialization
Scope* scope;
unsigned char* bytecode;
int length;
int count;

View File

@@ -43,9 +43,16 @@ void printLiteralCustom(Literal literal, void (printFn)(const char*)) {
}
break;
case LITERAL_IDENTIFIER: {
char buffer[256];
snprintf(buffer, 256, "%.*s", STRLEN_I(literal), AS_IDENTIFIER(literal));
printFn(buffer);
}
break;
default:
//should never bee seen
fprintf(stderr, "[Internal] Unrecognized literal type: %d", literal.type);
fprintf(stderr, "[Internal] Unrecognized literal type in print: %d\n", literal.type);
}
}
@@ -60,8 +67,12 @@ bool _isTruthy(Literal x) {
return (IS_NULL(x) || (IS_BOOLEAN(x) && AS_BOOLEAN(x)) || (IS_INTEGER(x) && AS_INTEGER(x) != 0) || (IS_FLOAT(x) && AS_FLOAT(x) != 0));
}
Literal _toStringLiteral(char* cstr) {
return ((Literal){LITERAL_STRING, { .string.ptr = (char*)cstr, .string.length = strlen((char*)cstr) }});
Literal _toStringLiteral(char* str) {
return ((Literal){LITERAL_STRING, {.string.ptr = (char*)str, .string.length = strlen((char*)str)}});
}
Literal _toIdentifierLiteral(char* str, unsigned char types) {
return ((Literal){LITERAL_IDENTIFIER,{.identifier.ptr = (char*)str,.identifier.length = strlen((char*)str),.identifier.types = types}});
}
char* copyString(char* original, int length) {
@@ -102,9 +113,15 @@ bool literalsAreEqual(Literal lhs, Literal rhs) {
}
return !strncmp(AS_STRING(lhs), AS_STRING(rhs), STRLEN(lhs));
case LITERAL_IDENTIFIER:
if (STRLEN_I(lhs) != STRLEN_I(rhs)) {
return false;
}
return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), STRLEN_I(lhs));
default:
//should never bee seen
fprintf(stderr, "[Internal] Unrecognized literal type: %d", lhs.type);
fprintf(stderr, "[Internal] Unrecognized literal type: %d\n", lhs.type);
return false;
}
}
@@ -140,14 +157,17 @@ int hashLiteral(Literal lit) {
return hash((unsigned int)AS_INTEGER(lit));
case LITERAL_FLOAT:
return hash( *(unsigned int*)(&AS_FLOAT(lit)) );
return hash(*(unsigned int*)(&AS_FLOAT(lit)));
case LITERAL_STRING:
return hashString(AS_STRING(lit), STRLEN(lit));
case LITERAL_IDENTIFIER:
return hashString(AS_IDENTIFIER(lit), STRLEN_I(lit));
default:
//should never bee seen
fprintf(stderr, "[Internal] Unrecognized literal type in hash: %d", lit.type);
fprintf(stderr, "[Internal] Unrecognized literal type in hash: %d\n", lit.type);
return 0;
}
}

View File

@@ -13,6 +13,7 @@ typedef enum {
// LITERAL_ARRAY,
// LITERAL_DICTIONARY,
// LITERAL_FUNCTION,
LITERAL_IDENTIFIER,
} LiteralType;
typedef struct {
@@ -23,13 +24,19 @@ typedef struct {
float number;
struct {
char* ptr;
int length; //could possibly cut it down further by removing this
int length;
} string;
// //experimental
// void* array;
// void* dictionary;
// void* function;
struct { //for variable names
char* ptr;
int length;
unsigned char types;
} identifier;
} as;
} Literal;
@@ -41,24 +48,46 @@ typedef struct {
#define IS_ARRAY(value) ((value).type == LITERAL_ARRAY)
#define IS_DICTIONARY(value) ((value).type == LITERAL_DICTIONARY)
#define IS_FUNCTION(value) ((value).type == LITERAL_FUNCTION)
#define IS_IDENTIFIER(value) ((value).type == LITERAL_IDENTIFIER)
#define AS_BOOLEAN(value) ((value).as.boolean)
#define AS_INTEGER(value) ((value).as.integer)
#define AS_FLOAT(value) ((value).as.number)
#define AS_STRING(value) ((value).as.string.ptr)
// #define AS_ARRAY_PTR(value)
// #define AS_DICTIONARY_PTR(value)
// #define AS_FUNCTION_PTR(value) ((Function*)((value).as.function))
// #define AS_ARRAY(value)
// #define AS_DICTIONARY(value)
// #define AS_FUNCTION(value)
#define AS_IDENTIFIER(value) ((value).as.identifier.ptr)
#define TO_NULL_LITERAL ((Literal){LITERAL_NULL, { .integer = 0 }})
#define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }})
#define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }})
#define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }})
#define TO_STRING_LITERAL(value) _toStringLiteral(value)
// #define TO_ARRAY_PTR
// #define TO_DICTIONARY_PTR
// #define TO_FUNCTION_PTR(value) ((Literal){LITERAL_FUNCTION, { .function = (Function*)value }})
// #define TO_ARRAY_LITERAL
// #define TO_DICTIONARY_LITERAL
// #define TO_FUNCTION_LITERAL
#define TO_IDENTIFIER_LITERAL(value, types) _toIdentifierLiteral(value, types)
#define MASK(x) (1 >> (x))
#define TYPE_CONST 0
#define TYPE_BOOLEAN 1
#define TYPE_INTEGER 2
#define TYPE_FLOAT 3
#define TYPE_STRING 4
#define TYPE_ARRAY 5
#define TYPE_DICTIONARY 6
#define TYPE_FUNCTION 7
#define MASK_CONST (MASK(TYPE_CONST))
#define MASK_BOOLEAN (MASK(TYPE_BOOLEAN))
#define MASK_INTEGER (MASK(TYPE_INTEGER))
#define MASK_FLOAT (MASK(TYPE_FLOAT))
#define MASK_STRING (MASK(TYPE_STRING))
#define MASK_ARRAY (MASK(TYPE_ARRAY))
#define MASK_DICTIONARY (MASK(TYPE_DICTIONARY))
#define MASK_FUNCTION (MASK(TYPE_FUNCTION))
//utils
void printLiteral(Literal literal);
void printLiteralCustom(Literal literal, void (printFn)(const char*));
void freeLiteral(Literal literal);
@@ -66,10 +95,13 @@ void freeLiteral(Literal literal);
#define IS_TRUTHY(x) _isTruthy(x)
#define STRLEN(lit) ((lit).as.string.length)
#define STRLEN_I(lit) ((lit).as.identifier.length)
#define TYPES(lit) ((lit).as.identifier.types)
//BUGFIX: macros are not functions
bool _isTruthy(Literal x);
Literal _toStringLiteral(char* cstr);
Literal _toStringLiteral(char* str);
Literal _toIdentifierLiteral(char* str, unsigned char types);
//utils
char* copyString(char* original, int length);

View File

@@ -199,3 +199,8 @@ void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
}
}
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key) {
//null & not tombstoned
_entry* entry = getEntryArray(dictionary->entries, dictionary->capacity, key, hashLiteral(key), false);
return !(IS_NULL(entry->key) && IS_NULL(entry->value));
}

View File

@@ -23,3 +23,5 @@ void freeLiteralDictionary(LiteralDictionary* dictionary);
void setLiteralDictionary(LiteralDictionary* dictionary, Literal key, Literal value);
Literal getLiteralDictionary(LiteralDictionary* dictionary, Literal key);
void removeLiteralDictionary(LiteralDictionary* dictionary, Literal key);
bool existsLiteralDictionary(LiteralDictionary* dictionary, Literal key);

View File

@@ -3,6 +3,7 @@
#include "memory.h"
#include <stdio.h>
#include <stdlib.h>
void freeNode(Node* node) {
//don't free a NULL node
@@ -31,6 +32,12 @@ void freeNode(Node* node) {
case NODE_GROUPING:
freeNode(node->grouping.child);
break;
case NODE_BLOCK:
for (int i = 0; i < node->block.count; i++) {
freeNode(node->block.nodes + i);
}
break;
}
FREE(Node, node);
@@ -73,6 +80,17 @@ void emitNodeGrouping(Node** nodeHandle) {
*nodeHandle = tmp;
}
void emitNodeBlock(Node** nodeHandle) {
Node* tmp = ALLOCATE(Node, 1);
tmp->type = NODE_BLOCK;
tmp->block.nodes = NULL;
tmp->block.capacity = 0;
tmp->block.count = 0;
*nodeHandle = tmp;
}
void printNode(Node* node) {
if (node == NULL) {
return;
@@ -106,5 +124,15 @@ void printNode(Node* node) {
printNode(node->grouping.child);
printf(")");
break;
case NODE_BLOCK:
printf("{\n");
for (int i = 0; i < node->block.count; i++) {
printNode(&(node->block.nodes[i]));
}
printf("}\n");
break;
}
}

View File

@@ -12,6 +12,7 @@ typedef enum NodeType {
NODE_UNARY, //one child
NODE_BINARY, //two children, left and right
NODE_GROUPING, //one child
NODE_BLOCK, //contains bytecode
// NODE_CONDITIONAL, //three children: conditional, then path, else path
} NodeType;
@@ -38,12 +39,20 @@ typedef struct NodeGrouping {
Node* child;
} NodeGrouping;
typedef struct NodeBlock {
NodeType type;
Node* nodes;
int capacity;
int count;
} NodeBlock;
union _node {
NodeType type;
NodeLiteral atomic;
NodeUnary unary;
NodeBinary binary;
NodeGrouping grouping;
NodeBlock block;
};
void freeNode(Node* node);
@@ -51,6 +60,7 @@ void emitNodeLiteral(Node** nodeHandle, Literal literal);
void emitNodeUnary(Node** nodeHandle, Opcode opcode);
void emitNodeBinary(Node** nodeHandle, Node* rhs, Opcode opcode);
void emitNodeGrouping(Node** nodeHandle);
void emitNodeBlock(Node** nodeHandle);
void printNode(Node* node);

View File

@@ -20,6 +20,8 @@ typedef enum Opcode {
OP_MODULO,
OP_GROUPING_BEGIN,
OP_GROUPING_END,
OP_SCOPE_BEGIN,
OP_SCOPE_END,
//meta
OP_SECTION_END,

View File

@@ -111,6 +111,7 @@ typedef struct {
ParseRule parseRules[];
//forward declarations
static void declaration(Parser* parser, Node** nodeHandle);
static void parsePrecedence(Parser* parser, Node** nodeHandle, PrecedenceRule rule);
//the expression rules
@@ -523,6 +524,37 @@ static void expression(Parser* parser, Node** nodeHandle) {
}
//statements
static void blockStmt(Parser* parser, Node* node) {
//init
node->type = NODE_BLOCK;
node->block.nodes = NULL;
node->block.capacity = 0;
node->block.count = 0;
//sub-scope, compile it and push it up in a node
while (!match(parser, TOKEN_BRACE_RIGHT)) {
if (node->block.capacity < node->block.count + 1) {
int oldCapacity = node->block.capacity;
node->block.capacity = GROW_CAPACITY(oldCapacity);
node->block.nodes = GROW_ARRAY(Node, node->block.nodes, oldCapacity, node->block.capacity);
}
//use the next node in sequence
node->block.nodes[node->block.count].type = NODE_ERROR; //BUGFIX: so freeing won't break the damn thing
Node* ptr = &(node->block.nodes[node->block.count++]);
//process the grammar rule for this line
declaration(parser, &ptr);
// Ground floor: perfumery / Stationery and leather goods / Wigs and haberdashery / Kitchenware and food / Going up!
if (parser->panic) {
return;
}
}
}
static void printStmt(Parser* parser, Node* node) {
//set the node info
node->type = NODE_UNARY;
@@ -550,6 +582,12 @@ static void expressionStmt(Parser* parser, Node* node) {
}
static void statement(Parser* parser, Node* node) {
//block
if (match(parser, TOKEN_BRACE_LEFT)) {
blockStmt(parser, node);
return;
}
//print
if (match(parser, TOKEN_PRINT)) {
printStmt(parser, node);
@@ -567,14 +605,9 @@ static void statement(Parser* parser, Node* node) {
}
static void declaration(Parser* parser, Node** nodeHandle) {
statement(parser, *nodeHandle);
//TODO: variable declarations
if (parser->panic) {
synchronize(parser);
//return an error node for this iteration
*nodeHandle = ALLOCATE(Node, 1);
(*nodeHandle)->type = NODE_ERROR;
}
statement(parser, *nodeHandle);
}
//exposed functions
@@ -610,5 +643,12 @@ Node* scanParser(Parser* parser) {
//process the grammar rule for this line
declaration(parser, &node);
if (parser->panic) {
synchronize(parser);
//return an error node for this iteration
node = ALLOCATE(Node, 1);
node->type = NODE_ERROR;
}
return node;
}

View File

@@ -194,23 +194,8 @@ void debug() {
initLiteralDictionary(&dictionary);
for (int i = 0; i < 100; i++) {
setLiteralDictionary(&dictionary, TO_INTEGER_LITERAL(i), TO_INTEGER_LITERAL(i * 2));
}
for (int i = 0; i < 100; i++) {
printf("%d: ", i);
printLiteral( getLiteralDictionary(&dictionary, TO_INTEGER_LITERAL(i)) );
printf("\n");
}
printf("-------------");
for (int i = 0; i < dictionary.capacity; i++) {
printf("%d: ", i);
printLiteral(dictionary.entries[i].key);
printf("\n");
}
setLiteralDictionary(&dictionary, TO_IDENTIFIER_LITERAL("variable", MASK_INTEGER), TO_INTEGER_LITERAL(2));
printLiteral( getLiteralDictionary(&dictionary, TO_IDENTIFIER_LITERAL("variable", MASK_INTEGER)) );
freeLiteralDictionary(&dictionary);
}

86
source/scope.c Normal file
View File

@@ -0,0 +1,86 @@
#include "scope.h"
#include "memory.h"
//run up the ancestor chain, freeing anything with 0 references left
static void freeAncestorChain(Scope* scope) {
scope->references--;
//free scope chain
if (scope->ancestor != NULL) {
freeAncestorChain(scope->ancestor);
}
if (scope->references > 0) {
return;
}
freeLiteralDictionary(&scope->variables);
FREE(Scope, scope);
}
//exposed functions
Scope* pushScope(Scope* ancestor) {
Scope* scope = ALLOCATE(Scope, 1);
scope->ancestor = ancestor;
initLiteralDictionary(&scope->variables);
//tick up all scope reference counts
scope->references = 0;
for (Scope* ptr = scope; ptr; ptr = ptr->ancestor) {
ptr->references++;
}
return scope;
}
Scope* popScope(Scope* scope) {
Scope* ret = scope->ancestor;
freeAncestorChain(scope);
return ret;
}
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key) {
//don't redefine a variable within this scope
if (existsLiteralDictionary(&scope->variables, key)) {
return false;
}
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
return true;
}
//return false if undefined
bool setScopeVariable(Scope* scope, Literal key, Literal value) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return setScopeVariable(scope->ancestor, key, value);
}
setLiteralDictionary(&scope->variables, key, value);
return true;
}
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
//dead end
if (scope == NULL) {
return false;
}
//if it's not in this scope, keep searching up the chain
if (!existsLiteralDictionary(&scope->variables, key)) {
return getScopeVariable(scope->ancestor, key, valueHandle);
}
*valueHandle = getLiteralDictionary(&scope->variables, key);
return true;
}

21
source/scope.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "common.h"
#include "literal_dictionary.h"
typedef struct Scope {
LiteralDictionary variables;
struct Scope* ancestor;
int references; //how many scopes point here
} Scope;
Scope* pushScope(Scope* scope);
Scope* popScope(Scope* scope);
//returns false if error
bool declareScopeVariable(Scope* scope, Literal key);
//return false if undefined
bool setScopeVariable(Scope* scope, Literal key, Literal value);
bool getScopeVariable(Scope* scope, Literal key, Literal* value);