some new things, mostly not working or finished
This commit is contained in:
2403
niacin/compiler.py
Normal file
2403
niacin/compiler.py
Normal file
File diff suppressed because it is too large
Load Diff
21
niacin/cpp/Makefile
Normal file
21
niacin/cpp/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Werror -O2 -std=c99 -D_DEFAULT_SOURCE
|
||||
CFLAGS += -fstack-protector-strong -D_FORTIFY_SOURCE=2
|
||||
LDFLAGS = -z relro -z now
|
||||
|
||||
SOURCES = main.c vm_core.c
|
||||
OBJECTS = $(SOURCES:.c=.o)
|
||||
TARGET = popvm
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) $(TARGET)
|
||||
|
||||
.PHONY: all clean
|
||||
76
niacin/cpp/main.c
Normal file
76
niacin/cpp/main.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "vm_types.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Safe file reading
|
||||
static uint8_t* read_file(const char* filename, size_t* size) {
|
||||
FILE* file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Cannot open file: %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long file_size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if (file_size <= 0 || file_size > 10 * 1024 * 1024) { // 10MB limit
|
||||
fclose(file);
|
||||
fprintf(stderr, "Invalid file size\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t* buffer = malloc(file_size);
|
||||
if (!buffer) {
|
||||
fclose(file);
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fread(buffer, 1, file_size, file) != (size_t)file_size) {
|
||||
fclose(file);
|
||||
free(buffer);
|
||||
fprintf(stderr, "File read error\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
*size = file_size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <file.popclass>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t file_size;
|
||||
uint8_t* bytecode = read_file(argv[1], &file_size);
|
||||
if (!bytecode) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
vm_t* vm = vm_create(bytecode, file_size);
|
||||
if (!vm) {
|
||||
fprintf(stderr, "Failed to create VM\n");
|
||||
free(bytecode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!vm_validate_bytecode(vm)) {
|
||||
fprintf(stderr, "Invalid bytecode file\n");
|
||||
vm_destroy(vm);
|
||||
free(bytecode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Executing program...\n");
|
||||
vm_execute(vm);
|
||||
printf("Program finished\n");
|
||||
|
||||
vm_destroy(vm);
|
||||
free(bytecode);
|
||||
return 0;
|
||||
}
|
||||
BIN
niacin/cpp/sample.popclass
Normal file
BIN
niacin/cpp/sample.popclass
Normal file
Binary file not shown.
285
niacin/cpp/vm_core.c
Normal file
285
niacin/cpp/vm_core.c
Normal file
@@ -0,0 +1,285 @@
|
||||
#include "vm_types.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
// Safe memory allocation with overflow checking
|
||||
static void* safe_malloc(size_t size) {
|
||||
if (size == 0 || size > SIZE_MAX / 2) return NULL;
|
||||
void* ptr = malloc(size);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void* safe_realloc(void* ptr, size_t size) {
|
||||
if (size == 0 || size > SIZE_MAX / 2) return NULL;
|
||||
void* new_ptr = realloc(ptr, size);
|
||||
if (!new_ptr) {
|
||||
fprintf(stderr, "Memory reallocation failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
// Initialize VM with security limits
|
||||
vm_t* vm_create(uint8_t* bytecode, size_t size) {
|
||||
vm_t* vm = safe_malloc(sizeof(vm_t));
|
||||
memset(vm, 0, sizeof(vm_t));
|
||||
|
||||
vm->bytecode = bytecode;
|
||||
vm->bytecode_size = size;
|
||||
vm->ip = 0;
|
||||
vm->halted = false;
|
||||
|
||||
// Security limits
|
||||
vm->max_stack_size = 65536;
|
||||
vm->max_call_depth = 256;
|
||||
vm->current_call_depth = 0;
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
// Safe bytecode reading with bounds checking
|
||||
static bool read_u8(vm_t* vm, uint8_t* result) {
|
||||
if (vm->ip >= vm->bytecode_size) return false;
|
||||
*result = vm->bytecode[vm->ip++];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_u16(vm_t* vm, uint16_t* result) {
|
||||
if (vm->ip + 2 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 2);
|
||||
vm->ip += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_u32(vm_t* vm, uint32_t* result) {
|
||||
if (vm->ip + 4 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 4);
|
||||
vm->ip += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_i32(vm_t* vm, int32_t* result) {
|
||||
if (vm->ip + 4 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 4);
|
||||
vm->ip += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_f32(vm_t* vm, float* result) {
|
||||
if (vm->ip + 4 > vm->bytecode_size) return false;
|
||||
memcpy(result, &vm->bytecode[vm->ip], 4);
|
||||
vm->ip += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stack operations with bounds checking
|
||||
static bool stack_push(frame_t* frame, value_t value) {
|
||||
if (frame->stack_size >= frame->stack_capacity) {
|
||||
size_t new_capacity = frame->stack_capacity * 2;
|
||||
if (new_capacity == 0) new_capacity = 16;
|
||||
|
||||
value_t* new_stack = safe_realloc(frame->stack, new_capacity * sizeof(value_t));
|
||||
if (!new_stack) return false;
|
||||
|
||||
frame->stack = new_stack;
|
||||
frame->stack_capacity = new_capacity;
|
||||
}
|
||||
|
||||
frame->stack[frame->stack_size++] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool stack_pop(frame_t* frame, value_t* result) {
|
||||
if (frame->stack_size == 0) return false;
|
||||
*result = frame->stack[--frame->stack_size];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Type checking and conversion
|
||||
static bool type_check_binary(value_t a, value_t b, type_code_t* result_type) {
|
||||
// Simple type checking - in real implementation, add proper type promotion
|
||||
if (a.type == b.type) {
|
||||
*result_type = a.type;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int32_t value_to_int(value_t val) {
|
||||
switch (val.type) {
|
||||
case TYPE_I8: return val.data.i8;
|
||||
case TYPE_U8: return val.data.u8;
|
||||
case TYPE_I16: return val.data.i16;
|
||||
case TYPE_U16: return val.data.u16;
|
||||
case TYPE_I32: return val.data.i32;
|
||||
case TYPE_U32: return (int32_t)val.data.u32;
|
||||
case TYPE_BOOL: return val.data.boolean ? 1 : 0;
|
||||
case TYPE_CHAR: return (int32_t)val.data.character;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static float value_to_float(value_t val) {
|
||||
switch (val.type) {
|
||||
case TYPE_F32: return val.data.f32;
|
||||
case TYPE_I32: return (float)val.data.i32;
|
||||
case TYPE_U32: return (float)val.data.u32;
|
||||
default: return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Bytecode validation
|
||||
bool vm_validate_bytecode(vm_t* vm) {
|
||||
// Check magic number
|
||||
if (vm->bytecode_size < 4 || memcmp(vm->bytecode, "POPC", 4) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add more validation as needed
|
||||
return true;
|
||||
}
|
||||
|
||||
// Execute single instruction with full error checking
|
||||
bool vm_execute_instruction(vm_t* vm) {
|
||||
if (vm->halted || vm->ip >= vm->bytecode_size) return false;
|
||||
|
||||
uint8_t opcode;
|
||||
if (!read_u8(vm, &opcode)) return false;
|
||||
|
||||
frame_t* frame = vm->current_frame;
|
||||
if (!frame) return false;
|
||||
|
||||
switch (opcode) {
|
||||
case OP_PUSH_CONST: {
|
||||
uint32_t const_idx;
|
||||
if (!read_u32(vm, &const_idx) || const_idx >= vm->constants_count) {
|
||||
return false;
|
||||
}
|
||||
if (!stack_push(frame, vm->constants[const_idx])) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PUSH_INT: {
|
||||
uint8_t width;
|
||||
int32_t value;
|
||||
if (!read_u8(vm, &width) || !read_i32(vm, &value)) return false;
|
||||
|
||||
value_t val = { 0 };
|
||||
switch (width) {
|
||||
case 8: val.type = TYPE_I8; val.data.i8 = (int8_t)value; break;
|
||||
case 16: val.type = TYPE_I16; val.data.i16 = (int16_t)value; break;
|
||||
case 32: val.type = TYPE_I32; val.data.i32 = value; break;
|
||||
default: return false;
|
||||
}
|
||||
if (!stack_push(frame, val)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_ADD: {
|
||||
value_t a, b;
|
||||
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
|
||||
|
||||
type_code_t result_type;
|
||||
if (!type_check_binary(a, b, &result_type)) return false;
|
||||
|
||||
value_t result = { 0 };
|
||||
result.type = result_type;
|
||||
|
||||
if (result_type == TYPE_F32) {
|
||||
result.data.f32 = value_to_float(b) + value_to_float(a);
|
||||
}
|
||||
else {
|
||||
result.data.i32 = value_to_int(b) + value_to_int(a);
|
||||
}
|
||||
|
||||
if (!stack_push(frame, result)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_CMP_EQ: {
|
||||
value_t a, b;
|
||||
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
|
||||
|
||||
value_t result = { 0 };
|
||||
result.type = TYPE_BOOL;
|
||||
result.data.boolean = (value_to_int(a) == value_to_int(b));
|
||||
|
||||
if (!stack_push(frame, result)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_JMP: {
|
||||
int32_t offset;
|
||||
if (!read_i32(vm, &offset)) return false;
|
||||
|
||||
// Check for negative jumps (security)
|
||||
if (offset < 0 && (size_t)(-offset) > vm->ip) return false;
|
||||
if (vm->ip + offset >= vm->bytecode_size) return false;
|
||||
|
||||
vm->ip += offset;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PRINT: {
|
||||
value_t val;
|
||||
if (!stack_pop(frame, &val)) return false;
|
||||
|
||||
switch (val.type) {
|
||||
case TYPE_I32: printf("%d\n", val.data.i32); break;
|
||||
case TYPE_F32: printf("%f\n", val.data.f32); break;
|
||||
case TYPE_BOOL: printf("%s\n", val.data.boolean ? "true" : "false"); break;
|
||||
case TYPE_STR: printf("%s\n", val.data.string); break;
|
||||
default: printf("<unknown>\n"); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_HALT:
|
||||
vm->halted = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown opcode: 0x%02x\n", opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Main execution loop
|
||||
void vm_execute(vm_t* vm) {
|
||||
while (!vm->halted && vm_execute_instruction(vm)) {
|
||||
// Continue execution
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
void vm_destroy(vm_t* vm) {
|
||||
if (!vm) return;
|
||||
|
||||
// Free constants
|
||||
for (size_t i = 0; i < vm->constants_count; i++) {
|
||||
if (vm->constants[i].type == TYPE_STR && vm->constants[i].data.string) {
|
||||
free(vm->constants[i].data.string);
|
||||
}
|
||||
}
|
||||
free(vm->constants);
|
||||
|
||||
// Free functions
|
||||
free(vm->functions);
|
||||
|
||||
// Free frames (simplified - in real impl, walk call stack)
|
||||
if (vm->current_frame) {
|
||||
free(vm->current_frame->locals);
|
||||
free(vm->current_frame->stack);
|
||||
free(vm->current_frame);
|
||||
}
|
||||
|
||||
free(vm);
|
||||
}
|
||||
120
niacin/cpp/vm_types.h
Normal file
120
niacin/cpp/vm_types.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef VM_TYPES_H
|
||||
#define VM_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Type codes
|
||||
typedef enum {
|
||||
TYPE_I8 = 0x01,
|
||||
TYPE_U8 = 0x02,
|
||||
TYPE_I16 = 0x03,
|
||||
TYPE_U16 = 0x04,
|
||||
TYPE_I32 = 0x05,
|
||||
TYPE_U32 = 0x06,
|
||||
TYPE_F32 = 0x07,
|
||||
TYPE_BOOL = 0x08,
|
||||
TYPE_CHAR = 0x09,
|
||||
TYPE_STR = 0x0A
|
||||
} type_code_t;
|
||||
|
||||
// Opcodes
|
||||
typedef enum {
|
||||
OP_PUSH_CONST = 0x01,
|
||||
OP_PUSH_INT = 0x02,
|
||||
OP_PUSH_FLOAT = 0x03,
|
||||
OP_PUSH_STR = 0x04,
|
||||
OP_LOAD_LOCAL = 0x10,
|
||||
OP_STORE_LOCAL = 0x11,
|
||||
OP_ADD = 0x20,
|
||||
OP_SUB = 0x21,
|
||||
OP_MUL = 0x22,
|
||||
OP_DIV = 0x23,
|
||||
OP_MOD = 0x24,
|
||||
OP_CMP_EQ = 0x40,
|
||||
OP_CMP_NEQ = 0x41,
|
||||
OP_CMP_LT = 0x42,
|
||||
OP_CMP_GT = 0x43,
|
||||
OP_CMP_LE = 0x44,
|
||||
OP_CMP_GE = 0x45,
|
||||
OP_JMP = 0x50,
|
||||
OP_JMP_IF = 0x51,
|
||||
OP_JMP_IF_NOT = 0x52,
|
||||
OP_CALL = 0x60,
|
||||
OP_RET = 0x61,
|
||||
OP_DUP = 0x80,
|
||||
OP_POP = 0x81,
|
||||
OP_PRINT = 0x90,
|
||||
OP_HALT = 0xA0
|
||||
} opcode_t;
|
||||
|
||||
// Value union with type safety
|
||||
typedef union {
|
||||
int8_t i8;
|
||||
uint8_t u8;
|
||||
int16_t i16;
|
||||
uint16_t u16;
|
||||
int32_t i32;
|
||||
uint32_t u32;
|
||||
float f32;
|
||||
bool boolean;
|
||||
char character;
|
||||
char* string;
|
||||
} value_data_t;
|
||||
|
||||
typedef struct {
|
||||
type_code_t type;
|
||||
value_data_t data;
|
||||
} value_t;
|
||||
|
||||
// Function definition
|
||||
typedef struct {
|
||||
uint32_t name_index;
|
||||
uint8_t arg_count;
|
||||
uint8_t local_count;
|
||||
uint16_t reserved;
|
||||
uint32_t code_size;
|
||||
uint32_t code_offset;
|
||||
} function_t;
|
||||
|
||||
// Call frame
|
||||
typedef struct frame {
|
||||
struct frame* prev;
|
||||
uint32_t return_ip;
|
||||
value_t* locals;
|
||||
value_t* stack;
|
||||
size_t stack_size;
|
||||
size_t stack_capacity;
|
||||
size_t locals_count;
|
||||
} frame_t;
|
||||
|
||||
// Virtual machine state
|
||||
typedef struct {
|
||||
uint8_t* bytecode;
|
||||
size_t bytecode_size;
|
||||
uint32_t ip;
|
||||
|
||||
value_t* constants;
|
||||
size_t constants_count;
|
||||
|
||||
function_t* functions;
|
||||
size_t functions_count;
|
||||
|
||||
frame_t* current_frame;
|
||||
bool halted;
|
||||
|
||||
// Security settings
|
||||
size_t max_stack_size;
|
||||
size_t max_call_depth;
|
||||
size_t current_call_depth;
|
||||
} vm_t;
|
||||
|
||||
// Function declarations
|
||||
vm_t* vm_create(uint8_t* bytecode, size_t size);
|
||||
bool vm_validate_bytecode(vm_t* vm);
|
||||
bool vm_execute_instruction(vm_t* vm);
|
||||
void vm_execute(vm_t* vm);
|
||||
void vm_destroy(vm_t* vm);
|
||||
|
||||
#endif
|
||||
16
niacin/sample.popasm
Normal file
16
niacin/sample.popasm
Normal file
@@ -0,0 +1,16 @@
|
||||
; POP Class File Version 1.0
|
||||
; Magic: b'POPC'
|
||||
|
||||
; Constant Pool
|
||||
; Count: 0
|
||||
|
||||
; Function Table
|
||||
; Count: 1
|
||||
; func[0]: func_0, args=0, locals=1, code_size=15, offset=0x00000024
|
||||
|
||||
; Function 0
|
||||
0x00000024: PUSH_INT i32 12
|
||||
0x0000002a: STORE_LOCAL local[0]
|
||||
0x0000002d: LOAD_LOCAL local[0]
|
||||
0x00000030: PRINT
|
||||
0x00000031: RET 0
|
||||
BIN
niacin/sample.popclass
Normal file
BIN
niacin/sample.popclass
Normal file
Binary file not shown.
4
niacin/sample.src
Normal file
4
niacin/sample.src
Normal file
@@ -0,0 +1,4 @@
|
||||
fun main() {
|
||||
u8 testint = 255;
|
||||
print(testint);
|
||||
}
|
||||
Reference in New Issue
Block a user