5 Commits

27 changed files with 912 additions and 1489 deletions

View File

@@ -75,7 +75,7 @@ build
- [x] String operations - [x] String operations
- [x] Maths - [x] Maths
- [x] Comparisions - [x] Comparisions
- [x] Type conversions - [ ] Type conversions
- [x] Functions - [x] Functions
- [x] Define functions - [x] Define functions
- [x] Call functions - [x] Call functions
@@ -83,7 +83,7 @@ build
- [x] Arguments for functions - [x] Arguments for functions
- [x] Jumping within functions - [x] Jumping within functions
- [x] Custom data structures - [x] Custom data structures
- [x] Working with external libraries - [ ] Working with external libraries
## Debugger ## Debugger
@@ -102,7 +102,7 @@ Commands:
CGround now includes an experimental Ground -> x86_64 Linux ASM compiler. You can try it by adding the `-c` flag when running Ground. This will print the generated ASM to the console. CGround now includes an experimental Ground -> x86_64 Linux ASM compiler. You can try it by adding the `-c` flag when running Ground. This will print the generated ASM to the console.
At present only the `int` data type is supported. At present only the `int`, `char`, and `bool` data types are supported.
Supported instructions so far: Supported instructions so far:
@@ -121,10 +121,3 @@ Supported instructions so far:
* print * print
* println * println
* end * end
## Closures
So I decided to add closures to Ground. Here's how they work:
* When you define a function, it makes a copy of the scope it's currently in. Variables are statically captured
* Use those variables in functions. Closure achieved!

View File

