Initial commit

This commit is contained in:
CPunch 2020-10-28 00:16:30 -05:00
commit 2e1b745624
34 changed files with 3540 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
bin

12
.vscode/settings.json vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
#include "coperators.h"

59
src/coperators.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

39
src/cparse.h Normal file
View 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 {
FTYPE_FUNCTION,
FTYPE_SCRIPT
} FunctionType;
typedef struct CCompilerState {
CObjFunction *function;
FunctionType type;
Local locals[256];
Upvalue upvalues[256];
int localCount;
int scopeDepth;
int pushedValues;
int savedPushed;
struct CCompilerState* enclosing;
} CCompilerState;
// compiles source into CChunk, if NULL is returned, a syntaxical error has occured and pushed onto the stack
CObjFunction* cosmoP_compileString(CState *state, const char *source);
#endif

74
src/cstate.c Normal file
View File

@ -0,0 +1,74 @@
#include "cstate.h"
#include "cchunk.h"
#include "cobj.h"
#include "cvm.h"
#include <string.h>
CState *cosmoV_newState() {
// we use C's malloc because we don't want to trigger a GC with an invalid state
CState *state = malloc(sizeof(CState));
if (state == NULL) {
CERROR("failed to allocate memory!");
exit(1);
}
state->panic = false;
state->freezeGC = false;
// GC
state->objects = NULL;
state->grayCount = 0;
state->grayCapacity = 2;
state->grayStack = NULL;
state->allocatedBytes = 0;
state->nextGC = 1024 * 8; // threshhold starts at 8kb
// init stack
state->top = state->stack;
state->frameCount = 0;
state->openUpvalues = NULL;
cosmoT_initTable(state, &state->strings, 8); // init string table
cosmoT_initTable(state, &state->globals, 8); // init global table
return state;
}
void cosmoV_freeState(CState *state) {
// frees all the objects
CObj *objs = state->objects;
while (objs != NULL) {
CObj *next = objs->next;
cosmoO_freeObject(state, objs);
objs = next;
}
// free our string & global table
cosmoT_clearTable(state, &state->strings);
cosmoT_clearTable(state, &state->globals);
// free our gray stack & finally free the state structure
free(state->grayStack);
free(state);
}
void cosmoV_register(CState *state, const char *identifier, CValue val) {
// we push the values so the garbage collector can find them
cosmoV_pushValue(state, cosmoV_newObj(cosmoO_copyString(state, identifier, strlen(identifier))));
cosmoV_pushValue(state, val);
CValue *oldVal = cosmoT_insert(state, &state->globals, *cosmoV_getTop(state, 1));
*oldVal = val;
cosmoV_setTop(state, 2); // pops the 2 values off the stack
}
void cosmoV_printStack(CState *state) {
printf("==== [[ stack dump ]] ====\n");
for (CValue *top = state->top - 1; top >= state->stack; top--) {
printf("%d: ", (int)(top - state->stack));
printValue(*top);
printf("\n");
}
}

63
src/cstate.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef CSTATE_H
#define CSTATE_H
#include "cosmo.h"
#include "cvalue.h"
#include "cobj.h"
#include "ctable.h"
typedef struct CCompilerState CCompilerState;
typedef struct CCallFrame {
CObjClosure *closure;
INSTRUCTION *pc;
CValue* base;
} CCallFrame;
typedef struct CState {
bool panic;
int freezeGC; // when > 0, GC events will be ignored (for internal use)
CObj *objects; // tracks all of our allocated objects
CObj **grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found
int grayCount;
int grayCapacity;
size_t allocatedBytes;
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues
CTable strings;
CTable globals;
CValue *top; // top of the stack
CValue stack[STACK_MAX]; // stack
CCallFrame callFrame[FRAME_MAX]; // call frames
int frameCount;
} CState;
COSMO_API CState *cosmoV_newState();
COSMO_API void cosmoV_register(CState *state, const char *identifier, CValue val);
COSMO_API void cosmoV_freeState(CState *state);
COSMO_API void cosmoV_printStack(CState *state);
// pushes value to the stack
static inline void cosmoV_pushValue(CState *state, CValue val) {
*(state->top++) = val;
}
// sets stack->top to stack->top - indx
static inline StkPtr cosmoV_setTop(CState *state, int indx) {
state->top -= indx;
return state->top;
}
// returns stack->top - indx - 1
static inline StkPtr cosmoV_getTop(CState *state, int indx) {
return &state->top[-(indx + 1)];
}
// pops 1 value off the stack
static inline StkPtr cosmoV_pop(CState *state) {
return cosmoV_setTop(state, 1);
}
#endif

