Skip to content

Commit c3c5536

Browse files
committed
feat: Implement core object model, compiler stages (lexer, parser, AST, bytecode), VM, GC, and FFI.
1 parent d33bc32 commit c3c5536

15 files changed

Lines changed: 440 additions & 5 deletions

File tree

CMakeLists.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,36 @@ else()
1414
add_compile_options(-Wall -Wextra)
1515
endif()
1616

17+
# --- LLVM Configuration ---
18+
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")
19+
1720
# --- LLVM Configuration ---
1821
find_package(LLVM CONFIG)
1922

23+
# --- LibFFI Configuration ---
24+
# specialized find_package might be needed if not in standard path, but let's try standard first
25+
find_package(PkgConfig QUIET)
26+
if(PKG_CONFIG_FOUND)
27+
pkg_check_modules(LIBFFI libffi)
28+
endif()
29+
30+
# Fallback or manual find if pkg-config fails or on simple windows setups
31+
if(NOT LIBFFI_FOUND)
32+
find_path(LIBFFI_INCLUDE_DIRS ffi.h)
33+
find_library(LIBFFI_LIBRARIES NAMES ffi libffi)
34+
if(LIBFFI_INCLUDE_DIRS AND LIBFFI_LIBRARIES)
35+
set(LIBFFI_FOUND TRUE)
36+
endif()
37+
endif()
38+
39+
if(LIBFFI_FOUND)
40+
message(STATUS "Found LibFFI: ${LIBFFI_LIBRARIES}")
41+
include_directories(${LIBFFI_INCLUDE_DIRS})
42+
add_definitions(-DUSE_LIBFFI)
43+
else()
44+
message(WARNING "LibFFI not found. FFI features will be disabled.")
45+
endif()
46+
2047
if (LLVM_FOUND)
2148
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
2249
include_directories(SYSTEM ${LLVM_INCLUDE_DIRS})
@@ -76,6 +103,9 @@ else()
76103
if (LLVM_FOUND)
77104
target_link_libraries(proxpl_lib PRIVATE ${llvm_libs})
78105
endif()
106+
if (LIBFFI_FOUND)
107+
target_link_libraries(proxpl_lib PRIVATE ${LIBFFI_LIBRARIES})
108+
endif()
79109
add_executable(proxpl src/main.c)
80110
target_link_libraries(proxpl PRIVATE proxpl_lib)
81111
endif()
@@ -86,6 +116,10 @@ if (LLVM_FOUND)
86116
target_link_libraries(proxpl PRIVATE ${llvm_libs})
87117
endif()
88118

119+
if (LIBFFI_FOUND)
120+
target_link_libraries(proxpl PRIVATE ${LIBFFI_LIBRARIES})
121+
endif()
122+
89123
if(UNIX)
90124
if(APPLE)
91125
target_link_libraries(proxpl PRIVATE pthread dl z)

include/ast.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ typedef enum {
4444
STMT_EXPRESSION, STMT_VAR_DECL, STMT_FUNC_DECL, STMT_CLASS_DECL, STMT_INTERFACE_DECL,
4545
STMT_USE_DECL, STMT_IF, STMT_WHILE, STMT_FOR, STMT_RETURN,
4646
STMT_BLOCK, STMT_BREAK, STMT_CONTINUE, STMT_SWITCH,
47-
STMT_TRY_CATCH, STMT_PRINT
47+
STMT_TRY_CATCH, STMT_PRINT, STMT_EXTERN_DECL
4848
} StmtType;
4949

5050
// --- List Structures ---
@@ -137,6 +137,7 @@ typedef struct { int dummy; } ContinueStmt;
137137
typedef struct { Expr *value; SwitchCaseList *cases; StmtList *default_case; } SwitchStmt;
138138
typedef struct { StmtList *try_block; char *catch_var; StmtList *catch_block; StmtList *finally_block; } TryCatchStmt;
139139
typedef struct { Expr *expression; } PrintStmt;
140+
typedef struct { char *libraryPath; char *symbolName; char *name; StringList *params; } ExternDeclStmt;
140141

141142
struct Stmt {
142143
StmtType type;
@@ -148,6 +149,7 @@ struct Stmt {
148149
WhileStmt while_stmt; ForStmt for_stmt; ReturnStmt return_stmt;
149150
BlockStmt block; BreakStmt break_stmt; ContinueStmt continue_stmt;
150151
SwitchStmt switch_stmt; TryCatchStmt try_catch; PrintStmt print;
152+
ExternDeclStmt extern_decl;
151153
} as;
152154
};
153155

@@ -185,6 +187,7 @@ Stmt *createContinueStmt(int line, int column);
185187
Stmt *createSwitchStmt(Expr *value, SwitchCaseList *cases, StmtList *def, int line, int column);
186188
Stmt *createTryCatchStmt(StmtList *try_blk, const char *catch_var, StmtList *catch_blk, StmtList *finally_blk, int line, int column);
187189
Stmt *createPrintStmt(Expr *expression, int line, int column);
190+
Stmt *createExternDeclStmt(const char *libPath, const char *symName, const char *name, StringList *params, int line, int column);
188191