@@ -362,23 +362,3 @@ Looks in the path $GROUND_LIBS/`$libraryName`.grnd for the library. ($GROUND_LIB
Attempts to import a shared library written in a compiled language like C or C++ for usage within the current program. Attempts to import a shared library written in a compiled language like C or C++ for usage within the current program.
Looks in the path $GROUND_LIBS/`$libraryName`.so for the library. ($GROUND_LIBS is a system environment variable.) Looks in the path $GROUND_LIBS/`$libraryName`.so for the library. ($GROUND_LIBS is a system environment variable.)
### Data Structures
#### struct -structName
Creates a new struct which can be initialised. Until the endstruct keyword, the only valid instructions are init, set, fun, endfun, struct, and endstruct.
Any value created inside the struct will be added to the struct.
#### endstruct
Ends the creation of a struct.
#### getfield $object &fieldName &outputVar
Gets a field from an initialised object. fieldName must be a valid name of a field in the object. Errors if the field does not exist.
#### setfield &object &fieldName $value
Sets a field to a new value in the object. The value must be of the same type as the field's old value.

View File

@@ -1,6 +1,5 @@
#ifndef LIBGROUND_H #ifndef LIBGROUND_H
#define LIBGROUND_H #define LIBGROUND_H
#define MAX_ID_LEN 64
/* /*
* groundvm.h * groundvm.h
@@ -14,9 +13,8 @@
#include <stdarg.h> #include <stdarg.h>
#include <uthash.h> #include <uthash.h>
typedef enum GroundInstType { typedef enum GroundInstType {
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
} GroundInstType; } GroundInstType;
typedef enum GroundValueType { typedef enum GroundValueType {
@@ -166,24 +164,6 @@ typedef struct GroundObject {
GroundObjectField* fields; GroundObjectField* fields;
} GroundObject; } GroundObject;
typedef struct GroundLabel {
char id[MAX_ID_LEN];
int lineNum;
UT_hash_handle hh;
} GroundLabel;
typedef struct GroundVariable {
char id[MAX_ID_LEN];
GroundValue value;
UT_hash_handle hh;
} GroundVariable;
typedef struct GroundScope {
GroundLabel** labels;
GroundVariable** variables;
bool isMainScope;
} GroundScope;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@@ -199,8 +179,6 @@ void groundAddValueToInstruction(GroundInstruction* inst, GroundValue value);
void groundAddReferenceToInstruction(GroundInstruction* inst, GroundArg value); void groundAddReferenceToInstruction(GroundInstruction* inst, GroundArg value);
GroundArg groundCreateReference(GroundArgType type, char* ref); GroundArg groundCreateReference(GroundArgType type, char* ref);
void groundAddValueToScope(GroundScope* gs, const char* name, GroundValue value);
GroundValue groundCreateValue(GroundValueType type, ...); GroundValue groundCreateValue(GroundValueType type, ...);
GroundProgram groundParseFile(const char* code); GroundProgram groundParseFile(const char* code);
@@ -208,16 +186,6 @@ GroundProgram groundParseFile(const char* code);
GroundStruct groundCreateStruct(); GroundStruct groundCreateStruct();
void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field); void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field);
GroundObjectField* groundFindField(GroundObject head, const char *id);
// Run function returned by Ground code
// Use argc to set count of args, accepts that amount of GroundValue's after
GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...);
bool serializeProgramToFile(const char* path, const GroundProgram* prog);
bool deserializeProgramFromFile(const char* path, GroundProgram* out);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,23 +1,35 @@
#include <groundext.h> #include <groundext.h>
#include <groundvm.h>
#include <curl/curl.h> #include <curl/curl.h>
#include <groundvm.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <string.h>
#include <stdlib.h>
CURL* curlHandle = NULL; bool curl_init = false;
GroundStruct template;
const char* VALID_REQUEST_TYPES[] = {
"GET",
"POST"
};
typedef struct { typedef struct {
char* data; char* data;
size_t size; size_t size;
} ResponseBuffer; } ResponseBuffer;
typedef struct {
CURL* handle;
bool connected;
char* url;
} WebSocketConnection;
// Global storage for WebSocket connections (simple implementation)
#define MAX_WS_CONNECTIONS 10
WebSocketConnection ws_connections[MAX_WS_CONNECTIONS] = {0};
void curl_setup() {
if (!curl_init) {
curl_global_init(CURL_GLOBAL_ALL);
curl_init = true;
}
}
// Proper write callback that accumulates data
size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) { size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) {
size_t total_size = size * nmemb; size_t total_size = size * nmemb;
ResponseBuffer* buffer = (ResponseBuffer*)userdata; ResponseBuffer* buffer = (ResponseBuffer*)userdata;
@@ -36,117 +48,295 @@ size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) {
return total_size; return total_size;
} }
GroundValue ground_request(GroundScope* scope, List args) { // GET request
GroundValue get_request(GroundScope* scope, List args) {
curl_setup();
CURL* handle = curl_easy_init();
GroundObject obj = *args.values[0].data.customVal; if (!handle) {
ERROR("CURL failed to init", "GenericRequestError");
// check arg types
GroundObjectField* address = groundFindField(obj, "address");
if (address == NULL || address->value.type != STRING) {
ERROR("Object does not have address field as string", "ValueError");
} }
GroundObjectField* type = groundFindField(obj, "type");
if (type == NULL || type->value.type != STRING) {
ERROR("Object does not have type field as string", "ValueError");
}
GroundObjectField* userAgent = groundFindField(obj, "userAgent");
if (userAgent == NULL || userAgent->value.type != STRING) {
ERROR("Object does not have userAgent field as string", "ValueError");
}
GroundObjectField* httpHeaders = groundFindField(obj, "httpHeaders");
if (httpHeaders == NULL || httpHeaders->value.type != LIST) {
ERROR("Object does not have httpHeaders field as list<string>", "ValueError");
}
GroundObjectField* verbose = groundFindField(obj, "verbose");
if (verbose == NULL || verbose->value.type != BOOL) {
ERROR("Object does not have verbose field as bool", "ValueError");
}
// check request type string
bool requestTypeOk = false;
for (int i = 0; i < sizeof(VALID_REQUEST_TYPES)/sizeof(VALID_REQUEST_TYPES[0]); i++) {
if (strcmp(type->value.data.stringVal, VALID_REQUEST_TYPES[i]) == 0) {
requestTypeOk = true;
break;
}
}
if (!requestTypeOk)
ERROR("Invalid request type! Choices: GET, POST", "ValueError");
ResponseBuffer buffer = {0}; ResponseBuffer buffer = {0};
buffer.data = malloc(1);
buffer.data[0] = '\0';
buffer.size = 0;
// set curl params curl_easy_setopt(handle, CURLOPT_URL, args.values[0].data.stringVal);
curl_easy_setopt(curlHandle, CURLOPT_URL, address->value.data.stringVal); curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void*)&buffer); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, userAgent->value.data.stringVal); curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curlHandle, CURLOPT_VERBOSE, verbose->value.data.boolVal);
// append headers to http request CURLcode result = curl_easy_perform(handle);
struct curl_slist* httpHeaderList = NULL;
GroundValue* httpHeaderStrings = httpHeaders->value.data.listVal.values;
for (int i = 0; i < httpHeaders->value.data.listVal.size; i++) {
httpHeaderList = curl_slist_append(httpHeaderList, httpHeaderStrings[i].data.stringVal);
}
curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, httpHeaderList);
if (result != CURLE_OK) {
CURLcode curlStatus = curl_easy_perform(curlHandle); char buf[256];
curl_slist_free_all(httpHeaderList); // dont want em' memory leaks :P - spooopy snprintf(buf, sizeof(buf) - 1, "Curl request failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionRequestError");
// check for any curl errors
if (curlStatus != CURLE_OK) {
char curlErrorMsg[255] = {0};
snprintf(curlErrorMsg, 255, "%s\n", curl_easy_strerror(curlStatus));
ERROR(curlErrorMsg, "CurlError");
} }
// get the http response code curl_easy_cleanup(handle);
long httpCode = 0;
curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &httpCode);
// create return value and return it GroundValue ret = groundCreateValue(STRING, buffer.data);
GroundValue value = groundCreateValue(CUSTOM, &template); return ret;
}
GroundObjectField* status = groundFindField(*value.data.customVal, "statusCode"); // POST request
status->value.data.intVal = httpCode; GroundValue post_request(GroundScope* scope, List args) {
curl_setup();
CURL* handle = curl_easy_init();
GroundObjectField* data = groundFindField(*value.data.customVal, "data"); if (!handle) {
data->value.data.stringVal = buffer.data; ERROR("CURL failed to init", "GenericRequestError");
}
value.type = CUSTOM; ResponseBuffer buffer = {0};
buffer.data = malloc(1);
buffer.data[0] = '\0';
buffer.size = 0;
return value; curl_easy_setopt(handle, CURLOPT_URL, args.values[0].data.stringVal);
curl_easy_setopt(handle, CURLOPT_POST, 1L);
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, args.values[1].data.stringVal);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
CURLcode result = curl_easy_perform(handle);
if (result != CURLE_OK) {
free(buffer.data);
char buf[256];
snprintf(buf, sizeof(buf) - 1, "Curl request failed: %s\n", curl_easy_strerror(result));
curl_easy_cleanup(handle);
ERROR(buf, "ActionRequestError");
}
curl_easy_cleanup(handle);
GroundValue ret = groundCreateValue(STRING, buffer.data);
return ret;
}
// ============== WebSocket Functions ==============
// Find an empty slot for a new WebSocket connection
int find_ws_slot() {
for (int i = 0; i < MAX_WS_CONNECTIONS; i++) {
if (!ws_connections[i].connected) {
return i;
}
}
return -1;
}
// WebSocket Connect - returns connection ID as INT
GroundValue ws_connect(GroundScope* scope, List args) {
curl_setup();
int slot = find_ws_slot();
if (slot == -1) {
ERROR("Maximum WebSocket connections reached", "OverflowError");
}
CURL* handle = curl_easy_init();
if (!handle) {
ERROR("CURL failed to init", "GenericWSError");
}
curl_easy_setopt(handle, CURLOPT_URL, args.values[0].data.stringVal);
curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 2L); // WebSocket mode
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 10L);
CURLcode result = curl_easy_perform(handle);
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "Curl WebSocket failed: %s\n", curl_easy_strerror(result));
curl_easy_cleanup(handle);
ERROR(buf, "ActionWSError");
}
ws_connections[slot].handle = handle;
ws_connections[slot].connected = true;
ws_connections[slot].url = strdup(args.values[0].data.stringVal);
return groundCreateValue(INT, (int64_t)slot);
}
// WebSocket Send - sends a text message, returns bytes sent as INT
GroundValue ws_send(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
const char* message = args.values[1].data.stringVal;
size_t sent;
CURLcode result = curl_ws_send(handle, message, strlen(message), &sent, 0, CURLWS_TEXT);
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket send failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
return groundCreateValue(INT, (int64_t)sent);
}
// WebSocket Receive - receives a message (blocking)
GroundValue ws_receive(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
char buffer[4096];
size_t received;
const struct curl_ws_frame* meta;
CURLcode result = curl_ws_recv(handle, buffer, sizeof(buffer) - 1, &received, &meta);
if (result != CURLE_OK) {
if (result == CURLE_AGAIN) {
// No data available right now
return groundCreateValue(STRING, "");
}
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket receive failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
buffer[received] = '\0';
// Handle different frame types
if (meta->flags & CURLWS_CLOSE) {
printf("WebSocket close frame received\n");
ws_connections[conn_id].connected = false;
return groundCreateValue(STRING, "[CLOSED]");
}
if (meta->flags & CURLWS_PING) {
// Automatically respond to ping with pong
curl_ws_send(handle, "", 0, &received, 0, CURLWS_PONG);
return groundCreateValue(STRING, "[PING]");
}
char* data = malloc(received + 1);
memcpy(data, buffer, received);
data[received] = '\0';
return groundCreateValue(STRING, data);
}
// WebSocket Receive with timeout (non-blocking version)
GroundValue ws_receive_timeout(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
long timeout_ms = (long)args.values[1].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
// Set socket to non-blocking mode
curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms);
char buffer[4096];
size_t received;
const struct curl_ws_frame* meta;
CURLcode result = curl_ws_recv(handle, buffer, sizeof(buffer) - 1, &received, &meta);
if (result == CURLE_AGAIN || result == CURLE_OPERATION_TIMEDOUT) {
return groundCreateValue(STRING, "");
}
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket receive failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
buffer[received] = '\0';
if (meta->flags & CURLWS_CLOSE) {
ws_connections[conn_id].connected = false;
return groundCreateValue(STRING, "[CLOSED]");
}
if (meta->flags & CURLWS_PING) {
curl_ws_send(handle, "", 0, &received, 0, CURLWS_PONG);
return groundCreateValue(STRING, "[PING]");
}
char* data = malloc(received + 1);
memcpy(data, buffer, received);
data[received] = '\0';
return groundCreateValue(STRING, data);
}
// WebSocket Close - properly close a connection, returns BOOL success
GroundValue ws_close(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid or already connected websocket", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
size_t sent;
// Send close frame
curl_ws_send(handle, "", 0, &sent, 0, CURLWS_CLOSE);
// Cleanup
curl_easy_cleanup(handle);
free(ws_connections[conn_id].url);
ws_connections[conn_id].handle = NULL;
ws_connections[conn_id].connected = false;
ws_connections[conn_id].url = NULL;
return groundCreateValue(BOOL, true);
}
// WebSocket Send Binary data, returns bytes sent as INT
GroundValue ws_send_binary(GroundScope* scope, List args) {
int conn_id = (int)args.values[0].data.intVal;
if (conn_id < 0 || conn_id >= MAX_WS_CONNECTIONS || !ws_connections[conn_id].connected) {
ERROR("Invalid WebSocket connection ID", "GenericWSError");
}
CURL* handle = ws_connections[conn_id].handle;
const char* data = args.values[1].data.stringVal;
size_t sent;
CURLcode result = curl_ws_send(handle, data, strlen(data), &sent, 0, CURLWS_BINARY);
if (result != CURLE_OK) {
char buf[256];
snprintf(buf, sizeof(buf) - 1, "WebSocket binary send failed: %s\n", curl_easy_strerror(result));
ERROR(buf, "ActionWSError");
}
return groundCreateValue(INT, (int64_t)sent);
} }
void ground_init(GroundScope* scope) { void ground_init(GroundScope* scope) {
// init libcurl // HTTP Functions
curl_global_init(CURL_GLOBAL_ALL); groundAddNativeFunction(scope, "request_Get", get_request, STRING, 1, STRING, "url");
curlHandle = curl_easy_init(); groundAddNativeFunction(scope, "request_Post", post_request, STRING, 2, STRING, "url", STRING, "data");
groundAddValueToScope(scope, "requestInitSuccess", groundCreateValue(BOOL, curlHandle != NULL)); // WebSocket Functions
if (curlHandle == NULL) { groundAddNativeFunction(scope, "ws_Connect", ws_connect, INT, 1, STRING, "url");
return; groundAddNativeFunction(scope, "ws_Send", ws_send, INT, 2, INT, "conn_id", STRING, "message");
} groundAddNativeFunction(scope, "ws_Receive", ws_receive, STRING, 1, INT, "conn_id");
groundAddNativeFunction(scope, "ws_ReceiveTimeout", ws_receive_timeout, STRING, 2, INT, "conn_id", INT, "timeout_ms");
// create struct groundAddNativeFunction(scope, "ws_Close", ws_close, BOOL, 1, INT, "conn_id");
template = groundCreateStruct(); groundAddNativeFunction(scope, "ws_SendBinary", ws_send_binary, INT, 2, INT, "conn_id", STRING, "data");
groundAddFieldToStruct(&template, "statusCode", groundCreateValue(INT, 200));
groundAddFieldToStruct(&template, "data", groundCreateValue(STRING, ""));
groundAddFieldToStruct(&template, "ok", groundCreateValue(BOOL, 1));
// create struct that users can pass to this library
GroundStruct requestStruct = groundCreateStruct();
groundAddFieldToStruct(&requestStruct, "address", groundCreateValue(STRING, ""));
groundAddFieldToStruct(&requestStruct, "type", groundCreateValue(STRING, "GET"));
groundAddFieldToStruct(&requestStruct, "userAgent", groundCreateValue(STRING, "Ground/1.0"));
groundAddFieldToStruct(&requestStruct, "httpHeaders", groundCreateValue(LIST, 0));
groundAddFieldToStruct(&requestStruct, "verbose", groundCreateValue(BOOL, 0));
groundAddValueToScope(scope, "request", groundCreateValue(STRUCTVAL, requestStruct));
// create functions
groundAddNativeFunction(scope, "request_Request", ground_request, CUSTOM, 1, CUSTOM, "requestInfo");
} }

View File

@@ -1,316 +0,0 @@
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <groundvm.h>
#include <groundext.h>
// Allows Ground to access the POSIX standard library.
// Read more here: https://en.wikipedia.org/wiki/Unistd.h
// Misc functions
GroundValue groundCrypt(GroundScope* scope, List args) {
char* hash = crypt(args.values[0].data.stringVal, args.values[1].data.stringVal);
if (hash == NULL) {
ERROR(strerror(errno), "CryptError");
}
return groundCreateValue(STRING, hash);
}
GroundValue groundGetHostId(GroundScope* scope, List args) {
return groundCreateValue(INT, (int64_t) gethostid());
}
GroundValue groundSetHostId(GroundScope* scope, List args) {
int result = sethostid((long) args.values[0].data.intVal);
if (result < 0) {
ERROR(strerror(errno), "SetHostIdError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundGetHostname(GroundScope* scope, List args) {
char* buf = malloc(256);
if (buf == NULL) {
ERROR("Couldn't allocate memory to store hostname", "GetHostnameError");
}
int result = gethostname(buf, 255);
if (result < 0) {
ERROR(strerror(errno), "GetHostnameError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSetHostname(GroundScope* scope, List args) {
int result = sethostname(args.values[0].data.stringVal, strlen(args.values[0].data.stringVal));
if (result < 0) {
ERROR(strerror(errno), "SetHostnameError");
}
return groundCreateValue(INT, (int64_t) result);
}
// Signals
GroundValue groundAlarm(GroundScope* scope, List args) {
unsigned result = alarm((unsigned) args.values[0].data.intVal);
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundPause(GroundScope* scope, List args) {
int result = pause();
ERROR(strerror(errno), "PauseError");
}
// Filesystem
GroundValue groundAccess(GroundScope* scope, List args) {
int result = access(args.values[0].data.stringVal, (int) args.values[1].data.intVal);
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundChdir(GroundScope* scope, List args) {
int result = chdir(args.values[0].data.stringVal);
if (result < 0) {
ERROR(strerror(errno), "ChdirError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundChown(GroundScope* scope, List args) {
int result = chown(args.values[0].data.stringVal, (uid_t) args.values[1].data.intVal, (gid_t) args.values[2].data.intVal);
if (result < 0) {
ERROR(strerror(errno), "ChownError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundLink(GroundScope* scope, List args) {
int result = link(args.values[0].data.stringVal, args.values[1].data.stringVal);
if (result < 1) {
ERROR(strerror(errno), "LinkError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundRmdir(GroundScope* scope, List args) {
int result = rmdir(args.values[0].data.stringVal);
if (result < 1) {
ERROR(strerror(errno), "RmdirError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSymlink(GroundScope* scope, List args) {
int result = symlink(args.values[0].data.stringVal, args.values[1].data.stringVal);
if (result < 1) {
ERROR(strerror(errno), "SymlinkError");
}
return groundCreateValue(INT, (int64_t) result);
}
// Process
GroundValue groundExit(GroundScope* scope, List args) {
_exit(args.values[0].data.intVal);
ERROR("Couldn't exit (huh?)", "ExitError");
}
GroundValue groundExecv(GroundScope* scope, List args) {
char** argv = malloc(sizeof(char*) * args.values[1].data.listVal.size + 1);
if (argv == NULL) {
ERROR("Couldn't allocate memory for execv list", "ExecvError");
}
for (size_t i = 0; i < args.values[1].data.listVal.size; i++) {
if (args.values[1].data.listVal.values[i].type != STRING) {
ERROR("Expecting all arguments in list to be of String type", "ExecvError");
}
argv[i] = args.values[1].data.listVal.values[i].data.stringVal;
}
argv[args.values[1].data.listVal.size] = NULL;
int result = execv(args.values[0].data.stringVal, argv);
ERROR(strerror(errno), "ExecvError");
}
GroundValue groundExecvp(GroundScope* scope, List args) {
char** argv = malloc(sizeof(char*) * args.values[1].data.listVal.size + 1);
if (argv == NULL) {
ERROR("Couldn't allocate memory for execv list", "ExecvpError");
}
for (size_t i = 0; i < args.values[1].data.listVal.size; i++) {
if (args.values[1].data.listVal.values[i].type != STRING) {
ERROR("Expecting all arguments in list to be of String type", "ExecvpError");
}
argv[i] = args.values[1].data.listVal.values[i].data.stringVal;
}
argv[args.values[1].data.listVal.size] = NULL;
int result = execvp(args.values[0].data.stringVal, argv);
ERROR(strerror(errno), "ExecvpError");
}
GroundValue groundFork(GroundScope* scope, List args) {
pid_t id = fork();
if (id < 0) {
ERROR(strerror(errno), "ForkError");
}
return groundCreateValue(INT, id);
}
GroundValue groundGetPid(GroundScope* scope, List args) {
return groundCreateValue(INT, (int64_t) getpid());
}
GroundValue groundGetPPid(GroundScope* scope, List args) {
return groundCreateValue(INT, (int64_t) getppid());
}
GroundValue groundGetSid(GroundScope* scope, List args) {
pid_t result = getsid((pid_t) args.values[0].data.intVal);
if (result < 0) {
ERROR(strerror(errno), "GetSidError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundNice(GroundScope* scope, List args) {
int result = nice((int) args.values[0].data.intVal);
if (result < 0) {
ERROR(strerror(errno), "NiceError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSetSid(GroundScope* scope, List args) {
pid_t result = setsid();
if (result < 0) {
ERROR(strerror(errno), "SetSidError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSleep(GroundScope* scope, List args) {
unsigned int result = sleep((unsigned int) args.values[0].data.intVal);
return groundCreateValue(INT, (int64_t) result);
}
// User/Group
GroundValue groundGetGid(GroundScope* scope, List args) {
return groundCreateValue(INT, (int64_t) getgid());
}
GroundValue groundGetEGid(GroundScope* scope, List args) {
return groundCreateValue(INT, (int64_t) getegid());
}
GroundValue groundGetUid(GroundScope* scope, List args) {
return groundCreateValue(INT, (int64_t) getuid());
}
GroundValue groundGetEUid(GroundScope* scope, List args) {
return groundCreateValue(INT, (int64_t) geteuid());
}
GroundValue groundGetLogin(GroundScope* scope, List args) {
char* login = getlogin();
if (login == NULL) {
ERROR(strerror(errno), "GetLoginError");
}
return groundCreateValue(STRING, login);
}
GroundValue groundSetEUid(GroundScope* scope, List args) {
int result = seteuid((uid_t) args.values[0].data.intVal);
if (result < -1) {
ERROR(strerror(errno), "SetEUidError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSetEGid(GroundScope* scope, List args) {
int result = setegid((uid_t) args.values[0].data.intVal);
if (result < -1) {
ERROR(strerror(errno), "SetEGidError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSetREUid(GroundScope* scope, List args) {
int result = setreuid((uid_t) args.values[0].data.intVal, (uid_t) args.values[1].data.intVal);
if (result < -1) {
ERROR(strerror(errno), "SetREUidError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSetREGid(GroundScope* scope, List args) {
int result = setregid((gid_t) args.values[0].data.intVal, (gid_t) args.values[1].data.intVal);
if (result < -1) {
ERROR(strerror(errno), "SetREGidError");
}
return groundCreateValue(INT, (int64_t) result);
}
GroundValue groundSetUid(GroundScope* scope, List args) {
int result = setuid((uid_t) args.values[0].data.intVal);
if (result < -1) {
ERROR(strerror(errno), "SetUidError");
}
return groundCreateValue(INT, (int64_t) result);
}
void ground_init(GroundScope* scope) {
// Misc
groundAddNativeFunction(scope, "unistd_Crypt", groundCrypt, STRING, 2, STRING, "key", STRING, "value");
groundAddNativeFunction(scope, "unistd_GetHostId", groundGetHostId, INT, 0);
groundAddNativeFunction(scope, "unistd_SetHostId", groundSetHostId, INT, 1, INT, "hostid");
groundAddNativeFunction(scope, "unistd_GetHostname", groundGetHostname, STRING, 0);
groundAddNativeFunction(scope, "unistd_SetHostname", groundSetHostname, INT, 1, STRING, "name");
// Signals
groundAddNativeFunction(scope, "unistd_Alarm", groundAlarm, INT, 1, INT, "seconds");
groundAddNativeFunction(scope, "unistd_Pause", groundPause, INT, 0);
// Filesystem
groundAddValueToScope(scope, "unistd_F_OK", groundCreateValue(INT, F_OK));
groundAddValueToScope(scope, "unistd_R_OK", groundCreateValue(INT, R_OK));
groundAddValueToScope(scope, "unistd_W_OK", groundCreateValue(INT, W_OK));
groundAddValueToScope(scope, "unistd_X_OK", groundCreateValue(INT, X_OK));
groundAddNativeFunction(scope, "unistd_Access", groundAccess, INT, 2, STRING, "path", INT, "mode");
groundAddNativeFunction(scope, "unistd_Chdir", groundAccess, INT, 1, STRING, "path");
groundAddNativeFunction(scope, "unistd_Chown", groundChown, INT, 3, STRING, "path", INT, "owner", INT, "group");
groundAddNativeFunction(scope, "unistd_Link", groundLink, INT, 2, STRING, "oldpath", STRING, "newpath");
groundAddNativeFunction(scope, "unistd_Rmdir", groundRmdir, INT, 1, STRING, "path");
groundAddNativeFunction(scope, "unistd_Symlink", groundSymlink, INT, 2, STRING, "target", STRING, "linkpath");
// Process
groundAddNativeFunction(scope, "unistd_Exit", groundExit, INT, 1, INT, "status");
groundAddNativeFunction(scope, "unistd_Execv", groundExecv, INT, 2, STRING, "path", LIST, "argv");
groundAddNativeFunction(scope, "unistd_Execvp", groundExecvp, INT, 2, STRING, "path", LIST, "argv");
groundAddNativeFunction(scope, "unistd_Fork", groundFork, INT, 0);
groundAddNativeFunction(scope, "unistd_GetPid", groundGetPid, INT, 0);
groundAddNativeFunction(scope, "unistd_GetPPid", groundGetPPid, INT, 0);
groundAddNativeFunction(scope, "unistd_GetSid", groundGetSid, INT, 1, INT, "pid");
groundAddNativeFunction(scope, "unistd_Nice", groundNice, INT, 1, INT, "inc");
groundAddNativeFunction(scope, "unistd_SetSid", groundSetSid, INT, 0);
groundAddNativeFunction(scope, "unistd_Sleep", groundSleep, INT, 1, INT, "seconds");
// User/Group
groundAddNativeFunction(scope, "unistd_GetGid", groundGetGid, INT, 0);
groundAddNativeFunction(scope, "unistd_GetEGid", groundGetEGid, INT, 0);
groundAddNativeFunction(scope, "unistd_GetUid", groundGetUid, INT, 0);
groundAddNativeFunction(scope, "unistd_GetEUid", groundGetEUid, INT, 0);
groundAddNativeFunction(scope, "unistd_GetLogin", groundGetLogin, STRING, 0);
groundAddNativeFunction(scope, "unistd_SetEUid", groundSetEUid, INT, 1, INT, "euid");
groundAddNativeFunction(scope, "unistd_SetEGid", groundSetEGid, INT, 1, INT, "egid");
groundAddNativeFunction(scope, "unistd_SetREUid", groundSetEGid, INT, 1, INT, "ruid", INT, "euid");
groundAddNativeFunction(scope, "unistd_SetREGid", groundSetEGid, INT, 1, INT, "rgid", INT, "egid");
}

View File

@@ -4,6 +4,7 @@
#include "include/estr.h" #include "include/estr.h"
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h>
VariableTable createVariableTable() { VariableTable createVariableTable() {
VariableTable vt; VariableTable vt;
@@ -13,7 +14,7 @@ VariableTable createVariableTable() {
return vt; return vt;
} }
void addVtVariable(VariableTable* vt, const char* name) { void addVtVariable(VariableTable* vt, const char* name, GroundValueType type) {
// check if it already exists // check if it already exists
for (size_t i = 0; i < vt->count; i++) { for (size_t i = 0; i < vt->count; i++) {
if (strcmp(vt->vars[i].name, name) == 0) { if (strcmp(vt->vars[i].name, name) == 0) {
@@ -29,10 +30,20 @@ void addVtVariable(VariableTable* vt, const char* name) {
// add the variable // add the variable
strcpy(vt->vars[vt->count].name, name); strcpy(vt->vars[vt->count].name, name);
vt->vars[vt->count].type = type;
vt->vars[vt->count].offset = vt->count * 8; vt->vars[vt->count].offset = vt->count * 8;
vt->count++; vt->count++;
} }
VariableInfo* getVariable(VariableTable* vt, const char* name) {
for (size_t i = 0; i < vt->count; i++) {
if (strcmp(vt->vars[i].name, name) == 0) {
return &vt->vars[i];
}
}
return NULL;
}
int getVariablePos(VariableTable* vt, const char* name) { int getVariablePos(VariableTable* vt, const char* name) {
for (size_t i = 0; i < vt->count; i++) { for (size_t i = 0; i < vt->count; i++) {
if (strcmp(vt->vars[i].name, name) == 0) { if (strcmp(vt->vars[i].name, name) == 0) {
@@ -42,6 +53,318 @@ int getVariablePos(VariableTable* vt, const char* name) {
return -1; return -1;
} }
DoubleTable createDoubleTable() {
DoubleTable dt;
dt.count = 0;
dt.capacity = 16;
dt.constants = malloc(sizeof(DoubleConstant) * 16);
return dt;
}
char* addDoubleConstant(DoubleTable* dt, Estr* rodata, double value, int* counter) {
for (size_t i = 0; i < dt->count; i++) {
if (dt->constants[i].value == value) {
return dt->constants[i].label;
}
}
if (dt->count >= dt->capacity) {
dt->capacity = dt->capacity == 0 ? 16 : dt->capacity * 2;
dt->constants = realloc(dt->constants, sizeof(DoubleConstant) * dt->capacity);
}
char label[64];
snprintf(label, sizeof(label), "float_const_%d", (*counter)++);
strcpy(dt->constants[dt->count].label, label);
dt->constants[dt->count].value = value;
dt->count++;
char line[128];
snprintf(line, 128, "%s: dq %.17g ; double constant\n", label, value);
APPEND_ESTR((*rodata), line);
return dt->constants[dt->count - 1].label;
}
GroundValueType getInstructionReturnType(GroundInstruction* gi, VariableTable* table) {
switch (gi->type) {
case JUMP:
case PRINT:
case PRINTLN:
case END:
case IF:
case RETURN:
case ENDFUN:
case PUSHARG:
case CALL: // FIXME do this when functions are added
case USE:
case EXTERN:
case CREATELABEL:
case PAUSE:
case DROP:
case ERRORCMD:
case ENDSTRUCT:
case INIT: // FIXME do this when objects are added
case GETFIELD: // FIXME do this when objects are added
case SETFIELD: // FIXME do this when objects are added
case DIVIDE: // FIXME do this when division is added
case SETLISTAT:
case GETLISTAT: // FIXME do this when lists are getting implemented
case LISTAPPEND: {
return NONE;
}
case TOSTRING:
case GETTYPE:
case INPUT: {
return STRING;
}
case STOI:
case GETLISTSIZE:
case GETSTRSIZE: {
return INT;
}
case STOD: {
return DOUBLE;
}
case EQUAL:
case INEQUAL:
case GREATER:
case LESSER:
case NOT:
case EXISTS: {
return BOOL;
}
case GETSTRCHARAT: {
return CHAR;
}
case SETLIST: {
return LIST;
}
case FUN: {
return FUNCTION;
}
case STRUCT: {
return STRUCTVAL;
}
case SET: {
if (gi->args.length == 2) {
if (gi->args.args[1].type == VALUE) {
return gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
return var->type;
} else {
return NONE;
}
} else {
return NONE;
}
}
case ADD: {
if (gi->args.length == 3) {
GroundValueType leftType;
GroundValueType rightType;
if (gi->args.args[0].type == VALUE) {
leftType = gi->args.args[0].value.value.type;
} else if (gi->args.args[0].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[0].value.refName);
if (var == NULL) {
return NONE;
}
leftType = var->type;
} else {
return NONE;
}
if (gi->args.args[1].type == VALUE) {
rightType = gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
rightType = var->type;
} else {
return NONE;
}
if (leftType == STRING) {
if (rightType == STRING) {
return STRING;
} else {
return NONE;
}
}
if (leftType == DOUBLE) {
if (rightType == DOUBLE || rightType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == DOUBLE) {
if (leftType == DOUBLE || leftType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == INT && leftType == INT) {
return INT;
}
return NONE;
} else {
return NONE;
}
}
case SUBTRACT: {
if (gi->args.length == 3) {
GroundValueType leftType;
GroundValueType rightType;
if (gi->args.args[0].type == VALUE) {
leftType = gi->args.args[0].value.value.type;
} else if (gi->args.args[0].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[0].value.refName);
if (var == NULL) {
return NONE;
}
leftType = var->type;
} else {
return NONE;
}
if (gi->args.args[1].type == VALUE) {
rightType = gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
rightType = var->type;
} else {
return NONE;
}
if (leftType == DOUBLE) {
if (rightType == DOUBLE || rightType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == DOUBLE) {
if (leftType == DOUBLE || leftType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == INT && leftType == INT) {
return INT;
}
return NONE;
} else {
return NONE;
}
}
case MULTIPLY: {
if (gi->args.length == 3) {
GroundValueType leftType;
GroundValueType rightType;
if (gi->args.args[0].type == VALUE) {
leftType = gi->args.args[0].value.value.type;
} else if (gi->args.args[0].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[0].value.refName);
if (var == NULL) {
return NONE;
}
leftType = var->type;
} else {
return NONE;
}
if (gi->args.args[1].type == VALUE) {
rightType = gi->args.args[1].value.value.type;
} else if (gi->args.args[1].type == VALREF) {
VariableInfo* var = getVariable(table, gi->args.args[1].value.refName);
if (var == NULL) {
return NONE;
}
rightType = var->type;
} else {
return NONE;
}
if (leftType == DOUBLE) {
if (rightType == DOUBLE || rightType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == DOUBLE) {
if (leftType == DOUBLE || leftType == INT) {
return DOUBLE;
} else {
return NONE;
}
}
if (rightType == INT && leftType == INT) {
return INT;
}
return NONE;
} else {
return NONE;
}
}
}
return INT;
}
char* processValueString(GroundArg arg, VariableTable* vt, GroundValueType* outType, DoubleTable* dt, Estr* rodata, int* doubleCounter) {
if (arg.type == VALREF) {
VariableInfo* var = getVariable(vt, arg.value.refName);
if (var && outType) *outType = var->type;
char* buf = malloc(260);
snprintf(buf, 260, "[%s]", arg.value.refName);
return buf;
}
if (arg.type == VALUE) {
if (outType) *outType = arg.value.value.type;
switch(arg.value.value.type) {
case INT: {
char* buf = malloc(64);
snprintf(buf, 64, "%" PRId64, arg.value.value.data.intVal);
return buf;
break;
}
case DOUBLE: {
char* label = addDoubleConstant(dt, rodata, arg.value.value.data.doubleVal, doubleCounter);
char* buf = malloc(128);
snprintf(buf, 128, "[rel %s]", label);
return buf;
break;
}
case BOOL:
return arg.value.value.data.boolVal ? "1" : "0";
break;
case CHAR: {
char* buf = malloc(8);
snprintf(buf, 8, "%d", (int)arg.value.value.data.charVal);
return buf;
break;
}
default: {
printf("For now, only int, bool, and char are supported data types in the compiler.");
exit(1);
break;
}
}
}
return NULL;
}
/*
char* processValueString(GroundArg arg) { char* processValueString(GroundArg arg) {
if (arg.type == VALREF) { if (arg.type == VALREF) {
char* buf = malloc(sizeof(char) * 260); char* buf = malloc(sizeof(char) * 260);
@@ -49,21 +372,43 @@ char* processValueString(GroundArg arg) {
return buf; return buf;
} }
if (arg.type == VALUE) { if (arg.type == VALUE) {
if (arg.value.value.type != INT) { switch (arg.value.value.type) {
printf("Only int is supported right now\n"); case INT: {
exit(1); char* buf = malloc(sizeof(char) * 64);
snprintf(buf, sizeof(char) * 260, "%" PRId64, arg.value.value.data.intVal);
return buf;
break;
}
case BOOL: {
return arg.value.value.data.boolVal ? "1" : "0";
break;
}
case CHAR: {
char* buf = malloc(8);
snprintf(buf, 8, "%d", (int) arg.value.value.data.charVal);
return buf;
break;
}
default: {
printf("For now, only int, bool, and char are supported data types in the compiler.");
exit(1);
break;
}
} }
char* buf = malloc(sizeof(char) * 64);
snprintf(buf, sizeof(char) * 260, "%" PRId64, arg.value.value.data.intVal);
return buf;
} }
return NULL; return NULL;
} }
*/
char* compileGroundProgram(GroundProgram* program) { char* compileGroundProgram(GroundProgram* program) {
Estr start = CREATE_ESTR("global _start\nsection .text\n_start:\n"); Estr start = CREATE_ESTR("global _start\nsection .text\n_start:\n");
Estr data = CREATE_ESTR("section .bss\n"); Estr data = CREATE_ESTR("section .bss\n");
Estr helpers = CREATE_ESTR(""); Estr helpers = CREATE_ESTR("");
Estr rodata = CREATE_ESTR("section .rodata\n");
APPEND_ESTR(rodata, " newline_char: db 10\n");
APPEND_ESTR(rodata, " space_char: db 32\n");
DoubleTable doubleTable = createDoubleTable();
int doubleConstCounter = 0;
APPEND_ESTR(helpers, "\n; Helper: Print integer in rax\n"); APPEND_ESTR(helpers, "\n; Helper: Print integer in rax\n");
APPEND_ESTR(helpers, "print_int:\n"); APPEND_ESTR(helpers, "print_int:\n");
@@ -124,7 +469,7 @@ char* compileGroundProgram(GroundProgram* program) {
GroundInstruction gi = program->instructions[i]; GroundInstruction gi = program->instructions[i];
for (size_t j = 0; j < gi.args.length; j++) { for (size_t j = 0; j < gi.args.length; j++) {
if (gi.args.args[j].type == DIRREF) { if (gi.args.args[j].type == DIRREF) {
addVtVariable(&varTable, gi.args.args[j].value.refName); addVtVariable(&varTable, gi.args.args[j].value.refName, getInstructionReturnType(&gi, &varTable));
} }
} }
} }
@@ -166,7 +511,7 @@ char* compileGroundProgram(GroundProgram* program) {
APPEND_ESTR(start, " ; set\n") APPEND_ESTR(start, " ; set\n")
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov ["); APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName); APPEND_ESTR(start, varName);
@@ -212,7 +557,7 @@ char* compileGroundProgram(GroundProgram* program) {
APPEND_ESTR(start, " ; if\n"); APPEND_ESTR(start, " ; if\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " test rax, rax\n"); APPEND_ESTR(start, " test rax, rax\n");
APPEND_ESTR(start, " jnz "); APPEND_ESTR(start, " jnz ");
@@ -239,7 +584,7 @@ char* compileGroundProgram(GroundProgram* program) {
APPEND_ESTR(start, " ; not\n"); APPEND_ESTR(start, " ; not\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " xor rax, 1\n"); APPEND_ESTR(start, " xor rax, 1\n");
APPEND_ESTR(start, " mov ["); APPEND_ESTR(start, " mov [");
@@ -265,17 +610,55 @@ char* compileGroundProgram(GroundProgram* program) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i); runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", &gi, i);
} }
char* varName= gi.args.args[2].value.refName; GroundValueType leftType, rightType;
APPEND_ESTR(start, " ; add\n");
APPEND_ESTR(start, " mov rax, "); char* left = processValueString(gi.args.args[0], &varTable, &leftType, &doubleTable, &rodata, &doubleConstCounter);
APPEND_ESTR(start, processValueString(gi.args.args[0])); char* right = processValueString(gi.args.args[1], &varTable, &rightType, &doubleTable, &rodata, &doubleConstCounter);
APPEND_ESTR(start, "\n"); char* varName = gi.args.args[2].value.refName;
APPEND_ESTR(start, " add rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); bool isDouble = (leftType == DOUBLE || rightType == DOUBLE);
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov ["); if (isDouble) {
APPEND_ESTR(start, varName); APPEND_ESTR(start, " ; add (double)\n");
APPEND_ESTR(start, "], rax\n");
if (leftType == INT) {
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, left);
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cvtsi2sd xmm0, rax ; Convert int to double\n");
} else {
APPEND_ESTR(start, " movsd xmm0, ");
APPEND_ESTR(start, left);
APPEND_ESTR(start, "\n");
}
if (rightType == INT) {
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, right);
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cvtsi2sd xmm1, rax ; Convert int to double\n");
APPEND_ESTR(start, " addsd xmm0, xmm1\n");
} else {
APPEND_ESTR(start, " addsd xmm0, ");
APPEND_ESTR(start, right);
APPEND_ESTR(start, "\n");
}
APPEND_ESTR(start, " movsd [");
APPEND_ESTR(start, varName);
APPEND_ESTR(start, "], xmm0\n");
} else {
APPEND_ESTR(start, " ; add\n");
APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " add rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName);
APPEND_ESTR(start, "], rax\n");
}
break; break;
} }
case SUBTRACT: { case SUBTRACT: {
@@ -298,10 +681,10 @@ char* compileGroundProgram(GroundProgram* program) {
char* varName= gi.args.args[2].value.refName; char* varName= gi.args.args[2].value.refName;
APPEND_ESTR(start, " ; subtract\n"); APPEND_ESTR(start, " ; subtract\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " sub rax, "); APPEND_ESTR(start, " sub rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov ["); APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName); APPEND_ESTR(start, varName);
@@ -328,10 +711,10 @@ char* compileGroundProgram(GroundProgram* program) {
char* varName= gi.args.args[2].value.refName; char* varName= gi.args.args[2].value.refName;
APPEND_ESTR(start, " ; multiply\n"); APPEND_ESTR(start, " ; multiply\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " imul rax, "); APPEND_ESTR(start, " imul rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " mov ["); APPEND_ESTR(start, " mov [");
APPEND_ESTR(start, varName); APPEND_ESTR(start, varName);
@@ -358,10 +741,10 @@ char* compileGroundProgram(GroundProgram* program) {
char* varName= gi.args.args[2].value.refName; char* varName= gi.args.args[2].value.refName;
APPEND_ESTR(start, " ; equal\n"); APPEND_ESTR(start, " ; equal\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, "); APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " sete al\n"); APPEND_ESTR(start, " sete al\n");
APPEND_ESTR(start, " movzx rax, al\n"); APPEND_ESTR(start, " movzx rax, al\n");
@@ -390,10 +773,10 @@ char* compileGroundProgram(GroundProgram* program) {
char* varName= gi.args.args[2].value.refName; char* varName= gi.args.args[2].value.refName;
APPEND_ESTR(start, " ; inequal\n"); APPEND_ESTR(start, " ; inequal\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, "); APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " setne al\n"); APPEND_ESTR(start, " setne al\n");
APPEND_ESTR(start, " movzx rax, al\n"); APPEND_ESTR(start, " movzx rax, al\n");
@@ -422,10 +805,10 @@ char* compileGroundProgram(GroundProgram* program) {
char* varName= gi.args.args[2].value.refName; char* varName= gi.args.args[2].value.refName;
APPEND_ESTR(start, " ; greater\n"); APPEND_ESTR(start, " ; greater\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, "); APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " setg al\n"); APPEND_ESTR(start, " setg al\n");
APPEND_ESTR(start, " movzx rax, al\n"); APPEND_ESTR(start, " movzx rax, al\n");
@@ -454,10 +837,10 @@ char* compileGroundProgram(GroundProgram* program) {
char* varName= gi.args.args[2].value.refName; char* varName= gi.args.args[2].value.refName;
APPEND_ESTR(start, " ; lesser\n"); APPEND_ESTR(start, " ; lesser\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " cmp rax, "); APPEND_ESTR(start, " cmp rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[1])); APPEND_ESTR(start, processValueString(gi.args.args[1], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " setl al\n"); APPEND_ESTR(start, " setl al\n");
APPEND_ESTR(start, " movzx rax, al\n"); APPEND_ESTR(start, " movzx rax, al\n");
@@ -487,7 +870,7 @@ char* compileGroundProgram(GroundProgram* program) {
} }
APPEND_ESTR(start, " ; print int\n"); APPEND_ESTR(start, " ; print int\n");
APPEND_ESTR(start, " mov rax, "); APPEND_ESTR(start, " mov rax, ");
APPEND_ESTR(start, processValueString(gi.args.args[j])); APPEND_ESTR(start, processValueString(gi.args.args[j], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " call print_int\n"); APPEND_ESTR(start, " call print_int\n");
} }
@@ -512,7 +895,7 @@ char* compileGroundProgram(GroundProgram* program) {
APPEND_ESTR(start, " ; end\n"); APPEND_ESTR(start, " ; end\n");
APPEND_ESTR(start, " mov rax, 60\n"); APPEND_ESTR(start, " mov rax, 60\n");
APPEND_ESTR(start, " mov rdi, "); APPEND_ESTR(start, " mov rdi, ");
APPEND_ESTR(start, processValueString(gi.args.args[0])); APPEND_ESTR(start, processValueString(gi.args.args[0], &varTable, NULL, &doubleTable, &rodata, &doubleConstCounter));
APPEND_ESTR(start, "\n"); APPEND_ESTR(start, "\n");
APPEND_ESTR(start, " syscall\n"); APPEND_ESTR(start, " syscall\n");
break; break;
@@ -532,10 +915,8 @@ char* compileGroundProgram(GroundProgram* program) {
APPEND_ESTR(complete, start.str); APPEND_ESTR(complete, start.str);
APPEND_ESTR(complete, " ; End of program\n mov rax, 60\n mov rdi, 0\n syscall\n"); APPEND_ESTR(complete, " ; End of program\n mov rax, 60\n mov rdi, 0\n syscall\n");
APPEND_ESTR(complete, helpers.str); APPEND_ESTR(complete, helpers.str);
APPEND_ESTR(complete, "\nsection .rodata\n"); APPEND_ESTR(complete, data.str);
APPEND_ESTR(complete, "newline_char: db 10\n"); APPEND_ESTR(complete, rodata.str);
APPEND_ESTR(complete, "space_char: db 32\n");
APPEND_ESTR(complete, data.str)
return complete.str; return complete.str;
} }

View File

@@ -7,6 +7,7 @@ char* compileGroundProgram(GroundProgram* program);
typedef struct VariableInfo { typedef struct VariableInfo {
char name[256]; char name[256];
int offset; int offset;
GroundValueType type;
} VariableInfo; } VariableInfo;
typedef struct VariableTable { typedef struct VariableTable {
@@ -15,4 +16,13 @@ typedef struct VariableTable {
size_t capacity; size_t capacity;
} VariableTable; } VariableTable;
typedef struct DoubleConstant {
double value;
char label[64];
} DoubleConstant;
typedef struct DoubleTable {
DoubleConstant* constants;
size_t count;
size_t capacity;
} DoubleTable;

View File

@@ -43,87 +43,60 @@ GroundValue groundCreateValue(GroundValueType type, ...) {
va_list args; va_list args;
va_start(args, type); va_start(args, type);
GroundValue gv;
switch (type) { switch (type) {
case INT: { case INT: {
gv = createIntGroundValue(va_arg(args, int64_t)); return createIntGroundValue(va_arg(args, int64_t));
break; break;
} }
case DOUBLE: { case DOUBLE: {
gv = createDoubleGroundValue(va_arg(args, double)); return createDoubleGroundValue(va_arg(args, double));
break; break;
} }
case STRING: { case STRING: {
gv = createStringGroundValue(va_arg(args, char*)); return createStringGroundValue(va_arg(args, char*));
break; break;
} }
case CHAR: { case CHAR: {
gv = createCharGroundValue((char)va_arg(args, int)); return createCharGroundValue((char)va_arg(args, int));
break; break;
} }
case BOOL: { case BOOL: {
gv = createBoolGroundValue((bool)va_arg(args, int)); return createBoolGroundValue((bool)va_arg(args, int));
break; break;
} }
case LIST: { case LIST: {
gv = createListGroundValue(va_arg(args, List)); return createListGroundValue(va_arg(args, List));
break; break;
} }
case FUNCTION: { case FUNCTION: {
gv = createFunctionGroundValue(va_arg(args, GroundFunction*)); return createFunctionGroundValue(va_arg(args, GroundFunction*));
break; break;
} }
case STRUCTVAL: { case STRUCTVAL: {
GroundValue gv;
gv.type = STRUCTVAL; gv.type = STRUCTVAL;
gv.data.structVal = malloc(sizeof(GroundStruct)); gv.data.structVal = malloc(sizeof(GroundStruct));
*gv.data.structVal = va_arg(args, GroundStruct); *gv.data.structVal = va_arg(args, GroundStruct);
break; return gv;
} }
case NONE: { case NONE: {
gv = createNoneGroundValue(); return createNoneGroundValue();
break; break;
} }
case ERROR:
case CUSTOM: { case CUSTOM: {
// CUSTOM values are created from structs
GroundStruct* gstruct = va_arg(args, GroundStruct*);
gv.type = CUSTOM;
gv.data.customVal = malloc(sizeof(GroundObject));
*gv.data.customVal = createObject(*gstruct);
// Deep copy the struct definition so it stays valid
gv.customType = malloc(sizeof(GroundStruct));
gv.customType->size = gstruct->size;
gv.customType->fields = malloc(gstruct->size * sizeof(GroundStructField));
for (size_t i = 0; i < gstruct->size; i++) {
strncpy(gv.customType->fields[i].id, gstruct->fields[i].id, 64);
gv.customType->fields[i].value = copyGroundValue(&gstruct->fields[i].value);
}
break;
}
case ERROR: {
// FIXME // FIXME
gv = createNoneGroundValue();
break;
}
default: {
gv = createNoneGroundValue();
break; break;
} }
} }
return createNoneGroundValue();
va_end(args); va_end(args);
return gv;
} }
GroundValue groundRunProgram(GroundProgram* program) { GroundValue groundRunProgram(GroundProgram* program) {
GroundVariable* variables = NULL; return interpretGroundProgram(program, NULL);
GroundLabel* labels = NULL;
GroundScope scope = {
.variables = &variables,
.labels = &labels,
.isMainScope = true
};
return interpretGroundProgram(program, &scope);
} }
void groundPrintProgram(GroundProgram* program) { void groundPrintProgram(GroundProgram* program) {
@@ -155,44 +128,3 @@ void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field
char* groundCompileProgram(GroundProgram* program) { char* groundCompileProgram(GroundProgram* program) {
return compileGroundProgram(program); return compileGroundProgram(program);
} }
GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) {
va_list args;
va_start(args, argc);
if (function == NULL) {
return createErrorGroundValue(createGroundError("Null function passed to groundRunFunction", "valueError", NULL, NULL));
}
// Seems to crash some functions
// GroundScope callScope = copyGroundScope(&function->closure);
GroundScope callScope = {
.labels = malloc(sizeof(GroundLabel*)),
.variables = malloc(sizeof(GroundVariable*)),
.isMainScope = false
};
*callScope.variables = NULL;
*callScope.labels = NULL;
if (argc != function->argSize) {
return createErrorGroundValue(createGroundError("Too few or too many arguments for function", "callError", NULL, NULL));
}
for (size_t i = 0; i < argc; i++) {
GroundValue gv = va_arg(args, GroundValue);
if (checkFnTypes(&gv, &function->args[i]) == false) {
return createErrorGroundValue(createGroundError("Mismatched argument type", "callError", NULL, NULL));
}
addVariable(callScope.variables, function->args[i].name, gv);
}
va_end(args);
return interpretGroundProgram(&function->program, &callScope);
}
GroundObjectField* groundFindField(GroundObject head, const char *id) {
GroundObjectField* s;
HASH_FIND_STR(head.fields, id, s);
return s;
}

View File

@@ -5,9 +5,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifndef _WIN32
#include <dlfcn.h> #include <dlfcn.h>
#endif
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h> #include <unistd.h>
@@ -15,8 +13,6 @@ int currentInstruction = 0;
bool isMainScopeGlobal = true; bool isMainScopeGlobal = true;
char* getFileContents(const char* filename);
[[noreturn]] void customError(GroundArg type, GroundArg what, GroundInstruction* where, int whereLine, int exitCode) { [[noreturn]] void customError(GroundArg type, GroundArg what, GroundInstruction* where, int whereLine, int exitCode) {
printf("Ground runtime error:\n ErrorType: "); printf("Ground runtime error:\n ErrorType: ");
printGroundArg(&type); printGroundArg(&type);
@@ -447,11 +443,9 @@ GroundStruct parseStruct(GroundProgram* in, GroundScope* scope, size_t errorOffs
addInstructionToProgram(&gp, in->instructions[i]); addInstructionToProgram(&gp, in->instructions[i]);
i++; i++;
} }
i--;
GroundFunction* function = parseFunction(&gp, errorOffset); GroundFunction* function = parseFunction(&gp, errorOffset);
function->startLine = i; function->startLine = i;
function->closure = copyGroundScope(scope);
GroundValue gv = createFunctionGroundValue(function); GroundValue gv = createFunctionGroundValue(function);
addFieldToStruct(&gstruct, name, gv); addFieldToStruct(&gstruct, name, gv);
break; break;
@@ -487,6 +481,41 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
if (in->instructions[i].type == CREATELABEL) { if (in->instructions[i].type == CREATELABEL) {
addLabel(scope.labels, in->instructions[i].args.args[0].value.refName, i); addLabel(scope.labels, in->instructions[i].args.args[0].value.refName, i);
} }
if (in->instructions[i].type == FUN) {
if (in->instructions[i].args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", &in->instructions[i], i);
}
if (in->instructions[i].args.args[0].type != FNREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 1", &in->instructions[i], i);
}
char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1);
strcpy(name, in->instructions[i].args.args[0].value.refName);
size_t counter = 1;
GroundProgram gp = createGroundProgram();
addInstructionToProgram(&gp, in->instructions[i]);
size_t errorOffset = i;
i++;
while (counter > 0) {
if (i >= in->size) {
runtimeError(PREMATURE_EOF, "Reached end of scope before function definition ended", &in->instructions[i - 1], i - 1);
}
if (in->instructions[i].type == FUN) {
counter++;
}
if (in->instructions[i].type == ENDFUN) {
counter--;
}
addInstructionToProgram(&gp, in->instructions[i]);
i++;
}
GroundFunction* function = parseFunction(&gp, errorOffset);
function->startLine = i;
GroundValue gv = createFunctionGroundValue(function);
addVariable(scope.variables, name, gv);
}
if (in->instructions[i].type == STRUCT) { if (in->instructions[i].type == STRUCT) {
if (in->instructions[i].args.length < 1) { if (in->instructions[i].args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", &in->instructions[i], i); runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", &in->instructions[i], i);
@@ -517,7 +546,6 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
addInstructionToProgram(&gp, in->instructions[i]); addInstructionToProgram(&gp, in->instructions[i]);
i++; i++;
} }
i--;
GroundValue gv = { GroundValue gv = {
.type = STRUCTVAL, .type = STRUCTVAL,
@@ -529,44 +557,6 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
} }
} }
for (size_t i = 0; i < in->size; i++) { for (size_t i = 0; i < in->size; i++) {
if (in->instructions[i].type == FUN) {
if (in->instructions[i].args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", &in->instructions[i], i);
}
if (in->instructions[i].args.args[0].type != FNREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 1", &in->instructions[i], i);
}
char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1);
strcpy(name, in->instructions[i].args.args[0].value.refName);
size_t counter = 1;
GroundProgram gp = createGroundProgram();
addInstructionToProgram(&gp, in->instructions[i]);
size_t errorOffset = i;
i++;
while (counter > 0) {
if (i >= in->size) {
runtimeError(PREMATURE_EOF, "Reached end of scope before function definition ended", &in->instructions[i - 1], i - 1);
}
if (in->instructions[i].type == FUN) {
counter++;
}
if (in->instructions[i].type == ENDFUN) {
counter--;
}
addInstructionToProgram(&gp, in->instructions[i]);
i++;
}
i--;
GroundFunction* function = parseFunction(&gp, errorOffset);
function->startLine = i;
function->closure = copyGroundScope(inScope);
GroundValue gv = createFunctionGroundValue(function);
addVariable(scope.variables, name, gv);
}
/*
if (in->instructions[i].type == FUN) { if (in->instructions[i].type == FUN) {
int count = 1; int count = 1;
while (count > 0) { while (count > 0) {
@@ -582,7 +572,6 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
} }
} }
} }
*/
if (in->instructions[i].type == STRUCT) { if (in->instructions[i].type == STRUCT) {
int count = 1; int count = 1;
while (count > 0) { while (count > 0) {
@@ -1115,7 +1104,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (in->args.args[1].type != DIRREF) { if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
} }
addVariable(scope->variables, in->args.args[1].value.refName, createIntGroundValue(atoll(in->args.args[0].value.value.data.stringVal))); addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(atoll(in->args.args[0].value.value.data.stringVal)));
break; break;
} }
case STOD: { case STOD: {
@@ -1131,39 +1120,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (in->args.args[1].type != DIRREF) { if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
} }
addVariable(scope->variables, in->args.args[1].value.refName, createDoubleGroundValue(atof(in->args.args[0].value.value.data.stringVal))); addVariable(scope->variables, in->args.args[1].value.refName, createIntGroundValue(atof(in->args.args[0].value.value.data.stringVal)));
break;
}
case ITOC: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE || in->args.args[0].value.value.type != INT) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[1].value.refName, createCharGroundValue((char)in->args.args[0].value.value.data.intVal));
break;
}
case CTOI: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE || in->args.args[0].value.value.type != CHAR) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Char for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[1].value.refName, createIntGroundValue((int)in->args.args[0].value.value.data.charVal));
break; break;
} }
case TOSTRING: { case TOSTRING: {
@@ -1180,53 +1137,57 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
} }
GroundValue* value = &in->args.args[0].value.value; GroundValue* value = &in->args.args[0].value.value;
char buf[256];
switch (value->type) { switch (value->type) {
case INT: { case INT: {
snprintf(buf, sizeof(buf), "%" PRId64, value->data.intVal); char* buf = malloc(sizeof(char) * 256);
snprintf(buf, sizeof(char) * 256, "%" PRId64, value->data.intVal);
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
break; break;
} }
case DOUBLE: { case DOUBLE: {
snprintf(buf, sizeof(buf), "%f", value->data.doubleVal); char* buf = malloc(sizeof(char) * 256);
snprintf(buf, sizeof(char) * 256, "%f", value->data.doubleVal);
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
break; break;
} }
case STRING: { case STRING: {
snprintf(buf, sizeof(buf), "%s", value->data.stringVal); addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(value->data.stringVal));
break; break;
} }
case CHAR: { case CHAR: {
char* buf = malloc(sizeof(char) * 2);
buf[0] = value->data.charVal; buf[0] = value->data.charVal;
buf[1] = '\0'; buf[1] = '\0';
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
break; break;
} }
case BOOL: { case BOOL: {
if (value->data.boolVal) { if (value->data.boolVal) {
snprintf(buf, sizeof(buf), "true"); addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("true"));
} else { } else {
snprintf(buf, sizeof(buf), "false"); addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("false"));
} }
break; break;
} }
case LIST: { case LIST: {
snprintf(buf, sizeof(buf), "<list>"); addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<list>"));
break; break;
} }
case FUNCTION: { case FUNCTION: {
snprintf(buf, sizeof(buf), "<function>"); addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<function>"));
break; break;
} }
case CUSTOM: { case CUSTOM: {
snprintf(buf, sizeof(buf), "<custom>"); addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<custom>"));
break; break;
} }
case NONE: case NONE:
default: { default: {
snprintf(buf, sizeof(buf), "<default>"); addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<default>"));
break; break;
} }
} }
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
break; break;
} }
@@ -1795,6 +1756,11 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (in->args.args[in->args.length - 1].type != DIRREF) { if (in->args.args[in->args.length - 1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction);
} }
GroundVariable* variables = NULL;
GroundLabel* labels = NULL;
GroundScope newScope;
newScope.variables = &variables;
newScope.labels = &labels;
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName); GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) { if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Function not found", in, currentInstruction); runtimeError(UNKNOWN_VARIABLE, "Function not found", in, currentInstruction);
@@ -1817,14 +1783,9 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (in->args.args[i + 1].type != VALUE) { if (in->args.args[i + 1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
} }
if (function->nativeFn) { //if (in->args.args[i + 1].value.value.type != function->args[i].type) {
if (in->args.args[i + 1].value.value.type != function->args[i].type) { if (!checkFnTypes(&in->args.args[i + 1].value.value, &function->args[i])) {
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
}
} else {
if (!checkFnTypes(&in->args.args[i + 1].value.value, &function->args[i])) {
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
}
} }
appendToList(&argsList, in->args.args[i + 1].value.value); appendToList(&argsList, in->args.args[i + 1].value.value);
} }
@@ -1847,7 +1808,6 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
} }
free(argsList.values); free(argsList.values);
} else { } else {
GroundScope newScope = copyGroundScope(&function->closure);
for (size_t i = 0; i < function->argSize; i++) { for (size_t i = 0; i < function->argSize; i++) {
if (in->args.args[i + 1].type != VALUE) { if (in->args.args[i + 1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
@@ -1888,17 +1848,17 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
char* envPath = getenv("GROUND_LIBS"); char* envPath = getenv("GROUND_LIBS");
if (envPath) { if (envPath) {
snprintf(path, sizeof(path), "%s/%s.grnd", envPath, libName); snprintf(path, sizeof(path), "%s/%s", envPath, libName);
if (access(path, F_OK) == 0) found = 1; if (access(path, F_OK) == 0) found = 1;
} }
if (!found) { if (!found) {
snprintf(path, sizeof(path), "/usr/lib/ground/%s.grnd", libName); snprintf(path, sizeof(path), "/usr/lib/ground/%s", libName);
if (access(path, F_OK) == 0) found = 1; if (access(path, F_OK) == 0) found = 1;
} }
if (!found) { if (!found) {
snprintf(path, sizeof(path), "%s.grnd", libName); snprintf(path, sizeof(path), "%s", libName);
if (access(path, F_OK) == 0) found = 1; if (access(path, F_OK) == 0) found = 1;
} }
@@ -1908,33 +1868,32 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
runtimeError(FIXME, errorMsg, in, currentInstruction); runtimeError(FIXME, errorMsg, in, currentInstruction);
} }
char* fileContents = getFileContents(path); FILE* f = fopen(path, "r");
if (!f) {
runtimeError(FIXME, "Failed to open file", in, currentInstruction);
}
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
GroundProgram program = parseFile(fileContents); char* content = malloc(fsize + 1);
free(fileContents); if (!content) {
fclose(f);
runtimeError(FIXME, "Failed to allocate memory for file content", in, currentInstruction);
}
fread(content, 1, fsize, f);
fclose(f);
content[fsize] = 0;
GroundScope newScope = { GroundProgram program = parseFile(content);
.variables = scope->variables, free(content);
.labels = malloc(sizeof(GroundLabel*)),
.isMainScope = false
};
*newScope.labels = NULL;
int ci = currentInstruction;
interpretGroundProgram(&program, &newScope);
currentInstruction = ci;
interpretGroundProgram(&program, scope);
freeGroundProgram(&program);
break; break;
} }
case EXTERN: { case EXTERN: {
#ifdef _WIN32
runtimeError(FIXME, "No Windows support for extern, sucker", in, currentInstruction);
#endif
#ifndef _WIN32
if (in->args.length < 1) { if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction); runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction);
} }
@@ -1984,7 +1943,6 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
} }
initFn(scope); initFn(scope);
#endif
break; break;
} }