193
src/ctable.c Normal file
View File

@ -0,0 +1,193 @@
#include "ctable.h"
#include "cmem.h"
#include "cvalue.h"
#include "cobj.h"
#include <string.h>
#define MAX_TABLE_FILL 0.75
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
tbl->capacity = startCap;
tbl->count = 0;
tbl->table = NULL; // to let out GC know we're initalizing
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap);
// init everything to NIL
for (int i = 0; i < startCap; i++) {
tbl->table[i].key = cosmoV_newNil();
tbl->table[i].val = cosmoV_newNil();
}
}
void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
for (int i = 0; i < from->capacity; i++) {
CTableEntry *entry = &from->table[i];
if (!(IS_NIL(entry->key))) {
CValue *newVal = cosmoT_insert(state, to, entry->key);
*newVal = entry->val;
}
}
}
void cosmoT_clearTable(CState *state, CTable *tbl) {
cosmoM_freearray(state, CTableEntry, tbl->table, tbl->capacity);
}
uint32_t getObjectHash(CObj *obj) {
switch(obj->type) {
case COBJ_STRING:
return ((CObjString*)obj)->hash;
default:
return 0;
}
}
uint32_t getValueHash(CValue *val) {
switch (val->type) {
case COSMO_TOBJ:
return getObjectHash(val->val.obj);
case COSMO_TNUMBER:
// how the fuck
// TODO: add support for other types
default:
return 0;
}
}
// mask should always be (capacity - 1)
static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
uint32_t hash = getValueHash(&key);
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
CTableEntry *tomb = NULL;
// keep looking for an open slot in the entries array
while (true) {
CTableEntry *entry = &entries[indx];
if (IS_NIL(entry->key)) {
// check if it's an empty bucket or a tombstone
if (IS_NIL(entry->val)) {
// it's empty! if we found a tombstone, return that so it'll be reused
return tomb != NULL ? tomb : entry;
} else {
// its a tombstone!
tomb = entry;
}
} else if (cosmoV_equal(entry->key, key)) {
return entry;
}
indx = (indx + 1) & mask; // fast mod here too
}
}
static void growTbl(CState *state, CTable *tbl, size_t newCapacity) {
CTableEntry *entries = cosmoM_xmalloc(state, sizeof(CTableEntry) * newCapacity);
int newCount;
// set all nodes as NIL : NIL
for (int i = 0; i < newCapacity; i++) {
entries[i].key = cosmoV_newNil();
entries[i].val = cosmoV_newNil();
}
// move over old values to the new buffer
for (int i = 0; i < tbl->capacity; i++) {
CTableEntry *oldEntry = &tbl->table[i];
if (IS_NIL(oldEntry->key))
continue; // skip empty keys
// get new entry location & update the node
CTableEntry *newEntry = findEntry(entries, newCapacity - 1, oldEntry->key);
newEntry->key = oldEntry->key;
newEntry->val = oldEntry->val;
newCount++; // inc count
}
// free the old table
cosmoM_freearray(state, CTableEntry, tbl->table, tbl->capacity);
tbl->table = entries;
tbl->capacity = newCapacity;
tbl->count = newCount;
}
// returns a pointer to the allocated value
COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
// make sure we have enough space allocated
if (tbl->count + 1 > tbl->capacity * MAX_TABLE_FILL) {
int newCap = tbl->capacity * GROW_FACTOR;
growTbl(state, tbl, newCap);
}
// insert into the table
CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key); // -1 for our capacity mask
if (IS_NIL(entry->key) && IS_NIL(entry->val)) // is it empty?
tbl->count++;
entry->key = key;
return &entry->val;
}
bool cosmoT_get(CTable *tbl, CValue key, CValue *val) {
if (tbl->count == 0) {
*val = cosmoV_newNil();
return false; // sanity check
}
CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key);
*val = entry->val;
return !(IS_NIL(entry->key));
}
bool cosmoT_remove(CTable *tbl, CValue key) {
if (tbl->count == 0) return 0; // sanity check
CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key);
if (IS_NIL(entry->key)) // sanity check
return false;
// crafts tombstone
entry->key = cosmoV_newNil(); // this has to be nil
entry->val = cosmoV_newBoolean(false); // doesn't reall matter what this is, as long as it isn't nil
return true;
}
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash) {
if (tbl->count == 0) return 0; // sanity check
uint32_t indx = hash & (tbl->capacity - 1); // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
// keep looking for an open slot in the entries array
while (true) {
CTableEntry *entry = &tbl->table[indx];
// check if it's an empty slot (meaning we dont have it in the table)
if (IS_NIL(entry->key) && IS_NIL(entry->val)) {
return NULL;
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length && memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
// it's a match!
return (CObjString*)entry->key.val.obj;
}
indx = (indx + 1) & (tbl->capacity - 1); // fast mod here too
}
}
// for debugging purposes
void cosmoT_printTable(CTable *tbl, const char *name) {
printf("==== [[%s]] ====\n", name);
for (int i = 0; i < tbl->capacity; i++) {
CTableEntry *entry = &tbl->table[i];
if (!(IS_NIL(entry->key))) {
printValue(entry->key);
printf(" - ");
printValue(entry->val);
printf("\n");
}
}
}

