mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-12-04 14:36:01 +00:00
Compare commits
18 Commits
3efee51224
...
5169aca6d0
Author | SHA1 | Date | |
---|---|---|---|
5169aca6d0 | |||
c34c5850e2 | |||
07ba4c731e | |||
788911130d | |||
c464a76849 | |||
8c6ba18848 | |||
93a09698a9 | |||
be49ec5af5 | |||
4fe437ea4e | |||
e854c5dbb3 | |||
c945c56482 | |||
89d443d767 | |||
54a959438b | |||
355842989b | |||
45f36e6e87 | |||
819e76b711 | |||
f116efa02c | |||
465f4d5e4a |
4
Makefile
4
Makefile
@ -19,6 +19,8 @@ CHDR=\
|
||||
src/cvm.h\
|
||||
src/cobj.h\
|
||||
src/cbaselib.h\
|
||||
src/cdump.h\
|
||||
src/cundump.h\
|
||||
|
||||
CSRC=\
|
||||
src/cchunk.c\
|
||||
@ -33,6 +35,8 @@ CSRC=\
|
||||
src/cvm.c\
|
||||
src/cobj.c\
|
||||
src/cbaselib.c\
|
||||
src/cdump.c\
|
||||
src/cundump.c\
|
||||
main.c\
|
||||
|
||||
COBJ=$(CSRC:.c=.o)
|
||||
|
19
README.md
19
README.md
@ -1,6 +1,20 @@
|
||||
# Cosmo
|
||||
[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/CPunch/Cosmo?svg=true)](https://ci.appveyor.com/project/CPunch/Cosmo)
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
Usage: ./bin/cosmo [-clsr] [args]
|
||||
|
||||
available options are:
|
||||
-c <in> <out> compile <in> and dump to <out>
|
||||
-l <in> load dump from <in>
|
||||
-s <in...> compile and run <in...> script(s)
|
||||
-r start the repl
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
```lua
|
||||
@ -39,7 +53,4 @@ end
|
||||
2 : 1
|
||||
1 : 2
|
||||
0 : 3
|
||||
```
|
||||
|
||||
# C API
|
||||
The Cosmo C API is currently undocumented, however as soon as development has reached a stable state documentation on full language features and the C API will start.
|
||||
```
|
@ -25,7 +25,7 @@ install:
|
||||
- sudo apt install clang cmake -y
|
||||
|
||||
build_script:
|
||||
- make && ./bin/cosmo examples/testsuite.cosmo examples/getters_setters.cosmo
|
||||
- make && ./bin/cosmo examples/testsuite.cosmo -s examples/getters_setters.cosmo
|
||||
|
||||
artifacts:
|
||||
- path: bin
|
||||
|
@ -1,13 +1,13 @@
|
||||
let object = {
|
||||
__setter = [
|
||||
"field1" = function(self, val)
|
||||
"field1" = func(self, val)
|
||||
print("setter for field1 called!")
|
||||
self.x = val
|
||||
end
|
||||
],
|
||||
|
||||
__getter = [
|
||||
"field1" = function(self)
|
||||
"field1" = func(self)
|
||||
print("getter for field1 called!")
|
||||
|
||||
return self.x + 1
|
||||
|
169
main.c
169
main.c
@ -1,21 +1,29 @@
|
||||
#include "cosmo.h"
|
||||
#include "cbaselib.h"
|
||||
#include "cchunk.h"
|
||||
#include "cdebug.h"
|
||||
#include "cvm.h"
|
||||
#include "cparse.h"
|
||||
#include "cbaselib.h"
|
||||
|
||||
#include "cdump.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;
|
||||
|
||||
int cosmoB_quitRepl(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_quitRepl(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
_ACTIVE = false;
|
||||
|
||||
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()!
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
CObjString *str = cosmoV_toString(state, args[i]);
|
||||
@ -26,16 +34,18 @@ int cosmoB_input(CState *state, int nargs, CValue *args) {
|
||||
char line[1024];
|
||||
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
|
||||
}
|
||||
|
||||
static bool interpret(CState *state, const char *script, const char *mod) {
|
||||
static bool interpret(CState *state, const char *script, const char *mod)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
||||
if (cosmoV_compileString(state, script, mod)) {
|
||||
if (cosmoV_compileString(state, script, mod)) {
|
||||
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
||||
|
||||
if (res == COSMOVM_RUNTIME_ERR)
|
||||
@ -50,15 +60,11 @@ static bool interpret(CState *state, const char *script, const char *mod) {
|
||||
return !ret;
|
||||
}
|
||||
|
||||
static void repl() {
|
||||
static void repl(CState *state)
|
||||
{
|
||||
char line[1024];
|
||||
_ACTIVE = true;
|
||||
|
||||
CState *state = cosmoV_newState();
|
||||
cosmoB_loadLibrary(state);
|
||||
cosmoB_loadOSLib(state);
|
||||
cosmoB_loadVM(state);
|
||||
|
||||
// add our custom REPL functions
|
||||
cosmoV_pushString(state, "quit");
|
||||
cosmoV_pushCFunction(state, cosmoB_quitRepl);
|
||||
@ -78,12 +84,11 @@ static void repl() {
|
||||
|
||||
interpret(state, line, "REPL");
|
||||
}
|
||||
|
||||
cosmoV_freeState(state);
|
||||
}
|
||||
|
||||
static char *readFile(const char* path) {
|
||||
FILE* file = fopen(path, "rb");
|
||||
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);
|
||||
@ -94,7 +99,7 @@ static char *readFile(const char* path) {
|
||||
size_t fileSize = ftell(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) {
|
||||
fprintf(stderr, "failed to allocate!");
|
||||
exit(1);
|
||||
@ -106,7 +111,7 @@ static char *readFile(const char* path) {
|
||||
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
|
||||
@ -114,12 +119,10 @@ static char *readFile(const char* path) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static bool runFile(const char* fileName) {
|
||||
static bool runFile(CState *state, const char *fileName)
|
||||
{
|
||||
bool ret;
|
||||
char* script = readFile(fileName);
|
||||
CState *state = cosmoV_newState();
|
||||
cosmoB_loadLibrary(state);
|
||||
cosmoB_loadOSLib(state);
|
||||
char *script = readFile(fileName);
|
||||
|
||||
// add our input() function to the global table
|
||||
cosmoV_pushString(state, "input");
|
||||
@ -129,21 +132,119 @@ static bool runFile(const char* fileName) {
|
||||
|
||||
ret = interpret(state, script, fileName);
|
||||
|
||||
cosmoV_freeState(state);
|
||||
free(script);
|
||||
return ret; // let the caller know if the script failed
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
if (argc == 1) {
|
||||
repl();
|
||||
} else if (argc >= 2) { // they passed a file (or more lol)
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!runFile(argv[i])) {
|
||||
int fileWriter(CState *state, const void *data, size_t size, const void *ud)
|
||||
{
|
||||
return !fwrite(data, size, 1, (FILE *)ud);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
isValid = true;
|
||||
break;
|
||||
case 'r':
|
||||
repl(state);
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
printUsage(argv[0]);
|
||||
}
|
||||
|
||||
cosmoV_freeState(state);
|
||||
return 0;
|
||||
}
|
||||
|
@ -980,6 +980,4 @@ void cosmoB_loadVM(CState *state)
|
||||
|
||||
// register "vm" to the global table
|
||||
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 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 FRAME_MAX 64
|
||||
#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 "cparse.h"
|
||||
#include "cstate.h"
|
||||
#include "cundump.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
@ -30,6 +31,28 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val)
|
||||
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)
|
||||
{
|
||||
CObjFunction *func;
|
||||
@ -46,7 +69,7 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
||||
return true;
|
||||
}
|
||||
|
||||
// fail
|
||||
// fail recovery
|
||||
state->panic = false;
|
||||
cosmoV_pushRef(state, (CObj *)state->error);
|
||||
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);
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user