Compare commits

...

12 Commits

Author SHA1 Message Date
5711ca218e improved readme, added asciinema demo 2023-12-29 18:18:22 -06:00
5cd3049d66 cdebug: added cosmoG_disassemble
for now cosmo will disassemble each script before running
2023-12-29 18:17:52 -06:00
cd37cfdae5 cparse: removed stale expressionStatement forward declare 2023-12-29 18:01:33 -06:00
e0455902b0 'return' is now a valid statement outside of a function
lets us do some cool things later. also the repl can print returned values. eg.

`return "hello world"` will print `<string> "hello world"` to the console
2023-12-28 23:41:10 -06:00
43d79a456e cparse.c: removed expressionStatement() 2023-12-28 23:27:08 -06:00
105a3d70c3 cparse.c: refactoring
- remove declaration(), replace calls with statement()
- fix possible UB in parsePrecedence; no idea how this *wasn't* causing a segfault tbh
2023-12-28 23:20:19 -06:00
93f3ae1106 cvm.c:cosmoV_printError -> cosmoV_printBacktrace 2023-12-28 22:56:57 -06:00
4816e64612 cvalue.c:printValue renamed to cosmoV_printValue 2023-12-28 22:52:20 -06:00
0df56bd42a ctable: hash COBJ_CFUNCTION based on cfunc and not the actual <obj> 2023-12-28 19:56:25 -06:00
e7b2d7d833 cbaselib.c:fileB_read() fix MSVC warnings 2023-12-27 21:12:12 -06:00
39060a67e9 fix cosmoB_loadObjLib()
- wrong # of pairs passed to cosmoV_makeTable
- minor refactoring of cmem.c:sweep()
2023-12-26 12:27:45 -06:00
5296495e47 formatting fixes 2023-12-26 10:59:42 -06:00
16 changed files with 102 additions and 84 deletions

View File

@@ -1,7 +1,4 @@
# Cosmo
[![Check Build](https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml/badge.svg?branch=main)](https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml)
## Usage
```
Usage: ./bin/cosmo [-clsr] [args]
@@ -13,6 +10,13 @@ available options are:
-r start the repl
```
<p align="center">
<a href="https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml"><img src="https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml/badge.svg?branch=main" alt="Workflow"></a>
<a href="https://github.com/CPunch/Cosmo/blob/main/LICENSE.md"><img src="https://img.shields.io/github/license/CPunch/Cosmo" alt="License"></a>
<br>
<a href="https://asciinema.org/a/629355" target="_blank"><img src="https://asciinema.org/a/629355.svg" /></a>
</p>
## What is a 'cosmo'?
Cosmo is a portable scripting language loosely based off of Lua. Cosmo easily allows the user to extend the language through the use of Proto objects, which describe the behavior of Objects. For example the following is a simple Vector Proto which describes behavior for a Vector-like object.

25
main.c
View File

@@ -50,13 +50,24 @@ static bool interpret(CState *state, const char *script, const char *mod)
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
if (cosmoV_compileString(state, script, mod)) {
// 0 args being passed, 0 results expected
if (!cosmoV_pcall(state, 0, 0)) {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoG_disassemble(cosmoV_readClosure(*cosmoV_getTop(state, 0)));
if (!cosmoV_pcall(state, 0, 1)) {
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return false;
}
// if the result is nil, we don't print it
if (IS_NIL(*cosmoV_getTop(state, 0))) {
cosmoV_pop(state);
return true;
}
// otherwise, we print the result
cosmoV_printValue(*cosmoV_getTop(state, 0));
printf("\n");
cosmoV_pop(state);
} else {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return false;
}
@@ -158,7 +169,7 @@ void compileScript(CState *state, const char *in, const char *out)
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
cosmoD_dump(state, func, fileWriter, (void *)fout);
} else {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
}
free(script);
@@ -171,13 +182,13 @@ void loadScript(CState *state, const char *in)
{
FILE *file = fopen(in, "rb");
if (!cosmoV_undump(state, fileReader, file)) {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return;
};
printf("[!] loaded %s!\n", in);
if (!cosmoV_pcall(state, 0, 0))
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
fclose(file);
}