29
src/ctable.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef CTABLE_H
#define CTABLE_H
#include "cosmo.h"
#include "cvalue.h"
typedef struct CTableEntry {
CValue key;
CValue val;
} CTableEntry;
typedef struct CTable {
int count;
int capacity;
CTableEntry *table;
} CTable;
COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap);
COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl);
COSMO_API void cosmoT_addTable(CState *state, CTable *from, CTable *to);
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key);
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash);
bool cosmoT_get(CTable *tbl, CValue key, CValue *val);
bool cosmoT_remove(CTable *tbl, CValue key);
void cosmoT_printTable(CTable *tbl, const char *name);
#endif

72
src/cvalue.c Normal file
View File

@ -0,0 +1,72 @@
#include "cmem.h"
#include "cvalue.h"
#include "cobj.h"
void initValArray(CState *state, CValueArray *val, size_t startCapacity) {
val->count = 0;
val->capacity = startCapacity;
val->values = NULL;
}
void cleanValArray(CState *state, CValueArray *array) {
cosmoM_freearray(state, CValue, array->values, array->capacity);
}
void appendValArray(CState *state, CValueArray *array, CValue val) {
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
array->values[array->count++] = val;
}
bool cosmoV_equal(CValue valA, CValue valB) {
if (valA.type != valB.type) // are they the same type?
return false;
// compare
switch (valA.type) {
case COSMO_TBOOLEAN: return valA.val.b == valB.val.b;
case COSMO_TNUMBER: return valA.val.num == valB.val.num;
case COSMO_TOBJ: return cosmoO_equalObject(valA.val.obj, valB.val.obj);
case COSMO_TNIL: return true;
default:
return false;
}
}
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val) {
switch (val.type) {
case COSMO_TNUMBER: {
char buf[32];
int size = snprintf((char*)&buf, 32, "%.14g", val.val.num);
return cosmoO_copyString(state, (char*)&buf, size);
}
case COSMO_TBOOLEAN: {
return val.val.b ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5);
}
case COSMO_TOBJ: {
return cosmoO_toString(state, val.val.obj);
}
default:
return cosmoO_copyString(state, "<unkn>", 6);
}
}
void printValue(CValue val) {
switch (val.type) {
case COSMO_TNUMBER:
printf("%g", val.val.num);
break;
case COSMO_TBOOLEAN:
printf(cosmoV_readBoolean(val) ? "true" : "false");
break;
case COSMO_TOBJ: {
printObject(val.val.obj);
break;
}
case COSMO_TNIL:
printf("nil");
break;
default:
printf("<unkn>");
}
}

