mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
added file operations and additional tests
This commit is contained in:
@@ -4,14 +4,29 @@
|
|||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
typedef struct Toy_File
|
typedef struct Toy_File
|
||||||
{
|
{
|
||||||
FILE* fp;
|
FILE* fp;
|
||||||
Toy_RefString* mode;
|
Toy_RefString* mode;
|
||||||
|
Toy_RefString* name;
|
||||||
} Toy_File;
|
} Toy_File;
|
||||||
|
|
||||||
|
Toy_File* createToyFile(const char* mode, const char* name) {
|
||||||
|
Toy_File* file = TOY_ALLOCATE(Toy_File, 1);
|
||||||
|
file->fp = NULL;
|
||||||
|
file->mode = Toy_createRefString(mode);
|
||||||
|
file->name = Toy_createRefString(name);
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteToyFile(Toy_File* file) {
|
||||||
|
Toy_deleteRefString(file->mode);
|
||||||
|
Toy_deleteRefString(file->name);
|
||||||
|
TOY_FREE(Toy_File, file);
|
||||||
|
}
|
||||||
|
|
||||||
static int nativeOpen(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
static int nativeOpen(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
if (arguments->count < 1 || arguments->count > 2) {
|
if (arguments->count < 1 || arguments->count > 2) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to open\n");
|
interpreter->errorOutput("Incorrect number of arguments to open\n");
|
||||||
@@ -69,9 +84,7 @@ static int nativeOpen(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
const char* mode = Toy_toCString(TOY_AS_STRING(modeLiteral));
|
const char* mode = Toy_toCString(TOY_AS_STRING(modeLiteral));
|
||||||
|
|
||||||
// build file object
|
// build file object
|
||||||
Toy_File* file = TOY_ALLOCATE(Toy_File, 1);
|
Toy_File* file = createToyFile(mode, filePath);
|
||||||
file->fp = NULL;
|
|
||||||
file->mode = Toy_createRefString(mode);
|
|
||||||
|
|
||||||
// attempt to open file
|
// attempt to open file
|
||||||
file->fp = fopen(filePath, mode);
|
file->fp = fopen(filePath, mode);
|
||||||
@@ -79,6 +92,7 @@ static int nativeOpen(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments)
|
|||||||
// result
|
// result
|
||||||
Toy_Literal fileLiteral = TOY_TO_NULL_LITERAL;
|
Toy_Literal fileLiteral = TOY_TO_NULL_LITERAL;
|
||||||
if (file->fp == NULL) {
|
if (file->fp == NULL) {
|
||||||
|
Toy_deleteRefString(file->mode);
|
||||||
TOY_FREE(Toy_File, file);
|
TOY_FREE(Toy_File, file);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -130,8 +144,7 @@ static int nativeClose(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
|
|||||||
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
TOY_FREE(Toy_File, file);
|
deleteToyFile(file);
|
||||||
Toy_deleteRefString(file->mode);
|
|
||||||
Toy_freeLiteral(selfLiteral);
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -319,6 +332,148 @@ static int nativeWrite(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nativeRename(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 2) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to rename\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_Literal valueLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
// parse the value (if it's an identifier)
|
||||||
|
Toy_Literal valueLiteralIdn = valueLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(valueLiteral) && Toy_parseIdentifierToValue(interpreter, &valueLiteral)) {
|
||||||
|
Toy_freeLiteral(valueLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the value type
|
||||||
|
if (!TOY_IS_STRING(valueLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to rename\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
Toy_freeLiteral(valueLiteral);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the self (if it's an identifier)
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check self type
|
||||||
|
if (!TOY_IS_OPAQUE(selfLiteral) && !(TOY_GET_OPAQUE_TAG(selfLiteral) == 900)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to read\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
Toy_freeLiteral(valueLiteral);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
|
||||||
|
|
||||||
|
int result = rename(Toy_toCString(file->name), Toy_toCString(TOY_AS_STRING(valueLiteral)));
|
||||||
|
|
||||||
|
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0);
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
Toy_freeLiteral(resultLiteral);
|
||||||
|
Toy_freeLiteral(valueLiteral);
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nativeSeek(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
|
if (arguments->count != 2) {
|
||||||
|
interpreter->errorOutput("Incorrect number of arguments to seek\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Toy_Literal originLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
Toy_Literal offsetLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
Toy_Literal selfLiteral = Toy_popLiteralArray(arguments);
|
||||||
|
|
||||||
|
// parse the value (if it's an identifier)
|
||||||
|
Toy_Literal offsetLiteralIdn = offsetLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(offsetLiteral) && Toy_parseIdentifierToValue(interpreter, &offsetLiteral)) {
|
||||||
|
Toy_freeLiteral(offsetLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the value type
|
||||||
|
if (!TOY_IS_INTEGER(offsetLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to seek\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
Toy_freeLiteral(offsetLiteral);
|
||||||
|
Toy_freeLiteral(originLiteral);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the origin (if it's an identifier)
|
||||||
|
Toy_Literal originLiteralIdn = originLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(originLiteral) && Toy_parseIdentifierToValue(interpreter, &originLiteral)) {
|
||||||
|
Toy_freeLiteral(originLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the origin type
|
||||||
|
if (!TOY_IS_INTEGER(originLiteral)) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to seek\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
Toy_freeLiteral(offsetLiteral);
|
||||||
|
Toy_freeLiteral(originLiteral);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the self (if it's an identifier)
|
||||||
|
Toy_Literal selfLiteralIdn = selfLiteral;
|
||||||
|
if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) {
|
||||||
|
Toy_freeLiteral(selfLiteralIdn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check self type
|
||||||
|
if (!TOY_IS_OPAQUE(selfLiteral) && TOY_GET_OPAQUE_TAG(selfLiteral) != 900) {
|
||||||
|
interpreter->errorOutput("Incorrect argument type passed to seek\n");
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
Toy_freeLiteral(offsetLiteral);
|
||||||
|
Toy_freeLiteral(originLiteral);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
|
||||||
|
int offset = TOY_AS_INTEGER(offsetLiteral);
|
||||||
|
const char* orginString = Toy_toCString(TOY_AS_STRING(originLiteral));
|
||||||
|
|
||||||
|
int origin = 0;
|
||||||
|
|
||||||
|
if (Toy_equalsRefString(orginString, "set")) {
|
||||||
|
origin = SEEK_SET;
|
||||||
|
}
|
||||||
|
else if (Toy_equalsRefString(orginString, "cur")) {
|
||||||
|
origin = SEEK_CUR;
|
||||||
|
}
|
||||||
|
else if (Toy_equalsRefString(orginString, "end")) {
|
||||||
|
origin = SEEK_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = fseek(file->fp, offset, origin);
|
||||||
|
|
||||||
|
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0);
|
||||||
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
Toy_freeLiteral(resultLiteral);
|
||||||
|
Toy_freeLiteral(offsetLiteral);
|
||||||
|
Toy_freeLiteral(selfLiteral);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int nativeError(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
static int nativeError(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) {
|
||||||
if (arguments->count != 1) {
|
if (arguments->count != 1) {
|
||||||
interpreter->errorOutput("Incorrect number of arguments to error\n");
|
interpreter->errorOutput("Incorrect number of arguments to error\n");
|
||||||
@@ -346,7 +501,7 @@ static int nativeError(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments
|
|||||||
int result = ferror(file->fp);
|
int result = ferror(file->fp);
|
||||||
|
|
||||||
// return the result
|
// return the result
|
||||||
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(result == 0);
|
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0);
|
||||||
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
@@ -382,7 +537,7 @@ static int nativeCompleted(Toy_Interpreter* interpreter, Toy_LiteralArray* argum
|
|||||||
int result = feof(file->fp);
|
int result = feof(file->fp);
|
||||||
|
|
||||||
// return the result
|
// return the result
|
||||||
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(result == 0);
|
Toy_Literal resultLiteral = TOY_TO_BOOLEAN_LITERAL(result != 0);
|
||||||
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
Toy_pushLiteralArray(&interpreter->stack, resultLiteral);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
@@ -414,16 +569,9 @@ static int nativePosition(Toy_Interpreter* interpreter, Toy_LiteralArray* argume
|
|||||||
}
|
}
|
||||||
|
|
||||||
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
|
Toy_File* file = (Toy_File*)TOY_AS_OPAQUE(selfLiteral);
|
||||||
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
// pervent integer overflow as ftell returns a long
|
// pervent integer overflow as ftell returns a long
|
||||||
if (ftell(file->fp) > INT_MAX) {
|
int size = ftell(file->fp) > INT_MAX? INT_MAX : ftell(file->fp);
|
||||||
size = INT_MAX;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
size = ftell(file->fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the result
|
// return the result
|
||||||
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(size);
|
Toy_Literal resultLiteral = TOY_TO_INTEGER_LITERAL(size);
|
||||||
@@ -599,6 +747,10 @@ int Toy_hookFileIO(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
|
|||||||
{"read", nativeRead},
|
{"read", nativeRead},
|
||||||
{"write", nativeWrite},
|
{"write", nativeWrite},
|
||||||
|
|
||||||
|
// operations
|
||||||
|
{"rename", nativeRename},
|
||||||
|
{"seek", nativeSeek},
|
||||||
|
|
||||||
// accessors
|
// accessors
|
||||||
{"error", nativeError},
|
{"error", nativeError},
|
||||||
{"completed", nativeCompleted},
|
{"completed", nativeCompleted},
|
||||||
@@ -617,13 +769,13 @@ int Toy_hookFileIO(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_Lit
|
|||||||
createToyVariableInt(&variables[1], "MAX_FILES_OPEN", FOPEN_MAX);
|
createToyVariableInt(&variables[1], "MAX_FILES_OPEN", FOPEN_MAX);
|
||||||
createToyVariableInt(&variables[2], "END_OF_FILE", EOF);
|
createToyVariableInt(&variables[2], "END_OF_FILE", EOF);
|
||||||
|
|
||||||
Toy_File* outFile = TOY_ALLOCATE(Toy_File, 1);
|
Toy_File* outFile = createToyFile("w", "outFile");
|
||||||
outFile->fp = stdout;
|
outFile->fp = stdout;
|
||||||
|
|
||||||
createToyVariableFile(&variables[3], "output", outFile);
|
createToyVariableFile(&variables[3], "output", outFile);
|
||||||
|
|
||||||
Toy_File* inFile = TOY_ALLOCATE(Toy_File, 1);
|
Toy_File* inFile = createToyFile("r", "inFile");
|
||||||
inFile->fp = stdin;
|
outFile->fp = stdin;
|
||||||
|
|
||||||
createToyVariableFile(&variables[4], "input", inFile);
|
createToyVariableFile(&variables[4], "input", inFile);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import fileio;
|
import fileio;
|
||||||
|
|
||||||
|
var PATH: string const = "scripts:/lib/file/fileio.txt";
|
||||||
|
|
||||||
// test global constants
|
// test global constants
|
||||||
{
|
{
|
||||||
assert MAX_FILENAME_SIZE > 0, "MAX_FILENAME_SIZE failed";
|
assert MAX_FILENAME_SIZE > 0, "MAX_FILENAME_SIZE failed";
|
||||||
@@ -9,7 +11,7 @@ import fileio;
|
|||||||
|
|
||||||
// test open and close
|
// test open and close
|
||||||
{
|
{
|
||||||
var file = open("scripts:/lib/file/fileio.txt", "r");
|
var file = open(PATH, "r");
|
||||||
assert file != null, "open failed";
|
assert file != null, "open failed";
|
||||||
|
|
||||||
var wrong = open("scripts:/doesNotExist", "r");
|
var wrong = open("scripts:/doesNotExist", "r");
|
||||||
@@ -18,38 +20,54 @@ import fileio;
|
|||||||
assert file.close() == true, "close failed";
|
assert file.close() == true, "close failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
// test accessors
|
// test append
|
||||||
{
|
{
|
||||||
var file = open("scripts:/lib/file/fileio.txt", "r");
|
var file = open(PATH, "a");
|
||||||
|
assert file.write("appended text") == true, "append failed";
|
||||||
print file.error();
|
|
||||||
print file.completed();
|
|
||||||
print file.position();
|
|
||||||
print file.size();
|
|
||||||
print file.mode();
|
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// test output
|
// test accessors
|
||||||
{
|
{
|
||||||
// assert output.write(8), "write int failed";
|
var file = open(PATH, "r");
|
||||||
// assert output.write("\n"), "write string failed";
|
|
||||||
// assert output.write(12.5), "write float failed";
|
assert file.error() == false, "error failed";
|
||||||
|
assert file.completed() == false, "completed failed";
|
||||||
|
assert file.position() == 0, "position failed";
|
||||||
|
assert file.size() >= 13, "size failed";
|
||||||
|
assert file.mode() == "r", "mode failed";
|
||||||
|
|
||||||
|
file.read(string);
|
||||||
|
|
||||||
|
assert file.error() == false, "error failed";
|
||||||
|
assert file.completed() == true, "completed failed";
|
||||||
|
assert file.position() >= 13, "position failed";
|
||||||
|
assert file.size() >= 13, "size failed";
|
||||||
|
// assert file.mode() == "r", "mode failed";
|
||||||
|
|
||||||
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// test input
|
// test write
|
||||||
{
|
{
|
||||||
// // enter 8
|
assert output.write(8), "write int failed";
|
||||||
// assert input.read(int) == 8, "read int failed";
|
assert output.write("\n"), "write string failed";
|
||||||
|
assert output.write(12.5), "write float failed";
|
||||||
// // enter 12.5
|
}
|
||||||
// assert input.read(float) == 12.5, "read float failed";
|
|
||||||
|
// test read
|
||||||
// // enter test
|
{
|
||||||
// assert input.read(string) == "test\n", "read string failed";
|
// enter 8
|
||||||
|
assert input.read(int) == 8, "read int failed";
|
||||||
// // invaild types
|
|
||||||
// assert input.read(type) == null, "type failed";
|
// enter 12.5
|
||||||
// assert input.read(any) == null, "any failed";
|
assert input.read(float) == 12.5, "read float failed";
|
||||||
|
|
||||||
|
// enter test
|
||||||
|
assert input.read(string) == "test\n", "read string failed";
|
||||||
|
|
||||||
|
// invaild types
|
||||||
|
assert input.read(type) == null, "type failed";
|
||||||
|
assert input.read(any) == null, "any failed";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user