added # operator, improved error messages

This commit is contained in:
CPunch 2020-11-30 12:32:04 -06:00
parent 9dcd1c909a
commit 08c640cd58
13 changed files with 76 additions and 20 deletions

View File

@ -154,6 +154,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
return simpleInstruction("OP_LESS_EQUAL", offset); return simpleInstruction("OP_LESS_EQUAL", offset);
case OP_NEGATE: case OP_NEGATE:
return simpleInstruction("OP_NEGATE", offset); return simpleInstruction("OP_NEGATE", offset);
case OP_COUNT:
return simpleInstruction("OP_COUNT", offset);
case OP_CONCAT: case OP_CONCAT:
return u8OperandInstruction("OP_CONCAT", chunk, offset); return u8OperandInstruction("OP_CONCAT", chunk, offset);
case OP_INCLOCAL: case OP_INCLOCAL:

View File

@ -5,6 +5,7 @@
CReservedWord reservedWords[] = { CReservedWord reservedWords[] = {
{TOKEN_AND, "and", 3}, {TOKEN_AND, "and", 3},
{TOKEN_CLASS, "class", 5},
{TOKEN_DO, "do", 2}, {TOKEN_DO, "do", 2},
{TOKEN_ELSE, "else", 4}, {TOKEN_ELSE, "else", 4},
{TOKEN_ELSEIF, "elseif", 6}, {TOKEN_ELSEIF, "elseif", 6},
@ -12,7 +13,6 @@ CReservedWord reservedWords[] = {
{TOKEN_FALSE, "false", 5}, {TOKEN_FALSE, "false", 5},
{TOKEN_FOR, "for", 3}, {TOKEN_FOR, "for", 3},
{TOKEN_FUNCTION, "function", 8}, {TOKEN_FUNCTION, "function", 8},
{TOKEN_CLASS, "class", 5},
{TOKEN_IF, "if", 2}, {TOKEN_IF, "if", 2},
{TOKEN_LOCAL, "local", 5}, {TOKEN_LOCAL, "local", 5},
{TOKEN_NIL, "nil", 3}, {TOKEN_NIL, "nil", 3},
@ -304,6 +304,7 @@ CToken cosmoL_scanToken(CLexState *state) {
case ';': return makeToken(state, TOKEN_EOS); case ';': return makeToken(state, TOKEN_EOS);
case ',': return makeToken(state, TOKEN_COMMA); case ',': return makeToken(state, TOKEN_COMMA);
case '*': return makeToken(state, TOKEN_STAR); case '*': return makeToken(state, TOKEN_STAR);
case '#': return makeToken(state, TOKEN_POUND);
case '/': return makeToken(state, TOKEN_SLASH); case '/': return makeToken(state, TOKEN_SLASH);
// two character tokens // two character tokens
case '+': case '+':

View File

@ -20,6 +20,7 @@ typedef enum {
TOKEN_PLUS_PLUS, TOKEN_PLUS_PLUS,
TOKEN_SLASH, TOKEN_SLASH,
TOKEN_STAR, TOKEN_STAR,
TOKEN_POUND,
TOKEN_EOS, // end of statement TOKEN_EOS, // end of statement
// equality operators // equality operators

View File

@ -50,7 +50,6 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
return false; return false;
} }
void markObject(CState *state, CObj *obj); void markObject(CState *state, CObj *obj);
void markValue(CState *state, CValue val); void markValue(CState *state, CValue val);

View File

@ -326,4 +326,19 @@ void printObject(CObj *o) {
default: default:
printf("<unkn obj>"); printf("<unkn obj>");
} }
}
const char *cosmoO_typeStr(CObj* obj) {
switch (obj->type) {
case COBJ_STRING: return "<string>";
case COBJ_OBJECT: return "<object>";
case COBJ_FUNCTION: return "<function>";
case COBJ_CFUNCTION: return "<c function>";
case COBJ_METHOD: return "<method>";
case COBJ_CLOSURE: return "<closure>";
case COBJ_UPVALUE: return "<upvalue>";
default:
return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes:
}
} }

View File

@ -129,6 +129,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t sz);
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash); CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash);
COSMO_API void printObject(CObj *o); COSMO_API void printObject(CObj *o);
const char *cosmoO_typeStr(CObj* obj);
#define cosmoO_readCString(x) ((CObjString*)x)->str #define cosmoO_readCString(x) ((CObjString*)x)->str

View File

@ -41,6 +41,7 @@ typedef enum {
OP_DIV, OP_DIV,
OP_NOT, OP_NOT,
OP_NEGATE, OP_NEGATE,
OP_COUNT,
OP_CONCAT, // concats uint8_t vars on the stack OP_CONCAT, // concats uint8_t vars on the stack
OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t] OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t]
OP_INCGLOBAL, // pushes old value to stack, adds (uint8_t-128) to globals[const[uint16_t]] OP_INCGLOBAL, // pushes old value to stack, adds (uint8_t-128) to globals[const[uint16_t]]

View File

