mirror of
https://github.com/CPunch/Cosmo.git
synced 2025-01-13 23:10:06 +00:00
Initial commit
This commit is contained in:
commit
2e1b745624
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.o
|
||||
bin
|
12
.vscode/settings.json
vendored
Normal file
12
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"functional": "cpp",
|
||||
"istream": "cpp",
|
||||
"ostream": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"vector": "cpp"
|
||||
}
|
||||
}
|
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Seth Stubbs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
48
Makefile
Normal file
48
Makefile
Normal file
@ -0,0 +1,48 @@
|
||||
# make clean && make && ./bin/cosmo
|
||||
|
||||
CC=clang
|
||||
CFLAGS=-fPIE -O3 #-g3
|
||||
LDFLAGS=#-fsanitize=address
|
||||
OUT=bin/cosmo
|
||||
|
||||
CHDR=\
|
||||
src/cchunk.h\
|
||||
src/cdebug.h\
|
||||
src/clex.h\
|
||||
src/cmem.h\
|
||||
src/coperators.h\
|
||||
src/cosmo.h\
|
||||
src/cparse.h\
|
||||
src/cstate.h\
|
||||
src/cvalue.h\
|
||||
src/ctable.h\
|
||||
src/cvm.h\
|
||||
src/cobj.h\
|
||||
src/cbaselib.h\
|
||||
|
||||
CSRC=\
|
||||
src/cchunk.c\
|
||||
src/cdebug.c\
|
||||
src/clex.c\
|
||||
src/cmem.c\
|
||||
src/coperators.c\
|
||||
src/cparse.c\
|
||||
src/cstate.c\
|
||||
src/cvalue.c\
|
||||
src/ctable.c\
|
||||
src/cvm.c\
|
||||
src/cobj.c\
|
||||
src/cbaselib.c\
|
||||
src/main.c\
|
||||
|
||||
COBJ=$(CSRC:.c=.o)
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(OUT): $(COBJ) $(CHDR)
|
||||
mkdir -p bin
|
||||
$(CC) $(COBJ) $(LDFLAGS) -o $(OUT)
|
||||
|
||||
clean:
|
||||
rm -rf $(COBJ) $(OUT)
|
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Cosmo
|
||||
Cosmo is a portable scripting language loosely based off of Lua. Designed for embeddability, Cosmo will have a built-in C++ wrapper for ease of use for embedding in in C++ applications.
|
||||
|
||||
# Why Cosmo?
|
||||
While C++ wrappers for Lua exist (see: SolLua), they're all maintained by outside entitties while Cosmo writes it's own first party wrapper. Additionally, Cosmo is very easily modifiable having been written in clean C99 with well documented code; this makes it a great candidate for early language hackers and researchers alike.
|
||||
|
||||
However Cosmo is not just a friendly developer tool, Cosmo's easy syntax and readability makes it a great scripting language for everyone to use.
|
13
fortest.lua
Normal file
13
fortest.lua
Normal file
@ -0,0 +1,13 @@
|
||||
local function fact(num)
|
||||
var total = 1
|
||||
for (var i = num; i > 0; i = i - 1) do
|
||||
total = total * i
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
for (var x = 0; x < 1000; x=x+1) do
|
||||
for (var z = 0; z < 100; z=z+1) do
|
||||
print("The factorial of " .. z .. " is " .. fact(z))
|
||||
end
|
||||
end
|
18
src/cbaselib.c
Normal file
18
src/cbaselib.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include "cbaselib.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
void cosmoB_loadlibrary(CState *state) {
|
||||
cosmoV_register(state, "print", cosmoV_newObj(cosmoO_newCFunction(state, cosmoB_print)));
|
||||
}
|
||||
|
||||
int cosmoB_print(CState *state, int nargs, CValue *args) {
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
CObjString *str = cosmoV_toString(state, args[i]);
|
||||
printf("%s", cosmoO_readCString(str));
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 0; // print doesn't return any args
|
||||
}
|
9
src/cbaselib.h
Normal file
9
src/cbaselib.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef COSMO_BASELIB
|
||||
#define COSMO_BASELIB
|
||||
|
||||
#include "cstate.h"
|
||||
|
||||
COSMO_API void cosmoB_loadlibrary(CState *state);
|
||||
COSMO_API int cosmoB_print(CState *state, int nargs, CValue *args);
|
||||
|
||||
#endif
|
70
src/cchunk.c
Normal file
70
src/cchunk.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include "cmem.h"
|
||||
#include "cchunk.h"
|
||||
#include "cvalue.h"
|
||||
#include "cvm.h"
|
||||
|
||||
CChunk *newChunk(CState* state, size_t startCapacity) {
|
||||
CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk));
|
||||
initChunk(state, chunk, startCapacity);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
|
||||
chunk->capacity = startCapacity;
|
||||
chunk->lineCapacity = startCapacity;
|
||||
chunk->count = 0;
|
||||
chunk->buf = NULL; // when writeByteChunk is called, it'll allocate the array for us
|
||||
chunk->lineInfo = NULL;
|
||||
|
||||
// constants
|
||||
initValArray(state, &chunk->constants, ARRAY_START);
|
||||
}
|
||||
|
||||
void cleanChunk(CState* state, CChunk *chunk) {
|
||||
// first, free the chunk buffer
|
||||
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
|
||||
// then the line info
|
||||
cosmoM_freearray(state, int, chunk->lineInfo, chunk->capacity);
|
||||
// free the constants
|
||||
cleanValArray(state, &chunk->constants);
|
||||
}
|
||||
|
||||
void freeChunk(CState* state, CChunk *chunk) {
|
||||
cleanChunk(state, chunk);
|
||||
// now, free the wrapper struct
|
||||
cosmoM_free(state, CChunk, chunk);
|
||||
}
|
||||
|
||||
int addConstant(CState* state, CChunk *chunk, CValue value) {
|
||||
// before adding the constant, check if we already have it
|
||||
for (int i = 0; i < chunk->constants.count; i++) {
|
||||
if (cosmoV_equal(value, chunk->constants.values[i]))
|
||||
return i; // we already have a matching constant!
|
||||
}
|
||||
|
||||
cosmoM_freezeGC(state); // so our GC doesn't free it
|
||||
appendValArray(state, &chunk->constants, value);
|
||||
cosmoM_unfreezeGC(state);
|
||||
return chunk->constants.count - 1; // return the index of the new constants
|
||||
}
|
||||
|
||||
// ================================================================ [WRITE TO CHUNK] ================================================================
|
||||
|
||||
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
|
||||
// does the buffer need to be reallocated?
|
||||
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||
|
||||
// write data to the chunk :)
|
||||
chunk->lineInfo[chunk->count] = line;
|
||||
chunk->buf[chunk->count++] = i;
|
||||
}
|
||||
|
||||
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) {
|
||||
INSTRUCTION *buffer = (INSTRUCTION*)(&i);
|
||||
int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
writeu8Chunk(state, chunk, buffer[i], line);
|
||||
}
|
||||
}
|
39
src/cchunk.h
Normal file
39
src/cchunk.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef CCHUNK_H
|
||||
#define CCHUNK_H
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
#include "coperators.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
typedef struct CValueArray CValueArray;
|
||||
|
||||
typedef struct CChunk {
|
||||
size_t capacity; // the ammount of space we've allocated for
|
||||
size_t count; // the space we're currently using
|
||||
INSTRUCTION *buf; // whole chunk
|
||||
CValueArray constants; // holds constants
|
||||
size_t lineCapacity;
|
||||
int *lineInfo;
|
||||
} CChunk;
|
||||
|
||||
CChunk *newChunk(CState* state, size_t startCapacity);
|
||||
void initChunk(CState* state, CChunk *chunk, size_t startCapacity);
|
||||
void cleanChunk(CState* state, CChunk *chunk); // free's everything but the struct
|
||||
void freeChunk(CState* state, CChunk *chunk); // free's everything including the struct
|
||||
int addConstant(CState* state, CChunk *chunk, CValue value);
|
||||
|
||||
// write to chunk
|
||||
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line);
|
||||
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line);
|
||||
|
||||
// read from chunk
|
||||
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) {
|
||||
return chunk->buf[offset];
|
||||
}
|
||||
|
||||
static inline uint16_t readu16Chunk(CChunk *chunk, int offset) {
|
||||
return *((uint16_t*)(&chunk->buf[offset]));
|
||||
}
|
||||
|
||||
#endif
|
158
src/cdebug.c
Normal file
158
src/cdebug.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include "cdebug.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
void printIndent(int indent) {
|
||||
for (int i = 0; i < indent; i++)
|
||||
printf("\t");
|
||||
}
|
||||
|
||||
int simpleInstruction(const char *name, int offset) {
|
||||
printf("%s", name);
|
||||
return offset + 1; // consume opcode
|
||||
}
|
||||
|
||||
int shortOperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
int longOperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||
}
|
||||
|
||||
int constInstruction(const char *name, CChunk *chunk, int offset, int indent) {
|
||||
int index = readu16Chunk(chunk, offset + 1);
|
||||
printf("%-16s [%05d] - ", name, index);
|
||||
CValue val = chunk->constants.values[index];
|
||||
|
||||
printValue(val);
|
||||
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint
|
||||
}
|
||||
|
||||
int ABOperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
int args = readu8Chunk(chunk, offset + 1);
|
||||
int nresults = readu8Chunk(chunk, offset + 2);
|
||||
|
||||
printf("%-16s [%03d] [%03d]", name, args, nresults);
|
||||
return offset + 3;
|
||||
}
|
||||
|
||||
// public methods in the cdebug.h header
|
||||
|
||||
void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
||||
printIndent(indent);
|
||||
printf("===[[ %s ]]===\n", name);
|
||||
|
||||
for (int offset = 0; offset < chunk->count;) {
|
||||
offset = disasmInstr(chunk, offset, indent);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
printIndent(indent);
|
||||
printf("%04d ", offset);
|
||||
|
||||
INSTRUCTION i = chunk->buf[offset];
|
||||
int line = chunk->lineInfo[offset];
|
||||
|
||||
if (offset > 0 && line == chunk->lineInfo[offset - 1]) {
|
||||
printf(" | ");
|
||||
} else {
|
||||
printf("%4d ", line);
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case OP_LOADCONST:
|
||||
return constInstruction("OP_LOADCONST", chunk, offset, indent);
|
||||
case OP_SETGLOBAL:
|
||||
return constInstruction("OP_SETGLOBAL", chunk, offset, indent);
|
||||
case OP_GETGLOBAL:
|
||||
return constInstruction("OP_GETGLOBAL", chunk, offset, indent);
|
||||
case OP_SETLOCAL:
|
||||
return shortOperandInstruction("OP_SETLOCAL", chunk, offset);
|
||||
case OP_GETLOCAL:
|
||||
return shortOperandInstruction("OP_GETLOCAL", chunk, offset);
|
||||
case OP_SETUPVAL:
|
||||
return shortOperandInstruction("OP_SETUPVAL", chunk, offset);
|
||||
case OP_GETUPVAL:
|
||||
return shortOperandInstruction("OP_GETUPVAL", chunk, offset);
|
||||
case OP_PEJMP:
|
||||
return longOperandInstruction("OP_PEJMP", chunk, offset);
|
||||
case OP_EJMP:
|
||||
return longOperandInstruction("OP_EJMP", chunk, offset);
|
||||
case OP_JMP:
|
||||
return longOperandInstruction("OP_JMP", chunk, offset);
|
||||
case OP_JMPBACK:
|
||||
return longOperandInstruction("OP_JMPBACK", chunk, offset);
|
||||
case OP_POP:
|
||||
return shortOperandInstruction("OP_POP", chunk, offset);
|
||||
case OP_CALL:
|
||||
return ABOperandInstruction("OP_CALL", chunk, offset);
|
||||
case OP_CLOSURE: {
|
||||
int index = readu16Chunk(chunk, offset + 1);
|
||||
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
|
||||
CValue val = chunk->constants.values[index];
|
||||
CObjFunction *cobjFunc = (CObjFunction*)val.val.obj;
|
||||
offset += 3; // we consumed the opcode + u16
|
||||
|
||||
printValue(val);
|
||||
printf("\n");
|
||||
|
||||
// list the upvalues/locals that are captured
|
||||
for (int i = 0; i < cobjFunc->upvals; i++) {
|
||||
uint8_t encoding = readu8Chunk(chunk, offset++);
|
||||
uint8_t index = readu8Chunk(chunk, offset++);
|
||||
printIndent(indent + 1);
|
||||
printf("references %s [%d]\n", encoding == OP_GETLOCAL ? "local" : "upvalue", index);
|
||||
}
|
||||
|
||||
// print the chunk
|
||||
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1);
|
||||
return offset;
|
||||
}
|
||||
case OP_CLOSE:
|
||||
return simpleInstruction("OP_CLOSE", offset);
|
||||
case OP_ADD:
|
||||
return simpleInstruction("OP_ADD", offset);
|
||||
case OP_SUB:
|
||||
return simpleInstruction("OP_SUB", offset);
|
||||
case OP_MULT:
|
||||
return simpleInstruction("OP_MULT", offset);
|
||||
case OP_DIV:
|
||||
return simpleInstruction("OP_DIV", offset);
|
||||
case OP_TRUE:
|
||||
return simpleInstruction("OP_TRUE", offset);
|
||||
case OP_FALSE:
|
||||
return simpleInstruction("OP_FALSE", offset);
|
||||
case OP_NIL:
|
||||
return simpleInstruction("OP_NIL", offset);
|
||||
case OP_NOT:
|
||||
return simpleInstruction("OP_NOT", offset);
|
||||
case OP_EQUAL:
|
||||
return simpleInstruction("OP_EQUAL", offset);
|
||||
case OP_GREATER:
|
||||
return simpleInstruction("OP_GREATER", offset);
|
||||
case OP_GREATER_EQUAL:
|
||||
return simpleInstruction("OP_GREATER_EQUAL", offset);
|
||||
case OP_LESS:
|
||||
return simpleInstruction("OP_LESS", offset);
|
||||
case OP_LESS_EQUAL:
|
||||
return simpleInstruction("OP_LESS_EQUAL", offset);
|
||||
case OP_NEGATE:
|
||||
return simpleInstruction("OP_NEGATE", offset);
|
||||
case OP_CONCAT:
|
||||
return shortOperandInstruction("OP_CONCAT", chunk, offset);
|
||||
case OP_RETURN:
|
||||
return shortOperandInstruction("OP_RETURN", chunk, offset);
|
||||
default:
|
||||
printf("Unknown opcode! [%d]\n", i);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
9
src/cdebug.h
Normal file
9
src/cdebug.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef CDEBUG_H
|
||||
#define CDEBUG_H
|
||||
|
||||
#include "cchunk.h"
|
||||
|
||||
COSMO_API void disasmChunk(CChunk *chunk, const char *name, int indent);
|
||||
COSMO_API int disasmInstr(CChunk *chunk, int offset, int indent);
|
||||
|
||||
#endif
|
239
src/clex.c
Normal file
239
src/clex.c
Normal file
@ -0,0 +1,239 @@
|
||||
#include "clex.h"
|
||||
#include "cmem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
CReservedWord reservedWords[] = {
|
||||
{TOKEN_AND, "and", 3},
|
||||
{TOKEN_DO, "do", 2},
|
||||
{TOKEN_ELSE, "else", 4},
|
||||
{TOKEN_ELSEIF, "elseif", 6},
|
||||
{TOKEN_END, "end", 3},
|
||||
{TOKEN_FALSE, "false", 5},
|
||||
{TOKEN_FOR, "for", 3},
|
||||
{TOKEN_FUNCTION, "function", 8},
|
||||
{TOKEN_IF, "if", 2},
|
||||
{TOKEN_LOCAL, "local", 5},
|
||||
{TOKEN_NIL, "nil", 3},
|
||||
{TOKEN_NOT, "not", 3},
|
||||
{TOKEN_OR, "or", 2},
|
||||
{TOKEN_RETURN, "return", 6},
|
||||
{TOKEN_THEN, "then", 4},
|
||||
{TOKEN_TRUE, "true", 4},
|
||||
{TOKEN_VAR, "var", 3},
|
||||
{TOKEN_WHILE, "while", 5}
|
||||
};
|
||||
|
||||
|
||||
static CToken makeToken(CLexState *state, CTokenType type) {
|
||||
CToken token;
|
||||
token.type = type;
|
||||
token.start = state->startChar;
|
||||
token.length = state->currentChar - state->startChar; // delta between start & current
|
||||
token.line = state->line;
|
||||
|
||||
state->lastType = type;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static CToken makeError(CLexState *state, const char *msg) {
|
||||
CToken token;
|
||||
token.type = TOKEN_ERROR;
|
||||
token.start = (char*)msg;
|
||||
token.length = strlen(msg);
|
||||
token.line = state->line;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static inline bool isEnd(CLexState *state) {
|
||||
return state->isEnd;
|
||||
}
|
||||
|
||||
static inline bool isNumerical(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_'
|
||||
}
|
||||
|
||||
static bool match(CLexState *state, char expected) {
|
||||
if (isEnd(state) || *state->currentChar != expected)
|
||||
return false;
|
||||
|
||||
// it matched, so increment the currentChar and return true
|
||||
state->currentChar++;
|
||||
return true;
|
||||
}
|
||||
|
||||
char peek(CLexState *state) {
|
||||
return *state->currentChar;
|
||||
}
|
||||
|
||||
static char peekNext(CLexState *state) {
|
||||
if (isEnd(state))
|
||||
return '\0';
|
||||
|
||||
return state->currentChar[1];
|
||||
}
|
||||
|
||||
char next(CLexState *state) {
|
||||
state->currentChar++;
|
||||
return state->currentChar[-1];
|
||||
}
|
||||
|
||||
CTokenType identifierType(CLexState *state) {
|
||||
int length = state->currentChar - state->startChar;
|
||||
|
||||
// check against reserved word list
|
||||
for (int i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) {
|
||||
// it matches the reserved word
|
||||
if (reservedWords[i].len == length && memcmp(state->startChar, reservedWords[i].word, length) == 0)
|
||||
return reservedWords[i].type;
|
||||
}
|
||||
|
||||
// else, it's an identifier
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
void skipWhitespace(CLexState *state) {
|
||||
while (true) {
|
||||
char c = peek(state);
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
next(state); // consume the whitespace
|
||||
break;
|
||||
case '\n': // mark new line, make the main loop consume it
|
||||
state->line++;
|
||||
return;
|
||||
case '-': // consume comments
|
||||
if (peekNext(state) == '-') {
|
||||
|
||||
// skip to next line (also let \n be consumed on the next iteration to properly handle that)
|
||||
while (!isEnd(state) && peek(state) != '\n' && peek(state) != '\0') // if it's not a newline or null terminator
|
||||
next(state);
|
||||
|
||||
break;
|
||||
}
|
||||
return; // it's a TOKEN_SLASH, let the main body handle that
|
||||
default: // it's no longer whitespace, return!
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CToken parseString(CLexState *state) {
|
||||
while (peek(state) != '"' && !isEnd(state)) {
|
||||
if (peek(state) == '\n') // strings can't stretch across lines
|
||||
return makeError(state, "Unterminated string!");
|
||||
next(state); // consume
|
||||
}
|
||||
|
||||
if (isEnd(state))
|
||||
return makeError(state, "Unterminated string!");
|
||||
|
||||
next(state); // consume closing quote
|
||||
return makeToken(state, TOKEN_STRING);
|
||||
}
|
||||
|
||||
CToken parseNumber(CLexState *state) {
|
||||
// consume number
|
||||
while (isNumerical(peek(state)))
|
||||
next(state);
|
||||
|
||||
if (peek(state) == '.' && isNumerical(peekNext(state))) {
|
||||
next(state); // consume '.'
|
||||
|
||||
// consume number
|
||||
while (isNumerical(peek(state)))
|
||||
next(state);
|
||||
}
|
||||
|
||||
return makeToken(state, TOKEN_NUMBER);
|
||||
}
|
||||
|
||||
CToken parseIdentifier(CLexState *state) {
|
||||
// read literal
|
||||
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
|
||||
next(state);
|
||||
|
||||
return makeToken(state, identifierType(state)); // is it a reserved word?
|
||||
}
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
||||
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
|
||||
state->startChar = (char*)source;
|
||||
state->currentChar = (char*)source;
|
||||
state->line = 1;
|
||||
state->lastLine = 0;
|
||||
state->openedBraces = 0;
|
||||
state->isEnd = false;
|
||||
state->lastType = TOKEN_ERROR;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate) {
|
||||
cosmoM_free(state, CLexState, lstate);
|
||||
}
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state) {
|
||||
_scanTokenEnter:
|
||||
skipWhitespace(state);
|
||||
|
||||
state->startChar = state->currentChar;
|
||||
|
||||
if (isEnd(state))
|
||||
return makeToken(state, TOKEN_EOF);
|
||||
|
||||
char c = next(state);
|
||||
|
||||
switch (c) {
|
||||
// single character tokens
|
||||
case '(': state->openedBraces++; return makeToken(state, TOKEN_LEFT_PAREN);
|
||||
case ')': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||
case '{': state->openedBraces++; return makeToken(state, TOKEN_LEFT_BRACE);
|
||||
case '}': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||
case '\0':
|
||||
state->isEnd = true;
|
||||
if (state->lastType == TOKEN_EOS)
|
||||
return makeToken(state, TOKEN_EOF);
|
||||
// fall through
|
||||
case ';': return makeToken(state, TOKEN_EOS);
|
||||
case ',': return makeToken(state, TOKEN_COMMA);
|
||||
case '+': return makeToken(state, TOKEN_PLUS);
|
||||
case '-': return makeToken(state, TOKEN_MINUS);
|
||||
case '*': return makeToken(state, TOKEN_STAR);
|
||||
case '/': return makeToken(state, TOKEN_SLASH);
|
||||
case '\n': { // might be treated like a TOKEN_EOS
|
||||
if (state->openedBraces == 0 && state->lastType != TOKEN_EOS)
|
||||
return makeToken(state, TOKEN_EOS);
|
||||
else // go back to the start
|
||||
goto _scanTokenEnter;
|
||||
}
|
||||
// two character tokens
|
||||
case '.':
|
||||
return match(state, '.') ? makeToken(state, TOKEN_DOT_DOT) : makeToken(state, TOKEN_DOT);
|
||||
case '!':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG);
|
||||
case '=':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) : makeToken(state, TOKEN_EQUAL);
|
||||
case '>':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) : makeToken(state, TOKEN_GREATER);
|
||||
case '<':
|
||||
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) : makeToken(state, TOKEN_LESS);
|
||||
// literals
|
||||
case '"': return parseString(state);
|
||||
default:
|
||||
if (isNumerical(c))
|
||||
return parseNumber(state);
|
||||
if (isAlpha(c))
|
||||
return parseIdentifier(state);
|
||||
}
|
||||
|
||||
return makeError(state, "Unknown symbol!");
|
||||
}
|
88
src/clex.h
Normal file
88
src/clex.h
Normal file
@ -0,0 +1,88 @@
|
||||
#ifndef CLEX_H
|
||||
#define CLEX_H
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
typedef enum {
|
||||
// single character tokens
|
||||
TOKEN_LEFT_PAREN,
|
||||
TOKEN_RIGHT_PAREN,
|
||||
TOKEN_LEFT_BRACE,
|
||||
TOKEN_RIGHT_BRACE,
|
||||
TOKEN_COMMA,
|
||||
TOKEN_DOT,
|
||||
TOKEN_DOT_DOT,
|
||||
TOKEN_MINUS,
|
||||
TOKEN_PLUS,
|
||||
TOKEN_SLASH,
|
||||
TOKEN_STAR,
|
||||
TOKEN_EOS, // end of statement
|
||||
|
||||
// equality operators
|
||||
TOKEN_BANG,
|
||||
TOKEN_BANG_EQUAL,
|
||||
TOKEN_EQUAL,
|
||||
TOKEN_EQUAL_EQUAL,
|
||||
TOKEN_GREATER,
|
||||
TOKEN_GREATER_EQUAL,
|
||||
TOKEN_LESS,
|
||||
TOKEN_LESS_EQUAL,
|
||||
|
||||
// literals
|
||||
TOKEN_IDENTIFIER,
|
||||
TOKEN_STRING,
|
||||
TOKEN_NUMBER,
|
||||
TOKEN_NIL,
|
||||
TOKEN_TRUE,
|
||||
TOKEN_FALSE,
|
||||
|
||||
// keywords & reserved words
|
||||
TOKEN_AND,
|
||||
TOKEN_DO,
|
||||
TOKEN_ELSE,
|
||||
TOKEN_ELSEIF,
|
||||
TOKEN_END,
|
||||
TOKEN_FOR,
|
||||
TOKEN_FUNCTION,
|
||||
TOKEN_IF,
|
||||
TOKEN_LOCAL,
|
||||
TOKEN_NOT,
|
||||
TOKEN_OR,
|
||||
TOKEN_RETURN,
|
||||
TOKEN_THEN,
|
||||
TOKEN_VAR,
|
||||
TOKEN_WHILE,
|
||||
|
||||
TOKEN_ERROR,
|
||||
TOKEN_EOF
|
||||
} CTokenType;
|
||||
|
||||
typedef struct {
|
||||
CTokenType type;
|
||||
const char *word;
|
||||
int len;
|
||||
} CReservedWord;
|
||||
|
||||
typedef struct {
|
||||
CTokenType type;
|
||||
char *start;
|
||||
int length;
|
||||
int line;
|
||||
} CToken;
|
||||
|
||||
typedef struct {
|
||||
char *currentChar;
|
||||
char *startChar;
|
||||
int line; // current line
|
||||
int lastLine; // line of the previous consumed token
|
||||
int openedBraces; // tracks open [], {}, or ()
|
||||
bool isEnd;
|
||||
CTokenType lastType;
|
||||
} CLexState;
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *state, const char *source);
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate);
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state);
|
||||
|
||||
#endif
|
217
src/cmem.c
Normal file
217
src/cmem.c
Normal file
@ -0,0 +1,217 @@
|
||||
#include "cmem.h"
|
||||
#include "cstate.h"
|
||||
#include "cvalue.h"
|
||||
#include "ctable.h"
|
||||
#include "cparse.h"
|
||||
#include "cobj.h"
|
||||
|
||||
/*
|
||||
copy buffer to new larger buffer, and free the old buffer
|
||||
*/
|
||||
void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize) {
|
||||
state->allocatedBytes += newSize - oldSize;
|
||||
|
||||
if (newSize == 0) { // it needs to be free'd
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef GC_STRESS
|
||||
if (!(cosmoM_isFrozen(state)) && newSize > oldSize) {
|
||||
cosmoM_collectGarbage(state);
|
||||
}
|
||||
#else
|
||||
// if the state isn't frozen && we've reached the GC event
|
||||
if (!(cosmoM_isFrozen(state)) && state->allocatedBytes > state->nextGC) {
|
||||
cosmoM_collectGarbage(state); // cya lol
|
||||
}
|
||||
#endif
|
||||
|
||||
// otherwise just use realloc to do all the heavy lifting
|
||||
void *newBuf = realloc(buf, newSize);
|
||||
|
||||
if (newBuf == NULL) {
|
||||
CERROR("failed to allocate memory!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
void markObject(CState *state, CObj *obj);
|
||||
void markValue(CState *state, CValue val);
|
||||
|
||||
void markTable(CState *state, CTable *tbl) {
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
for (int i = 0; i < tbl->capacity; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
markValue(state, entry->key);
|
||||
markValue(state, entry->val);
|
||||
}
|
||||
}
|
||||
|
||||
// free's white members from the table
|
||||
void tableRemoveWhite(CState *state, CTable *tbl) {
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
for (int i = 0; i < tbl->capacity; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (IS_OBJ(entry->key) && !(entry->key.val.obj)->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table
|
||||
cosmoT_remove(tbl, entry->key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void markArray(CState *state, CValueArray *array) {
|
||||
for (int i = 0; i < array->count; i++) {
|
||||
markValue(state, array->values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// mark all references associated with the object
|
||||
void blackenObject(CState *state, CObj *obj) {
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING:
|
||||
case COBJ_CFUNCTION:
|
||||
// stubbed
|
||||
break;
|
||||
case COBJ_UPVALUE: {
|
||||
markValue(state, ((CObjUpval*)obj)->closed);
|
||||
|
||||
break;
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *func = (CObjFunction*)obj;
|
||||
markObject(state, (CObj*)func->name);
|
||||
markArray(state, &func->chunk.constants);
|
||||
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure = (CObjClosure*)obj;
|
||||
markObject(state, (CObj*)closure->function);
|
||||
|
||||
// mark all upvalues
|
||||
for (int i = 0; i < closure->upvalueCount; i++) {
|
||||
markObject(state, (CObj*)closure->upvalues[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("Unknown type in blackenObject with %p, type %d\n", obj, obj->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void markObject(CState *state, CObj *obj) {
|
||||
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
|
||||
return;
|
||||
|
||||
obj->isMarked = true;
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("marking %p, [", obj);
|
||||
printObject(obj);
|
||||
printf("]\n");
|
||||
#endif
|
||||
|
||||
// they don't need to be added to the gray stack, they don't reference any other CObjs
|
||||
if (obj->type == COBJ_CFUNCTION || obj->type == COBJ_STRING)
|
||||
return;
|
||||
|
||||
// we don't use cosmoM_growarray because we don't want to trigger another GC event while in the GC!
|
||||
if (state->grayCount >= state->grayCapacity || state->grayStack == NULL) {
|
||||
int old = state->grayCapacity;
|
||||
state->grayCapacity = old * GROW_FACTOR;
|
||||
state->grayStack = (CObj**)realloc(state->grayStack, sizeof(CObj*) * state->grayCapacity);
|
||||
|
||||
if (state->grayStack == NULL) {
|
||||
CERROR("failed to allocate memory for grayStack!");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
state->grayStack[state->grayCount++] = obj;
|
||||
}
|
||||
|
||||
void markValue(CState *state, CValue val) {
|
||||
if (IS_OBJ(val))
|
||||
markObject(state, cosmoV_readObj(val));
|
||||
}
|
||||
|
||||
// trace our gray references
|
||||
void traceGrays(CState *state) {
|
||||
while (state->grayCount > 0) {
|
||||
CObj* obj = state->grayStack[--state->grayCount];
|
||||
blackenObject(state, obj);
|
||||
}
|
||||
}
|
||||
|
||||
void sweep(CState *state) {
|
||||
CObj *prev = NULL;
|
||||
CObj *object = state->objects;
|
||||
while (object != NULL) {
|
||||
if (object->isMarked) { // skip over it
|
||||
object->isMarked = false; // rest to white
|
||||
prev = object;
|
||||
object = object->next;
|
||||
} else { // free it!
|
||||
CObj *oldObj = object;
|
||||
|
||||
object = object->next;
|
||||
if (prev == NULL) {
|
||||
state->objects = object;
|
||||
} else {
|
||||
prev->next = object;
|
||||
}
|
||||
|
||||
cosmoO_freeObject(state, oldObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void markRoots(CState *state) {
|
||||
// mark all values on the stack
|
||||
for (StkPtr value = state->stack; value < state->top; value++) {
|
||||
markValue(state, *value);
|
||||
}
|
||||
|
||||
// mark all active callframe closures
|
||||
for (int i = 0; i < state->frameCount; i++) {
|
||||
markObject(state, (CObj*)state->callFrame[i].closure);
|
||||
}
|
||||
|
||||
// mark all open upvalues
|
||||
for (CObjUpval *upvalue = state->openUpvalues; upvalue != NULL; upvalue = upvalue->next) {
|
||||
markObject(state, (CObj*)upvalue);
|
||||
}
|
||||
|
||||
markTable(state, &state->globals);
|
||||
|
||||
traceGrays(state);
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state) {
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC start\n");
|
||||
size_t start = state->allocatedBytes;
|
||||
#endif
|
||||
|
||||
markRoots(state);
|
||||
|
||||
tableRemoveWhite(state, &state->strings); // make sure we aren't referencing any strings that are about to be free'd
|
||||
// now finally, free all the unmarked objects
|
||||
sweep(state);
|
||||
|
||||
// set our next GC event
|
||||
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n",
|
||||
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
|
||||
#endif
|
||||
}
|
47
src/cmem.h
Normal file
47
src/cmem.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef CMEME_C
|
||||
#define CMEME_C // meme lol
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
#include "cstate.h"
|
||||
|
||||
//#define GC_STRESS
|
||||
//#define GC_DEBUG
|
||||
// arrays will grow by a factor of 2
|
||||
#define GROW_FACTOR 2
|
||||
#define HEAP_GROW_FACTOR 2
|
||||
#define ARRAY_START 8
|
||||
|
||||
#define cosmoM_freearray(state, type, buf, capacity) \
|
||||
cosmoM_reallocate(state, buf, sizeof(type) *capacity, 0)
|
||||
|
||||
#define cosmoM_growarray(state, type, buf, count, capacity) \
|
||||
if (count >= capacity || buf == NULL) { \
|
||||
int old = capacity; \
|
||||
capacity = old *GROW_FACTOR; \
|
||||
buf = (type*)cosmoM_reallocate(state, buf, sizeof(type) *old, sizeof(type) *capacity); \
|
||||
}
|
||||
|
||||
#define cosmoM_free(state, type, x) \
|
||||
cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||
|
||||
#define cosmoM_isFrozen(state) \
|
||||
state->freezeGC > 0
|
||||
|
||||
#define cosmoM_freezeGC(state) \
|
||||
state->freezeGC++
|
||||
|
||||
#define cosmoM_unfreezeGC(state) \
|
||||
state->freezeGC--
|
||||
|
||||
COSMO_API void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize);
|
||||
COSMO_API void cosmoM_collectGarbage(CState* state);
|
||||
|
||||
/*
|
||||
wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)
|
||||
*/
|
||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz) {
|
||||
return cosmoM_reallocate(state, NULL, 0, sz);
|
||||
}
|
||||
|
||||
#endif
|
209
src/cobj.c
Normal file
209
src/cobj.c
Normal file
@ -0,0 +1,209 @@
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
#include "cobj.h"
|
||||
#include "cmem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// we don't actually hash the whole string :eyes:
|
||||
uint32_t hashString(const char *str, size_t sz) {
|
||||
uint32_t hash = sz;
|
||||
size_t step = (sz>>5)+1;
|
||||
|
||||
for (int i = sz; i >= step; i-=step)
|
||||
hash = ((hash << 5) + (hash>>2)) + str[i-1];
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
CObj *cosmoO_allocateObject(CState *state, size_t sz, CObjType type) {
|
||||
CObj* obj = (CObj*)cosmoM_xmalloc(state, sz);
|
||||
obj->type = type;
|
||||
obj->isMarked = false;
|
||||
|
||||
obj->next = state->objects;
|
||||
state->objects = obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void cosmoO_freeObject(CState *state, CObj* obj) {
|
||||
#ifdef GC_DEBUG
|
||||
printf("freeing %p [", obj);
|
||||
printObject(obj);
|
||||
printf("]\n");
|
||||
#endif
|
||||
switch(obj->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString*)obj;
|
||||
cosmoM_freearray(state, char, objStr->str, objStr->length);
|
||||
cosmoM_free(state, CObjString, objStr);
|
||||
break;
|
||||
}
|
||||
case COBJ_UPVALUE: {
|
||||
cosmoM_free(state, CObjUpval, obj);
|
||||
break;
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *objFunc = (CObjFunction*)obj;
|
||||
cleanChunk(state, &objFunc->chunk);
|
||||
cosmoM_free(state, CObjFunction, objFunc);
|
||||
break;
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
cosmoM_free(state, CObjCFunction, obj);
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure* closure = (CObjClosure*)obj;
|
||||
cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount);
|
||||
cosmoM_free(state, CObjClosure, closure);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cosmoO_equalObject(CObj* obj1, CObj* obj2) {
|
||||
if (obj1->type != obj2->type)
|
||||
return false;
|
||||
|
||||
switch (obj1->type) {
|
||||
case COBJ_STRING:
|
||||
return obj1 == obj2; // compare pointers because we already intern all strings :)
|
||||
default:
|
||||
return false; // they're some unknown type, probably malformed :(
|
||||
}
|
||||
}
|
||||
|
||||
CObjFunction *cosmoO_newFunction(CState *state) {
|
||||
CObjFunction *func = (CObjFunction*)cosmoO_allocateObject(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
||||
func->args = 0;
|
||||
func->upvals = 0;
|
||||
func->name = NULL;
|
||||
|
||||
initChunk(state, &func->chunk, ARRAY_START);
|
||||
return func;
|
||||
}
|
||||
|
||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
|
||||
CObjCFunction *cfunc = (CObjCFunction*)cosmoO_allocateObject(state, sizeof(CObjCFunction), COBJ_CFUNCTION);
|
||||
cfunc->cfunc = func;
|
||||
return cfunc;
|
||||
}
|
||||
|
||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||
// intialize array of pointers
|
||||
CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval*) * func->upvals);
|
||||
|
||||
for (int i = 0; i < func->upvals; i++) {
|
||||
upvalues[i] = NULL;
|
||||
}
|
||||
|
||||
CObjClosure *closure = (CObjClosure*)cosmoO_allocateObject(state, sizeof(CObjClosure), COBJ_CLOSURE);
|
||||
closure->function = func;
|
||||
closure->upvalues = upvalues;
|
||||
closure->upvalueCount = func->upvals;
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
|
||||
CObjUpval *upval = (CObjUpval*)cosmoO_allocateObject(state, sizeof(CObjUpval), COBJ_UPVALUE);
|
||||
upval->val = val;
|
||||
upval->closed = cosmoV_newNil();
|
||||
upval->next = NULL;
|
||||
|
||||
return upval;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t sz) {
|
||||
uint32_t hash = hashString(str, sz);
|
||||
CObjString *lookup = cosmoT_lookupString(&state->strings, str, sz, hash);
|
||||
|
||||
// have we already interned this string?
|
||||
if (lookup != NULL)
|
||||
return lookup;
|
||||
|
||||
char *buf = cosmoM_xmalloc(state, sizeof(char) * (sz + 1)); // +1 for null terminator
|
||||
memcpy(buf, str, sz); // copy string to heap
|
||||
buf[sz] = '\0'; // don't forget our null terminator
|
||||
|
||||
return cosmoO_allocateString(state, buf, sz, hash);
|
||||
}
|
||||
|
||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t sz) {
|
||||
uint32_t hash = hashString(str, sz);
|
||||
|
||||
CObjString *lookup = cosmoT_lookupString(&state->strings, str, sz, hash);
|
||||
|
||||
// have we already interned this string?
|
||||
if (lookup != NULL) {
|
||||
cosmoM_freearray(state, char, str, sz); // free our passed character array, it's unneeded!
|
||||
return lookup;
|
||||
}
|
||||
|
||||
return cosmoO_allocateString(state, str, sz, hash);
|
||||
}
|
||||
|
||||
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) {
|
||||
CObjString *strObj = (CObjString*)cosmoO_allocateObject(state, sizeof(CObjString), COBJ_STRING);
|
||||
strObj->str = (char*)str;
|
||||
strObj->length = sz;
|
||||
strObj->hash = hash;
|
||||
|
||||
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we *want* a GC event to happen)
|
||||
cosmoV_pushValue(state, cosmoV_newObj(strObj));
|
||||
cosmoT_insert(state, &state->strings, cosmoV_newObj((CObj*)strObj));
|
||||
cosmoV_pop(state);
|
||||
|
||||
return strObj;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_toString(CState *state, CObj *val) {
|
||||
switch (val->type) {
|
||||
case COBJ_STRING: {
|
||||
return (CObjString*)val;
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *func = (CObjFunction*)val;
|
||||
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
||||
}
|
||||
default:
|
||||
return cosmoO_copyString(state, "<unkn>", 6);
|
||||
}
|
||||
}
|
||||
|
||||
void printObject(CObj *o) {
|
||||
switch (o->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString*)o;
|
||||
printf("\"%.*s\"", objStr->length, objStr->str);
|
||||
break;
|
||||
}
|
||||
case COBJ_UPVALUE: {
|
||||
CObjUpval *upval = (CObjUpval*)o;
|
||||
printf("<upvalue %p> -> ", upval->val);
|
||||
printValue(*upval->val);
|
||||
break;
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *objFunc = (CObjFunction*)o;
|
||||
if (objFunc->name != NULL)
|
||||
printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
|
||||
else
|
||||
printf("<function> _main");
|
||||
break;
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
CObjCFunction *objCFunc = (CObjCFunction*)o;
|
||||
printf("<c function> %p", objCFunc->cfunc);
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure = (CObjClosure*)o;
|
||||
printObject((CObj*)closure->function); // just print the function
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("<unkn>");
|
||||
}
|
||||
}
|
98
src/cobj.h
Normal file
98
src/cobj.h
Normal file
@ -0,0 +1,98 @@
|
||||
#ifndef COBJ_H
|
||||
#define COBJ_H
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "cchunk.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
typedef struct CState CState;
|
||||
|
||||
typedef enum {
|
||||
COBJ_STRING,
|
||||
COBJ_UPVALUE,
|
||||
COBJ_FUNCTION,
|
||||
COBJ_CFUNCTION,
|
||||
COBJ_CLOSURE
|
||||
} CObjType;
|
||||
|
||||
#define CommonHeader CObj obj;
|
||||
|
||||
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
||||
|
||||
typedef struct CObj {
|
||||
CObjType type;
|
||||
bool isMarked; // for the GC
|
||||
struct CObj *next;
|
||||
} CObj;
|
||||
|
||||
typedef struct CObjString {
|
||||
CommonHeader; // "is a" CObj
|
||||
int length;
|
||||
char *str;
|
||||
uint32_t hash; // for hashtable lookup
|
||||
} CObjString;
|
||||
|
||||
typedef struct CObjUpval {
|
||||
CommonHeader; // "is a" CObj
|
||||
CValue *val;
|
||||
CValue closed;
|
||||
struct CObjUpval *next;
|
||||
} CObjUpval;
|
||||
|
||||
typedef struct CObjFunction {
|
||||
CommonHeader; // "is a" CObj
|
||||
CChunk chunk;
|
||||
int args;
|
||||
int upvals;
|
||||
CObjString *name;
|
||||
} CObjFunction;
|
||||
|
||||
typedef struct CObjCFunction {
|
||||
CommonHeader; // "is a" CObj
|
||||
CosmoCFunction cfunc;
|
||||
} CObjCFunction;
|
||||
|
||||
typedef struct CObjClosure {
|
||||
CommonHeader;
|
||||
CObjFunction *function;
|
||||
CObjUpval **upvalues;
|
||||
int upvalueCount;
|
||||
} CObjClosure;
|
||||
|
||||
#define IS_STRING(x) isObjType(x, COBJ_STRING)
|
||||
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
|
||||
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
|
||||
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
|
||||
|
||||
#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj(x))
|
||||
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj(x))
|
||||
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj(x))->cfunc)
|
||||
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj(x))
|
||||
|
||||
static inline bool isObjType(CValue val, CObjType type) {
|
||||
return IS_OBJ(val) && cosmoV_readObj(val)->type == type;
|
||||
}
|
||||
|
||||
CObj *cosmoO_allocateObject(CState *state, size_t sz, CObjType type);
|
||||
void cosmoO_freeObject(CState *state, CObj* obj);
|
||||
|
||||
bool cosmoO_equalObject(CObj* obj1, CObj* obj2);
|
||||
|
||||
CObjFunction *cosmoO_newFunction(CState *state);
|
||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
|
||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
||||
CObjString *cosmoO_toString(CState *state, CObj *val);
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
||||
|
||||
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t sz);
|
||||
// pass an already allocated str buffer!
|
||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t sz);
|
||||
// allocates a CObjStruct pointing directly to *str
|
||||
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash);
|
||||
|
||||
COSMO_API void printObject(CObj *o);
|
||||
|
||||
#define cosmoO_readCString(x) ((CObjString*)x)->str
|
||||
|
||||
#endif
|
1
src/coperators.c
Normal file
1
src/coperators.c
Normal file
@ -0,0 +1 @@
|
||||
#include "coperators.h"
|
59
src/coperators.h
Normal file
59
src/coperators.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef COPERATORS_H
|
||||
#define COPERATORS_H
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
// instruction types
|
||||
typedef enum {
|
||||
I_O, // just the operand (uint8_t)
|
||||
I_OBYTE, // operand (uint8_t) + uint8_t
|
||||
I_OSHORT, // operand (uint8_t) + uint16_t
|
||||
} InstructionType;
|
||||
|
||||
// instructions
|
||||
|
||||
typedef enum {
|
||||
// STACK MANIPULATION
|
||||
OP_LOADCONST,
|
||||
OP_SETGLOBAL,
|
||||
OP_GETGLOBAL,
|
||||
OP_SETLOCAL,
|
||||
OP_GETLOCAL,
|
||||
OP_GETUPVAL,
|
||||
OP_SETUPVAL,
|
||||
OP_PEJMP, // pops, if false jumps uint16_t
|
||||
OP_EJMP, // if peek(0) is falsey jumps uint16_t
|
||||
OP_JMP, // always jumps uint16_t
|
||||
OP_JMPBACK, // jumps -uint16_t
|
||||
OP_POP, // - pops[uint8_t] from stack
|
||||
OP_CALL, // calls top[-uint8_t]
|
||||
OP_CLOSURE,
|
||||
OP_CLOSE,
|
||||
|
||||
// ARITHMETIC
|
||||
OP_ADD,
|
||||
OP_SUB,
|
||||
OP_MULT,
|
||||
OP_DIV,
|
||||
OP_NOT,
|
||||
OP_NEGATE,
|
||||
OP_CONCAT, // concats uint8_t vars on the stack
|
||||
|
||||
// EQUALITY
|
||||
OP_EQUAL,
|
||||
OP_LESS,
|
||||
OP_GREATER,
|
||||
OP_LESS_EQUAL,
|
||||
OP_GREATER_EQUAL,
|
||||
|
||||
// LITERALS
|
||||
OP_TRUE,
|
||||
OP_FALSE,
|
||||
OP_NIL,
|
||||
|
||||
OP_RETURN,
|
||||
|
||||
OP_NONE // used as an error result
|
||||
} COPCODE;
|
||||
|
||||
#endif
|
35
src/cosmo.h
Normal file
35
src/cosmo.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef COSMOMAIN_H
|
||||
#define COSMOMAIN_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// forward declare *most* stuff so our headers are cleaner
|
||||
typedef struct CState CState;
|
||||
typedef struct CChunk CChunk;
|
||||
typedef struct CValue CValue;
|
||||
|
||||
// objs
|
||||
typedef struct CObj CObj;
|
||||
typedef struct CObjString CObjString;
|
||||
typedef struct CObjUpval CObjUpval;
|
||||
typedef struct CObjFunction CObjFunction;
|
||||
typedef struct CObjCFunction CObjCFunction;
|
||||
typedef struct CObjClosure CObjClosure;
|
||||
|
||||
typedef uint8_t INSTRUCTION;
|
||||
|
||||
#define COSMOMAX_UPVALS 80
|
||||
#define FRAME_MAX 64
|
||||
#define STACK_MAX (256 * FRAME_MAX)
|
||||
|
||||
#define COSMO_API extern
|
||||
#define UNNAMEDCHUNK "_main"
|
||||
|
||||
#define CERROR(err) \
|
||||
printf("%s : %s\n", "[ERROR]", err)
|
||||
|
||||
#endif
|
1017
src/cparse.c
Normal file
1017
src/cparse.c
Normal file
File diff suppressed because it is too large
Load Diff
39
src/cparse.h
Normal file
39
src/cparse.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef CPARSE_H
|
||||
#define CPARSE_H
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "clex.h"
|
||||
|
||||
typedef struct {
|
||||
CToken name;
|
||||
int depth;
|
||||
bool isCaptured; // is the Local referenced in an upvalue?
|
||||
} Local;
|
||||
|
||||
typedef struct {
|
||||
uint8_t index;
|
||||
bool isLocal;
|
||||
} Upvalue;
|
||||
|
||||
typedef enum {
|
||||