Skip to content

Commit

Permalink
class constructors + fix GC bug
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafaquraish committed Apr 28, 2024
1 parent eda1132 commit f0603da
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 71 deletions.
12 changes: 10 additions & 2 deletions compiler/ast/nodes.oc
Original file line number Diff line number Diff line change
Expand Up @@ -226,19 +226,27 @@ def Operator::needs_lhs_pointer_for_overload(this): bool => match this {
else => false
}

enum FunctionType {
Script
Function
Method
Constructor
}

struct Function {
type: FunctionType
sym: &Symbol
params: &Vector<&Variable>
body: &AST
exits: bool
span: Span

operator_overloads: &Vector<Operator>
is_method: bool
}

def Function::new(): &Function {
def Function::new(type: FunctionType): &Function {
let func = mem::alloc<Function>()
func.type = type
func.params = Vector<&Variable>::new()
return func
}
Expand Down
44 changes: 20 additions & 24 deletions compiler/compiler.oc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import std::span::{ Span }
import std::sv::{ SV }
import std::mem

import @ast::nodes::{ this, AST, Variable, Symbol }
import @ast::nodes::{ this, AST, Variable, Symbol, FunctionType }
import @errors::{ Error }
import @bytecode::{ Chunk, OpCode }
import @vm::{ VM }
Expand All @@ -30,22 +30,13 @@ struct Compiler {
locals: &Vector<LocalVar>
scope_depth: u32
enclosing: &Compiler
function_type: FunctionType
}

enum CompileType {
Script
Function
Method
}

def Compiler::new(
vm: &VM, name: SV, span: Span,
type: CompileType,
enclosing: &Compiler = null
): &Compiler {
def Compiler::new(vm: &VM, sym: &Symbol, type: FunctionType, enclosing: &Compiler = null): &Compiler {
let compiler = mem::alloc<Compiler>()

let chunk = Chunk::new(span)
let chunk = Chunk::new(sym.span)
let locals = Vector<LocalVar>::new(capacity: 8)
let upvars = Vector<UpVar>::new(capacity: 256)
let func = gc::allocate_object<FunctionCode>(FunctionCode, vm, enclosing)
Expand All @@ -57,17 +48,18 @@ def Compiler::new(
upvars,
locals,
scope_depth: 0,
enclosing
enclosing,
function_type: type
)
gc::set_compiler(compiler)

let name_val = vm.copy_string(name.data, name.len)
let name_val = compiler.make_str(sym.name)
func.init(name_val.as_string(), chunk, 0) // Replace arity later...

if type == Method {
locals.push(LocalVar(SV("this",4), span, 0, false))
if type != Function {
locals.push(LocalVar(SV("this",4), sym.span, 0, false))
} else {
locals.push(LocalVar(SV("",0), span, 0, false))
locals.push(LocalVar(SV("",0), sym.span, 0, false))
}

return compiler
Expand Down Expand Up @@ -369,12 +361,10 @@ def Compiler::compile_expression(&this, node: &AST) {
}

def Compiler::compile_function(&this, node: &AST, func: &nodes::Function) {
let compile_type: CompileType = if func.is_method then Method else Function
let cc = Compiler::new(
.vm,
func.sym.name,
func.sym.span,
compile_type,
func.sym,
func.type,
enclosing: this
)
cc.func.arity = func.params.size as u8
Expand All @@ -388,7 +378,11 @@ def Compiler::compile_function(&this, node: &AST, func: &nodes::Function) {
cc.compile_statement(func.body)

// Add a placeholder return statement if none was provided
cc.chunk.push_with_literal(.vm, Constant, Value::Null(), node.span)
if func.type == Constructor {
cc.chunk.push_with_arg_u16(GetLocal, 0, node.span)
} else {
cc.chunk.push_with_literal(.vm, Constant, Value::Null(), node.span)
}
cc.chunk.push_op(Return, node.span)
cc.end_scope(func.sym.span)

Expand Down Expand Up @@ -455,6 +449,8 @@ def Compiler::compile_statement(&this, node: &AST) {
let method_name = .make_str(method.sym.name)
.chunk.push_with_literal(.vm, Method, method_name, method.sym.span)
}

.chunk.push_op(Pop, sym.span)
}
If => .compile_if(node, is_expression: false)
While => {
Expand Down Expand Up @@ -524,7 +520,7 @@ def Compiler::compile_ns(&this, root: &AST) {

// TODO: More complex structures than just expressions?
def compile_program(vm: &VM, root: &AST): &FunctionCode {
let compiler = Compiler::new(vm, SV("",0), root.span, Script)
let compiler = Compiler::new(vm, root.u.ns.sym, Script)
compiler.compile_ns(root)
compiler.chunk.push_op(Halt, root.span)
gc::set_compiler(null)
Expand Down
1 change: 1 addition & 0 deletions compiler/main.oc
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def main(argc: i32, argv: &str) {
let vm = VM::make()
gc::set_vm(&vm)
gc::state::paused = false

let script = compile_program(&vm, ast)

if debug {
Expand Down
15 changes: 9 additions & 6 deletions compiler/parser.oc
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ def Parser::parse_atom(&this, end_type: TokenType): &AST {
}
Dot => {
let tok = .consume(Dot)
if not .cur_func? or not .cur_func.is_method {
if not .cur_func? or (.cur_func.type != Method and .cur_func.type != Constructor) {
.error(Error::new(tok.span, "Cannot use dot shorthand outside of a method"))
return AST::new(Error, tok.span)
}
Expand Down Expand Up @@ -1005,7 +1005,7 @@ def Parser::parse_class(&this): &AST {
}

while not .token_is_eof_or(CloseCurly) {
let method = .parse_function(is_method: true)
let method = .parse_function(Method)
if method? node.u.class.methods.push(method)
}

Expand Down Expand Up @@ -1052,7 +1052,7 @@ def Parser::parse_statement(&this): &AST {

match .token().type {
OpenCurly => node = .parse_block()
Def => node = .parse_function(is_method: false)
Def => node = .parse_function(Function)
Class => node = .parse_class()
Return => {
.consume(Return)
Expand Down Expand Up @@ -1193,12 +1193,16 @@ def Parser::parse_function_body(&this): &AST => match .token().type {
else => .parse_block()
}

def Parser::parse_function(&this, is_method: bool): &AST {
def Parser::parse_function(&this, type: FunctionType): &AST {
let start = .consume(Def)

let name = .consume(Identifier)

let func = Function::new()
if type == Method and name.text == "init" {
type = Constructor
}

let func = Function::new(type)
let node = AST::new(Function, start.span.join(name.span))
node.u.func = func

Expand All @@ -1220,7 +1224,6 @@ def Parser::parse_function(&this, is_method: bool): &AST {
}
let end_span = .consume(CloseParen).span

func.is_method = is_method
.cur_func = func

let body = .parse_function_body()
Expand Down
23 changes: 17 additions & 6 deletions compiler/vm/gc.oc
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def gc_mem(old: untyped_ptr, new_size: u32): untyped_ptr {
// [ xxxxxxxx | ........ ........ ........ ........ ........ ]
// ^ ^
// 8 byte size Pointer to actual memory return from this function

let old_base = if old? then old - 8 else null
let old_size = if old? then *(old_base as &u64) else 0u64
if ALLOC_DEBUG then println(f"[GC MEM] Requested {new_size} bytes, old_ptr: {old}")

if old_size < new_size as u64 and not state::paused {
if bytes_allocated > next_gc or STRESS_GC {
Expand All @@ -55,15 +55,18 @@ def gc_mem(old: untyped_ptr, new_size: u32): untyped_ptr {
// free
if new_size == 0 {
mem::impl::free(old_base)
if ALLOC_DEBUG then println(f"[GC] Deleted {old_size} bytes from {bytes_allocated} bytes allocated")
if ALLOC_DEBUG then println(f"[GC MEM] Deleted {old_size} bytes from {bytes_allocated} bytes allocated, {old}")
bytes_allocated -= old_size as u64
return null
}

// alloc / realloc
let new_base = mem::impl::realloc(old_base, new_size + 8)
let new_base = match old_size {
0 => mem::impl::calloc(1, new_size + 8),
else => mem::impl::realloc(old_base, new_size + 8)
}
bytes_allocated += new_size as u64 - old_size as u64
if ALLOC_DEBUG then println(f"[GC] Reallocated {old_size} -> {new_size} bytes, total: {bytes_allocated} bytes allocated")
if ALLOC_DEBUG then println(f"[GC MEM] Reallocated {old_size} -> {new_size} bytes, total: {bytes_allocated} bytes allocated, res={new_base+8}")
*(new_base as &u64) = new_size as u64
return new_base + 8
}
Expand All @@ -78,7 +81,7 @@ def initialize_gc_allocator() {
if stress_gc? {
STRESS_GC = true
}
mem::set_allocator(null, gc_alloc, gc_free, gc_realloc)
mem::set_allocator(null, gc_alloc, gc_free)
}

/////
Expand All @@ -93,6 +96,7 @@ let num_garbage_collections: u32 = 0
//!
//! Precondition: First field of `T` must be a `Object` field.
def allocate_object<T>(type: ObjectType, vm: &VM, compiler: &Compiler = null): &T {
if GC_DEBUG then println(f"[GC] Requesting allocation of object {type}")
let res = mem::alloc<T>()
res.obj.type = type
res.obj.next = vm.objects
Expand Down Expand Up @@ -187,6 +191,10 @@ def GarbageCollector::mark_roots(&this) {
.mark_value(value)
}

for value in .vm.tmp.iter() {
.mark_value(value)
}

.mark_object(&.vm.func.obj)
for frame in .vm.frames.iter() {
.mark_object(&frame.func.obj)
Expand All @@ -201,10 +209,13 @@ def GarbageCollector::mark_roots(&this) {
.mark_value(it.value)
}

.mark_object(&.vm.init_string.obj)

.mark_compiler_roots()
}

def GarbageCollector::blacken_object(&this, obj: &Object) {
if not obj? return
if GC_DEBUG {
print(`[GC] Blackening object {obj} : `)
obj.print()
Expand All @@ -225,7 +236,7 @@ def GarbageCollector::blacken_object(&this, obj: &Object) {
Class => {
let c = obj as &Class
.mark_object(&c.name.obj)
if c.methods? {
if c.methods? and c.methods.items? {
for it in c.methods.items.iter() {
.mark_object(&it.key.obj)
.mark_object(&it.value.obj)
Expand Down
Loading

0 comments on commit f0603da

Please sign in to comment.