@ -395,6 +395,7 @@ static void unary(CParseState *pstate, bool canAssign) {
switch(type) { switch(type) {
case TOKEN_MINUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_NEGATE, cachedLine); break; case TOKEN_MINUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_NEGATE, cachedLine); break;
case TOKEN_BANG: writeu8Chunk(pstate->state, getChunk(pstate), OP_NOT, cachedLine); break; case TOKEN_BANG: writeu8Chunk(pstate->state, getChunk(pstate), OP_NOT, cachedLine); break;
case TOKEN_POUND: writeu8Chunk(pstate->state, getChunk(pstate), OP_COUNT, cachedLine); break;
default: default:
error(pstate, "Unexpected unary operator!"); error(pstate, "Unexpected unary operator!");
} }
@ -718,6 +719,7 @@ ParseRule ruleTable[] = {
[TOKEN_PLUS_PLUS] = {preincrement, NULL, PREC_TERM}, [TOKEN_PLUS_PLUS] = {preincrement, NULL, PREC_TERM},
[TOKEN_SLASH] = {NULL, binary, PREC_FACTOR}, [TOKEN_SLASH] = {NULL, binary, PREC_FACTOR},
[TOKEN_STAR] = {NULL, binary, PREC_FACTOR}, [TOKEN_STAR] = {NULL, binary, PREC_FACTOR},
[TOKEN_POUND] = {unary, NULL, PREC_NONE},
[TOKEN_EOS] = {NULL, NULL, PREC_NONE}, [TOKEN_EOS] = {NULL, NULL, PREC_NONE},
[TOKEN_BANG] = {unary, NULL, PREC_NONE}, [TOKEN_BANG] = {unary, NULL, PREC_NONE},
[TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY}, [TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY},
@ -1219,9 +1221,11 @@ CObjFunction* cosmoP_compileString(CState *state, const char *source) {
} }
CObjFunction* resFunc = compiler.function; CObjFunction* resFunc = compiler.function;
// VM expects the closure on the stack :P (we do this before ending the compiler so our GC doesn't free it) // VM expects the closure on the stack :P (we do this before ending the compiler so our GC doesn't free it)
cosmoV_pushValue(state, cosmoV_newObj((CObj*)cosmoO_newClosure(state, resFunc))); cosmoV_pushValue(state, cosmoV_newObj((CObj*)cosmoO_newClosure(state, resFunc)));
// finally free out parser states
endCompiler(&parser); endCompiler(&parser);
freeParseState(&parser); freeParseState(&parser);
cosmoM_unfreezeGC(state); cosmoM_unfreezeGC(state);

View File

@ -209,6 +209,11 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
return true; return true;
} }
// returns the active entry count
COSMO_API int cosmoT_count(CTable *tbl) {
return tbl->count - tbl->tombstones;
}
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash) { CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash) {
if (tbl->count == 0) return 0; // sanity check 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 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

View File

@ -20,6 +20,7 @@ COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap);
COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl); COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl);
COSMO_API void cosmoT_addTable(CState *state, CTable *from, CTable *to); COSMO_API void cosmoT_addTable(CState *state, CTable *from, CTable *to);
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key); COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key);
COSMO_API int cosmoT_count(CTable *tbl);
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash); CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash);
bool cosmoT_checkShrink(CState *state, CTable *tbl); bool cosmoT_checkShrink(CState *state, CTable *tbl);

View File