189192
ExprList *createExprList();
190193
void appendExpr(ExprList *list, Expr *expr);

include/bytecode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ typedef enum {
6464
OP_END_TRY,
6565
OP_INTERFACE,
6666
OP_IMPLEMENT,
67+
OP_MAKE_FOREIGN,
6768
OP_HALT = 0xFF
6869
} OpCode;
6970

include/ffi_bridge.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef PROX_FFI_BRIDGE_H
2+
#define PROX_FFI_BRIDGE_H
3+
4+
#include "value.h"
5+
#include "object.h"
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
// Call a foreign function interface object
12+
Value callForeign(ObjForeign* foreign, int argCount, Value* args);
13+
14+
// Load a dynamic library and find a symbol
15+
// Returns successful ObjForeign* or NULL if failed
16+
// If libraryPath is NULL, looks in current process (e.g. libc/msvcrt)
17+
ObjForeign* loadForeign(ObjString* libraryPath, ObjString* symbolName);
18+
19+
#ifdef __cplusplus
20+
}
21+
#endif
22+
23+
#endif // PROX_FFI_BRIDGE_H

include/object.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
#define IS_INTERFACE(value) isObjType(value, OBJ_INTERFACE)
5858
#define AS_INTERFACE(value) ((ObjInterface *)AS_OBJ(value))
5959