View File

@@ -1,5 +1,6 @@
#ifndef INTERPRETER_H #ifndef INTERPRETER_H
#define INTERPRETER_H #define INTERPRETER_H
#define MAX_ID_LEN 64
#include "types.h" #include "types.h"
#include "parser.h" #include "parser.h"
@@ -13,6 +14,24 @@ typedef enum GroundDebugInstructionType {
DUMP, INSPECT, EVAL, CONTINUE, EXIT, STEP, VIEW, HELP, UNKNOWN DUMP, INSPECT, EVAL, CONTINUE, EXIT, STEP, VIEW, HELP, UNKNOWN
} GroundDebugInstructionType; } GroundDebugInstructionType;
typedef struct GroundLabel {
char id[MAX_ID_LEN];
int lineNum;
UT_hash_handle hh;
} GroundLabel;
typedef struct GroundVariable {
char id[MAX_ID_LEN];
GroundValue value;
UT_hash_handle hh;
} GroundVariable;
typedef struct GroundScope {
GroundLabel** labels;
GroundVariable** variables;
bool isMainScope;
} GroundScope;
typedef struct GroundDebugInstruction { typedef struct GroundDebugInstruction {
GroundDebugInstructionType type; GroundDebugInstructionType type;
char* arg; char* arg;
@@ -23,6 +42,7 @@ GroundFunction* parseFunction(GroundProgram* in, size_t errorOffset);
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope); GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope);
GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope); GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope);
void addVariable(GroundVariable **head, const char *id, GroundValue data);