62
src/cvalue.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef CVALUE_H
#define CVALUE_H
#include "cosmo.h"
typedef enum {
COSMO_TNIL,
COSMO_TBOOLEAN,
COSMO_TNUMBER,
COSMO_TOBJ,
COSMO_TUSERDATA
} CosmoType;
typedef double cosmo_Number;
/*
holds primitive cosmo types
*/
typedef struct CValue {
CosmoType type;
union {
cosmo_Number num;
bool b; // boolean
void *ptr; // userdata
CObj *obj;
} val;
} CValue;
typedef CValue* StkPtr;
typedef struct CValueArray {
size_t capacity;
size_t count;
CValue *values;
} CValueArray;
COSMO_API void initValArray(CState *state, CValueArray *val, size_t startCapacity);
COSMO_API void cleanValArray(CState *state, CValueArray *array); // cleans array
COSMO_API void appendValArray(CState *state, CValueArray *array, CValue val);
COSMO_API void printValue(CValue val);
COSMO_API bool cosmoV_equal(CValue valA, CValue valB);
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
#define IS_NUMBER(x) x.type == COSMO_TNUMBER
#define IS_BOOLEAN(x) x.type == COSMO_TBOOLEAN
#define IS_NIL(x) x.type == COSMO_TNIL
#define IS_OBJ(x) x.type == COSMO_TOBJ
// create CValues
#define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = x}})
#define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = x}})
#define cosmoV_newObj(x) ((CValue){COSMO_TOBJ, {.obj = (CObj*)x}})
#define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}})
// read CValues
#define cosmoV_readNumber(x) ((cosmo_Number)x.val.num)
#define cosmoV_readBoolean(x) ((bool)x.val.b)
#define cosmoV_readObj(x) ((CObj*)x.val.obj)
#endif

417
src/cvm.c Normal file
View File