View File

@@ -18,7 +18,7 @@ int cosmoB_print(CState *state, int nargs, CValue *args)
CObjString *str = cosmoV_toString(state, args[i]);
printf("%s", cosmoO_readCString(str));
} else { // else, thats pretty expensive for primitives, just print the raw value
printValue(args[i]);
cosmoV_printValue(args[i]);
}
}
printf("\n");
@@ -179,7 +179,7 @@ int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
}
cosmoV_pushRef(state, proto); // just return the proto
return 1; // 1 result
return 1; // 1 result
}
int cosmoB_ogetKeys(CState *state, int nargs, CValue *args)
@@ -243,7 +243,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
cosmoV_pushString(state, "__proto"); // key
cosmoV_pushCFunction(state, cosmoB_ogetProto); // value
cosmoV_makeTable(state, 2);
cosmoV_makeTable(state, 1);
// make __setter table
cosmoV_pushString(state, "__setter");
@@ -295,14 +295,14 @@ int fileB_read(CState *state, int nargs, CValue *args)
}
// allocate a buffer for the read data
buffer = cosmoM_xmalloc(state, length + 1);
buffer = cosmoM_xmalloc(state, (size_t)length + 1);
// read the data
fread(buffer, sizeof(char), length, file);
fread(buffer, sizeof(char), (size_t)length, file);
buffer[(int)length] = '\0'; // write the NULL terminator
// push the read data
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, length));
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, (size_t)length));
cosmoV_pushValue(state, temp);
} else if (IS_STRING(args[1])) {
if (strcmp(cosmoV_readCString(args[1]), "a") == 0) {
@@ -316,17 +316,17 @@ int fileB_read(CState *state, int nargs, CValue *args)
fseek(file, 0, SEEK_SET);
// allocate a buffer for the read data
buffer = cosmoM_xmalloc(state, length + 1);
buffer = cosmoM_xmalloc(state, (size_t)length + 1);
// read the data
fread(buffer, sizeof(char), length, file);
fread(buffer, sizeof(char), (size_t)length, file);
buffer[length] = '\0'; // write the NULL terminator
// push the read data
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, length));
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, (size_t)length));
cosmoV_pushValue(state, temp);
} else {
cosmoV_error(state, "file:read() expected \"a\" or <number>, got %s!",
cosmoV_error(state, "file:read() expected \"a\" or <number>, got \"%s\"!",
cosmoV_readCString(args[1]));
}
} else {
@@ -337,7 +337,8 @@ int fileB_read(CState *state, int nargs, CValue *args)
return 1;
}
int fileB_write(CState *state, int nargs, CValue *args) {
int fileB_write(CState *state, int nargs, CValue *args)
{
CObjObject *fileObj;
CObjString *str;
FILE *file;
@@ -365,14 +366,14 @@ int fileB_write(CState *state, int nargs, CValue *args) {
return 0;
}
int fileB_gc(CState *state, int nargs, CValue *args) {
int fileB_gc(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "file:read() expected 1 argument, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:__gc()", "<file>", "%s",
cosmoV_typeStr(args[0]));
cosmoV_typeError(state, "file:__gc()", "<file>", "%s", cosmoV_typeStr(args[0]));
}
CObjObject *fileObj = cosmoV_readObject(args[0]);
@@ -411,7 +412,8 @@ int cosmoB_osOpen(CState *state, int nargs, CValue *args)
if (nargs == 2) {
if (!IS_STRING(args[1])) {
cosmoV_typeError(state, "os.open()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
cosmoV_typeError(state, "os.open()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
mode = cosmoV_readCString(args[1]);

View File

@@ -62,7 +62,7 @@ static int constInstruction(const char *name, CChunk *chunk, int offset)
printf("%-16s [%05d] - ", name, index);
CValue val = chunk->constants.values[index];
printValue(val);
cosmoV_printValue(val);
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint
}
@@ -128,7 +128,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent)
CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val);
offset += 3; // we consumed the opcode + u16
printValue(val);
cosmoV_printValue(val);
printf("\n");
// list the upvalues/locals that are captured
@@ -223,3 +223,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent)
return 1;
}
void cosmoG_disassemble(CObjClosure *closure)
{
disasmChunk(&closure->function->chunk, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, 0);
}

View File

@@ -2,10 +2,13 @@
#define CDEBUG_H
#include "cchunk.h"
#include "cobj.h"
COSMO_API void disasmChunk(CChunk *chunk, const char *name, int indent);
COSMO_API int disasmInstr(CChunk *chunk, int offset, int indent);
void printIndent(int indent);
COSMO_API void cosmoG_disassemble(CObjClosure *closure);
#endif

View File

@@ -231,11 +231,11 @@ static void traceGrays(CState *state)
static void sweep(CState *state)
{
CObj *prev = NULL;
CObj *object = state->objects;
CObj *prev = NULL, *object = state->objects;
while (object != NULL) {
if (object->isMarked) { // skip over it
object->isMarked = false; // rest to white
object->isMarked = false; // reset to white
prev = object;
object = object->next;
} else { // free it!
@@ -259,7 +259,6 @@ static void sweep(CState *state)
cosmoV_call(state, 1, 0);
}
cosmoO_free(state, oldObj);
}
}

View File

@@ -493,7 +493,6 @@ void cosmoO_unlock(CObjObject *object)
object->isLocked = false;
}
bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val)
{
if (readFlag(object->istringFlags, flag))
@@ -716,13 +715,13 @@ void printObject(CObj *o)
case COBJ_ERROR: {
CObjError *err = (CObjError *)o;
printf("%p -> ", (void *)o);
printValue(err->err);
cosmoV_printValue(err->err);
break;
}
case COBJ_METHOD: {
CObjMethod *method = (CObjMethod *)o;
printf("%p -> ", (void *)method);
printValue(method->func);
cosmoV_printValue(method->func);
break;
}
case COBJ_CLOSURE: {
@@ -734,7 +733,7 @@ void printObject(CObj *o)
case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval *)o;
printf("%p -> ", (void *)upval->val);
printValue(*upval->val);
cosmoV_printValue(*upval->val);
break;
}
default:

View File

@@ -55,7 +55,7 @@ struct CObjError
CCallFrame *frames;
int frameCount;
int line; // reserved for parser errors
bool parserError; // if true, cosmoV_printError will format the error to the lexer
bool parserError; // if true, cosmoV_printBacktrace will format the error to the lexer
};
struct CObjObject

View File

@@ -102,9 +102,7 @@ static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec
// returns # of pushed values onto the stack
static int expression(CParseState *pstate, int needed, bool forceNeeded);
static void statement(CParseState *pstate);
static void declaration(CParseState *pstate);
static void parseFunction(CParseState *pstate, FunctionType type);
static void expressionStatement(CParseState *pstate);
static ParseRule *getRule(CTokenType type);
static CObjFunction *endCompiler(CParseState *pstate);
@@ -1099,18 +1097,19 @@ static ParseRule *getRule(CTokenType type)
// returns true if it got past the first token (aka prefix wasn't null)
static bool parsePrecedence(CParseState *pstate, Precedence prec)
{
bool canAssign;
ParseFunc prefix, infix;
advance(pstate);
ParseFunc prefix = getRule(pstate->previous.type)->prefix;
if (prefix == NULL)
if ((prefix = getRule(pstate->previous.type)->prefix) == NULL)
return false;
bool canAssign = prec <= PREC_ASSIGNMENT;
canAssign = prec <= PREC_ASSIGNMENT;
prefix(pstate, canAssign, prec);
while (prec <= getRule(pstate->current.type)->level) {
ParseFunc infix = getRule(pstate->current.type)->infix;
if ((infix = getRule(pstate->current.type)->infix) == NULL)
break;
advance(pstate);
infix(pstate, canAssign, prec);
}
@@ -1264,7 +1263,7 @@ static void endScope(CParseState *pstate)
static void block(CParseState *pstate)
{
while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) {
declaration(pstate);
statement(pstate);
}
consume(pstate, TOKEN_END, "'end' expected to end block.'");
@@ -1321,7 +1320,7 @@ static void ifStatement(CParseState *pstate)
while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) &&
!check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) &&
!check(pstate, TOKEN_ERROR)) {
declaration(pstate);
statement(pstate);
}
endScope(pstate);
@@ -1471,10 +1470,10 @@ static void functionDeclaration(CParseState *pstate)
static void returnStatement(CParseState *pstate)
{
if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) {
error(pstate, "Expected 'return' in function!");
return;
}
// if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) {
// error(pstate, "Expected 'return' in function!");
// return;
// }
if (blockFollow(pstate->current)) { // does this return have a value
writeu8(pstate, OP_NIL);
@@ -1589,7 +1588,7 @@ static void forLoop(CParseState *pstate)
// parse initializer
if (!match(pstate, TOKEN_EOS)) {
expressionStatement(pstate);
statement(pstate);
consume(pstate, TOKEN_EOS, "Expected ';' after initializer");
}
@@ -1705,7 +1704,7 @@ static int expression(CParseState *pstate, int needed, bool forceNeeded)
forceNeeded); // anything above assignments are an expression
}
static void expressionStatement(CParseState *pstate)
static void statement(CParseState *pstate)
{
int savedPushed = pstate->compiler->pushedValues;
@@ -1760,16 +1759,6 @@ static void expressionStatement(CParseState *pstate)
alignStack(pstate, savedPushed);
}
static void statement(CParseState *pstate)
{
expressionStatement(pstate);
}
static void declaration(CParseState *pstate)
{
statement(pstate);
}
static CObjFunction *endCompiler(CParseState *pstate)
{
popLocals(pstate, pstate->compiler->scopeDepth + 1); // remove the locals from other scopes
@@ -1794,7 +1783,7 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
advance(&parser);
while (!match(&parser, TOKEN_EOF)) {
declaration(&parser);
statement(&parser);
}
consume(&parser, TOKEN_EOF, "End of file expected!");

View File

@@ -168,7 +168,8 @@ void cosmoV_addRegistry(CState *state, int pairs)
}
// expects 1 key on the stack, pushes result
void cosmoV_getRegistry(CState *state) {
void cosmoV_getRegistry(CState *state)
{
CValue key = *cosmoV_pop(state);
CValue val;
@@ -179,12 +180,14 @@ void cosmoV_getRegistry(CState *state) {
cosmoV_pushValue(state, val);
}
void cosmoV_setProto(CState *state) {
void cosmoV_setProto(CState *state)
{
StkPtr objVal = cosmoV_getTop(state, 1);
StkPtr protoVal = cosmoV_getTop(state, 0);
if (!IS_REF(*objVal) || !IS_OBJECT(*protoVal)) {
cosmoV_error(state, "cannot set %s to proto of type %s", cosmoV_typeStr(*objVal), cosmoV_typeStr(*protoVal));
cosmoV_error(state, "cannot set %s to proto of type %s", cosmoV_typeStr(*objVal),
cosmoV_typeStr(*protoVal));
}
// actually set the protos
@@ -200,7 +203,7 @@ 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);
cosmoV_printValue(*top);
printf("\n");
}
}

View File

@@ -63,13 +63,13 @@ struct CState
CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues
CObjTable *globals;
CValue *top; // top of the stack
CObj *objects; // tracks all of our allocated objects
CValue *top; // top of the stack
CObj *objects; // tracks all of our allocated objects
CPanic *panic;
size_t allocatedBytes;
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount;
};

