mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-12-11 17:40:56 +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 {
|
||||||
|
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
74
src/cstate.c
Normal 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
63
src/cstate.h
Normal 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
193
src/ctable.c
Normal 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
29
src/ctable.h
Normal 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
72
src/cvalue.c
Normal 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
62
src/cvalue.h
Normal 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
417
src/cvm.c
Normal 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
16
src/cvm.h
Normal 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
131
src/main.c
Normal 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
21
test.lua
Normal 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
|
Loading…
Reference in New Issue
Block a user