mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-22 15:30:06 +00:00
commit
788911130d
4
Makefile
4
Makefile
@ -19,6 +19,8 @@ CHDR=\
|
|||||||
src/cvm.h\
|
src/cvm.h\
|
||||||
src/cobj.h\
|
src/cobj.h\
|
||||||
src/cbaselib.h\
|
src/cbaselib.h\
|
||||||
|
src/cdump.h\
|
||||||
|
src/cundump.h\
|
||||||
|
|
||||||
CSRC=\
|
CSRC=\
|
||||||
src/cchunk.c\
|
src/cchunk.c\
|
||||||
@ -33,6 +35,8 @@ CSRC=\
|
|||||||
src/cvm.c\
|
src/cvm.c\
|
||||||
src/cobj.c\
|
src/cobj.c\
|
||||||
src/cbaselib.c\
|
src/cbaselib.c\
|
||||||
|
src/cdump.c\
|
||||||
|
src/cundump.c\
|
||||||
main.c\
|
main.c\
|
||||||
|
|
||||||
COBJ=$(CSRC:.c=.o)
|
COBJ=$(CSRC:.c=.o)
|
||||||
|
165
main.c
165
main.c
@ -1,21 +1,29 @@
|
|||||||
#include "cosmo.h"
|
#include "cbaselib.h"
|
||||||
#include "cchunk.h"
|
#include "cchunk.h"
|
||||||
#include "cdebug.h"
|
#include "cdebug.h"
|
||||||
#include "cvm.h"
|
#include "cdump.h"
|
||||||
#include "cparse.h"
|
|
||||||
#include "cbaselib.h"
|
|
||||||
|
|
||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
|
#include "cosmo.h"
|
||||||
|
#include "cparse.h"
|
||||||
|
#include "cundump.h"
|
||||||
|
#include "cvm.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
static bool _ACTIVE = false;
|
static bool _ACTIVE = false;
|
||||||
|
|
||||||
int cosmoB_quitRepl(CState *state, int nargs, CValue *args) {
|
int cosmoB_quitRepl(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
_ACTIVE = false;
|
_ACTIVE = false;
|
||||||
|
|
||||||
return 0; // we don't return anything
|
return 0; // we don't return anything
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_input(CState *state, int nargs, CValue *args) {
|
int cosmoB_input(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
// input() accepts the same params as print()!
|
// input() accepts the same params as print()!
|
||||||
for (int i = 0; i < nargs; i++) {
|
for (int i = 0; i < nargs; i++) {
|
||||||
CObjString *str = cosmoV_toString(state, args[i]);
|
CObjString *str = cosmoV_toString(state, args[i]);
|
||||||
@ -26,12 +34,14 @@ int cosmoB_input(CState *state, int nargs, CValue *args) {
|
|||||||
char line[1024];
|
char line[1024];
|
||||||
fgets(line, sizeof(line), stdin);
|
fgets(line, sizeof(line), stdin);
|
||||||
|
|
||||||
cosmoV_pushRef(state, (CObj*)cosmoO_copyString(state, line, strlen(line)-1)); // -1 for the \n
|
cosmoV_pushRef(state,
|
||||||
|
(CObj *)cosmoO_copyString(state, line, strlen(line) - 1)); // -1 for the \n
|
||||||
|
|
||||||
return 1; // 1 return value
|
return 1; // 1 return value
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool interpret(CState *state, const char *script, const char *mod) {
|
static bool interpret(CState *state, const char *script, const char *mod)
|
||||||
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
||||||
@ -50,15 +60,11 @@ static bool interpret(CState *state, const char *script, const char *mod) {
|
|||||||
return !ret;
|
return !ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void repl() {
|
static void repl(CState *state)
|
||||||
|
{
|
||||||
char line[1024];
|
char line[1024];
|
||||||
_ACTIVE = true;
|
_ACTIVE = true;
|
||||||
|
|
||||||
CState *state = cosmoV_newState();
|
|
||||||
cosmoB_loadLibrary(state);
|
|
||||||
cosmoB_loadOSLib(state);
|
|
||||||
cosmoB_loadVM(state);
|
|
||||||
|
|
||||||
// add our custom REPL functions
|
// add our custom REPL functions
|
||||||
cosmoV_pushString(state, "quit");
|
cosmoV_pushString(state, "quit");
|
||||||
cosmoV_pushCFunction(state, cosmoB_quitRepl);
|
cosmoV_pushCFunction(state, cosmoB_quitRepl);
|
||||||
@ -78,12 +84,11 @@ static void repl() {
|
|||||||
|
|
||||||
interpret(state, line, "REPL");
|
interpret(state, line, "REPL");
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_freeState(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *readFile(const char* path) {
|
static char *readFile(const char *path)
|
||||||
FILE* file = fopen(path, "rb");
|
{
|
||||||
|
FILE *file = fopen(path, "rb");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
fprintf(stderr, "Could not open file \"%s\".\n", path);
|
fprintf(stderr, "Could not open file \"%s\".\n", path);
|
||||||
exit(74);
|
exit(74);
|
||||||
@ -94,7 +99,7 @@ static char *readFile(const char* path) {
|
|||||||
size_t fileSize = ftell(file);
|
size_t fileSize = ftell(file);
|
||||||
rewind(file);
|
rewind(file);
|
||||||
|
|
||||||
char *buffer = (char*)malloc(fileSize + 1); // make room for the null byte
|
char *buffer = (char *)malloc(fileSize + 1); // make room for the null byte
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
fprintf(stderr, "failed to allocate!");
|
fprintf(stderr, "failed to allocate!");
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -114,12 +119,10 @@ static char *readFile(const char* path) {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool runFile(const char* fileName) {
|
static bool runFile(CState *state, const char *fileName)
|
||||||
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
char* script = readFile(fileName);
|
char *script = readFile(fileName);
|
||||||
CState *state = cosmoV_newState();
|
|
||||||
cosmoB_loadLibrary(state);
|
|
||||||
cosmoB_loadOSLib(state);
|
|
||||||
|
|
||||||
// add our input() function to the global table
|
// add our input() function to the global table
|
||||||
cosmoV_pushString(state, "input");
|
cosmoV_pushString(state, "input");
|
||||||
@ -129,21 +132,119 @@ static bool runFile(const char* fileName) {
|
|||||||
|
|
||||||
ret = interpret(state, script, fileName);
|
ret = interpret(state, script, fileName);
|
||||||
|
|
||||||
cosmoV_freeState(state);
|
|
||||||
free(script);
|
free(script);
|
||||||
return ret; // let the caller know if the script failed
|
return ret; // let the caller know if the script failed
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
int fileWriter(CState *state, const void *data, size_t size, const void *ud)
|
||||||
if (argc == 1) {
|
{
|
||||||
repl();
|
return !fwrite(data, size, 1, (FILE *)ud);
|
||||||
} else if (argc >= 2) { // they passed a file (or more lol)
|
}
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
if (!runFile(argv[i])) {
|
int fileReader(CState *state, void *data, size_t size, const void *ud)
|
||||||
|
{
|
||||||
|
return fread(data, size, 1, (FILE *)ud) != 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compileScript(CState *state, const char *in, const char *out)
|
||||||
|
{
|
||||||
|
char *script = readFile(in);
|
||||||
|
|
||||||
|
FILE *fout = fopen(out, "wb");
|
||||||
|
|
||||||
|
if (cosmoV_compileString(state, script, in)) {
|
||||||
|
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
|
||||||
|
cosmoD_dump(state, func, fileWriter, (void *)fout);
|
||||||
|
} else {
|
||||||
|
cosmoV_pop(state); // pop the error off the stack
|
||||||
|
cosmoV_printError(state, state->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(script);
|
||||||
|
fclose(fout);
|
||||||
|
|
||||||
|
printf("[!] compiled %s to %s successfully!\n", in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadScript(CState *state, const char *in)
|
||||||
|
{
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
|
file = fopen(in, "rb");
|
||||||
|
if (!cosmoV_undump(state, fileReader, file)) {
|
||||||
|
cosmoV_pop(state); // pop the error off the stack
|
||||||
|
cosmoV_printError(state, state->error);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
printf("[!] loaded %s!\n", in);
|
||||||
|
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
||||||
|
|
||||||
|
if (res == COSMOVM_RUNTIME_ERR)
|
||||||
|
cosmoV_printError(state, state->error);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printUsage(const char *name)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [-clsr] [args]\n\n", name);
|
||||||
|
printf("available options are:\n"
|
||||||
|
"-c <in> <out>\tcompile <in> and dump to <out>\n"
|
||||||
|
"-l <in>\t\tload dump from <in>\n"
|
||||||
|
"-s <in...>\tcompile and run <in...> script(s)\n"
|
||||||
|
"-r\t\tstart the repl\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *const argv[])
|
||||||
|
{
|
||||||
|
CState *state = cosmoV_newState();
|
||||||
|
cosmoB_loadLibrary(state);
|
||||||
|
cosmoB_loadOSLib(state);
|
||||||
|
cosmoB_loadVM(state);
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
bool isValid = false;
|
||||||
|
while ((opt = getopt(argc, argv, "clsr")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'c':
|
||||||
|
if (optind >= argc - 1) {
|
||||||
|
printf("Usage: %s -c <in> <out>\n", argv[0]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
compileScript(state, argv[optind], argv[optind + 1]);
|
||||||
|
}
|
||||||
|
isValid = true;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (optind >= argc) {
|
||||||
|
printf("Usage: %s -l <in>\n", argv[0]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
loadScript(state, argv[optind]);
|
||||||
|
}
|
||||||
|
isValid = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
for (int i = optind; i < argc; i++) {
|
||||||
|
if (!runFile(state, argv[i])) {
|
||||||
|
printf("failed to run %s!\n", argv[i]);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isValid = true;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
repl(state);
|
||||||
|
isValid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
printUsage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cosmoV_freeState(state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -980,6 +980,4 @@ void cosmoB_loadVM(CState *state)
|
|||||||
|
|
||||||
// register "vm" to the global table
|
// register "vm" to the global table
|
||||||
cosmoV_register(state, 1);
|
cosmoV_register(state, 1);
|
||||||
|
|
||||||
printf("[WARNING] the vm.* library has been loaded!\n");
|
|
||||||
}
|
}
|
||||||
|
204
src/cdump.c
Normal file
204
src/cdump.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "cdump.h"
|
||||||
|
|
||||||
|
#include "cdebug.h"
|
||||||
|
#include "cmem.h"
|
||||||
|
#include "cobj.h"
|
||||||
|
#include "cvalue.h"
|
||||||
|
#include "cvm.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CState *state;
|
||||||
|
const void *userData;
|
||||||
|
cosmo_Writer writer;
|
||||||
|
int writerStatus;
|
||||||
|
} DumpState;
|
||||||
|
|
||||||
|
static bool writeCValue(DumpState *dstate, CValue val);
|
||||||
|
|
||||||
|
#define check(e) \
|
||||||
|
if (!e) { \
|
||||||
|
return false; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initDumpState(CState *state, DumpState *dstate, cosmo_Writer writer,
|
||||||
|
const void *userData)
|
||||||
|
{
|
||||||
|
dstate->state = state;
|
||||||
|
dstate->userData = userData;
|
||||||
|
dstate->writer = writer;
|
||||||
|
dstate->writerStatus = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeBlock(DumpState *dstate, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
if (dstate->writerStatus == 0) {
|
||||||
|
dstate->writerStatus = dstate->writer(dstate->state, data, size, dstate->userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dstate->writerStatus == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeu8(DumpState *dstate, uint8_t d)
|
||||||
|
{
|
||||||
|
return writeBlock(dstate, &d, sizeof(uint8_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeu32(DumpState *dstate, uint32_t d)
|
||||||
|
{
|
||||||
|
return writeBlock(dstate, &d, sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeSize(DumpState *dstate, size_t d)
|
||||||
|
{
|
||||||
|
return writeBlock(dstate, &d, sizeof(size_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeVector(DumpState *dstate, const void *data, size_t size, size_t count)
|
||||||
|
{
|
||||||
|
check(writeSize(dstate, count));
|
||||||
|
check(writeBlock(dstate, data, size * count));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeHeader(DumpState *dstate)
|
||||||
|
{
|
||||||
|
check(writeBlock(dstate, COSMO_MAGIC, COSMO_MAGIC_LEN));
|
||||||
|
|
||||||
|
/* after the magic, we write some platform information */
|
||||||
|
check(writeu8(dstate, cosmoD_isBigEndian()));
|
||||||
|
check(writeu8(dstate, sizeof(cosmo_Number)));
|
||||||
|
check(writeu8(dstate, sizeof(size_t)));
|
||||||
|
check(writeu8(dstate, sizeof(int)));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeCObjString(DumpState *dstate, CObjString *obj)
|
||||||
|
{
|
||||||
|
if (obj == NULL) { /* this is in case cobjfunction's name or module strings are null */
|
||||||
|
check(writeu32(dstate, 0));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write string length */
|
||||||
|
check(writeu32(dstate, obj->length));
|
||||||
|
|
||||||
|
/* write string data */
|
||||||
|
check(writeBlock(dstate, obj->str, obj->length));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeCObjFunction(DumpState *dstate, CObjFunction *obj)
|
||||||
|
{
|
||||||
|
check(writeCObjString(dstate, obj->name));
|
||||||
|
check(writeCObjString(dstate, obj->module));
|
||||||
|
|
||||||
|
check(writeu32(dstate, obj->args));
|
||||||
|
check(writeu32(dstate, obj->upvals));
|
||||||
|
check(writeu8(dstate, obj->variadic));
|
||||||
|
|
||||||
|
/* write chunk info */
|
||||||
|
check(writeVector(dstate, obj->chunk.buf, sizeof(uint8_t), obj->chunk.count));
|
||||||
|
|
||||||
|
/* write line info */
|
||||||
|
check(writeVector(dstate, obj->chunk.lineInfo, sizeof(int), obj->chunk.count));
|
||||||
|
|
||||||
|
/* write constants */
|
||||||
|
check(writeSize(dstate, obj->chunk.constants.count));
|
||||||
|
for (int i = 0; i < obj->chunk.constants.count; i++) {
|
||||||
|
check(writeCValue(dstate, obj->chunk.constants.values[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeCObj(DumpState *dstate, CObj *obj)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
we can kind of cheat here since our parser only emits a few very limited CObjs...
|
||||||
|
CChunks will only ever have the following CObj's in their constant table:
|
||||||
|
- COBJ_STRING
|
||||||
|
- COBJ_FUNCTION
|
||||||
|
|
||||||
|
the rest of the objects are created during runtime. yay!
|
||||||
|
*/
|
||||||
|
CObjType t = cosmoO_readType(obj);
|
||||||
|
|
||||||
|
/* write cobj type */
|
||||||
|
writeu8(dstate, t);
|
||||||
|
|
||||||
|
/* write object payload/body */
|
||||||
|
switch (t) {
|
||||||
|
case COBJ_STRING:
|
||||||
|
check(writeCObjString(dstate, (CObjString *)obj));
|
||||||
|
break;
|
||||||
|
case COBJ_FUNCTION:
|
||||||
|
check(writeCObjFunction(dstate, (CObjFunction *)obj));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cosmoV_error(dstate->state, "invalid cobj type: %d", t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define WRITE_VAR(dstate, type, expression) \
|
||||||
|
{ \
|
||||||
|
type _tmp = expression; \
|
||||||
|
check(writeBlock(dstate, &_tmp, sizeof(_tmp))); \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writeCValue(DumpState *dstate, CValue val)
|
||||||
|
{
|
||||||
|
CosmoType t = GET_TYPE(val);
|
||||||
|
|
||||||
|
/* write value type */
|
||||||
|
writeu8(dstate, t);
|
||||||
|
|
||||||
|
/* write value payload/body */
|
||||||
|
switch (t) {
|
||||||
|
case COSMO_TNUMBER:
|
||||||
|
WRITE_VAR(dstate, cosmo_Number, cosmoV_readNumber(val))
|
||||||
|
case COSMO_TBOOLEAN:
|
||||||
|
WRITE_VAR(dstate, bool, cosmoV_readBoolean(val))
|
||||||
|
case COSMO_TREF:
|
||||||
|
check(writeCObj(dstate, cosmoV_readRef(val)));
|
||||||
|
break;
|
||||||
|
case COSMO_TNIL: /* no body */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cosmoV_error(dstate->state, "invalid value type: %d", t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef WRITE_VAR
|
||||||
|
|
||||||
|
bool cosmoD_isBigEndian()
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t c[4];
|
||||||
|
} _indxint = {0xDEADB33F};
|
||||||
|
|
||||||
|
return _indxint.c[0] == 0xDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cosmoD_dump(CState *state, CObjFunction *func, cosmo_Writer writer, const void *userData)
|
||||||
|
{
|
||||||
|
DumpState dstate;
|
||||||
|
initDumpState(state, &dstate, writer, userData);
|
||||||
|
|
||||||
|
check(writeHeader(&dstate));
|
||||||
|
check(writeCObjFunction(&dstate, func));
|
||||||
|
|
||||||
|
return dstate.writerStatus;
|
||||||
|
}
|
17
src/cdump.h
Normal file
17
src/cdump.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef COSMO_DUMP_H
|
||||||
|
#define COSMO_DUMP_H
|
||||||
|
|
||||||
|
#include "cobj.h"
|
||||||
|
#include "cosmo.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define COSMO_MAGIC "COS\x12"
|
||||||
|
#define COSMO_MAGIC_LEN 4
|
||||||
|
|
||||||
|
bool cosmoD_isBigEndian();
|
||||||
|
|
||||||
|
/* returns non-zero on error */
|
||||||
|
int cosmoD_dump(CState *state, CObjFunction *func, cosmo_Writer writer, const void *userData);
|
||||||
|
|
||||||
|
#endif
|
@ -45,6 +45,9 @@ typedef struct CObjClosure CObjClosure;
|
|||||||
|
|
||||||
typedef uint8_t INSTRUCTION;
|
typedef uint8_t INSTRUCTION;
|
||||||
|
|
||||||
|
typedef int (*cosmo_Reader)(CState *state, void *data, size_t size, const void *ud);
|
||||||
|
typedef int (*cosmo_Writer)(CState *state, const void *data, size_t size, const void *ud);
|
||||||
|
|
||||||
#define COSMOMAX_UPVALS 80
|
#define COSMOMAX_UPVALS 80
|
||||||
#define FRAME_MAX 64
|
#define FRAME_MAX 64
|
||||||
#define STACK_MAX (256 * FRAME_MAX)
|
#define STACK_MAX (256 * FRAME_MAX)
|
||||||
|
219
src/cundump.c
Normal file
219
src/cundump.c
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
#include "cundump.h"
|
||||||
|
|
||||||
|
#include "cchunk.h"
|
||||||
|
#include "cdump.h"
|
||||||
|
#include "cmem.h"
|
||||||
|
#include "cvm.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CState *state;
|
||||||
|
const void *userData;
|
||||||
|
cosmo_Reader reader;
|
||||||
|
int readerStatus;
|
||||||
|
} UndumpState;
|
||||||
|
|
||||||
|
static bool readCValue(UndumpState *udstate, CValue *val);
|
||||||
|
|
||||||
|
#define check(e) \
|
||||||
|
if (!e) { \
|
||||||
|
return false; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initUndumpState(CState *state, UndumpState *udstate, cosmo_Reader reader,
|
||||||
|
const void *userData)
|
||||||
|
{
|
||||||
|
udstate->state = state;
|
||||||
|
udstate->userData = userData;
|
||||||
|
udstate->reader = reader;
|
||||||
|
udstate->readerStatus = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readBlock(UndumpState *udstate, void *data, size_t size)
|
||||||
|
{
|
||||||
|
if (udstate->readerStatus == 0) {
|
||||||
|
/* if reader returns 1, we expect an error was thrown */
|
||||||
|
udstate->readerStatus = udstate->reader(udstate->state, data, size, udstate->userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return udstate->readerStatus == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readu8(UndumpState *udstate, uint8_t *d)
|
||||||
|
{
|
||||||
|
return readBlock(udstate, d, sizeof(uint8_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readu32(UndumpState *udstate, uint32_t *d)
|
||||||
|
{
|
||||||
|
return readBlock(udstate, d, sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readSize(UndumpState *udstate, size_t *d)
|
||||||
|
{
|
||||||
|
return readBlock(udstate, d, sizeof(size_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readVector(UndumpState *udstate, void **data, size_t size, size_t *count)
|
||||||
|
{
|
||||||
|
check(readSize(udstate, count));
|
||||||
|
*data = cosmoM_xmalloc(udstate->state, (*count) * size);
|
||||||
|
return readBlock(udstate, *data, (*count) * size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define checku8(udstate, d, tmp) \
|
||||||
|
check(readu8(udstate, &tmp)); \
|
||||||
|
if (d != tmp) { \
|
||||||
|
cosmoV_error(udstate->state, "bad header!"); \
|
||||||
|
return false; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkHeader(UndumpState *udstate)
|
||||||
|
{
|
||||||
|
char magic[COSMO_MAGIC_LEN];
|
||||||
|
uint8_t tmp;
|
||||||
|
|
||||||
|
/* check header */
|
||||||
|
check(readBlock(udstate, magic, COSMO_MAGIC_LEN));
|
||||||
|
if (memcmp(magic, COSMO_MAGIC, COSMO_MAGIC_LEN) != 0) {
|
||||||
|
cosmoV_error(udstate->state, "bad header!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* after the magic, we read some platform information */
|
||||||
|
checku8(udstate, cosmoD_isBigEndian(), tmp);
|
||||||
|
checku8(udstate, sizeof(cosmo_Number), tmp);
|
||||||
|
checku8(udstate, sizeof(size_t), tmp);
|
||||||
|
checku8(udstate, sizeof(int), tmp);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef checku8
|
||||||
|
|
||||||
|
static bool readCObjString(UndumpState *udstate, CObjString **str)
|
||||||
|
{
|
||||||
|
uint32_t size;
|
||||||
|
char *data;
|
||||||
|
|
||||||
|
check(readu32(udstate, (uint32_t *)&size));
|
||||||
|
if (size == 0) { /* empty string */
|
||||||
|
*str = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = cosmoM_xmalloc(udstate->state, size + 1);
|
||||||
|
check(readBlock(udstate, (void *)data, size));
|
||||||
|
data[size] = '\0'; /* add NULL-terminator */
|
||||||
|
|
||||||
|
*str = cosmoO_takeString(udstate->state, data, size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readCObjFunction(UndumpState *udstate, CObjFunction **func)
|
||||||
|
{
|
||||||
|
size_t constants;
|
||||||
|
CValue val;
|
||||||
|
|
||||||
|
*func = cosmoO_newFunction(udstate->state);
|
||||||
|
|
||||||
|
/* make sure our GC can see that we're currently using this function (and the values it uses) */
|
||||||
|
cosmoV_pushRef(udstate->state, (CObj *)*func);
|
||||||
|
|
||||||
|
check(readCObjString(udstate, &(*func)->name));
|
||||||
|
check(readCObjString(udstate, &(*func)->module));
|
||||||
|
|
||||||
|
check(readu32(udstate, (uint32_t *)&(*func)->args));
|
||||||
|
check(readu32(udstate, (uint32_t *)&(*func)->upvals));
|
||||||
|
check(readu8(udstate, (uint8_t *)&(*func)->variadic));
|
||||||
|
|
||||||
|
/* read chunk info */
|
||||||
|
check(
|
||||||
|
readVector(udstate, (void **)&(*func)->chunk.buf, sizeof(uint8_t), &(*func)->chunk.count));
|
||||||
|
check(
|
||||||
|
readVector(udstate, (void **)&(*func)->chunk.lineInfo, sizeof(int), &(*func)->chunk.count));
|
||||||
|
|
||||||
|
/* read constants */
|
||||||
|
check(readSize(udstate, &constants));
|
||||||
|
for (int i = 0; i < constants; i++) {
|
||||||
|
check(readCValue(udstate, &val));
|
||||||
|
addConstant(udstate->state, &(*func)->chunk, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pop function off stack */
|
||||||
|
cosmoV_pop(udstate->state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readCObj(UndumpState *udstate, CObj **obj)
|
||||||
|
{
|
||||||
|
uint8_t type;
|
||||||
|
check(readu8(udstate, &type));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case COBJ_STRING:
|
||||||
|
return readCObjString(udstate, (CObjString **)obj);
|
||||||
|
case COBJ_FUNCTION:
|
||||||
|
return readCObjFunction(udstate, (CObjFunction **)obj);
|
||||||
|
default:
|
||||||
|
cosmoV_error(udstate->state, "unknown object type!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define READ_VAR(udstate, val, type, creator) \
|
||||||
|
{ \
|
||||||
|
type _tmp; \
|
||||||
|
check(readBlock(udstate, &_tmp, sizeof(type))); \
|
||||||
|
*val = creator(_tmp); \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readCValue(UndumpState *udstate, CValue *val)
|
||||||
|
{
|
||||||
|
uint8_t t;
|
||||||
|
check(readu8(udstate, &t));
|
||||||
|
|
||||||
|
switch (t) {
|
||||||
|
case COSMO_TNUMBER:
|
||||||
|
READ_VAR(udstate, val, cosmo_Number, cosmoV_newNumber)
|
||||||
|
case COSMO_TBOOLEAN:
|
||||||
|
READ_VAR(udstate, val, bool, cosmoV_newBoolean)
|
||||||
|
case COSMO_TREF: {
|
||||||
|
CObj *obj;
|
||||||
|
check(readCObj(udstate, (CObj **)&obj));
|
||||||
|
*val = cosmoV_newRef(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case COSMO_TNIL:
|
||||||
|
*val = cosmoV_newNil();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cosmoV_error(udstate->state, "invalid value type: %d", t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef READ_VAR
|
||||||
|
#undef check
|
||||||
|
|
||||||
|
int cosmoD_undump(CState *state, cosmo_Reader reader, const void *userData, CObjFunction **func)
|
||||||
|
{
|
||||||
|
UndumpState udstate;
|
||||||
|
initUndumpState(state, &udstate, reader, userData);
|
||||||
|
|
||||||
|
if (!checkHeader(&udstate)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!readCObjFunction(&udstate, func)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cosmoV_pushRef(state, (CObj *)*func);
|
||||||
|
return udstate.readerStatus;
|
||||||
|
}
|
12
src/cundump.h
Normal file
12
src/cundump.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef COSMO_UNDUMP_H
|
||||||
|
#define COSMO_UNDUMP_H
|
||||||
|
|
||||||
|
#include "cobj.h"
|
||||||
|
#include "cosmo.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* returns non-zero on error */
|
||||||
|
int cosmoD_undump(CState *state, cosmo_Reader reader, const void *userData, CObjFunction **func);
|
||||||
|
|
||||||
|
#endif
|
25
src/cvm.c
25
src/cvm.c
@ -4,6 +4,7 @@
|
|||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
#include "cparse.h"
|
#include "cparse.h"
|
||||||
#include "cstate.h"
|
#include "cstate.h"
|
||||||
|
#include "cundump.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@ -30,6 +31,28 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val)
|
|||||||
state->top++;
|
state->top++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
|
||||||
|
{
|
||||||
|
CObjFunction *func;
|
||||||
|
|
||||||
|
if (cosmoD_undump(state, reader, ud, &func)) {
|
||||||
|
// fail recovery
|
||||||
|
state->panic = false;
|
||||||
|
cosmoV_pushRef(state, (CObj *)state->error);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef VM_DEBUG
|
||||||
|
disasmChunk(&func->chunk, func->name ? func->name->str : UNNAMEDCHUNK, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||||
|
// location put our closure
|
||||||
|
cosmoV_pushRef(state, (CObj *)func);
|
||||||
|
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
||||||
{
|
{
|
||||||
CObjFunction *func;
|
CObjFunction *func;
|
||||||
@ -46,7 +69,7 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fail
|
// fail recovery
|
||||||
state->panic = false;
|
state->panic = false;
|
||||||
cosmoV_pushRef(state, (CObj *)state->error);
|
cosmoV_pushRef(state, (CObj *)state->error);
|
||||||
return false;
|
return false;
|
||||||
|
12
src/cvm.h
12
src/cvm.h
@ -38,7 +38,7 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
|||||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj);
|
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack
|
compiles string into a <closure>. if successful, <closure> will be pushed onto the stack
|
||||||
otherwise the <error> will be pushed.
|
otherwise the <error> will be pushed.
|
||||||
|
|
||||||
returns:
|
returns:
|
||||||
@ -47,6 +47,16 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
|
|||||||
*/
|
*/
|
||||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name);
|
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
loads a <closure> from a dump. if successful, <closure> will be pushed onto the stack
|
||||||
|
otherwise the <error> will be pushed.
|
||||||
|
|
||||||
|
returns:
|
||||||
|
false : <error> is at the top of the stack
|
||||||
|
true : <closure> is at the top of the stack
|
||||||
|
*/
|
||||||
|
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
expects object to be pushed, then the key.
|
expects object to be pushed, then the key.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user