Compare commits

...

3 Commits
v0.3.0 ... main

Author SHA1 Message Date
Talles Amadeu 4bb15c84dd add support to read json files 2025-12-09 15:36:06 -03:00
Talles Amadeu 31d7ac51ed add support do i++ and >= and <= 2025-12-09 14:37:28 -03:00
Talles Amadeu 05b3ff6e1e attempt to fix linux install 2025-12-09 14:17:40 -03:00
21 changed files with 593 additions and 37 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -11,6 +11,7 @@ void SetPropExpr::accept(ASTVisitor& visitor) { visitor.visit(*this); }
void ArrayExpr::accept(ASTVisitor& visitor) { visitor.visit(*this); }
void IndexExpr::accept(ASTVisitor& visitor) { visitor.visit(*this); }
void ArrayAssignExpr::accept(ASTVisitor& visitor) { visitor.visit(*this); }
void IncrementExpr::accept(ASTVisitor& visitor) { visitor.visit(*this); }
void BinaryExpr::accept(ASTVisitor& visitor) { visitor.visit(*this); }
void ReturnStmt::accept(ASTVisitor& visitor) { visitor.visit(*this); }
void VarDeclStmt::accept(ASTVisitor& visitor) { visitor.visit(*this); }

View File

@ -108,6 +108,13 @@ public:
void accept(ASTVisitor& visitor) override;
};
class IncrementExpr : public Expr {
public:
std::unique_ptr<Expr> variable; // Can be VariableExpr, GetPropExpr, IndexExpr
IncrementExpr(std::unique_ptr<Expr> variable) : variable(std::move(variable)) {}
void accept(ASTVisitor& visitor) override;
};
class BinaryExpr : public Expr {
public:
std::unique_ptr<Expr> left;
@ -239,6 +246,7 @@ public:
virtual void visit(ArrayExpr& node) = 0;
virtual void visit(IndexExpr& node) = 0;
virtual void visit(ArrayAssignExpr& node) = 0;
virtual void visit(IncrementExpr& node) = 0;
virtual void visit(BinaryExpr& node) = 0;
virtual void visit(ReturnStmt& node) = 0;
virtual void visit(VarDeclStmt& node) = 0;

View File

@ -130,3 +130,60 @@ void ASTPrinter::visit(ClassDef& node) {
}
std::cout << "}";
}
void ASTPrinter::visit(ArrayExpr& node) {
std::cout << "[";
for (size_t i = 0; i < node.elements.size(); ++i) {
node.elements[i]->accept(*this);
if (i < node.elements.size() - 1) std::cout << ", ";
}
std::cout << "]";
}
void ASTPrinter::visit(IndexExpr& node) {
node.array->accept(*this);
std::cout << "[";
node.index->accept(*this);
std::cout << "]";
}
void ASTPrinter::visit(ArrayAssignExpr& node) {
node.array->accept(*this);
std::cout << "[";
node.index->accept(*this);
std::cout << "] = ";
node.value->accept(*this);
}
void ASTPrinter::visit(IncrementExpr& node) {
node.variable->accept(*this);
std::cout << "++";
}
void ASTPrinter::visit(ForInStmt& node) {
std::cout << "for (var " << node.variableName << " in ";
node.collection->accept(*this);
std::cout << ") ";
node.body->accept(*this);
}
void ASTPrinter::visit(SwitchStmt& node) {
std::cout << "switch (";
node.condition->accept(*this);
std::cout << ") {" << std::endl;
for (const auto& c : node.cases) {
std::cout << "case ";
c.value->accept(*this);
std::cout << ": ";
c.body->accept(*this);
}
if (node.defaultCase) {
std::cout << "default: ";
node.defaultCase->accept(*this);
}
std::cout << "}";
}
void ASTPrinter::visit(BreakStmt& node) {
std::cout << "break;";
}

View File