@ -0,0 +1,417 @@
#include "cvm.h"
#include "cstate.h"
#include "cdebug.h"
#include "cmem.h"
#include <stdarg.h>
#include <string.h>
void runtimeError(CState *state, const char *format, ...) {
if (state->panic)
return;
// print stack trace
for (int i = 0; i < state->frameCount; i++) {
CCallFrame *frame = &state->callFrame[i];
CObjFunction *function = frame->closure->function;
CChunk *chunk = &function->chunk;
int line = chunk->lineInfo[frame->pc - chunk->buf - 1];
if (i == state->frameCount - 1) { // it's the last call frame, prepare for the objection to be printed
fprintf(stderr, "Objection on [line %d] in ", line);
if (function->name == NULL) { // unnamed chunk
fprintf(stderr, "%s\n\t", UNNAMEDCHUNK);
} else {
fprintf(stderr, "%.*s()\n\t", function->name->length, function->name->str);
}
} else {
fprintf(stderr, "[line %d] in ", line);
if (function->name == NULL) { // unnamed chunk
fprintf(stderr, "%s\n", UNNAMEDCHUNK);
} else {
fprintf(stderr, "%.*s()\n", function->name->length, function->name->str);
}
}
}
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fputs("\n", stderr);
// TODO: push error onto the stack :P
state->panic = true;
}
CObjUpval *captureUpvalue(CState *state, CValue *local) {
CObjUpval *prev = NULL;
CObjUpval *upvalue = state->openUpvalues;
while (upvalue != NULL && upvalue->val > local) { // while upvalue exists and is higher on the stack than local
prev = upvalue;
upvalue = upvalue->next;
}
if (upvalue != NULL && upvalue->val == local) { // we found the local we were going to capture
return upvalue;
}
CObjUpval *newUpval = cosmoO_newUpvalue(state, local);
newUpval->next = upvalue;
// the list is sorted, so insert it at our found upvalue
if (prev == NULL) {
state->openUpvalues = newUpval;
} else {
prev->next = newUpval;
}
return newUpval;
}
void closeUpvalues(CState *state, CValue *local) {
while (state->openUpvalues != NULL && state->openUpvalues->val >= local) { // for every upvalue that points to the local or anything above it
CObjUpval *upvalue = state->openUpvalues;
upvalue->closed = *upvalue->val;
upvalue->val = &upvalue->closed; // upvalue now points to itself :P
state->openUpvalues = upvalue->next;
}
}
void pushCallFrame(CState *state, CObjClosure *closure, int args) {
CCallFrame *frame = &state->callFrame[state->frameCount++];
frame->base = state->top - args - 1; // - 1 for the function
frame->pc = closure->function->chunk.buf;
frame->closure = closure;
}
void popCallFrame(CState *state) {
closeUpvalues(state, state->callFrame[state->frameCount - 1].base); // close any upvalue still open
state->top = state->callFrame[state->frameCount - 1].base; // resets the stack
state->frameCount--;
}
CObjString *cosmoV_concat(CState *state, CObjString *strA, CObjString *strB) {
size_t sz = strA->length + strB->length;
char *buf = cosmoM_xmalloc(state, sz + 1); // +1 for null terminator
memcpy(buf, strA->str, strA->length);
memcpy(buf + strA->length, strB->str, strB->length);
buf[sz] = '\0';
return cosmoO_takeString(state, buf, sz);
}
int cosmoV_execute(CState *state);
typedef enum {
CALL_CLOSURE,
CALL_CFUNCTION
} preCallResult;
int cosmoV_preCall(CState *state, int args, int nresults) {
return -1;
}
// args = # of pass parameters, nresults = # of expected results
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
if (!(val->type == COSMO_TOBJ)) {
runtimeError(state, "Cannot call non-function value!");
return COSMOVM_RUNTIME_ERR;
}
switch (val->val.obj->type) {
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)(val->val.obj);
// missmatched args, thats an obvious user error, so error.
if (args != closure->function->args) {
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
return COSMOVM_RUNTIME_ERR;
}
// load function into callframe
pushCallFrame(state, closure, closure->function->args);
// execute
int res = cosmoV_execute(state);
// so, since we can have any # of results, we need to move the expected results to the original call frame (that means popping/adding however many results)
CValue* results = state->top;
// pop the callframe and return result :)
popCallFrame(state);
// return the results to the stack
for (int i = 1; i <= nresults; i++) {
if (i <= res)
cosmoV_pushValue(state, results[-i]);
else
cosmoV_pushValue(state, cosmoV_newNil());
}
break;
}
case COBJ_CFUNCTION: {
// it's a C function, so call it
CosmoCFunction cfunc = ((CObjCFunction*)(val->val.obj))->cfunc;
CValue *savedBase = state->top - args - 1;
cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
int res = cfunc(state, args, state->top - args);
cosmoM_unfreezeGC(state);
// so, since we can have any # of results, we need to move the expected results to the original call frame
CValue* results = state->top;
state->top = savedBase;
// return the results to the stack
for (int i = 1; i <= nresults; i++) {
if (i <= res)
cosmoV_pushValue(state, results[-i]);
else
cosmoV_pushValue(state, cosmoV_newNil());
}
break;
}
default:
runtimeError(state, "Cannot call non-function value!");
return COSMOVM_RUNTIME_ERR;
}
return state->panic ? COSMOVM_RUNTIME_ERR : COSMOVM_OK;
}
static inline bool isFalsey(StkPtr val) {
return val->type == COSMO_TNIL || (val->type == COSMO_TBOOLEAN && !val->val.b);
}
#define BINARYOP(typeConst, op) \
StkPtr valA = cosmoV_getTop(state, 1); \
StkPtr valB = cosmoV_getTop(state, 0); \
if (valA->type == COSMO_TNUMBER && valB->type == COSMO_TNUMBER) { \
cosmoV_setTop(state, 2); /* pop the 2 values */ \
cosmoV_pushValue(state, typeConst((valA->val.num) op (valB->val.num))); \
} else { \
runtimeError(state, "Expected number! got %d and %d", valA->type, valB->type); \
} \
// returns -1 if error, otherwise returns ammount of results
int cosmoV_execute(CState *state) {
CCallFrame* frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
#define READBYTE() *frame->pc++
#define READUINT() (frame->pc += 2, *(uint16_t*)(&frame->pc[-2]))
while (!state->panic) {
/*disasmInstr(&frame->closure->function->chunk, frame->pc - frame->closure->function->chunk.buf, 0);
printf("\n");*/
switch (READBYTE()) {
case OP_LOADCONST: { // push const[uint] to stack
uint16_t indx = READUINT();
cosmoV_pushValue(state, constants[indx]);
break;
}
case OP_SETGLOBAL: {
uint16_t indx = READUINT();
CValue ident = constants[indx]; // grabs identifier
CValue *val = cosmoT_insert(state, &state->globals, ident);
*val = *cosmoV_pop(state); // sets the value in the hash table
break;
}
case OP_GETGLOBAL: {
uint16_t indx = READUINT();
CValue ident = constants[indx]; // grabs identifier
CValue val; // to hold our value
cosmoT_get(&state->globals, ident, &val);
cosmoV_pushValue(state, val); // pushes the value to the stack
break;
}
case OP_SETLOCAL: {
frame->base[READBYTE()] = *cosmoV_pop(state);
break;
}
case OP_GETLOCAL: {
cosmoV_pushValue(state, frame->base[READBYTE()]);
break;
}
case OP_GETUPVAL: {
uint8_t indx = READBYTE();
cosmoV_pushValue(state, *frame->closure->upvalues[indx]->val);
break;
}
case OP_SETUPVAL: {
uint8_t indx = READBYTE();
*frame->closure->upvalues[indx]->val = *cosmoV_pop(state);
break;
}
case OP_PEJMP: {
uint16_t offset = READUINT();
if (isFalsey(cosmoV_pop(state))) { // pop, if the condition is false, jump!
frame->pc += offset;
}
break;
}
case OP_EJMP: {
uint16_t offset = READUINT();
if (isFalsey(cosmoV_getTop(state, 0))) { // if the condition is false, jump!
frame->pc += offset;
}
break;
}
case OP_JMP: {
uint16_t offset = READUINT();
frame->pc += offset;
break;
}
case OP_JMPBACK: {
uint16_t offset = READUINT();
frame->pc -= offset;
break;
}
case OP_POP: { // pops value off the stack
cosmoV_setTop(state, READBYTE());
break;
}
case OP_CALL: {
uint8_t args = READBYTE();
uint8_t results = READBYTE();
COSMOVMRESULT result = cosmoV_call(state, args, results);
if (result != COSMOVM_OK) {
return result;
}
break;
}
case OP_CLOSURE: {
uint16_t index = READUINT();
CObjFunction *func = cosmoV_readFunction(constants[index]);
CObjClosure *closure = cosmoO_newClosure(state, func);
cosmoV_pushValue(state, cosmoV_newObj((CObj*)closure));
for (int i = 0; i < closure->upvalueCount; i++) {
uint8_t encoding = READBYTE();
uint8_t index = READBYTE();
if (encoding == OP_GETUPVAL) {
// capture upvalue from current frame's closure
closure->upvalues[i] = frame->closure->upvalues[index];
} else {
// capture local
closure->upvalues[i] = captureUpvalue(state, frame->base + index);
}
}
break;
}
case OP_CLOSE: {
closeUpvalues(state, state->top - 1);
cosmoV_pop(state);
break;
}
case OP_ADD: { // pop 2 values off the stack & try to add them together
BINARYOP(cosmoV_newNumber, +);
break;
}
case OP_SUB: { // pop 2 values off the stack & try to subtracts them
BINARYOP(cosmoV_newNumber, -)
break;
}
case OP_MULT: { // pop 2 values off the stack & try to multiplies them together
BINARYOP(cosmoV_newNumber, *)
break;
}
case OP_DIV: { // pop 2 values off the stack & try to divides them
BINARYOP(cosmoV_newNumber, /)
break;
}
case OP_NOT: {
cosmoV_pushValue(state, cosmoV_newBoolean(isFalsey(cosmoV_pop(state))));
break;
}
case OP_NEGATE: { // pop 1 value off the stack & try to negate
StkPtr val = cosmoV_getTop(state, 0);
if (val->type == COSMO_TNUMBER) {
cosmoV_pop(state);
cosmoV_pushValue(state, cosmoV_newNumber(-(val->val.num)));
} else {
runtimeError(state, "Expected number!");
}
break;
}
case OP_CONCAT: {
uint8_t vals = READBYTE();
StkPtr start = state->top - vals;
StkPtr end = cosmoV_getTop(state, 0);
StkPtr current;
CObjString *result = cosmoV_toString(state, *start);
for (StkPtr current = start + 1; current <= end; current++) {
cosmoV_pushValue(state, cosmoV_newObj(result)); // so our GC can find our current result string
CObjString *otherStr = cosmoV_toString(state, *current);
cosmoV_pushValue(state, cosmoV_newObj(otherStr)); // also so our GC won't free otherStr
result = cosmoV_concat(state, result, otherStr);
cosmoV_setTop(state, 2); // pop result & otherStr off the stack
}
state->top = start;
cosmoV_pushValue(state, cosmoV_newObj(result));
break;
}
case OP_EQUAL: {
// pop vals
StkPtr valB = cosmoV_pop(state);
StkPtr valA = cosmoV_pop(state);
// compare & push
cosmoV_pushValue(state, cosmoV_newBoolean(cosmoV_equal(*valA, *valB)));
break;
}
case OP_GREATER: {
BINARYOP(cosmoV_newBoolean, >)
break;
}
case OP_LESS: {
BINARYOP(cosmoV_newBoolean, <)
break;
}
case OP_GREATER_EQUAL: {
BINARYOP(cosmoV_newBoolean, >=)
break;
}
case OP_LESS_EQUAL: {
BINARYOP(cosmoV_newBoolean, <=)
break;
}
case OP_TRUE: cosmoV_pushValue(state, cosmoV_newBoolean(true)); break;
case OP_FALSE: cosmoV_pushValue(state, cosmoV_newBoolean(false)); break;
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); break;
case OP_RETURN: {
uint8_t results = READBYTE();
return results;
}
default:
CERROR("unknown opcode!");
exit(0);
}
//cosmoV_printStack(state);
}
#undef READBYTE
#undef READUINT
// we'll only reach this is state->panic is true
return COSMOVM_RUNTIME_ERR;
}
#undef BINARYOP