@ -33,7 +33,7 @@ bool cosmoV_equal(CValue valA, CValue valB) {
} }
} }
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val) { CObjString *cosmoV_toString(CState *state, CValue val) {
switch (val.type) { switch (val.type) {
case COSMO_TNUMBER: { case COSMO_TNUMBER: {
char buf[32]; char buf[32];
@ -54,6 +54,18 @@ COSMO_API CObjString *cosmoV_toString(CState *state, CValue val) {
} }
} }
const char *cosmoV_typeStr(CValue val) {
switch (val.type) {
case COSMO_TNIL: return "<nil>";
case COSMO_TBOOLEAN: return "<bool>";
case COSMO_TNUMBER: return "<number>";
case COSMO_TOBJ: return cosmoO_typeStr(val.val.obj);
default:
return "<unkn val>";
}
}
void printValue(CValue val) { void printValue(CValue val) {
switch (val.type) { switch (val.type) {
case COSMO_TNUMBER: case COSMO_TNUMBER:

View File

@ -38,6 +38,7 @@ void appendValArray(CState *state, CValueArray *array, CValue val);
void printValue(CValue val); void printValue(CValue val);
COSMO_API bool cosmoV_equal(CValue valA, CValue valB); COSMO_API bool cosmoV_equal(CValue valA, CValue valB);
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
COSMO_API const char *cosmoV_typeStr(CValue val);
#define IS_NUMBER(x) (x.type == COSMO_TNUMBER) #define IS_NUMBER(x) (x.type == COSMO_TNUMBER)
#define IS_BOOLEAN(x) (x.type == COSMO_TBOOLEAN) #define IS_BOOLEAN(x) (x.type == COSMO_TBOOLEAN)

View File

@ -154,7 +154,7 @@ bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args) {
} else if (IS_CLOSURE(func)) { } else if (IS_CLOSURE(func)) {
call(state, cosmoV_readClosure(func), args+1, 1); // offset = 1 so our stack is properly reset call(state, cosmoV_readClosure(func), args+1, 1); // offset = 1 so our stack is properly reset
} else { } else {
cosmoV_error(state, "Cannot call non-function value!"); cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func));
} }
return true; return true;
@ -164,8 +164,8 @@ bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args) {
COSMOVMRESULT cosmoV_call(CState *state, int args) { COSMOVMRESULT cosmoV_call(CState *state, int args) {
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
if (!(val->type == COSMO_TOBJ)) { if (val->type != COSMO_TOBJ) {
cosmoV_error(state, "Cannot call non-function value!"); cosmoV_error(state, "Cannot call non-function type %s!", cosmoV_typeStr(*val));
return COSMOVM_RUNTIME_ERR; return COSMOVM_RUNTIME_ERR;
} }
@ -265,7 +265,7 @@ COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, C
cosmoV_setTop(state, 2); /* pop the 2 values */ \ cosmoV_setTop(state, 2); /* pop the 2 values */ \
cosmoV_pushValue(state, typeConst((valA->val.num) op (valB->val.num))); \ cosmoV_pushValue(state, typeConst((valA->val.num) op (valB->val.num))); \
} else { \ } else { \
cosmoV_error(state, "Expected number!"); \ cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); \
} \ } \
// returns false if panic // returns false if panic
@ -393,8 +393,8 @@ bool cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 1); // after that should be the object StkPtr temp = cosmoV_getTop(state, 1); // after that should be the object
// sanity check // sanity check
if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) {
cosmoV_error(state, "Couldn't get from non-object!"); cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
break; break;
} }
@ -412,8 +412,8 @@ bool cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 2); // object is after the key StkPtr temp = cosmoV_getTop(state, 2); // object is after the key
// sanity check // sanity check
if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) {
cosmoV_error(state, "Couldn't set a field on a non-object!"); cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp));
break; break;
} }
@ -430,8 +430,8 @@ bool cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, args+1); // grabs object from stack StkPtr temp = cosmoV_getTop(state, args+1); // grabs object from stack
// sanity check // sanity check
if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) {
cosmoV_error(state, "Couldn't get from non-object!"); cosmoV_error(state, "Couldn't get from non-object type %s!", cosmoV_typeStr(*temp));
break; break;
} }
@ -475,10 +475,23 @@ bool cosmoV_execute(CState *state) {
cosmoV_pop(state); cosmoV_pop(state);
cosmoV_pushValue(state, cosmoV_newNumber(-(val->val.num))); cosmoV_pushValue(state, cosmoV_newNumber(-(val->val.num)));
} else { } else {
cosmoV_error(state, "Expected number!"); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
} }
break; break;
} }
case OP_COUNT: { // pop 1 value off the stack & if it's an object return the ammount of active entries it has
StkPtr temp = cosmoV_getTop(state, 0);
if (temp->type != COSMO_TOBJ || ((CObj*)temp->val.obj)->type != COBJ_OBJECT) {
cosmoV_error(state, "Expected object, got %s!", cosmoV_typeStr(*temp));
break;
}
CObjObject *obj = (CObjObject*)temp->val.obj;
cosmoV_pop(state);
cosmoV_pushNumber(state, cosmoT_count(&obj->tbl)); // pushes the count onto the stack
break;
}
case OP_CONCAT: { case OP_CONCAT: {
uint8_t vals = READBYTE(); uint8_t vals = READBYTE();
StkPtr start = state->top - vals; StkPtr start = state->top - vals;
@ -508,7 +521,7 @@ bool cosmoV_execute(CState *state) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :) cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(val->val.num + inc); *val = cosmoV_newNumber(val->val.num + inc);
} else { } else {
cosmoV_error(state, "Expected number!"); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
} }
break; break;
@ -524,7 +537,7 @@ bool cosmoV_execute(CState *state) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :) cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(val->val.num + inc); *val = cosmoV_newNumber(val->val.num + inc);
} else { } else {
cosmoV_error(state, "Expected number!"); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
} }
break; break;
@ -539,7 +552,7 @@ bool cosmoV_execute(CState *state) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :) cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(val->val.num + inc); *val = cosmoV_newNumber(val->val.num + inc);
} else { } else {
cosmoV_error(state, "Expected number!"); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
} }
break; break;
@ -551,8 +564,8 @@ bool cosmoV_execute(CState *state) {
CValue ident = constants[indx]; // grabs identifier CValue ident = constants[indx]; // grabs identifier
// sanity check // sanity check
if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) {
cosmoV_error(state, "Couldn't set a field on a non-object!"); cosmoV_error(state, "Couldn't set a field on non-object type %s!", cosmoV_typeStr(*temp));
break; break;
} }
@ -567,7 +580,7 @@ bool cosmoV_execute(CState *state) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :) cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(val->val.num + inc); *val = cosmoV_newNumber(val->val.num + inc);
} else { } else {
cosmoV_error(state, "Expected number!"); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
} }
break; break;