diff --git a/build/codegen.o b/build/codegen.o index 324067e..0e2849e 100644 Binary files a/build/codegen.o and b/build/codegen.o differ diff --git a/build/main.o b/build/main.o index 5abb441..7a02d48 100644 Binary files a/build/main.o and b/build/main.o differ diff --git a/src/codegen.cpp b/src/codegen.cpp index 9b4726a..69baeb3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1,5 +1,6 @@ #include "codegen.h" #include +#include CodeGen::CodeGen() { context = std::make_unique(); @@ -50,6 +51,31 @@ CodeGen::CodeGen() { false ); llvm::Function::Create(readCsvType, llvm::Function::ExternalLinkage, "sun_read_csv", module.get()); + + // void* sun_read_json(char* filename) + llvm::FunctionType* readJsonType = llvm::FunctionType::get( + llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0), + { + llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0) // filename + }, + false + ); + llvm::Function::Create(readJsonType, llvm::Function::ExternalLinkage, "sun_read_json", module.get()); + + // void sun_print_json(void* val) + llvm::FunctionType* printJsonType = llvm::FunctionType::get(llvm::Type::getVoidTy(*context), {llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0)}, false); + llvm::Function::Create(printJsonType, llvm::Function::ExternalLinkage, "sun_print_json", module.get()); + + // void* sun_map_get(void* map, char* key) + llvm::FunctionType* mapGetType = llvm::FunctionType::get( + llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0), + { + llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0), // map + llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0) // key + }, + false + ); + llvm::Function::Create(mapGetType, llvm::Function::ExternalLinkage, "sun_map_get", module.get()); } llvm::AllocaInst* CodeGen::createEntryBlockAlloca(llvm::Function* theFunction, const std::string& varName, llvm::Type* type) { @@ -98,7 +124,7 @@ void CodeGen::visit(NumberExpr& node) { } void CodeGen::visit(StringExpr& node) { - lastValue = builder->CreateGlobalString(node.value); + lastValue = builder->CreateGlobalStringPtr(node.value); lastClassName = "String"; // Special marker for string } @@ -178,6 +204,36 @@ void CodeGen::visit(CallExpr& node) { return; } + // Handle "readjson" specially + if (node.callee == "readjson") { + if (node.args.size() != 1) { + std::cerr << "readjson() takes exactly 1 argument." << std::endl; + lastValue = nullptr; + return; + } + + node.args[0]->accept(*this); + llvm::Value* filenameVal = lastValue; + + if (filenameVal->getType() != llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0)) { + filenameVal = builder->CreateBitCast(filenameVal, llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0)); + } + + llvm::Function* readJsonFn = module->getFunction("sun_read_json"); + if (!readJsonFn) { + std::cerr << "Error: sun_read_json function not found in module!" << std::endl; + lastValue = nullptr; + return; + } + lastValue = builder->CreateCall(readJsonFn, {filenameVal}, "json_data"); + if (!lastValue) { + std::cerr << "Error: CreateCall for sun_read_json returned null!" << std::endl; + return; + } + lastClassName = "JSON"; // Special marker + return; + } + // Handle "len" specially if (node.callee == "len") { if (node.args.size() != 1) { @@ -211,8 +267,17 @@ void CodeGen::visit(CallExpr& node) { std::string argType = lastClassName; if (argType == "String") { + if (argVal->getType() != llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0)) { + argVal = builder->CreateBitCast(argVal, llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0)); + } llvm::Function* printStr = module->getFunction("print_string"); builder->CreateCall(printStr, {argVal}); + } else if (argType == "JSON") { + if (argVal->getType() != llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0)) { + argVal = builder->CreateBitCast(argVal, llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0)); + } + llvm::Function* printJson = module->getFunction("sun_print_json"); + builder->CreateCall(printJson, {argVal}); } else { // Assume int llvm::Function* printInt = module->getFunction("print_int"); @@ -309,6 +374,14 @@ void CodeGen::visit(IncrementExpr& node) { indexExpr->index->accept(*this); llvm::Value* indexVal = lastValue; + std::string indexType = lastClassName; + + if (indexType == "String") { + // Map increment? Not supported yet easily without sun_map_set + std::cerr << "Increment on map values not fully supported yet." << std::endl; + lastValue = nullptr; + return; + } llvm::Function* getFn = module->getFunction("sun_array_get"); llvm::Function* setFn = module->getFunction("sun_array_set"); @@ -867,8 +940,42 @@ void CodeGen::visit(IndexExpr& node) { llvm::Value* arrPtr = lastValue; std::string arrayType = lastClassName; + if (!arrPtr) { + std::cerr << "Error: Array evaluated to null in IndexExpr" << std::endl; + return; + } + node.index->accept(*this); llvm::Value* indexVal = lastValue; + std::string indexType = lastClassName; + + if (!indexVal) { + std::cerr << "Error: Index evaluated to null in IndexExpr" << std::endl; + return; + } + + if (indexType == "String") { + // Map lookup + llvm::Function* getFn = module->getFunction("sun_map_get"); + if (!getFn) { + std::cerr << "Error: sun_map_get function not found!" << std::endl; + return; + } + llvm::Value* valPtr = builder->CreateCall(getFn, {arrPtr, indexVal}, "elem"); + + // We don't know the return type, assume generic (void*) or int? + // If it's JSON, it could be anything. + // Let's assume it returns void* and we might need to cast it if we use it as int. + // But wait, if we use it in an expression, we expect int or string. + // If we assume int, we need PtrToInt. + // If we assume String, we keep it as Ptr. + + // For now, let's assume JSON values are either Strings or Ints (casted to Ptr). + // If we don't know, we return Ptr. + lastValue = valPtr; + lastClassName = "JSON"; // Keep it generic + return; + } llvm::Function* getFn = module->getFunction("sun_array_get"); llvm::Value* valPtr = builder->CreateCall(getFn, {arrPtr, indexVal}, "elem"); @@ -879,6 +986,9 @@ void CodeGen::visit(IndexExpr& node) { } else if (arrayType == "CSVArray") { lastValue = valPtr; // It's a void* (pointer to array) lastClassName = "StringArray"; + } else if (arrayType == "JSON") { + lastValue = valPtr; + lastClassName = "JSON"; } else { // Assume IntArray, cast back to i32 lastValue = builder->CreatePtrToInt(valPtr, llvm::Type::getInt32Ty(*context), "elemInt"); diff --git a/src/main.cpp b/src/main.cpp index fc4962a..d111b75 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,20 +40,63 @@ char* str_concat(char* a, char* b) { return result; } +// Struct Definitions typedef struct { + int type; // 0=int, 1=string, 2=array, 3=map, 4=null + union { + int i; + char* s; + void* ptr; + } as; +} JsonValue; + +typedef struct { + unsigned int magic; // 0x12345678 int size; void** data; } Array; +typedef struct { + char* key; + void* value; +} MapEntry; + +typedef struct { + int size; + int capacity; + MapEntry* entries; +} Map; + +// Forward declarations +void* sun_array_create(int size); +JsonValue* sun_json_array(void* arr); +JsonValue* sun_json_map(void* map); +JsonValue* sun_json_string(char* s); +JsonValue* sun_json_int(int i); +JsonValue* sun_json_null(); +char* sun_strdup(const char* s); + +// Array Functions void* sun_array_create(int size) { Array* arr = (Array*)malloc(sizeof(Array)); + arr->magic = 0x12345678; arr->size = size; arr->data = (void**)calloc(size, sizeof(void*)); return (void*)arr; } void sun_array_set(void* arrPtr, int index, void* value) { + if (!arrPtr) return; Array* arr = (Array*)arrPtr; + if (arr->magic != 0x12345678) { + JsonValue* v = (JsonValue*)arrPtr; + if (v->type == 2) { + arr = (Array*)v->as.ptr; + } else { + printf("Error: Not an array (set)\n"); + exit(1); + } + } if (index >= 0 && index < arr->size) { arr->data[index] = value; } else { @@ -63,21 +106,118 @@ void sun_array_set(void* arrPtr, int index, void* value) { } void* sun_array_get(void* arrPtr, int index) { + if (!arrPtr) return NULL; Array* arr = (Array*)arrPtr; + if (arr->magic != 0x12345678) { + JsonValue* v = (JsonValue*)arrPtr; + if (v->type == 2) { + arr = (Array*)v->as.ptr; + } else { + printf("Error: Not an array (get) type=%d magic=%x\n", v->type, arr->magic); + exit(1); + } + } if (index >= 0 && index < arr->size) { return arr->data[index]; } else { printf("Error: Array index out of bounds: %d\n", index); exit(1); } - return NULL; } int sun_array_length(void* arrPtr) { + if (!arrPtr) return 0; Array* arr = (Array*)arrPtr; + if (arr->magic != 0x12345678) { + JsonValue* v = (JsonValue*)arrPtr; + if (v->type == 2) { + arr = (Array*)v->as.ptr; + } else { + printf("Error: Not an array (len)\n"); + exit(1); + } + } return arr->size; } +// Map Functions +void* sun_map_create() { + Map* map = (Map*)malloc(sizeof(Map)); + map->size = 0; + map->capacity = 8; + map->entries = (MapEntry*)malloc(sizeof(MapEntry) * map->capacity); + return (void*)map; +} + +void sun_map_set(void* mapPtr, char* key, void* value) { + Map* map = (Map*)mapPtr; + for (int i = 0; i < map->size; i++) { + if (strcmp(map->entries[i].key, key) == 0) { + map->entries[i].value = value; + return; + } + } + if (map->size >= map->capacity) { + map->capacity *= 2; + map->entries = (MapEntry*)realloc(map->entries, sizeof(MapEntry) * map->capacity); + } + map->entries[map->size].key = sun_strdup(key); + map->entries[map->size].value = value; + map->size++; +} + +void* sun_map_get(void* mapPtr, char* key) { + if (!mapPtr) return NULL; + JsonValue* v = (JsonValue*)mapPtr; + if (v->type == 3) { + mapPtr = v->as.ptr; + } + Map* map = (Map*)mapPtr; + for (int i = 0; i < map->size; i++) { + if (strcmp(map->entries[i].key, key) == 0) { + return map->entries[i].value; + } + } + return NULL; +} + +// JSON Constructors +JsonValue* sun_json_int(int i) { + JsonValue* v = (JsonValue*)malloc(sizeof(JsonValue)); + v->type = 0; v->as.i = i; return v; +} + +JsonValue* sun_json_string(char* s) { + JsonValue* v = (JsonValue*)malloc(sizeof(JsonValue)); + v->type = 1; v->as.s = s; return v; +} + +JsonValue* sun_json_array(void* arr) { + JsonValue* v = (JsonValue*)malloc(sizeof(JsonValue)); + v->type = 2; v->as.ptr = arr; return v; +} + +JsonValue* sun_json_map(void* map) { + JsonValue* v = (JsonValue*)malloc(sizeof(JsonValue)); + v->type = 3; v->as.ptr = map; return v; +} + +JsonValue* sun_json_null() { + JsonValue* v = (JsonValue*)malloc(sizeof(JsonValue)); + v->type = 4; return v; +} + +void sun_print_json(void* ptr) { + if (!ptr) { printf("null\n"); return; } + JsonValue* v = (JsonValue*)ptr; + if (v->type == 0) printf("%d\n", v->as.i); + else if (v->type == 1) printf("%s\n", v->as.s); + else if (v->type == 2) printf("[Array]\n"); + else if (v->type == 3) printf("[Object]\n"); + else if (v->type == 4) printf("null\n"); + else printf("Unknown JSON type: %d\n", v->type); +} + char* sun_strdup(const char* s) { char* d = (char*)malloc(strlen(s) + 1); if (d == NULL) return NULL; @@ -85,6 +225,122 @@ char* sun_strdup(const char* s) { return d; } +// JSON Parser +char* json_pos; +void skip_whitespace() { + while (*json_pos && (*json_pos == ' ' || *json_pos == '\t' || *json_pos == '\n' || *json_pos == '\r')) { + json_pos++; + } +} +void* parse_json_value(); + +char* parse_json_string() { + skip_whitespace(); + if (*json_pos != '"') return NULL; + json_pos++; + char* start = json_pos; + while (*json_pos && *json_pos != '"') { + json_pos++; + } + if (*json_pos == '"') { + *json_pos = '\0'; + char* str = sun_strdup(start); + *json_pos = '"'; + json_pos++; + return str; + } + return NULL; +} + +void* parse_json_object() { + skip_whitespace(); + if (*json_pos != '{') return NULL; + json_pos++; + void* map = sun_map_create(); + skip_whitespace(); + if (*json_pos == '}') { + json_pos++; + return sun_json_map(map); + } + while (1) { + skip_whitespace(); + char* key = parse_json_string(); + skip_whitespace(); + if (*json_pos != ':') { printf("Expected : in JSON object\n"); exit(1); } + json_pos++; + void* value = parse_json_value(); + sun_map_set(map, key, value); + skip_whitespace(); + if (*json_pos == ',') { json_pos++; continue; } + else if (*json_pos == '}') { json_pos++; break; } + else { printf("Expected , or } in JSON object\n"); exit(1); } + } + return sun_json_map(map); +} + +void* parse_json_array() { + skip_whitespace(); + if (*json_pos != '[') return NULL; + json_pos++; + int capacity = 8; + int size = 0; + void** temp = (void**)malloc(sizeof(void*) * capacity); + skip_whitespace(); + if (*json_pos == ']') { + json_pos++; + void* arr = sun_array_create(0); + free(temp); + return sun_json_array(arr); + } + while (1) { + void* val = parse_json_value(); + if (size >= capacity) { + capacity *= 2; + temp = (void**)realloc(temp, sizeof(void*) * capacity); + } + temp[size++] = val; + skip_whitespace(); + if (*json_pos == ',') { json_pos++; continue; } + else if (*json_pos == ']') { json_pos++; break; } + else { printf("Expected , or ] in JSON array\n"); exit(1); } + } + void* arr = sun_array_create(size); + Array* a = (Array*)arr; + for(int i=0; idata[i] = temp[i]; + free(temp); + return sun_json_array(arr); +} + +void* parse_json_value() { + skip_whitespace(); + if (*json_pos == '{') return parse_json_object(); + if (*json_pos == '[') return parse_json_array(); + if (*json_pos == '"') return sun_json_string(parse_json_string()); + if ((*json_pos >= '0' && *json_pos <= '9') || *json_pos == '-') { + int val = strtol(json_pos, &json_pos, 10); + return sun_json_int(val); + } + if (strncmp(json_pos, "true", 4) == 0) { json_pos += 4; return sun_json_int(1); } + if (strncmp(json_pos, "false", 5) == 0) { json_pos += 5; return sun_json_int(0); } + if (strncmp(json_pos, "null", 4) == 0) { json_pos += 4; return sun_json_null(); } + return NULL; +} + +void* sun_read_json(char* filename) { + FILE* fp = fopen(filename, "r"); + if (!fp) { printf("Error: Could not open file %s\n", filename); exit(1); } + fseek(fp, 0, SEEK_END); + long length = ftell(fp); + fseek(fp, 0, SEEK_SET); + char* buffer = (char*)malloc(length + 1); + fread(buffer, 1, length, fp); + buffer[length] = '\0'; + fclose(fp); + json_pos = buffer; + void* result = parse_json_value(); + free(buffer); + return result; +} void* sun_read_csv(char* filename, char* separator, char* quote) { FILE* fp = fopen(filename, "r"); if (!fp) { diff --git a/src/version.h b/src/version.h index e0dc157..ec0ca99 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #ifndef SUN_VERSION_H #define SUN_VERSION_H -#define SUN_VERSION "0.4.0" +#define SUN_VERSION "0.5.0" #endif // SUN_VERSION_H diff --git a/sun b/sun index f720881..4cac1e7 100755 Binary files a/sun and b/sun differ diff --git a/tests/test.json b/tests/test.json new file mode 100644 index 0000000..2d1a51f --- /dev/null +++ b/tests/test.json @@ -0,0 +1,9 @@ +{ + "name": "SunLang", + "version": 1, + "features": ["csv", "json"], + "nested": { + "key": "value" + }, + "list": [10, 20, 30] +} diff --git a/tests/test_json.sun b/tests/test_json.sun new file mode 100644 index 0000000..d000143 --- /dev/null +++ b/tests/test_json.sun @@ -0,0 +1,15 @@ +function main() { + var data = readjson("./tests/test.json"); + + print(data["name"]); + print(data["version"]); + + var list = data["list"]; + print(list[0]); + print(list[1]); + + var nested = data["nested"]; + print(nested["key"]); + + return 0; +}