60+
#define IS_FOREIGN(value) isObjType(value, OBJ_FOREIGN)
61+
#define AS_FOREIGN(value) ((ObjForeign *)AS_OBJ(value))
62+
6063
typedef enum {
6164
OBJ_STRING,
6265
OBJ_FUNCTION,
@@ -69,6 +72,7 @@ typedef enum {
6972
OBJ_BOUND_METHOD,
7073
OBJ_LIST,
7174
OBJ_DICTIONARY,
75+
OBJ_FOREIGN,
7276

7377
OBJ_TASK,
7478
OBJ_INTERFACE
@@ -167,6 +171,13 @@ struct ObjDictionary {
167171
Table items;
168172
};
169173

174+
typedef struct ObjForeign {
175+
Obj obj;
176+
ObjString* name;
177+
void* library; // dlopen/LoadLibrary handle
178+
void* function; // dlsym/GetProcAddress pointer
179+
} ObjForeign;
180+
170181
typedef struct ObjTask ObjTask;
171182

172183
typedef void (*ResumeFn)(void*);
@@ -198,6 +209,7 @@ struct ObjInstance *newInstance(struct ObjClass *klass);
198209
struct ObjBoundMethod *newBoundMethod(Value receiver, ObjClosure *method);
199210
struct ObjList *newList();
200211
struct ObjDictionary *newDictionary();
212+
ObjForeign *newForeign(ObjString* name, void* library, void* function);
201213
struct ObjTask *newTask(void* hdl, ResumeFn resume);
202214
void printObject(Value value);
203215

include/scanner.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ typedef enum {
106106
TOKEN_IS,
107107
TOKEN_TYPEOF,
108108
TOKEN_EXTENDS,
109+
TOKEN_EXTERN,
109110
TOKEN_IMPLEMENTS,
110111
TOKEN_INTERFACE,
111112
TOKEN_ENUM,

src/compiler/bytecode_gen.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,33 @@ static void genStmt(BytecodeGen* gen, Stmt* stmt) {
481481
writeChunk(gen->chunk, OP_RETURN, stmt->line);
482482
break;
483483
}
484+
case STMT_EXTERN_DECL: {
485+
// Push library path
486+
Value libVal = OBJ_VAL(copyString(stmt->as.extern_decl.libraryPath, strlen(stmt->as.extern_decl.libraryPath)));
487+
int libConst = addConstant(gen->chunk, libVal);
488+
writeChunk(gen->chunk, OP_CONSTANT, stmt->line);
489+
writeChunk(gen->chunk, (uint8_t)libConst, stmt->line);
490+
491+
// Push symbol name
492+
Value symVal = OBJ_VAL(copyString(stmt->as.extern_decl.symbolName, strlen(stmt->as.extern_decl.symbolName)));
493+
int symConst = addConstant(gen->chunk, symVal);
494+
writeChunk(gen->chunk, OP_CONSTANT, stmt->line);
495+
writeChunk(gen->chunk, (uint8_t)symConst, stmt->line);
496+
497+
// Make Foreign Object
498+
writeChunk(gen->chunk, OP_MAKE_FOREIGN, stmt->line);
499+
500+
// Define Global
501+
if (gen->compiler->scopeDepth > 0) {
502+
addLocal(gen, stmt->as.extern_decl.name);
503+
} else {
504+
Value nameVal = OBJ_VAL(copyString(stmt->as.extern_decl.name, strlen(stmt->as.extern_decl.name)));
505+
int nameConst = addConstant(gen->chunk, nameVal);
506+
writeChunk(gen->chunk, OP_DEFINE_GLOBAL, stmt->line);
507+
writeChunk(gen->chunk, (uint8_t)nameConst, stmt->line);
508+
}
509+
break;
510+
}
484511
case STMT_IF: {
485512
genExpr(gen, stmt->as.if_stmt.condition);
486513
writeChunk(gen->chunk, OP_JUMP_IF_FALSE, stmt->line);

src/compiler/lexer/scanner.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,13 @@ static PxTokenType identifierType(Scanner *scanner) {
242242
case 'p':
243243
return checkKeyword(scanner, 3, 3, "ort", TOKEN_EXPORT);
244244
case 't':
245-
return checkKeyword(scanner, 3, 4, "ends", TOKEN_EXTENDS);
245+
if (scanner->current - scanner->start > 3) {
246+
switch(scanner->start[3]) {
247+
case 'e': return checkKeyword(scanner, 4, 2, "rn", TOKEN_EXTERN);
248+
case 'n': return checkKeyword(scanner, 4, 3, "nds", TOKEN_EXTENDS);
249+
}
250+
}
251+
break;
246252
}
247253
}
248254
break;

src/compiler/parser/ast.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,18 @@ Stmt *createPrintStmt(Expr *expression, int line, int column) {
492492
return stmt;
493493
}
494494

495+
Stmt *createExternDeclStmt(const char *libPath, const char *symName, const char *name, StringList *params, int line, int column) {
496+
Stmt *stmt = ALLOCATE(Stmt, 1);
497+
stmt->type = STMT_EXTERN_DECL;
498+
stmt->line = line;
499+
stmt->column = column;
500+
stmt->as.extern_decl.libraryPath = strdup(libPath);
501+
stmt->as.extern_decl.symbolName = strdup(symName);
502+
stmt->as.extern_decl.name = strdup(name);
503+
stmt->as.extern_decl.params = params;
504+
return stmt;
505+
}
506+
495507
// --- Free Functions ---
496508

497509
void freeExpr(Expr *expr) {
@@ -640,6 +652,12 @@ void freeStmt(Stmt *stmt) {
640652
case STMT_PRINT:
641653
freeExpr(stmt->as.print.expression);
642654
break;
655+
case STMT_EXTERN_DECL:
656+
free(stmt->as.extern_decl.libraryPath);
657+
free(stmt->as.extern_decl.symbolName);
658+
free(stmt->as.extern_decl.name);
659+
freeStringList(stmt->as.extern_decl.params);
660+
break;
643661
}
644662

645663
FREE(Stmt, stmt);

src/compiler/parser/parser.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ StmtList *parse(Parser *parser) {
205205

206206
// === Declarations ===
207207

208+
static Stmt *externDecl(Parser *p);
209+
208210
static Stmt *declaration(Parser *p) {
209211
if (match(p, 1, TOKEN_ASYNC)) {
210212
if (match(p, 1, TOKEN_FUNC))
@@ -217,6 +219,8 @@ static Stmt *declaration(Parser *p) {
217219
return funcDecl(p, "function", false, ACCESS_PUBLIC, false, false);
218220
if (match(p, 1, TOKEN_CLASS))
219221
return classDecl(p);
222+
if (match(p, 1, TOKEN_EXTERN))
223+
return externDecl(p);
220224
if (match(p, 1, TOKEN_INTERFACE))
221225
return interfaceDecl(p);
222226
if (match(p, 1, TOKEN_USE))
@@ -398,6 +402,39 @@ static Stmt *useDecl(Parser *p) {
398402
return createUseDeclStmt(modules, previous(p).line, 0);
399403
}
400404

405+
static Stmt *externDecl(Parser *p) {
406+
Token libToken = consume(p, TOKEN_STRING, "Expect library path string.");
407+
char *libPath = tokenToString(libToken);
408+
409+
Token symToken = consume(p, TOKEN_STRING, "Expect symbol name string.");
410+
char *symName = tokenToString(symToken);
411+
412+
consume(p, TOKEN_FUNC, "Expect 'func' after extern strings.");
413+
Token nameToken = consume(p, TOKEN_IDENTIFIER, "Expect function name.");
414+
char *name = tokenToString(nameToken);
415+
416+
consume(p, TOKEN_LEFT_PAREN, "Expect '(' after name.");
417+
418+
StringList *params = createStringList();
419+
if (!check(p, TOKEN_RIGHT_PAREN)) {
420+
do {
421+
Token param = consume(p, TOKEN_IDENTIFIER, "Expect parameter name.");
422+
char *paramName = tokenToString(param);
423+
appendString(params, paramName);
424+
free(paramName);
425+
} while (match(p, 1, TOKEN_COMMA));
426+
}
427+
428+
consume(p, TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
429+
consume(p, TOKEN_SEMICOLON, "Expect ';' after extern declaration.");
430+
431+
Stmt *stmt = createExternDeclStmt(libPath, symName, name, params, libToken.line, 0);
432+
free(libPath);
433+
free(symName);
434+
free(name);
435+
return stmt;
436+
}
437+
401438
// === Statements ===
402439

403440
static Stmt *statement(Parser *p) {

0 commit comments

Comments
 (0)