Cleanup fileio module with new macros

This commit is contained in:
K. Lange 2021-02-06 21:39:23 +09:00
parent 47329df343
commit 421d5ea000

View File

@ -13,29 +13,39 @@
#include "memory.h"
#include "util.h"
static KrkClass * FileClass = NULL;
static KrkClass * BinaryFileClass = NULL;
static KrkClass * Directory = NULL;
struct FileObject {
static KrkClass * File = NULL;
static KrkClass * BinaryFile = NULL;
struct File {
KrkInstance inst;
FILE * filePtr;
};
#define IS_File(o) (krk_isInstanceOf(o, File))
#define AS_File(o) ((struct File*)AS_OBJECT(o))
#define IS_BinaryFile(o) (krk_isInstanceOf(o, BinaryFile))
#define AS_BinaryFile(o) ((struct File*)AS_OBJECT(o))
static KrkClass * Directory = NULL;
struct Directory {
KrkInstance inst;
DIR * dirPtr;
};
KrkValue krk_open(int argc, KrkValue argv[]) {
if (argc < 1) return krk_runtimeError(vm.exceptions.argumentError, "open() takes at least 1 argument.");
if (argc > 2) return krk_runtimeError(vm.exceptions.argumentError, "open() takes at most 2 argument.");
if (!IS_STRING(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "open: first argument should be a filename string, not '%s'", krk_typeName(argv[0]));
if (argc == 2 && !IS_STRING(argv[1])) return krk_runtimeError(vm.exceptions.typeError, "open: second argument should be a mode string, not '%s'", krk_typeName(argv[1]));
#define IS_Directory(o) (krk_isInstanceOf(o,Directory))
#define AS_Directory(o) ((struct Directory*)AS_OBJECT(o))
#define CURRENT_CTYPE struct File *
#define CURRENT_NAME self
KRK_FUNC(open,{
FUNCTION_TAKES_AT_LEAST(1);
FUNCTION_TAKES_AT_MOST(2);
CHECK_ARG(0,str,KrkString*,filename);
if (argc == 2 && !IS_STRING(argv[1])) return TYPE_ERROR(str,argv[1]);
KrkValue arg;
int isBinary = 0;
if (argc == 1) {
arg = OBJECT_VAL(S("r"));
krk_push(arg); /* Will be peeked to find arg string for fopen */
@ -57,44 +67,42 @@ KrkValue krk_open(int argc, KrkValue argv[]) {
}
}
FILE * file = fopen(AS_CSTRING(argv[0]), AS_CSTRING(krk_peek(0)));
FILE * file = fopen(filename->chars, AS_CSTRING(krk_peek(0)));
if (!file) return krk_runtimeError(vm.exceptions.ioError, "open: failed to open file; system returned: %s", strerror(errno));
/* Now let's build an object to hold it */
KrkInstance * fileObject = krk_newInstance(isBinary ? BinaryFileClass : FileClass);
KrkInstance * fileObject = krk_newInstance(isBinary ? BinaryFile : File);
krk_push(OBJECT_VAL(fileObject));
/* Let's put the filename in there somewhere... */
krk_attachNamedValue(&fileObject->fields, "filename", argv[0]);
krk_attachNamedValue(&fileObject->fields, "filename", OBJECT_VAL(filename));
krk_attachNamedValue(&fileObject->fields, "modestr", arg);
((struct FileObject*)fileObject)->filePtr = file;
((struct File*)fileObject)->filePtr = file;
krk_pop();
krk_pop();
return OBJECT_VAL(fileObject);
}
})
#define BLOCK_SIZE 1024
static KrkValue krk_file_str(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],FileClass)) return krk_runtimeError(vm.exceptions.typeError, "argument must be File");
KrkInstance * fileObj = AS_INSTANCE(argv[0]);
KrkValue filename, modestr;
if (!krk_tableGet(&fileObj->fields, OBJECT_VAL(S("filename")), &filename) || !IS_STRING(filename)) return krk_runtimeError(vm.exceptions.baseException, "Corrupt File");
if (!krk_tableGet(&fileObj->fields, OBJECT_VAL(S("modestr")), &modestr) || !IS_STRING(modestr)) return krk_runtimeError(vm.exceptions.baseException, "Corrupt File");
KRK_METHOD(File,__str__,{
METHOD_TAKES_NONE();
KrkValue filename;
KrkValue modestr;
if (!krk_tableGet(&self->inst.fields, OBJECT_VAL(S("filename")), &filename) || !IS_STRING(filename)) return krk_runtimeError(vm.exceptions.baseException, "Corrupt File");
if (!krk_tableGet(&self->inst.fields, OBJECT_VAL(S("modestr")), &modestr) || !IS_STRING(modestr)) return krk_runtimeError(vm.exceptions.baseException, "Corrupt File");
char * tmp = malloc(AS_STRING(filename)->length + AS_STRING(modestr)->length + 100); /* safety */
sprintf(tmp, "<%s file '%s', mode '%s' at %p>", ((struct FileObject*)fileObj)->filePtr ? "open" : "closed", AS_CSTRING(filename), AS_CSTRING(modestr), (void*)fileObj);
sprintf(tmp, "<%s file '%s', mode '%s' at %p>", self->filePtr ? "open" : "closed", AS_CSTRING(filename), AS_CSTRING(modestr), (void*)self);
KrkString * out = krk_copyString(tmp, strlen(tmp));
free(tmp);
return OBJECT_VAL(out);
}
})
static KrkValue krk_file_readline(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],FileClass)) return krk_runtimeError(vm.exceptions.typeError, "argument must be File");
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
KRK_METHOD(File,readline,{
METHOD_TAKES_NONE();
FILE * file = self->filePtr;
if (!file || feof(file)) {
return NONE_VAL();
@ -130,16 +138,15 @@ _finish_line: (void)0;
KrkString * out = krk_copyString(buffer,sizeRead);
free(buffer);
return OBJECT_VAL(out);
}
static KrkValue krk_file_readlines(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],FileClass)) return krk_runtimeError(vm.exceptions.typeError, "argument must be File");
})
KRK_METHOD(File,readlines,{
METHOD_TAKES_NONE();
KrkValue myList = krk_list_of(0,NULL);
krk_push(myList);
for (;;) {
KrkValue line = krk_file_readline(1, argv);
KrkValue line = FUNC_NAME(File,readline)(1, argv, 0);
if (IS_NONE(line)) break;
krk_push(line);
@ -149,13 +156,12 @@ static KrkValue krk_file_readlines(int argc, KrkValue argv[]) {
krk_pop(); /* myList */
return myList;
}
static KrkValue krk_file_read(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],FileClass)) return krk_runtimeError(vm.exceptions.typeError, "argument must be File");
})
KRK_METHOD(File,read,{
METHOD_TAKES_NONE();
/* Get the file ptr reference */
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
FILE * file = self->filePtr;
if (!file || feof(file)) {
return NONE_VAL();
@ -189,73 +195,51 @@ static KrkValue krk_file_read(int argc, KrkValue argv[]) {
KrkString * out = krk_copyString(buffer,sizeRead);
free(buffer);
return OBJECT_VAL(out);
}
static KrkValue krk_file_write(int argc, KrkValue argv[]) {
/* Expect just a string as arg 2 */
if (argc < 2 || !krk_isInstanceOf(argv[0], FileClass) || !IS_STRING(argv[1]))
return krk_runtimeError(vm.exceptions.typeError, "write: expected string");
})
KRK_METHOD(File,write,{
METHOD_TAKES_EXACTLY(1);
if (!IS_STRING(argv[1])) return TYPE_ERROR(str,argv[1]);
/* Find the file ptr reference */
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
FILE * file = self->filePtr;
if (!file || feof(file)) {
return NONE_VAL();
}
return INTEGER_VAL(fwrite(AS_CSTRING(argv[1]), 1, AS_STRING(argv[1])->length, file));
}
})
static KrkValue krk_file_close(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],FileClass))
return krk_runtimeError(vm.exceptions.typeError, "argument must be File");
KRK_METHOD(File,close,{
METHOD_TAKES_NONE();
FILE * file = self->filePtr;
if (file) fclose(file);
self->filePtr = NULL;
})
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
KRK_METHOD(File,flush,{
METHOD_TAKES_NONE();
FILE * file = self->filePtr;
if (file) fflush(file);
})
if (!file) return NONE_VAL();
fclose(file);
((struct FileObject*)AS_OBJECT(argv[0]))->filePtr = NULL;
return NONE_VAL();
}
static KrkValue krk_file_flush(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],FileClass))
return krk_runtimeError(vm.exceptions.typeError, "argument must be File");
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
if (!file) return NONE_VAL();
fflush(file);
return NONE_VAL();
}
static KrkValue krk_file_reject_init(int argc, KrkValue argv[]) {
KRK_METHOD(File,__init__,{
return krk_runtimeError(vm.exceptions.typeError, "File objects can not be instantiated; use fileio.open() to obtain File objects.");
}
})
static KrkValue krk_file_enter(int argc, KrkValue argv[]) {
/* Does nothing. */
return NONE_VAL();
}
static KrkValue krk_file_exit(int argc, KrkValue argv[]) {
/* Just an alias to close that triggers when a context manager is exited */
return krk_file_close(argc,argv);
}
KRK_METHOD(File,__enter__,{})
KRK_METHOD(File,__exit__,{
return FUNC_NAME(File,close)(argc,argv,0);
})
static void makeFileInstance(KrkInstance * module, const char name[], FILE * file) {
KrkInstance * fileObject = krk_newInstance(FileClass);
KrkInstance * fileObject = krk_newInstance(File);
krk_push(OBJECT_VAL(fileObject));
KrkValue filename = OBJECT_VAL(krk_copyString(name,strlen(name)));
krk_push(filename);
krk_attachNamedValue(&fileObject->fields, "filename", filename);
((struct FileObject*)fileObject)->filePtr = file;
((struct File*)fileObject)->filePtr = file;
krk_attachNamedObject(&module->fields, name, (KrkObj*)fileObject);
@ -263,11 +247,9 @@ static void makeFileInstance(KrkInstance * module, const char name[], FILE * fil
krk_pop(); /* fileObject */
}
static KrkValue krk_file_readline_b(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],BinaryFileClass))
return krk_runtimeError(vm.exceptions.typeError, "argument must be BinaryFile");
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
KRK_METHOD(BinaryFile,readline,{
METHOD_TAKES_NONE();
FILE * file = self->filePtr;
if (!file || feof(file)) {
return NONE_VAL();
@ -303,16 +285,15 @@ _finish_line: (void)0;
KrkBytes * out = krk_newBytes(sizeRead, (unsigned char*)buffer);
free(buffer);
return OBJECT_VAL(out);
}
})
static KrkValue krk_file_readlines_b(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0],BinaryFileClass))
return krk_runtimeError(vm.exceptions.typeError, "argument must be BinaryFile");
KRK_METHOD(BinaryFile,readlines,{
METHOD_TAKES_NONE();
KrkValue myList = krk_list_of(0,NULL);
krk_push(myList);
for (;;) {
KrkValue line = krk_file_readline_b(1, argv);
KrkValue line = FUNC_NAME(BinaryFile,readline)(1, argv, 0);
if (IS_NONE(line)) break;
krk_push(line);
@ -322,15 +303,12 @@ static KrkValue krk_file_readlines_b(int argc, KrkValue argv[]) {
krk_pop(); /* myList */
return myList;
}
static KrkValue krk_file_read_b(int argc, KrkValue argv[]) {
if (argc < 1 || !krk_isInstanceOf(argv[0], BinaryFileClass)) {
return krk_runtimeError(vm.exceptions.typeError, "argument must be BinaryFile");
}
})
KRK_METHOD(BinaryFile,read,{
METHOD_TAKES_NONE();
/* Get the file ptr reference */
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
FILE * file = self->filePtr;
if (!file || feof(file)) {
return NONE_VAL();
@ -364,26 +342,25 @@ static KrkValue krk_file_read_b(int argc, KrkValue argv[]) {
KrkBytes * out = krk_newBytes(sizeRead, (unsigned char*)buffer);
free(buffer);
return OBJECT_VAL(out);
}
static KrkValue krk_file_write_b(int argc, KrkValue argv[]) {
/* Expect just a string as arg 2 */
if (argc < 2 || !krk_isInstanceOf(argv[0], BinaryFileClass) || !IS_BYTES(argv[1])) {
return krk_runtimeError(vm.exceptions.typeError, "write: expected bytes");
}
})
KRK_METHOD(BinaryFile,write,{
METHOD_TAKES_EXACTLY(1);
if (!IS_BYTES(argv[1])) return TYPE_ERROR(bytes,argv[1]);
/* Find the file ptr reference */
FILE * file = ((struct FileObject*)AS_OBJECT(argv[0]))->filePtr;
FILE * file = self->filePtr;
if (!file || feof(file)) {
return NONE_VAL();
}
return INTEGER_VAL(fwrite(AS_BYTES(argv[1])->bytes, 1, AS_BYTES(argv[1])->length, file));
}
})
#undef CURRENT_CTYPE
static void _file_sweep(KrkInstance * self) {
struct FileObject * me = (void *)self;
struct File * me = (void *)self;
if (me->filePtr) {
fclose(me->filePtr);
me->filePtr = NULL;
@ -398,27 +375,23 @@ static void _dir_sweep(KrkInstance * self) {
}
}
static KrkValue _opendir(int argc, KrkValue argv[], int hasKw) {
if (argc != 1) return krk_runtimeError(vm.exceptions.argumentError, "opendir() expects exactly one argument");
if (!IS_STRING(argv[0])) return krk_runtimeError(vm.exceptions.typeError, "expected str, not '%s'", krk_typeName(argv[0]));
char * path = AS_CSTRING(argv[0]);
KRK_FUNC(opendir,{
FUNCTION_TAKES_EXACTLY(1);
CHECK_ARG(0,str,KrkString*,path);
DIR * dir = opendir(path);
DIR * dir = opendir(path->chars);
if (!dir) return krk_runtimeError(vm.exceptions.ioError, "opendir: %s", strerror(errno));
struct Directory * dirObj = (void *)krk_newInstance(Directory);
krk_push(OBJECT_VAL(dirObj));
krk_attachNamedValue(&dirObj->inst.fields, "path", argv[0]);
krk_attachNamedValue(&dirObj->inst.fields, "path", OBJECT_VAL(path));
dirObj->dirPtr = dir;
return krk_pop();
}
})
#define IS_Directory(o) (krk_isInstanceOf(o,Directory))
#define AS_Directory(o) ((struct Directory*)AS_OBJECT(o))
#define CURRENT_CTYPE struct Directory *
#define CURRENT_NAME self
KRK_METHOD(Directory,__call__,{
METHOD_TAKES_NONE();
@ -468,30 +441,30 @@ KrkValue krk_module_onload_fileio(void) {
krk_push(OBJECT_VAL(module));
/* Define a class to represent files. (Should this be a helper method?) */
krk_makeClass(module, &FileClass, "File", vm.objectClass);
FileClass->allocSize = sizeof(struct FileObject);
FileClass->_ongcsweep = _file_sweep;
krk_makeClass(module, &File, "File", vm.objectClass);
File->allocSize = sizeof(struct File);
File->_ongcsweep = _file_sweep;
/* Add methods to it... */
krk_defineNative(&FileClass->methods, ".read", krk_file_read);
krk_defineNative(&FileClass->methods, ".readline", krk_file_readline);
krk_defineNative(&FileClass->methods, ".readlines", krk_file_readlines);
krk_defineNative(&FileClass->methods, ".write", krk_file_write);
krk_defineNative(&FileClass->methods, ".close", krk_file_close);
krk_defineNative(&FileClass->methods, ".flush", krk_file_flush);
krk_defineNative(&FileClass->methods, ".__str__", krk_file_str);
krk_defineNative(&FileClass->methods, ".__repr__", krk_file_str);
krk_defineNative(&FileClass->methods, ".__init__", krk_file_reject_init);
krk_defineNative(&FileClass->methods, ".__enter__", krk_file_enter);
krk_defineNative(&FileClass->methods, ".__exit__", krk_file_exit);
krk_finalizeClass(FileClass);
BIND_METHOD(File,read);
BIND_METHOD(File,readline);
BIND_METHOD(File,readlines);
BIND_METHOD(File,write);
BIND_METHOD(File,close);
BIND_METHOD(File,flush);
BIND_METHOD(File,__str__);
BIND_METHOD(File,__init__);
BIND_METHOD(File,__enter__);
BIND_METHOD(File,__exit__);
krk_defineNative(&File->methods, ".__repr__", FUNC_NAME(File,__str__));
krk_finalizeClass(File);
krk_makeClass(module, &BinaryFileClass, "BinaryFile", FileClass);
krk_defineNative(&BinaryFileClass->methods, ".read", krk_file_read_b);
krk_defineNative(&BinaryFileClass->methods, ".readline", krk_file_readline_b);
krk_defineNative(&BinaryFileClass->methods, ".readlines", krk_file_readlines_b);
krk_defineNative(&BinaryFileClass->methods, ".write", krk_file_write_b);
krk_finalizeClass(BinaryFileClass);
krk_makeClass(module, &BinaryFile, "BinaryFile", File);
BIND_METHOD(BinaryFile,read);
BIND_METHOD(BinaryFile,readline);
BIND_METHOD(BinaryFile,readlines);
BIND_METHOD(BinaryFile,write);
krk_finalizeClass(BinaryFile);
krk_makeClass(module, &Directory, "Directory", vm.objectClass);
Directory->allocSize = sizeof(struct Directory);
@ -508,8 +481,8 @@ KrkValue krk_module_onload_fileio(void) {
makeFileInstance(module, "stderr", stderr);
/* Our base will be the open method */
krk_defineNative(&module->fields, "open", krk_open);
krk_defineNative(&module->fields, "opendir", _opendir);
BIND_FUNC(module,open);
BIND_FUNC(module,opendir);
/* Pop the module object before returning; it'll get pushed again
* by the VM before the GC has a chance to run, so it's safe. */