16
src/cvm.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef COSMOVM_H
#define COSMOVM_H
#include "cosmo.h"
#include "cstate.h"
typedef enum {
COSMOVM_OK,
COSMOVM_RUNTIME_ERR,
COSMOVM_BUILDTIME_ERR
} COSMOVMRESULT;
// args = # of pass parameters, nresults = # of expected results
COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults);
#endif

131
src/main.c Normal file
View File

@ -0,0 +1,131 @@
#include "cosmo.h"
#include "cchunk.h"
#include "cdebug.h"
#include "cvm.h"
#include "cparse.h"
#include "cbaselib.h"
#include "cmem.h"
static void interpret(const char* script) {
CState *state = cosmoV_newState();
// cosmoP_compileString pushes the result onto the stack (NIL or COBJ_FUNCTION)
CObjFunction* func = cosmoP_compileString(state, script);
cosmoB_loadlibrary(state);
if (func != NULL) {
disasmChunk(&func->chunk, "_main", 0);
cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
//cosmoV_printStack(state);
//cosmoT_printTable(&state->globals, "globals");
//cosmoT_printTable(&state->strings, "strings");
}
cosmoV_freeState(state);
}
static void repl() {
char line[1024];
while (true) {
printf("> ");
if (!fgets(line, sizeof(line), stdin)) { // better than gets()
printf("\n> ");
break;
}
interpret(line);
}
}
static char *readFile(const char* path) {
FILE* file = fopen(path, "rb");
if (file == NULL) {
fprintf(stderr, "Could not open file \"%s\".\n", path);
exit(74);
}
// first, we need to know how big our file is
fseek(file, 0L, SEEK_END);
size_t fileSize = ftell(file);
rewind(file);
char *buffer = (char*)malloc(fileSize + 1); // make room for the null byte
if (buffer == NULL) {
fprintf(stderr, "failed to allocate!");
exit(1);
}
size_t bytesRead = fread(buffer, sizeof(char), fileSize, file);
if (bytesRead < fileSize) {
printf("failed to read file \"%s\"!\n", path);
exit(74);
}
buffer[bytesRead] = '\0'; // place our null terminator
// close the file handler and return the script buffer
fclose(file);
return buffer;
}
static void runFile(const char* fileName) {
char* script = readFile(fileName);
interpret(script);
free(script);
}
int main(int argc, const char *argv[]) {
//interpret("\"hello world!\"");
if (argc == 1) {
repl();
} else if (argc >= 2) { // they passed a file (or more lol)
for (int i = 1; i < argc; i++) {
runFile(argv[i]);
}
}
/*
CChunk *chnk = newChunk(1);
CState *state = cosmoV_newState();
// adds our constant values
int constIndx = addConstant(chnk, cosmoV_newNumber(2));
int const2Indx = addConstant(chnk, cosmoV_newNumber(4));
// pushes constant to the stack
writeu8Chunk(chnk, OP_LOADCONST, 1);
writeu16Chunk(chnk, constIndx, 1);
writeu8Chunk(chnk, OP_LOADCONST, 1);
writeu16Chunk(chnk, const2Indx, 1);
// pops 2 values off the stack, multiples them together and pushes the result
writeu8Chunk(chnk, OP_MULT, 1);
// pops a value off the stack, negates it, and pushes the result
writeu8Chunk(chnk, OP_NEGATE, 2);
// prints to the console
writeu8Chunk(chnk, OP_RETURN, 2);
disasmChunk(chnk, "test");
// load chunk to the state & run it
cosmoV_loadChunk(state, chnk);
cosmoV_execute(state, 0);
// clean up :)
freeChunk(chnk);
cosmoV_freeState(state);*/
return 0;
}

21
test.lua Normal file
View File

@ -0,0 +1,21 @@
local function fact(i)
local total = 1
local x = i
while (x > 1) do
total = total * x
x = x - 1
end
return total
end
local i = 1
while i < 1000 do
local x = 1
while x < 100 do
print("The factorial of " .. x .. " is " .. fact(x))
x = x + 1
end
i = i + 1
end

7
test.py Normal file
View File

@ -0,0 +1,7 @@
def fib(i):
if i < 2:
return i
return fib(i - 2) + fib(i - 1)
print(fib(35))