View File

@@ -1,10 +1,7 @@
#include "parser.h" #include "parser.h"
#include "interpreter.h" #include "interpreter.h"
#include "compiler.h" #include "compiler.h"
#include "types.h"
#include "serialize.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
char* getFileContents(const char* filename) { char* getFileContents(const char* filename) {
// https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c // https://stackoverflow.com/questions/3747086/reading-the-whole-text-file-into-a-char-array-in-c
@@ -43,87 +40,57 @@ char* getFileContents(const char* filename) {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
/*
if (argc < 2) {
printf("Usage: ground [file]\n");
exit(1);
}
char* file = getFileContents(argv[1]);
GroundProgram program = parseFile(file);
free(file);
char* compiled = compileGroundProgram(&program);
printf("%s\n", compiled);
interpretGroundProgram(&program, NULL);
*/
if (argc < 2) { if (argc < 2) {
printf("Usage: %s <file> [-c] [--compile] [-h] [--help]\n", argv[0]); printf("Usage: %s <file> [-c] [--compile] [-h] [--help]\n", argv[0]);
exit(1); exit(1);
} }
bool compile = false; bool compile = false;
bool writeBytecode = false;
bool readBytecode = false;
char* fileName = NULL; char* fileName = NULL;
char* outFileName = NULL;
List groundArgs = createList();
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp("--compile", argv[i]) == 0 || strcmp("-c", argv[i]) == 0) { if (strcmp("--compile", argv[i]) == 0 || strcmp("-c", argv[i]) == 0) {
if (writeBytecode) {
printf("Cannot choose both bytecode and compilation");
exit(1);
}
compile = true; compile = true;
} }
else if (strcmp("--help", argv[i]) == 0 || strcmp("-h", argv[i]) == 0) { else if (strcmp("--help", argv[i]) == 0 || strcmp("-h", argv[i]) == 0) {
printf("GroundVM help\n"); printf("GroundVM help\n");
printf("Usage: %s <file> [-c] [--compile] [-h] [--help] [-w <output>] [--writeBytecode <output>] [-b] [--bytecode]\n", argv[0]); printf("Usage: %s <file> [-c] [--compile] [-h] [--help]\n", argv[0]);
printf("Options:\n"); printf("Options:\n");
printf(" -c or --compile: Outputs Linux x86_64 assembly instead of interpreting (WIP)\n"); printf(" -c or --compile: Outputs Linux x86_64 assembly instead of interpreting (WIP)\n");
printf(" -h or --help: Shows this help message\n"); printf(" -h or --help: Shows this help message\n");
printf(" -w <output> or --writebytecode <output>: Outputs binary Ground bytecode");
printf(" -b <output> or --bytecode <output>: Inputs binary Ground bytecode");
exit(0); exit(0);
} else if (strcmp("--writebytecode", argv[i]) == 0 || strcmp("-w", argv[i]) == 0) {
if (compile) {
printf("Cannot choose both bytecode and compilation");
exit(1);
}
writeBytecode = true;
if (i + 1 >= argc) {
printf("Usage: %s %s <output>", argv[0], argv[i]);
exit(1);
}
i++;
outFileName = argv[i];
} else if (strcmp("--bytecode", argv[i]) == 0 || strcmp("-b", argv[i]) == 0) {
readBytecode = true;
} else { } else {
if (fileName == NULL) { fileName = argv[i];
fileName = argv[i];
} else {
appendToList(&groundArgs, createStringGroundValue(argv[i]));
}
} }
} }
if (fileName == NULL) { if (fileName == NULL) {
printf("Usage: %s <file> [-c] [--compile] [-h] [--help] [-w <output>] [--writeBytecode <output>] [-b] [--bytecode]\n", argv[0]); printf("Usage: %s <file> [-c] [--compile] [-h] [--help]\n", argv[0]);
printf("Error: No file name provided\n"); printf("Error: No file name provided\n");
exit(1); exit(1);
} }
GroundProgram program; char* file = getFileContents(fileName);
GroundProgram program = parseFile(file);
if (readBytecode) { free(file);
deserializeProgramFromFile(fileName, &program);
} else {
char* file = getFileContents(fileName);
program = parseFile(file);
free(file);
}
if (compile) { if (compile) {
char* compiled = compileGroundProgram(&program); char* compiled = compileGroundProgram(&program);
printf("%s\n", compiled); printf("%s\n", compiled);
} else if (writeBytecode) {
serializeProgramToFile(outFileName, &program);
} else { } else {
GroundVariable* variables = NULL; interpretGroundProgram(&program, NULL);
GroundLabel* labels = NULL;
GroundScope scope;
scope.variables = &variables;
scope.labels = &labels;
scope.isMainScope = true;
addVariable(scope.variables, "CMDLINE_ARGS", createListGroundValue(groundArgs));
interpretGroundProgram(&program, &scope);
} }
} }