@ -16,6 +16,10 @@ public:
void visit(NewExpr& node) override;
void visit(GetPropExpr& node) override;
void visit(SetPropExpr& node) override;
void visit(ArrayExpr& node) override;
void visit(IndexExpr& node) override;
void visit(ArrayAssignExpr& node) override;
void visit(IncrementExpr& node) override;
void visit(BinaryExpr& node) override;
void visit(ReturnStmt& node) override;
void visit(VarDeclStmt& node) override;
@ -23,6 +27,9 @@ public:
void visit(IfStmt& node) override;
void visit(WhileStmt& node) override;
void visit(ForStmt& node) override;
void visit(ForInStmt& node) override;
void visit(SwitchStmt& node) override;
void visit(BreakStmt& node) override;
void visit(ExpressionStmt& node) override;
void visit(FunctionDef& node) override;
void visit(ClassDef& node) override;

View File

@ -1,5 +1,6 @@
#include "codegen.h"
#include <iostream>
#include <typeinfo>
CodeGen::CodeGen() {
context = std::make_unique<llvm::LLVMContext>();
@ -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");
@ -248,6 +313,95 @@ void CodeGen::visit(CallExpr& node) {
lastClassName = "";
}
void CodeGen::visit(IncrementExpr& node) {
if (auto varExpr = dynamic_cast<VariableExpr*>(node.variable.get())) {
if (namedValues.find(varExpr->name) == namedValues.end()) {
std::cerr << "Unknown variable: " << varExpr->name << std::endl;
lastValue = nullptr;
return;
}
llvm::AllocaInst* alloca = namedValues[varExpr->name];
llvm::Value* val = builder->CreateLoad(alloca->getAllocatedType(), alloca, varExpr->name.c_str());
lastValue = val; // Return old value
llvm::Value* inc = builder->CreateAdd(val, llvm::ConstantInt::get(*context, llvm::APInt(32, 1)), "inc");
builder->CreateStore(inc, alloca);
} else if (auto getProp = dynamic_cast<GetPropExpr*>(node.variable.get())) {
getProp->object->accept(*this);
llvm::Value* objectPtr = lastValue;
std::string className = lastClassName;
if (!objectPtr) return;
if (classFields.find(className) == classFields.end()) {
std::cerr << "Unknown class type: " << className << std::endl;
lastValue = nullptr;
return;
}
const auto& fields = classFields[className];
int fieldIndex = -1;
for (size_t i = 0; i < fields.size(); ++i) {
if (fields[i] == getProp->name) {
fieldIndex = i;
break;
}
}
if (fieldIndex == -1) {
std::cerr << "Unknown field: " << getProp->name << " in class " << className << std::endl;
lastValue = nullptr;
return;
}
std::vector<llvm::Value*> indices;
indices.push_back(llvm::ConstantInt::get(*context, llvm::APInt(32, 0)));
indices.push_back(llvm::ConstantInt::get(*context, llvm::APInt(32, fieldIndex)));
llvm::Type* structType = classStructs[className];
llvm::Value* fieldPtr = builder->CreateGEP(structType, objectPtr, indices, "fieldptr");
llvm::Value* val = builder->CreateLoad(llvm::Type::getInt32Ty(*context), fieldPtr, "fieldval");
lastValue = val;
llvm::Value* inc = builder->CreateAdd(val, llvm::ConstantInt::get(*context, llvm::APInt(32, 1)), "inc");
builder->CreateStore(inc, fieldPtr);
} else if (auto indexExpr = dynamic_cast<IndexExpr*>(node.variable.get())) {
indexExpr->array->accept(*this);
llvm::Value* arrPtr = lastValue;
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");
llvm::Value* valPtr = builder->CreateCall(getFn, {arrPtr, indexVal}, "elem");
// Assume IntArray for increment
llvm::Value* val = builder->CreatePtrToInt(valPtr, llvm::Type::getInt32Ty(*context), "elemInt");
lastValue = val;
llvm::Value* inc = builder->CreateAdd(val, llvm::ConstantInt::get(*context, llvm::APInt(32, 1)), "inc");
// Store back
llvm::Value* voidVal = builder->CreateIntToPtr(inc, llvm::PointerType::get(llvm::Type::getInt8Ty(*context), 0));
builder->CreateCall(setFn, {arrPtr, indexVal, voidVal});
} else {
std::cerr << "Invalid increment target." << std::endl;
lastValue = nullptr;
}
}
void CodeGen::visit(BinaryExpr& node) {
node.left->accept(*this);
llvm::Value* L = lastValue;
@ -311,6 +465,12 @@ void CodeGen::visit(BinaryExpr& node) {
} else if (node.op == ">") {
lastValue = builder->CreateICmpSGT(L, R, "cmptmp");
lastValue = builder->CreateIntCast(lastValue, llvm::Type::getInt32Ty(*context), true, "booltmp");
} else if (node.op == "<=") {
lastValue = builder->CreateICmpSLE(L, R, "cmptmp");
lastValue = builder->CreateIntCast(lastValue, llvm::Type::getInt32Ty(*context), true, "booltmp");
} else if (node.op == ">=") {
lastValue = builder->CreateICmpSGE(L, R, "cmptmp");
lastValue = builder->CreateIntCast(lastValue, llvm::Type::getInt32Ty(*context), true, "booltmp");
} else if (node.op == "==") {
lastValue = builder->CreateICmpEQ(L, R, "cmptmp");
lastValue = builder->CreateIntCast(lastValue, llvm::Type::getInt32Ty(*context), true, "booltmp");
@ -370,8 +530,8 @@ void CodeGen::visit(IfStmt& node) {
llvm::Function* theFunction = builder->GetInsertBlock()->getParent();
llvm::BasicBlock* thenBB = llvm::BasicBlock::Create(*context, "then", theFunction);
llvm::BasicBlock* elseBB = llvm::BasicBlock::Create(*context, "else");
llvm::BasicBlock* mergeBB = llvm::BasicBlock::Create(*context, "ifcont");
llvm::BasicBlock* elseBB = llvm::BasicBlock::Create(*context, "else", theFunction);
llvm::BasicBlock* mergeBB = llvm::BasicBlock::Create(*context, "ifcont", theFunction);
builder->CreateCondBr(condV, thenBB, elseBB);
@ -385,7 +545,6 @@ void CodeGen::visit(IfStmt& node) {
}
// Emit else block.
theFunction->insert(theFunction->end(), elseBB);
builder->SetInsertPoint(elseBB);
if (node.elseBranch) {
node.elseBranch->accept(*this);
@ -396,7 +555,6 @@ void CodeGen::visit(IfStmt& node) {
}
// Emit merge block.
theFunction->insert(theFunction->end(), mergeBB);
builder->SetInsertPoint(mergeBB);
}
@ -404,8 +562,8 @@ void CodeGen::visit(WhileStmt& node) {
llvm::Function* theFunction = builder->GetInsertBlock()->getParent();
llvm::BasicBlock* condBB = llvm::BasicBlock::Create(*context, "loopcond", theFunction);
llvm::BasicBlock* bodyBB = llvm::BasicBlock::Create(*context, "loopbody");
llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(*context, "loopafter");
llvm::BasicBlock* bodyBB = llvm::BasicBlock::Create(*context, "loopbody", theFunction);
llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(*context, "loopafter", theFunction);
// Jump to condition
builder->CreateBr(condBB);
@ -420,7 +578,6 @@ void CodeGen::visit(WhileStmt& node) {
builder->CreateCondBr(condV, bodyBB, afterBB);
// Body Block
theFunction->insert(theFunction->end(), bodyBB);
builder->SetInsertPoint(bodyBB);
breakStack.push_back(afterBB);
@ -430,7 +587,6 @@ void CodeGen::visit(WhileStmt& node) {
builder->CreateBr(condBB); // Loop back to condition
// After Block
theFunction->insert(theFunction->end(), afterBB);
builder->SetInsertPoint(afterBB);
}
@ -443,8 +599,8 @@ void CodeGen::visit(ForStmt& node) {
}
llvm::BasicBlock* condBB = llvm::BasicBlock::Create(*context, "loopcond", theFunction);
llvm::BasicBlock* bodyBB = llvm::BasicBlock::Create(*context, "loopbody");
llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(*context, "loopafter");
llvm::BasicBlock* bodyBB = llvm::BasicBlock::Create(*context, "loopbody", theFunction);
llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(*context, "loopafter", theFunction);
// Jump to condition
builder->CreateBr(condBB);
@ -466,7 +622,6 @@ void CodeGen::visit(ForStmt& node) {
builder->CreateCondBr(condV, bodyBB, afterBB);
// Body Block
theFunction->insert(theFunction->end(), bodyBB);
builder->SetInsertPoint(bodyBB);
breakStack.push_back(afterBB);
@ -481,7 +636,6 @@ void CodeGen::visit(ForStmt& node) {
builder->CreateBr(condBB); // Loop back to condition
// After Block
theFunction->insert(theFunction->end(), afterBB);
builder->SetInsertPoint(afterBB);
}
@ -504,8 +658,8 @@ void CodeGen::visit(ForInStmt& node) {
// 4. Create Loop Blocks
llvm::BasicBlock* condBB = llvm::BasicBlock::Create(*context, "loopcond", theFunction);
llvm::BasicBlock* bodyBB = llvm::BasicBlock::Create(*context, "loopbody");
llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(*context, "loopafter");
llvm::BasicBlock* bodyBB = llvm::BasicBlock::Create(*context, "loopbody", theFunction);
llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(*context, "loopafter", theFunction);
builder->CreateBr(condBB);
@ -516,7 +670,6 @@ void CodeGen::visit(ForInStmt& node) {
builder->CreateCondBr(condV, bodyBB, afterBB);
// 6. Body
theFunction->insert(theFunction->end(), bodyBB);
builder->SetInsertPoint(bodyBB);
// Fetch element at index
@ -568,7 +721,6 @@ void CodeGen::visit(ForInStmt& node) {
builder->CreateBr(condBB);
// After Block
theFunction->insert(theFunction->end(), afterBB);
builder->SetInsertPoint(afterBB);
}
@ -788,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");
@ -800,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");
@ -840,18 +1029,18 @@ void CodeGen::visit(SwitchStmt& node) {
llvm::Function* theFunction = builder->GetInsertBlock()->getParent();
// 2. Create Merge Block (after switch)
llvm::BasicBlock* mergeBB = llvm::BasicBlock::Create(*context, "switchmerge");
llvm::BasicBlock* mergeBB = llvm::BasicBlock::Create(*context, "switchmerge", theFunction);
// Push mergeBB to breakStack so 'break' jumps here
breakStack.push_back(mergeBB);
// 3. Create Default Block
llvm::BasicBlock* defaultBB = llvm::BasicBlock::Create(*context, "switchdefault");
llvm::BasicBlock* defaultBB = llvm::BasicBlock::Create(*context, "switchdefault", theFunction);
// 4. Create Blocks for Cases
std::vector<llvm::BasicBlock*> caseBBs;
for (size_t i = 0; i < node.cases.size(); ++i) {
caseBBs.push_back(llvm::BasicBlock::Create(*context, "case" + std::to_string(i)));
caseBBs.push_back(llvm::BasicBlock::Create(*context, "case" + std::to_string(i), theFunction));
}
// 5. Generate Comparisons (If-Else Chain)
@ -886,7 +1075,6 @@ void CodeGen::visit(SwitchStmt& node) {
// 6. Generate Case Bodies
for (size_t i = 0; i < node.cases.size(); ++i) {
theFunction->insert(theFunction->end(), caseBBs[i]);
builder->SetInsertPoint(caseBBs[i]);
node.cases[i].body->accept(*this);
@ -902,7 +1090,6 @@ void CodeGen::visit(SwitchStmt& node) {
}
// 7. Generate Default Body
theFunction->insert(theFunction->end(), defaultBB);
builder->SetInsertPoint(defaultBB);
if (node.defaultCase) {
node.defaultCase->accept(*this);
@ -914,7 +1101,6 @@ void CodeGen::visit(SwitchStmt& node) {
// 8. Finish
breakStack.pop_back();
theFunction->insert(theFunction->end(), mergeBB);
builder->SetInsertPoint(mergeBB);
}
@ -927,7 +1113,6 @@ void CodeGen::visit(BreakStmt& node) {
builder->CreateBr(breakStack.back());
llvm::Function* theFunction = builder->GetInsertBlock()->getParent();
llvm::BasicBlock* deadBB = llvm::BasicBlock::Create(*context, "dead");
theFunction->insert(theFunction->end(), deadBB);
llvm::BasicBlock* deadBB = llvm::BasicBlock::Create(*context, "dead", theFunction);
builder->SetInsertPoint(deadBB);
}

View File

@ -28,6 +28,7 @@ public:
void visit(ArrayExpr& node) override;
void visit(IndexExpr& node) override;
void visit(ArrayAssignExpr& node) override;
void visit(IncrementExpr& node) override;
void visit(BinaryExpr& node) override;
void visit(ReturnStmt& node) override;
void visit(VarDeclStmt& node) override;

View File

@ -107,7 +107,12 @@ Token Lexer::scanToken() {
case '.': return makeToken(TokenType::DOT, ".");
case ':': return makeToken(TokenType::COLON, ":");
case ';': return makeToken(TokenType::SEMICOLON, ";");
case '+': return makeToken(TokenType::PLUS, "+");
case '+':
if (peek() == '+') {
advance();
return makeToken(TokenType::PLUS_PLUS, "++");
}
return makeToken(TokenType::PLUS, "+");
case '-': return makeToken(TokenType::MINUS, "-");
case '*': return makeToken(TokenType::STAR, "*");
case '/':
@ -122,8 +127,18 @@ Token Lexer::scanToken() {
return makeToken(TokenType::EQUAL_EQUAL, "==");
}
return makeToken(TokenType::EQUALS, "=");
case '<': return makeToken(TokenType::LESS, "<");
case '>': return makeToken(TokenType::GREATER, ">");
case '<':
if (peek() == '=') {
advance();
return makeToken(TokenType::LESS_EQUAL, "<=");
}
return makeToken(TokenType::LESS, "<");
case '>':
if (peek() == '=') {
advance();
return makeToken(TokenType::GREATER_EQUAL, ">=");
}
return makeToken(TokenType::GREATER, ">");
case '"': return string();
default: return makeToken(TokenType::UNKNOWN, std::string(1, c));
}

View File

@ -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; i<size; i++) a->data[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) {

View File

@ -366,7 +366,7 @@ std::unique_ptr<Expr> Parser::equality() {
std::unique_ptr<Expr> Parser::comparison() {
std::unique_ptr<Expr> expr = term();
while (match(TokenType::LESS) || match(TokenType::GREATER)) {
while (match(TokenType::LESS) || match(TokenType::GREATER) || match(TokenType::LESS_EQUAL) || match(TokenType::GREATER_EQUAL)) {
std::string op = previous().value;
std::unique_ptr<Expr> right = term();
expr = std::make_unique<BinaryExpr>(std::move(expr), op, std::move(right));
@ -410,6 +410,8 @@ std::unique_ptr<Expr> Parser::call() {
std::unique_ptr<Expr> index = expression();
consume(TokenType::RBRACKET, "Expect ']' after index.");
expr = std::make_unique<IndexExpr>(std::move(expr), std::move(index));
} else if (match(TokenType::PLUS_PLUS)) {
expr = std::make_unique<IncrementExpr>(std::move(expr));
} else {
break;
}

View File

@ -41,6 +41,9 @@ enum class TokenType {
EQUAL_EQUAL,// ==
LESS, // <
GREATER, // >
LESS_EQUAL, // <=
GREATER_EQUAL, // >=
PLUS_PLUS, // ++
END_OF_FILE,
UNKNOWN
};

View File

@ -1,6 +1,6 @@
#ifndef SUN_VERSION_H
#define SUN_VERSION_H
#define SUN_VERSION "0.3.0"
#define SUN_VERSION "0.5.0"
#endif // SUN_VERSION_H

BIN
sun

Binary file not shown.

9
tests/test.json Normal file
View File

@ -0,0 +1,9 @@
{
"name": "SunLang",
"version": 1,
"features": ["csv", "json"],
"nested": {
"key": "value"
},
"list": [10, 20, 30]
}

View File

@ -1,7 +1,4 @@
function main() {
print("Counting:");
for (var i = 0; i < 5; i = i + 1) {
print(i);
}
return 0;
print("Counting:");
for (var i = 0; i <= 5; i++) {
print(i);
}

15
tests/test_json.sun Normal file
View File

@ -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;
}