View File

@@ -69,6 +69,8 @@ static uint32_t getObjectHash(CObj *obj)
switch (obj->type) {
case COBJ_STRING:
return ((CObjString *)obj)->hash;
case COBJ_CFUNCTION:
return (uint32_t)((CObjCFunction *)obj)->cfunc;
default:
return (uint32_t)obj; // just "hash" the pointer
}
@@ -286,9 +288,9 @@ void cosmoT_printTable(CTable *tbl, const char *name)
for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i];
if (!(IS_NIL(entry->key))) {
printValue(entry->key);
cosmoV_printValue(entry->key);
printf(" - ");
printValue(entry->val);
cosmoV_printValue(entry->val);
printf("\n");
}
}

View File

@@ -101,7 +101,7 @@ const char *cosmoV_typeStr(CValue val)
}
}
void printValue(CValue val)
void cosmoV_printValue(CValue val)
{
switch (GET_TYPE(val)) {
case COSMO_TNUMBER:

View File

@@ -119,7 +119,7 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity);
void cleanValArray(CState *state, CValueArray *array); // cleans array
void appendValArray(CState *state, CValueArray *array, CValue val);
void printValue(CValue val);
void cosmoV_printValue(CValue val);
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);

View File

@@ -78,7 +78,7 @@ bool cosmoV_compileString(CState *state, const char *src, const char *name)
return false;
}
void cosmoV_printError(CState *state, CObjError *err)
void cosmoV_printBacktrace(CState *state, CObjError *err)
{
// print stack trace
for (int i = 0; i < err->frameCount; i++) {
@@ -129,7 +129,7 @@ void cosmoV_throw(CState *state)
} else {
cosmoV_pushValue(state, val);
fprintf(stderr, "Unhandled panic! ");
cosmoV_printError(state, error);
cosmoV_printBacktrace(state, error);
exit(1);
}
}
@@ -340,7 +340,7 @@ void callCValue(CState *state, CValue func, int args, int nresults, int offset)
#ifdef VM_DEBUG
printf("\n");
printIndent(state->frameCount - 1);
printValue(func);
cosmoV_printValue(func);
printf("(%d args)\n", args);
#endif
@@ -382,7 +382,7 @@ void callCValue(CState *state, CValue func, int args, int nresults, int offset)
// push the nils to fill up the expected return values.
// -1 since the we already pushed the important value
for (int i = 0; i < nresults - 1;i++) {
for (int i = 0; i < nresults - 1; i++) {
cosmoV_pushValue(state, cosmoV_newNil());
}
}
@@ -582,7 +582,8 @@ void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
}
}
bool cosmoV_isValueUserType(CState *state, CValue val, int userType) {
bool cosmoV_isValueUserType(CState *state, CValue val, int userType)
{
if (!IS_OBJECT(val)) {
return false;
}
@@ -853,8 +854,7 @@ int cosmoV_execute(CState *state)
CObj *obj = cosmoV_readRef(*temp);
CObjObject *proto = cosmoO_grabProto(obj);
CValue val = cosmoV_newNil(); // to hold our value
CValue val = cosmoV_newNil(); // to hold our value
if (proto != NULL) {
// check for __index metamethod

View File

@@ -33,7 +33,7 @@ COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs);
COSMO_API void cosmoV_makeTable(CState *state, int pairs);
COSMO_API void cosmoV_concat(CState *state, int vals);
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
COSMO_API void cosmoV_printError(CState *state, CObjError *err);
COSMO_API void cosmoV_printBacktrace(CState *state, CObjError *err);
COSMO_API void cosmoV_throw(CState *state);
COSMO_API void cosmoV_error(CState *state, const char *format, ...);
COSMO_API void cosmoV_insert(CState *state, int indx, CValue val);
@@ -72,7 +72,8 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
COSMO_API void cosmoV_get(CState *state);
/*
expects object to be pushed, then the key, and finally the new value. pops the object, key & value
expects object to be pushed, then the key, and finally the new value. pops the object, key &
value
*/
COSMO_API void cosmoV_set(CState *state);