View File

@@ -5,25 +5,6 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#ifdef _WIN32
size_t strnlen(const char *src, size_t n) {
size_t len = 0;
while (len < n && src[len])
len++;
return len;
}
char* strndup(const char *s, size_t n) {
size_t len = strnlen(s, n);
char *p = malloc(len + 1);
if (p) {
memcpy(p, s, len);
p[len] = '\0';
}
return p;
}
#endif
GroundProgram createGroundProgram() { GroundProgram createGroundProgram() {
GroundProgram gp; GroundProgram gp;
gp.size = 0; gp.size = 0;
@@ -178,8 +159,6 @@ static GroundInstType getInstructionType(const char* inst) {
if (strcmp(inst, "lesser") == 0) return LESSER; if (strcmp(inst, "lesser") == 0) return LESSER;
if (strcmp(inst, "stoi") == 0) return STOI; if (strcmp(inst, "stoi") == 0) return STOI;
if (strcmp(inst, "stod") == 0) return STOD; if (strcmp(inst, "stod") == 0) return STOD;
if (strcmp(inst, "ctoi") == 0) return CTOI;
if (strcmp(inst, "itoc") == 0) return ITOC;
if (strcmp(inst, "tostring") == 0) return TOSTRING; if (strcmp(inst, "tostring") == 0) return TOSTRING;
if (strcmp(inst, "fun") == 0) return FUN; if (strcmp(inst, "fun") == 0) return FUN;
if (strcmp(inst, "return") == 0) return RETURN; if (strcmp(inst, "return") == 0) return RETURN;

View File

@@ -1,302 +0,0 @@
#include "serialize.h"
#include "types.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
static bool writeBytes(FILE* f, const void* data, size_t n) {
return fwrite(data, 1, n, f) == n;
}
static bool readBytes(FILE* f, void* data, size_t n) {
return fread(data, 1, n, f) == n;
}
/* Convenience macros for writing/reading a single typed value. */
#define WRITE(f, val) writeBytes((f), &(val), sizeof(val))
#define READ(f, val) readBytes((f), &(val), sizeof(val))
bool serializeWriteString(FILE* f, const char* s) {
if (s == NULL) {
uint32_t sentinel = UINT32_MAX;
return WRITE(f, sentinel);
}
uint32_t len = (uint32_t)strlen(s);
if (!WRITE(f, len)) return false;
if (len > 0 && !writeBytes(f, s, len)) return false;
return true;
}
char* serializeReadString(FILE* f) {
uint32_t len;
if (!READ(f, len)) return NULL;
if (len == UINT32_MAX) return NULL;
char* s = malloc(len + 1);
if (!s) return NULL;
if (len > 0 && !readBytes(f, s, len)) {
free(s);
return NULL;
}
s[len] = '\0';
return s;
}
/* -----------------------------------------------------------------------
* GroundValue
*
* Format:
* uint32_t type
* <payload depending on type>
*
* Only INT, DOUBLE, CHAR, BOOL, STRING, NONE are expected here.
* Any other type is treated as a serialization error.
* -----------------------------------------------------------------------
*/
bool serializeWriteValue(FILE* f, const GroundValue* gv) {
uint32_t type = (uint32_t)gv->type;
if (!WRITE(f, type)) return false;
switch (gv->type) {
case INT:
return WRITE(f, gv->data.intVal);
case DOUBLE:
return WRITE(f, gv->data.doubleVal);
case CHAR:
return WRITE(f, gv->data.charVal);
case BOOL:
return WRITE(f, gv->data.boolVal);
case STRING:
return serializeWriteString(f, gv->data.stringVal);
case NONE:
return true;
default:
/* LIST, FUNCTION, STRUCTVAL, CUSTOM, ERROR:
* These don't exist at serialization time. If you're hitting
* this, something has gone wrong well before we got here. */
fprintf(stderr, "serializeWriteValue: unexpected type %d\n", gv->type);
return false;
}
}
bool serializeReadValue(FILE* f, GroundValue* out) {
memset(out, 0, sizeof(*out));
uint32_t type;
if (!READ(f, type)) return false;
out->type = (GroundValueType)type;
switch (out->type) {
case INT:
return READ(f, out->data.intVal);
case DOUBLE:
return READ(f, out->data.doubleVal);
case CHAR:
return READ(f, out->data.charVal);
case BOOL:
return READ(f, out->data.boolVal);
case STRING: {
char* s = serializeReadString(f);
/* NULL is a valid encoded value (the sentinel case), but
* we only wrote non-NULL strings, so treat NULL-read as error. */
if (!s) return false;
out->data.stringVal = s;
return true;
}
case NONE:
return true;
default:
fprintf(stderr, "serializeReadValue: unexpected type %d\n", out->type);
return false;
}
}
/* -----------------------------------------------------------------------
* GroundArg
*
* Format:
* uint32_t argType
* <if VALUE> serialized GroundValue
* <otherwise> length-prefixed refName string
* -----------------------------------------------------------------------
*/
bool serializeWriteArg(FILE* f, const GroundArg* ga) {
uint32_t type = (uint32_t)ga->type;
if (!WRITE(f, type)) return false;
if (ga->type == VALUE) {
return serializeWriteValue(f, &ga->value.value);
} else {
/* VALREF, DIRREF, LINEREF, LABEL, FNREF, TYPEREF — all carry a refName */
return serializeWriteString(f, ga->value.refName);
}
}
bool serializeReadArg(FILE* f, GroundArg* out) {
memset(out, 0, sizeof(*out));
uint32_t type;
if (!READ(f, type)) return false;
out->type = (GroundArgType)type;
if (out->type == VALUE) {
return serializeReadValue(f, &out->value.value);
} else {
char* ref = serializeReadString(f);
if (!ref) return false;
out->value.refName = ref;
return true;
}
}
/* -----------------------------------------------------------------------
* GroundInstruction
*
* Format:
* uint32_t instType
* uint64_t argCount
* <argCount x serialized GroundArg>
* -----------------------------------------------------------------------
*/
bool serializeWriteInstruction(FILE* f, const GroundInstruction* gi) {
uint32_t type = (uint32_t)gi->type;
if (!WRITE(f, type)) return false;
uint64_t argc = (uint64_t)gi->args.length;
if (!WRITE(f, argc)) return false;
for (size_t i = 0; i < gi->args.length; i++) {
if (!serializeWriteArg(f, &gi->args.args[i])) return false;
}
return true;
}
bool serializeReadInstruction(FILE* f, GroundInstruction* out) {
uint32_t type;
if (!READ(f, type)) return false;
*out = createGroundInstruction((GroundInstType)type);
uint64_t argc;
if (!READ(f, argc)) return false;
for (uint64_t i = 0; i < argc; i++) {
GroundArg arg;
if (!serializeReadArg(f, &arg)) {
/* Free whatever args we've already read before bailing. */
freeGroundInstruction(out);
return false;
}
addArgToInstruction(out, arg);
}
return true;
}
/* -----------------------------------------------------------------------
* GroundProgram — top-level
*
* File layout:
* uint32_t magic (GROUND_MAGIC)
* uint32_t version (GROUND_VERSION)
* uint64_t instrCount
* <instrCount x serialized GroundInstruction>
* -----------------------------------------------------------------------
*/
bool serializeProgramToFile(const char* path, const GroundProgram* prog) {
FILE* f = fopen(path, "wb");
if (!f) {
perror("serializeProgramToFile: fopen");
return false;
}
bool ok = true;
/* Header */
uint32_t magic = GROUND_MAGIC;
uint32_t version = GROUND_VERSION;
ok = ok && WRITE(f, magic);
ok = ok && WRITE(f, version);
/* Instruction count, then instructions */
uint64_t count = (uint64_t)prog->size;
ok = ok && WRITE(f, count);
for (size_t i = 0; i < prog->size && ok; i++) {
ok = serializeWriteInstruction(f, &prog->instructions[i]);
}
if (!ok) {
fprintf(stderr, "serializeProgramToFile: write error\n");
}
fclose(f);
return ok;
}
bool deserializeProgramFromFile(const char* path, GroundProgram* out) {
memset(out, 0, sizeof(*out));
FILE* f = fopen(path, "rb");
if (!f) {
perror("deserializeProgramFromFile: fopen");
return false;
}
bool ok = true;
/* Validate header */
uint32_t magic, version;
ok = ok && READ(f, magic);
ok = ok && READ(f, version);
if (!ok || magic != GROUND_MAGIC) {
fprintf(stderr, "deserializeProgramFromFile: bad magic (got 0x%08X)\n", magic);
fclose(f);
return false;
}
if (version != GROUND_VERSION) {
fprintf(stderr, "deserializeProgramFromFile: unsupported version %u\n", version);
fclose(f);
return false;
}
/* Read instruction count */
uint64_t count;
if (!READ(f, count)) {
fclose(f);
return false;
}
out->instructions = malloc(sizeof(GroundInstruction) * count);
if (!out->instructions && count > 0) {
fprintf(stderr, "deserializeProgramFromFile: malloc failed\n");
fclose(f);
return false;
}
out->size = 0; /* incremented as we go so partial frees are safe */
for (uint64_t i = 0; i < count; i++) {
if (!serializeReadInstruction(f, &out->instructions[i])) {
fprintf(stderr, "deserializeProgramFromFile: failed at instruction %llu\n",
(unsigned long long)i);
/* Free everything successfully read so far. */
for (size_t j = 0; j < out->size; j++)
freeGroundInstruction(&out->instructions[j]);
free(out->instructions);
memset(out, 0, sizeof(*out));
fclose(f);
return false;
}
out->size++;
}
fclose(f);
return true;
}

View File

@@ -1,48 +0,0 @@
#ifndef SERIALIZE_H
#define SERIALIZE_H
#include <stdio.h>
#include <stdbool.h>
#include "types.h"
/*
* Magic number and version for Ground bytecode files.
* The magic bytes spell 'GRND'
*/
#define GROUND_MAGIC 0x47524E44u
#define GROUND_VERSION 1u
/*
* File header written at the start of every .grbc file.
*/
typedef struct GroundBytecodeHeader {
uint32_t magic;
uint32_t version;
} GroundBytecodeHeader;
/*
* Writes a length-prefixed UTF-8 string to (f).
* NULL is encoded as a sentinel length (UINT32_MAX).
* Returns true on success.
*/
bool serializeWriteString(FILE* f, const char* s);
char* serializeReadString(FILE* f);
bool serializeWriteValue(FILE* f, const GroundValue* gv);
bool serializeReadValue(FILE* f, GroundValue* out);
bool serializeWriteArg(FILE* f, const GroundArg* ga);
bool serializeReadArg(FILE* f, GroundArg* out);
bool serializeWriteInstruction(FILE* f, const GroundInstruction* gi);
bool serializeReadInstruction(FILE* f, GroundInstruction* out);
bool serializeProgramToFile(const char* path, const GroundProgram* prog);
bool deserializeProgramFromFile(const char* path, GroundProgram* out);
#endif

View File

@@ -2,20 +2,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef __EMSCRIPTEN__
static char out_buf[65536];
static int out_pos = 0;
void wasm_print(const char* str) {
int len = strlen(str);
if (out_pos + len < (int)sizeof(out_buf) - 1) {
memcpy(out_buf + out_pos, str, len);
out_pos += len;
out_buf[out_pos] = '\0';
}
}
#endif
GroundValue createIntGroundValue(int64_t in) { GroundValue createIntGroundValue(int64_t in) {
GroundValue gv; GroundValue gv;
gv.data.intVal = in; gv.data.intVal = in;
@@ -104,63 +90,16 @@ GroundValue copyGroundValue(const GroundValue* gv) {
} }
case FUNCTION: newGv.data.fnVal = gv->data.fnVal; break; case FUNCTION: newGv.data.fnVal = gv->data.fnVal; break;
case STRUCTVAL: { case STRUCTVAL: {
newGv.data.structVal = malloc(sizeof(GroundStruct)); // do this later lmao
if (newGv.data.structVal == NULL) { // FIXME
printf("Couldn't allocate memory for GroundStruct copy\n"); newGv.data.structVal = gv->data.structVal;
exit(1);
}
newGv.data.structVal->size = gv->data.structVal->size;
newGv.data.structVal->fields = malloc(gv->data.structVal->size * sizeof(GroundStructField));
if (newGv.data.structVal->fields == NULL && gv->data.structVal->size > 0) {
printf("Couldn't allocate memory for GroundStruct fields copy\n");
exit(1);
}
for (size_t i = 0; i < gv->data.structVal->size; i++) {
strncpy(newGv.data.structVal->fields[i].id,
gv->data.structVal->fields[i].id,
sizeof(newGv.data.structVal->fields[i].id) - 1);
newGv.data.structVal->fields[i].id[sizeof(newGv.data.structVal->fields[i].id) - 1] = '\0';
newGv.data.structVal->fields[i].value = copyGroundValue(&gv->data.structVal->fields[i].value);
}
break; break;
} }
case CUSTOM: { case CUSTOM:
newGv.data.customVal = malloc(sizeof(GroundObject)); // also do this later
if (newGv.data.customVal == NULL) { // FIXME
printf("Couldn't allocate memory for GroundObject copy\n"); newGv.data.customVal = gv->data.customVal;
exit(1);
}
newGv.data.customVal->fields = NULL;
// Deep copy the struct definition as well
newGv.customType = malloc(sizeof(GroundStruct));
newGv.customType->size = gv->customType->size;
newGv.customType->fields = malloc(gv->customType->size * sizeof(GroundStructField));
for (size_t i = 0; i < gv->customType->size; i++) {
strncpy(newGv.customType->fields[i].id, gv->customType->fields[i].id, 64);
newGv.customType->fields[i].value = copyGroundValue(&gv->customType->fields[i].value);
}
GroundObjectField *field, *tmp;
HASH_ITER(hh, gv->data.customVal->fields, field, tmp) {
GroundObjectField* newField = malloc(sizeof(GroundObjectField));
if (newField == NULL) {
printf("Couldn't allocate memory for GroundObjectField copy\n");
exit(1);
}
strncpy(newField->id, field->id, sizeof(newField->id) - 1);
newField->id[sizeof(newField->id) - 1] = '\0';
newField->value = copyGroundValue(&field->value);
HASH_ADD_STR(newGv.data.customVal->fields, id, newField);
}
break; break;
}
case NONE: case NONE:
default: { default: {
@@ -210,47 +149,8 @@ void printGroundValue(GroundValue* gv) {
printf("<function>"); printf("<function>");
break; break;
} }
case STRUCTVAL: { default: {
printf("<struct fields: { "); printf("FIXME");
for (size_t i = 0; i < gv->data.structVal->size; i++) {
if (i != 0) {
printf(", ");
}
printf("%s: ", gv->data.structVal->fields[i].id);
if (gv->data.structVal->fields[i].value.type == STRING) {
printf("\"");
printGroundValue(&gv->data.structVal->fields[i].value);
printf("\"");
} else {
printGroundValue(&gv->data.structVal->fields[i].value);
}
}
printf(" }>");
break;
}
case CUSTOM: {
printf("<object fields: { ");
for (size_t i = 0; i < gv->customType->size; i++) {
if (i != 0) {
printf(", ");
}
printf("%s: ", gv->customType->fields[i].id);
GroundObjectField* field = findField(*gv->data.customVal, gv->customType->fields[i].id);
if (field == NULL) {
printf("<missing>");
} else {
printGroundValue(&field->value);
}
}
printf(" }>");
break;
}
case ERROR: {
printf("<error type: %s, what: %s>", gv->data.errorVal.type, gv->data.errorVal.what);
break;
}
case NONE: {
printf("<none>");
break; break;
} }
} }
@@ -275,20 +175,6 @@ void freeGroundValue(GroundValue* gv) {
freeGroundStruct(gstruct); freeGroundStruct(gstruct);
free(gstruct); free(gstruct);
} }
if (gv->type == CUSTOM && gv->data.customVal != NULL) {
freeGroundObject(gv->data.customVal);
free(gv->data.customVal);
gv->data.customVal = NULL;
if (gv->customType != NULL) {
for (size_t i = 0; i < gv->customType->size; i++) {
freeGroundValue(&gv->customType->fields[i].value);
}
free(gv->customType->fields);
free(gv->customType);
gv->customType = NULL;
}
}
if (gv->type == ERROR) { if (gv->type == ERROR) {
GroundError* error = &gv->data.errorVal; GroundError* error = &gv->data.errorVal;
if (error->type != NULL) { if (error->type != NULL) {
@@ -488,12 +374,6 @@ void printGroundInstruction(GroundInstruction* gi) {
case STOD: case STOD:
printf("stod"); printf("stod");
break; break;
case CTOI:
printf("ctoi");
break;
case ITOC:
printf("itoc");
break;
case TOSTRING: case TOSTRING:
printf("tostring"); printf("tostring");
break; break;
@@ -541,7 +421,6 @@ void printGroundInstruction(GroundInstruction* gi) {
} }
if (gi->type != CREATELABEL) printf(" "); if (gi->type != CREATELABEL) printf(" ");
for (size_t i = 0; i < gi->args.length; i++) { for (size_t i = 0; i < gi->args.length; i++) {
if (i != 0) printf(" ");
if (gi->args.args[i].type == VALUE && gi->args.args[i].value.value.type == STRING) { if (gi->args.args[i].type == VALUE && gi->args.args[i].value.value.type == STRING) {
printf("\""); printf("\"");
printGroundArg(&gi->args.args[i]); printGroundArg(&gi->args.args[i]);
@@ -549,6 +428,7 @@ void printGroundInstruction(GroundInstruction* gi) {
} else { } else {
printGroundArg(&gi->args.args[i]); printGroundArg(&gi->args.args[i]);
} }
printf(" ");
} }
} }
@@ -561,12 +441,12 @@ List createList() {
void appendToList(List* list, GroundValue value) { void appendToList(List* list, GroundValue value) {
if (list == NULL) { if (list == NULL) {
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n"); printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
GroundValue* ptr = realloc(list->values, (list->size + 1) * sizeof(GroundValue)); GroundValue* ptr = realloc(list->values, (list->size + 1) * sizeof(GroundValue));
if (ptr == NULL) { if (ptr == NULL) {
printf("There was an error allocating memory for a list.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n"); printf("There was an error allocating memory for a list.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
list->size++; list->size++;
@@ -576,7 +456,7 @@ void appendToList(List* list, GroundValue value) {
ListAccess getListAt(List* list, size_t idx) { ListAccess getListAt(List* list, size_t idx) {
if (list == NULL) { if (list == NULL) {
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n"); printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (idx < list->size) { if (idx < list->size) {
@@ -594,7 +474,7 @@ ListAccess getListAt(List* list, size_t idx) {
ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) { ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) {
if (list == NULL) { if (list == NULL) {
printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/ground\n"); printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (idx < list->size) { if (idx < list->size) {
@@ -745,24 +625,3 @@ bool checkTypes(GroundValue* left, GroundValue* right) {
} }
return true; return true;
} }
GroundScope copyGroundScope(GroundScope* scope) {
GroundScope newScope = {
.labels = malloc(sizeof(GroundLabel*)),
.variables = malloc(sizeof(GroundVariable*)),
.isMainScope = false
};
*newScope.variables = NULL;
*newScope.labels = NULL;
if (scope == NULL) {
printf("oh no scope is null\n");
}
GroundVariable *var, *tmp = NULL;
HASH_ITER(hh, *scope->variables, var, tmp) {
addVariable(newScope.variables, var->id, var->value);
}
return newScope;
}

View File

@@ -8,28 +8,8 @@
#include <string.h> #include <string.h>
#include "include/uthash.h" #include "include/uthash.h"
#define MAX_ID_LEN 64
// If targeting WASM, define WASM specific stuff
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
void wasm_print(const char* str);
#undef printf
#define printf(fmt, ...) do { \
int __needed = snprintf(NULL, 0, fmt, ##__VA_ARGS__) + 1; \
char* __buf = malloc(__needed); \
if (__buf) { \
snprintf(__buf, __needed, fmt, ##__VA_ARGS__); \
wasm_print(__buf); \
free(__buf); \
} \
} while(0)
#endif
typedef enum GroundInstType { typedef enum GroundInstType {
IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD
} GroundInstType; } GroundInstType;
typedef enum GroundValueType { typedef enum GroundValueType {
@@ -83,7 +63,7 @@ typedef struct GroundError {
typedef struct GroundValue { typedef struct GroundValue {
GroundValueType type; GroundValueType type;
struct GroundStruct* customType; struct GroundStruct* customType;
union { struct {
int64_t intVal; int64_t intVal;
double doubleVal; double doubleVal;
char* stringVal; char* stringVal;
@@ -97,24 +77,6 @@ typedef struct GroundValue {
} data; } data;
} GroundValue; } GroundValue;
typedef struct GroundLabel {
char id[MAX_ID_LEN];
int lineNum;
UT_hash_handle hh;
} GroundLabel;
typedef struct GroundVariable {
char id[MAX_ID_LEN];
GroundValue value;
UT_hash_handle hh;
} GroundVariable;
typedef struct GroundScope {
GroundLabel** labels;
GroundVariable** variables;
bool isMainScope;
} GroundScope;
/* /*
* Indicates status when accessing a list. * Indicates status when accessing a list.
* Associated functions: * Associated functions:
@@ -183,7 +145,6 @@ typedef struct GroundFunction {
GroundProgram program; GroundProgram program;
size_t startLine; size_t startLine;
bool isNative; bool isNative;
GroundScope closure;
NativeGroundFunction nativeFn; NativeGroundFunction nativeFn;
} GroundFunction; } GroundFunction;
@@ -323,10 +284,5 @@ bool checkFnTypes(GroundValue* left, GroundFunctionArgs* arg);
// Compares types of two values. // Compares types of two values.
bool checkTypes(GroundValue* left, GroundValue* right); bool checkTypes(GroundValue* left, GroundValue* right);
// Adds variable to GroundScope
void addVariable(GroundVariable **head, const char *id, GroundValue data);
// Deep copies a GroundScope
GroundScope copyGroundScope(GroundScope* scope);
#endif #endif

View File

@@ -1,15 +0,0 @@
set &x 5
fun !dingle -function -int &a
fun !capture -int -int &b
add $a $b &tmp
add $tmp $x &tmp
return $tmp
endfun
return $capture
endfun
set &x 10
call !dingle 3 &result
call !result 5 &y
println $y $x

View File

@@ -1,11 +1,3 @@
tostring 32 &str tostring 32 &int
stoi "12" &int
stod "3.14" &dou
itoc 353 &chr
ctoi 'a' &it2
println $str
println $int println $int
println $dou
println $chr
println $it2

View File

@@ -1,4 +1,4 @@
set &x "dingus" set &x "dingus"
println $x PAUSE
drop &x drop &x
println $x PAUSE

View File

@@ -1,4 +0,0 @@
fun !lib_PrintHello -int
println "Hello"
return 0
endfun

View File

@@ -1,6 +1,5 @@
# A cool list # A cool list
setlist &favWords "hello" "there" "general" setlist &favWords "hello" "there" "general" "kenobi"
listappend &favWords "kenobi"
println $favWords println $favWords
set &count 0 set &count 0

View File

@@ -1,5 +0,0 @@
[hello, there, general, kenobi][hello, there, general, kenobi][hello, there, general, hello]
hello
there
general
Nqb˜V

11
tests/pause.grnd Normal file
View File

@@ -0,0 +1,11 @@
fun !dingle -int
endfun
set &x 5
set &y "dingle"
PAUSE
println "continuing"
println "step through here"
println "step again"
println "and again"

View File

@@ -18,7 +18,7 @@ fun !fib -int -int &n -function &fib
endfun endfun
# Main program # Main program
println "Computing fib(20) recursively..." println "Computing fib(30) recursively..."
call !fib 20 $fib &answer call !fib 30 $fib &answer
println "Result:" $answer println "Result:" $answer
end end

View File

@@ -2,10 +2,10 @@ input &str
getstrsize $str &size getstrsize $str &size
set &idx 0 set &idx 0
@loop @loop
getstrcharat $str $idx &char getstrcharat $str $idx &char
println $char println $char
add 1 $idx &idx add 1 $idx &idx
equal $idx $size &cond equal $idx $size &cond
if $cond %loopend if $cond %loopend
jump %loop jump %loop
@loopend @loopend

View File

@@ -1,80 +0,0 @@
#!/usr/bin/env bash
echo "" > log.txt
for f in *.grnd; do
[ -e "$f" ] || continue # skip if no files match
# Files to skip over
if [[ "$f" == "lib.grnd" ]] ||
[[ "$f" == "string.grnd" ]] ||
[[ "$f" == "test.grnd" ]] ||
[[ "$f" == "to1000.grnd" ]] ||
[[ "$f" == "uhoh.grnd" ]];
then continue
fi
echo "Running $f"
ground "$f" > log.txt
FILE="log.txt"
FAILED="\033[31mFailed:\n\033[0m"
if [[ "$f" == "closure.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "13 10\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "convs.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "32\n12\n3.140000\na\n97\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "drop.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "dingus\nGround runtime error:\n ErrorType: UnknownVariable\n ErrorInstruction: println \$x\n ErrorLine: 4\n"));
then printf "\033[31mFailed\n\033[0m"
exit 1
fi
elif [[ "$f" == "error.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Ground runtime error:\n ErrorType: Hello\n ErrorContext: [1, 2, 3, Hi!]\n ErrorInstruction: error \"Hello\" [1, 2, 3, Hi!] 1\n ErrorLine: 2\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "fib.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Fibonacci result: 7540113804746346429\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "function.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "dingle\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "list.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "[hello, there, general, kenobi]\nhello\nthere\ngeneral\nkenobi\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "recursivefib.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Computing fib(20) recursively...\nResult: 6765\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "simple.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "dingus\ndinglefart\n5.840000\n464773025\n5164.120000\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "struct.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "53\n32\n"));
then printf $FAILED
exit 1
fi
elif [[ "$f" == "use.grnd" ]]; then
if !(cmp -s "$FILE" <(printf "Hello\n"));
then printf $FAILED
exit 1
fi
else
printf "\033[31mCould not find test case\n\033[0m"
exit 1
fi
done
printf "\033[32mAll tests passed!\n\033[0m"
exit 0

View File

@@ -1,2 +0,0 @@
use "lib"
call !lib_PrintHello &tmp