diff --git a/README.md b/README.md index 4a066df..f3e9f8f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ type inference, namespaces, stricter types and a module system. It transpiles to Use the following command to build the initial compiler: ```shell -$ ./meta/bootstrap.sh # Generates ./bootstrap/ocen if successful +$ ./meta/bootstrap.sh # Generates initial compiler +$ ./bootstrap/ocen --help ``` ### Compiling other programs diff --git a/bootstrap/stage0.c b/bootstrap/stage0.c index 8371fd9..1a5a98d 100644 --- a/bootstrap/stage0.c +++ b/bootstrap/stage0.c @@ -1508,6 +1508,7 @@ bool silent = false; bool debug = false; u32 error_level = ((u32)2); char *docs_path = NULL; +bool include_stdlib = true; /* function declarations */ FILE *std_File_open(char *path, char *mode); i32 std_File_read(FILE *this, void *buf, u32 size); @@ -1531,8 +1532,12 @@ bool char_is_alnum(char this); bool char_is_print(char this); i32 i32_min(i32 this, i32 b); i32 i32_max(i32 this, i32 b); +i8 i8_min(i8 this, i8 b); +i8 i8_max(i8 this, i8 b); u32 u32_min(u32 this, u32 other); u32 u32_max(u32 this, u32 other); +u8 u8_min(u8 this, u8 other); +u8 u8_max(u8 this, u8 other); std_value_Value *std_compact_map_Map__0_at(std_compact_map_Map__0 *this, char *key); void std_compact_map_Map__0_free(std_compact_map_Map__0 *this); u32 std_compact_map_Map__0_get_index(std_compact_map_Map__0 *this, char *key, u32 hash); @@ -2196,7 +2201,7 @@ bool parser_Parser_load_import_path(parser_Parser *this, ast_nodes_AST *import_s void parser_Parser_load_file(parser_Parser *this, char *filename); void parser_couldnt_find_stdlib(void); void parser_Parser_find_and_import_stdlib(parser_Parser *this); -void parser_Parser_parse_toplevel(ast_program_Program *program, char *filename); +void parser_Parser_parse_toplevel(ast_program_Program *program, char *filename, bool include_stdlib); u32 utils_edit_distance(char *str1, char *str2); char *utils_find_word_suggestion(char *s, std_vector_Vector__5 *options); bool utils_directory_exists(char *path); @@ -2542,6 +2547,14 @@ i32 i32_max(i32 this, i32 b) { return ((this > b) ? this : b); } +i8 i8_min(i8 this, i8 b) { + return ((this < b) ? this : b); +} + +i8 i8_max(i8 this, i8 b) { + return ((this > b) ? this : b); +} + u32 u32_min(u32 this, u32 other) { return ((this < other) ? this : other); } @@ -2550,6 +2563,14 @@ u32 u32_max(u32 this, u32 other) { return ((this > other) ? this : other); } +u8 u8_min(u8 this, u8 other) { + return ((this < other) ? this : other); +} + +u8 u8_max(u8 this, u8 other) { + return ((this > other) ? this : other); +} + std_value_Value *std_compact_map_Map__0_at(std_compact_map_Map__0 *this, char *key) { u32 hash = str_hash(key); u32 index = std_compact_map_Map__0_get_index(this, key, hash); @@ -8544,7 +8565,7 @@ void parser_Parser_find_and_import_stdlib(parser_Parser *this) { std_map_Map__3_insert(this->program->global->namespaces, "std", std_ns); } -void parser_Parser_parse_toplevel(ast_program_Program *program, char *filename) { +void parser_Parser_parse_toplevel(ast_program_Program *program, char *filename, bool include_stdlib) { char *t1 = strdup(filename); char *dir = strdup(dirname(t1)); free(t1); @@ -8565,7 +8586,9 @@ void parser_Parser_parse_toplevel(ast_program_Program *program, char *filename) ns->sym->u.ns=ns; std_map_Map__3_insert(project_ns->namespaces, base, ns); parser_Parser parser = parser_Parser_make(program, ns); - parser_Parser_find_and_import_stdlib(&parser); + if (include_stdlib) { + parser_Parser_find_and_import_stdlib(&parser); + } parser_Parser_load_file(&parser, filename); } @@ -8940,6 +8963,12 @@ void passes_generic_pass_GenericPass_import_all_from_namespace(passes_generic_pa passes_generic_pass_GenericPass_insert_into_scope_checked(this, var->sym, NULL); } } + for (std_map_Iterator__2 __iter = std_map_Map__2_iter(ns->typedefs); std_map_Iterator__2_has_value(&__iter); std_map_Iterator__2_next(&__iter)) { + std_map_Node__2 *it = std_map_Iterator__2_cur(&__iter); + { + passes_generic_pass_GenericPass_insert_into_scope_checked(this, it->value->sym, it->key); + } + } } void passes_generic_pass_GenericPass_import_all_from_symbol(passes_generic_pass_GenericPass *this, ast_scopes_Symbol *sym) { @@ -10901,7 +10930,6 @@ types_Type *passes_typechecker_TypeChecker_check_member(passes_typechecker_TypeC node->resolved_symbol=method->sym; return method->type; } - passes_typechecker_TypeChecker_error(this, errors_Error_new(node->span, format_string("Type %s has no member named '%s'", types_Type_str(lhs), node->u.member.rhs_name))); } passes_typechecker_TypeChecker_error(this, errors_Error_new(node->span, format_string("Type %s has no member named '%s'", types_Type_str(lhs), node->u.member.rhs_name))); return NULL; @@ -12001,13 +12029,6 @@ void passes_typechecker_TypeChecker_check_function_declaration(passes_typechecke void passes_typechecker_TypeChecker_check_post_import(passes_typechecker_TypeChecker *this, ast_program_Namespace *ns) { passes_generic_pass_GenericPass_push_scope(this->o, ns->scope); passes_generic_pass_GenericPass_push_namespace(this->o, ns); - for (std_map_Iterator__2 __iter = std_map_Map__2_iter(ns->typedefs); std_map_Iterator__2_has_value(&__iter); std_map_Iterator__2_next(&__iter)) { - std_map_Node__2 *it = std_map_Iterator__2_cur(&__iter); - { - ast_scopes_Symbol *sym = ast_scopes_Scope_lookup_recursive(passes_generic_pass_GenericPass_scope(this->o), it->key); - ae_assert(((bool)sym), "compiler/passes/typechecker.oc:1837:16: Assertion failed: `sym?`", "Should have added the symbol into scope already"); ae_assert(sym->type==ast_scopes_SymbolType_TypeDef, "compiler/passes/typechecker.oc:1838:16: Assertion failed: `sym.type == TypeDef`", NULL); sym->u.type_def=passes_typechecker_TypeChecker_resolve_type(this, it->value, false, true, true); - } - } for (std_vector_Iterator__10 __iter = std_vector_Vector__10_iter(ns->functions); std_vector_Iterator__10_has_value(&__iter); std_vector_Iterator__10_next(&__iter)) { ast_nodes_Function *func = std_vector_Iterator__10_cur(&__iter); { @@ -12064,6 +12085,15 @@ void passes_typechecker_TypeChecker_check_pre_import(passes_typechecker_TypeChec passes_typechecker_TypeChecker_pre_check_function(this, ns, func); } } + for (std_map_Iterator__2 __iter = std_map_Map__2_iter(ns->typedefs); std_map_Iterator__2_has_value(&__iter); std_map_Iterator__2_next(&__iter)) { + std_map_Node__2 *it = std_map_Iterator__2_cur(&__iter); + { + ast_scopes_Symbol *sym = ast_scopes_Scope_lookup_recursive(passes_generic_pass_GenericPass_scope(this->o), it->key); + ae_assert(((bool)sym), "compiler/passes/typechecker.oc:1879:16: Assertion failed: `sym?`", "Should have added the symbol into scope already"); ae_assert(sym->type==ast_scopes_SymbolType_TypeDef, "compiler/passes/typechecker.oc:1880:16: Assertion failed: `sym.type == TypeDef`", NULL); types_Type *res = passes_typechecker_TypeChecker_resolve_type(this, it->value, false, true, true); + sym->u.type_def=res; + it->value=res; + } + } for (std_map_ValueIterator__3 __iter = std_map_Map__3_iter_values(ns->namespaces); std_map_ValueIterator__3_has_value(&__iter); std_map_ValueIterator__3_next(&__iter)) { ast_program_Namespace *child = std_map_ValueIterator__3_cur(&__iter); { @@ -12413,6 +12443,10 @@ ast_scopes_Symbol *ast_program_Namespace_find_importable_symbol(ast_program_Name } } + types_Type *td = std_map_Map__2_get(this->typedefs, name, NULL); + if (((bool)td)) + return td->sym; + return NULL; } @@ -12515,7 +12549,7 @@ bool ast_program_NSIterator_has_value(ast_program_NSIterator *this) { } void ast_program_NSIterator_next(ast_program_NSIterator *this) { - ae_assert(!std_vector_Vector__7_is_empty(this->stack), "compiler/ast/program.oc:244:12: Assertion failed: `not .stack.is_empty()`", NULL); this->curr=std_vector_Vector__7_pop(this->stack); + ae_assert(!std_vector_Vector__7_is_empty(this->stack), "compiler/ast/program.oc:247:12: Assertion failed: `not .stack.is_empty()`", NULL); this->curr=std_vector_Vector__7_pop(this->stack); for (std_map_ValueIterator__3 __iter = std_map_Map__3_iter_values(this->curr->namespaces); std_map_ValueIterator__3_has_value(&__iter); std_map_ValueIterator__3_next(&__iter)) { ast_program_Namespace *ns = std_map_ValueIterator__3_cur(&__iter); { @@ -13794,7 +13828,7 @@ char *types_Type_str(types_Type *this) { __yield_0 = this->u.enum_->sym->display; } break; case types_BaseType_Alias: { - __yield_0 = format_string("%s", this->name); + __yield_0 = this->name; } break; default: { __yield_0 = types_BaseType_str(this->base); @@ -13809,6 +13843,7 @@ void usage(i32 code) { printf("Options:""\n"); printf(" -o path Output executable (default: ./out)""\n"); printf(" -c path Output C code (default: {out}.c)""\n"); + printf(" --no-stdlid Don't include the standard library""\n"); printf(" -e0 Minimal one-line errors""\n"); printf(" -e1 Error messages with source code (default)""\n"); printf(" -e2 Error messages with source / hints""\n"); @@ -13885,6 +13920,8 @@ void parse_args(i32 argc, char **argv, ast_program_Program *program) { i+=((u32)1); docs_path=argv[i]; program->check_doc_links=true; + } else if (!strcmp(__match_str, "--no-stdlib")) { + include_stdlib=false; } else { if (argv[i][((u32)0)]=='-') { printf("Unknown option: %s""\n", argv[i]); @@ -13915,7 +13952,7 @@ i32 main(i32 argc, char **argv) { parse_args(argc, argv, program); program->error_level=error_level; program->gen_debug_info=debug; - parser_Parser_parse_toplevel(program, filename); + parser_Parser_parse_toplevel(program, filename, include_stdlib); if (!std_vector_Vector__14_is_empty(program->errors)) ast_program_Program_exit_with_errors(program); diff --git a/compiler/ast/program.oc b/compiler/ast/program.oc index 84669dc..ad84c8e 100644 --- a/compiler/ast/program.oc +++ b/compiler/ast/program.oc @@ -78,7 +78,6 @@ def Namespace::find_importable_symbol(&this, name: str): &Symbol { if var.sym.name.eq(name) return var.sym } - for func : .functions.iter() { if func.is_method then continue if func.sym.name.eq(name) return func.sym @@ -91,6 +90,10 @@ def Namespace::find_importable_symbol(&this, name: str): &Symbol { for enum_ : .enums.iter() { if enum_.sym.name.eq(name) return enum_.sym } + + let td = .typedefs.get(name, null) + if td? then return td.sym + return null } diff --git a/compiler/main.oc b/compiler/main.oc index 8261598..9b7124d 100644 --- a/compiler/main.oc +++ b/compiler/main.oc @@ -13,6 +13,7 @@ def usage(code: i32) { println("Options:") println(" -o path Output executable (default: ./out)") println(" -c path Output C code (default: {out}.c)") + println(" --no-stdlid Don't include the standard library") println(" -e0 Minimal one-line errors") println(" -e1 Error messages with source code (default)") println(" -e2 Error messages with source / hints") @@ -34,6 +35,7 @@ let silent: bool = false let debug: bool = false let error_level: u32 = 2 let docs_path: str = null +let include_stdlib: bool = true def save_and_compile_code(program: &Program, code: str) { if not c_path? { @@ -93,6 +95,7 @@ def parse_args(argc: i32, argv: &str, program: &Program) { docs_path = argv[i] program.check_doc_links = true } + "--no-stdlib" => include_stdlib = false else => { if argv[i][0] == '-' { println("Unknown option: %s", argv[i]) @@ -126,7 +129,7 @@ def main(argc: i32, argv: &str) { program.error_level = error_level program.gen_debug_info = debug - Parser::parse_toplevel(program, filename) + Parser::parse_toplevel(program, filename, include_stdlib) if not program.errors.is_empty() then program.exit_with_errors() diff --git a/compiler/parser.oc b/compiler/parser.oc index d17d532..fc1228b 100644 --- a/compiler/parser.oc +++ b/compiler/parser.oc @@ -1734,7 +1734,7 @@ def Parser::find_and_import_stdlib(&this) { .program.global.namespaces.insert("std", std_ns) } -def Parser::parse_toplevel(program: &Program, filename: str) { +def Parser::parse_toplevel(program: &Program, filename: str, include_stdlib: bool) { // Path to the directory of the file let t1 = filename.copy() let dir = dirname(t1).copy() @@ -1780,6 +1780,8 @@ def Parser::parse_toplevel(program: &Program, filename: str) { let parser = Parser::make(program, ns) - parser.find_and_import_stdlib() + if include_stdlib { + parser.find_and_import_stdlib() + } parser.load_file(filename) } diff --git a/compiler/passes/generic_pass.oc b/compiler/passes/generic_pass.oc index 78b8013..b7e7c27 100644 --- a/compiler/passes/generic_pass.oc +++ b/compiler/passes/generic_pass.oc @@ -169,6 +169,10 @@ def GenericPass::import_all_from_namespace(&this, ns: &Namespace) { let var = node.u.var_decl.var .insert_into_scope_checked(var.sym) } + + for it : ns.typedefs.iter() { + .insert_into_scope_checked(it.value.sym, it.key) + } } def GenericPass::import_all_from_symbol(&this, sym: &Symbol) { diff --git a/compiler/passes/typechecker.oc b/compiler/passes/typechecker.oc index 50fa887..4604e8e 100644 --- a/compiler/passes/typechecker.oc +++ b/compiler/passes/typechecker.oc @@ -1831,16 +1831,6 @@ def TypeChecker::check_post_import(&this, ns: &Namespace) { .o.push_scope(ns.scope) .o.push_namespace(ns) - for it : ns.typedefs.iter() { - let sym = .o.scope().lookup_recursive(it.key) - assert sym?, "Should have added the symbol into scope already" - assert sym.type == TypeDef - - // FIXME: Why is there a `typedef` and `alias` at symbol/type level? Only one - // should really be sufficient. - sym.u.type_def = .resolve_type(it.value) - } - for func : ns.functions.iter() { .check_function_declaration(func) } @@ -1884,6 +1874,18 @@ def TypeChecker::check_pre_import(&this, ns: &Namespace) { .pre_check_function(ns, func) } + for it : ns.typedefs.iter() { + let sym = .o.scope().lookup_recursive(it.key) + assert sym?, "Should have added the symbol into scope already" + assert sym.type == TypeDef + + // FIXME: Why is there a `typedef` and `alias` at symbol/type level? Only one + // should really be sufficient. + let res = .resolve_type(it.value) + sym.u.type_def = res + it.value = res + } + for child : ns.namespaces.iter_values() { .check_pre_import(child) } diff --git a/compiler/types.oc b/compiler/types.oc index d85ea30..1eefe8d 100644 --- a/compiler/types.oc +++ b/compiler/types.oc @@ -227,6 +227,6 @@ def Type::str(&this): str => match .base { Function => "" Structure => .u.struc.sym.display Enum => .u.enum_.sym.display - Alias => `{.name}` + Alias => .name else => .base.str() } diff --git a/examples/jpegify.oc b/examples/jpegify.oc index a9b7019..9b547c6 100644 --- a/examples/jpegify.oc +++ b/examples/jpegify.oc @@ -1,6 +1,6 @@ import std::fft::{ fft2, ifft2 } import std::math::{ TAU } -import std::image::Image +import std::image::{ Image, Color } import std::vec::Vec import std::complex::Complex import std::libc::{ free, memcpy } @@ -10,10 +10,10 @@ def get_image_channel(img: &Image, out: &Complex, channel: i32) { for let i = 0; i < img.height * img.width; i++ { let col = img.data[i] let val = match channel { - 0 => col.x, - 1 => col.y, - 2 => col.z, - else => (col.x + col.y + col.z) / 3.0 + 0 => col.r as f32 / 255.0, + 1 => col.g as f32 / 255.0, + 2 => col.b as f32 / 255.0, + else => (col.r + col.g + col.b) as f32 / (3.0 * 255.0) } out[i] = Complex::new(val, 0.0) } @@ -21,13 +21,13 @@ def get_image_channel(img: &Image, out: &Complex, channel: i32) { def save_image_channel(img: &Image, data: &Complex, channel: i32) { for let i = 0; i < img.height * img.width; i++ { - let col = img.data[i] let val = data[i].real().clamp01() + let val_u8 = (val * 255.0) as u8 match channel { - 0 => img.data[i].x = val - 1 => img.data[i].y = val - 2 => img.data[i].z = val - else => img.data[i] = Vec(val, val, val) + 0 => img.data[i].r = val_u8 + 1 => img.data[i].g = val_u8 + 2 => img.data[i].b = val_u8 + else => img.data[i] = Color(val_u8, val_u8, val_u8) } } } diff --git a/examples/raytrace.oc b/examples/raytrace.oc index 1f73ba0..7d29226 100644 --- a/examples/raytrace.oc +++ b/examples/raytrace.oc @@ -3,7 +3,7 @@ import std::vec::Vec import std::vector::Vector -import std::image::Image +import std::image::{ Image, Color } import std::math::{ rand01 } import std::libc::malloc @@ -164,7 +164,13 @@ def main() { let color = raytrace(&ray, objs, 5) total_col = total_col.add(color) } - img.set(x, y, total_col.multf(div_factor)) + total_col = total_col.multf(div_factor * 255.0) + let u8col = Color( + total_col.x as u8, + total_col.y as u8, + total_col.z as u8, + ) + img.set(x, y, u8col) } } println("") diff --git a/examples/sdl_raytrace.oc b/examples/sdl_raytrace.oc index 5c90bf7..7ac2428 100644 --- a/examples/sdl_raytrace.oc +++ b/examples/sdl_raytrace.oc @@ -1,7 +1,6 @@ import std::sdl::{this, ttf::{this, Font}} import std::vec::Vec import std::vector::Vector -import std::image::Image import std::math::{ rand01 } import std::libc::{ calloc, free } @@ -140,7 +139,7 @@ struct Camera { w: Vec } -let img: &Image +let img: [[Vec; HEIGHT]; WIDTH] let objs: &Vector<&Sphere> let samples: u32 = 0 @@ -194,9 +193,9 @@ def draw(screen: &u8, pitch: u32) { let ray = camera.ray(x, y) let color = raytrace(&ray, objs, 5) - let prev_color = img.get(x, y) + let prev_color = img[x][y] let new_color = prev_color.add(color) - img.set(x, y, new_color) + img[x][y] = new_color // Save to screen let r = (new_color.x / samples as f32) * 255.0 @@ -256,8 +255,6 @@ def main() { let direction = Vec(0.0, 0.0, 1.0) camera = Camera::make(origin, direction) - img = Image::new(WIDTH, HEIGHT) - let e: sdl::Event let quit = false @@ -296,7 +293,7 @@ def main() { if modified { camera = Camera::make(origin, direction) - img.clear() + std::libc::memset(img, 0, sizeof(Vec) * WIDTH * HEIGHT) samples = 0 } diff --git a/meta/compile_examples.sh b/meta/compile_examples.sh new file mode 100755 index 0000000..578757c --- /dev/null +++ b/meta/compile_examples.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -xe + +ocen examples/jpegify.oc -o build/jpegify -n +ocen examples/raytrace.oc -o build/raytrace -n +ocen examples/sdl_raytrace.oc -o build/sdl_raytrace -n diff --git a/std/deque.oc b/std/deque.oc index 7080106..53b51f9 100644 --- a/std/deque.oc +++ b/std/deque.oc @@ -8,7 +8,7 @@ struct Deque { size: u32 } -def Deque::new(capacity: u32 = 16): Deque { +def Deque::new(capacity: u32 = 16): &Deque { let deq = calloc(1, sizeof(Deque)) as &Deque deq.capacity = capacity deq.data = calloc(capacity, sizeof(T)) as &T diff --git a/std/image/mod.oc b/std/image/mod.oc index 11b3a61..2f18a53 100644 --- a/std/image/mod.oc +++ b/std/image/mod.oc @@ -1,23 +1,25 @@ //* Image struct / IO utilities (PPM) -import std::vec::Vec import std::libc::{ calloc, free } -import std::math import std::buffer::Buffer -//* A type that holds an Image -//* -//* The image is stored in 32-bit floating point format. -//* Each of the RGB values are supposed to be in the range [0,1]. +//* A type that holds an Image in 24-bit RGB format + +struct Color { + r: u8 + g: u8 + b: u8 +} + struct Image { width: u32 height: u32 - data: &Vec + data: &Color } def Image::new(width: u32, height: u32): &Image { let img = calloc(1, sizeof(Image)) as &Image - *img = Image(width, height, calloc(width * height, sizeof(Vec))) + *img = Image(width, height, calloc(width * height, sizeof(Color))) return img } @@ -26,8 +28,8 @@ def Image::free(&this) { free(this) } -def Image::get(&this, x: u32, y: u32): Vec => .data[y * .width + x] -def Image::set(&this, x: u32, y: u32, col: Vec) { .data[y * .width + x] = col } +def Image::get(&this, x: u32, y: u32): Color => .data[y * .width + x] +def Image::set(&this, x: u32, y: u32, col: Color) { .data[y * .width + x] = col } def Image::save(&this, filename: str) { import .ppm @@ -70,5 +72,5 @@ def Image::load(filename: str): &Image { def Image::clear(&this) { - std::libc::memset(.data, 0, .width * .height * sizeof(Vec)) + std::libc::memset(.data, 0, .width * .height * sizeof(Color)) } \ No newline at end of file diff --git a/std/image/ppm.oc b/std/image/ppm.oc index 1be6d57..daf18ea 100644 --- a/std/image/ppm.oc +++ b/std/image/ppm.oc @@ -5,23 +5,11 @@ import std::bufferio::BufferIO def encode(img: &Image, data: &Buffer) { let io = BufferIO::make(data) - - let u8buf = calloc(1, img.width * img.height * 3) as &u8 - defer free(u8buf) - - for let i = 0; i < img.width * img.height; i += 1 { - let col = *(img.data + i) - let offset = u8buf + i * 3 - *(offset + 0) = ((col.x).clamp01() * 255.0) as u8 - *(offset + 1) = ((col.y).clamp01() * 255.0) as u8 - *(offset + 2) = ((col.z).clamp01() * 255.0) as u8 - } - let header = `P6 {img.width} {img.height} 255\n` defer free(header) io.write_bytes(header, header.len()) - io.write_bytes(u8buf, img.width * img.height * 3) + io.write_bytes(img.data, img.width * img.height * 3) } def decode(data: &Buffer): &Image { diff --git a/std/image/qoi.oc b/std/image/qoi.oc index 03027a1..e94fd18 100644 --- a/std/image/qoi.oc +++ b/std/image/qoi.oc @@ -4,14 +4,14 @@ import std::libc::memset import std::image::Image import std::vec::Vec -struct Pixel { +struct RGBA { r: u8, g: u8, b: u8, a: u8, } -def Pixel::hash(this): u32 { +def RGBA::hash(this): u32 { let r = .r as u32 let g = .g as u32 let b = .b as u32 @@ -19,7 +19,7 @@ def Pixel::hash(this): u32 { return (r * 3 + g * 5 + b * 7 + a * 11) % 64 } -def Pixel::eq(this, other: Pixel): bool { +def RGBA::eq(this, other: RGBA): bool { return .r == other.r and .g == other.g and .b == other.b and .a == other.a } @@ -32,8 +32,8 @@ struct QOIDecoder { colorspace: u8, pixels_done: u32, - prev: Pixel, - seen: [Pixel; 64], + prev: RGBA, + seen: [RGBA; 64], img: &Image } @@ -42,7 +42,7 @@ def QOIDecoder::make(data: &Buffer): QOIDecoder { let decoder: QOIDecoder memset(&decoder, 0, sizeof(QOIDecoder)) decoder.io = BufferIO::make(data) - decoder.prev = Pixel(0, 0, 0, 255) + decoder.prev = RGBA(0, 0, 0, 255) return decoder } @@ -65,20 +65,14 @@ def QOIDecoder::decode(&this): &Image { return .img } -def QOIDecoder::handle_pixel(&this, pix: Pixel) { +def QOIDecoder::handle_pixel(&this, pix: RGBA) { let hash = pix.hash() let seen = .seen[hash] .seen[hash] = pix .prev = pix // ignoring alpha - let col = Vec( - pix.r as f32 / 255.0, - pix.g as f32 / 255.0, - pix.b as f32 / 255.0, - ) - .img.data[.pixels_done] = col - + .img.data[.pixels_done] = Color(pix.r, pix.g, pix.b) .pixels_done += 1 } @@ -113,7 +107,7 @@ def QOIDecoder::decode_op_rgb(&this) { let r = .io.read_u8() let g = .io.read_u8() let b = .io.read_u8() - let pixel = Pixel(r, g, b, 255) + let pixel = RGBA(r, g, b, 255) .handle_pixel(pixel) } @@ -123,7 +117,7 @@ def QOIDecoder::decode_op_rgba(&this) { let g = .io.read_u8() let b = .io.read_u8() let a = .io.read_u8() - let pixel = Pixel(r, g, b, a) + let pixel = RGBA(r, g, b, a) .handle_pixel(pixel) } @@ -137,7 +131,7 @@ def QOIDecoder::decode_op_luma(&this, first: u8) { let dr = dg + dr_dg let db = dg + db_dg - let pixel = Pixel( + let pixel = RGBA( .prev.r + dr, .prev.g + dg, .prev.b + db, @@ -157,7 +151,7 @@ def QOIDecoder::decode_op_diff(&this, first: u8) { let r = (first >> 4 & 0b11) - 2 let g = (first >> 2 & 0b11) - 2 let b = (first >> 0 & 0b11) - 2 - let pixel = Pixel( + let pixel = RGBA( .prev.r + (r as u8), .prev.g + (g as u8), .prev.b + (b as u8), @@ -177,8 +171,8 @@ struct QOIEncoder { io: BufferIO, img: &Image - seen: [Pixel; 64] - prev: Pixel + seen: [RGBA; 64] + prev: RGBA pixels_done: u32 } @@ -187,7 +181,7 @@ def QOIEncoder::make(img: &Image, buffer: &Buffer): QOIEncoder { memset(&encoder, 0, sizeof(QOIEncoder)) encoder.img = img encoder.io = BufferIO::make(buffer) - encoder.prev = Pixel(0, 0, 0, 255) + encoder.prev = RGBA(0, 0, 0, 255) return encoder } @@ -199,24 +193,19 @@ def QOIEncoder::encode_header(&this) { .io.write_u8(0) // Colorspace } -def QOIEncoder::img_pix(&this, idx: u32): Pixel { +def QOIEncoder::img_pix(&this, idx: u32): RGBA { let col = .img.data[idx] - return Pixel( - (col.x * 255.0) as u8, - (col.y * 255.0) as u8, - (col.z * 255.0) as u8, - 255 - ) + return RGBA(col.r, col.g, col.b, 255) } -def QOIEncoder::handle_pixel(&this, pix: Pixel, num: u32 = 1) { +def QOIEncoder::handle_pixel(&this, pix: RGBA, num: u32 = 1) { let hash = pix.hash() .seen[hash] = pix .prev = pix .pixels_done += num } -def QOIEncoder::encode_pixel(&this, pix: Pixel) { +def QOIEncoder::encode_pixel(&this, pix: RGBA) { // Case 1: Is this a run? if .prev.eq(pix) { let run = 1 diff --git a/std/libc.oc b/std/libc.oc index 7971d03..a656e5c 100644 --- a/std/libc.oc +++ b/std/libc.oc @@ -7,7 +7,7 @@ def malloc(size: u32): untyped_ptr extern def realloc(old: untyped_ptr, size: u32): untyped_ptr extern def calloc(size: u32, elem_size: u32): untyped_ptr extern def free(ptr: untyped_ptr) extern -def exit(code: i32) extern +def exit(code: i32) exits extern def getenv(name: str): str extern def system(cmd: str): i32 extern diff --git a/std/math.oc b/std/math.oc index ab61f14..4716431 100644 --- a/std/math.oc +++ b/std/math.oc @@ -17,7 +17,9 @@ def f32::min(this, b: f32): f32 => if this < b then this else b def f32::max(this, b: f32): f32 => if this > b then this else b def f32::clamp(this, min: f32, max: f32): f32 => f32::max(f32::min(this, max), min) -def f32::clamp01(this): f32 => this.clamp(0.0, 1.0) +def f32::clamp01(this): f32 => this.clamp(0.0, 1.0)\ + +def i32::abs(this): i32 => if this < 0 then -this else this const PI: f32 extern("M_PI") const TAU: f32 = PI * 2.0 diff --git a/std/mod.oc b/std/mod.oc index ebb3b5b..112e3c1 100644 --- a/std/mod.oc +++ b/std/mod.oc @@ -112,8 +112,12 @@ def char::is_print(this): bool => libc::isprint(this) def i32::min(this, b: i32): i32 => if this < b then this else b def i32::max(this, b: i32): i32 => if this > b then this else b +def i8::min(this, b: i8): i8 => if this < b then this else b +def i8::max(this, b: i8): i8 => if this > b then this else b def u32::min(this, other: u32): u32 => if this < other then this else other def u32::max(this, other: u32): u32 => if this > other then this else other +def u8::min(this, other: u8): u8 => if this < other then this else other +def u8::max(this, other: u8): u8 => if this > other then this else other def new(count: u32 = 1): &T => libc::calloc(count, sizeof(T)) as &T \ No newline at end of file diff --git a/std/random.oc b/std/random.oc new file mode 100644 index 0000000..bca5f3e --- /dev/null +++ b/std/random.oc @@ -0,0 +1,42 @@ + +struct RandomState { + x: u32 + y: u32 + z: u32 + w: u32 +} + +let global_random_state: RandomState = RandomState(123456789, 362436069, 521288629, 88675123) + +def RandomState::seed(&this, s: u32) { + this.x = s + this.y = s ^ 0x49616E42 + this.z = s ^ 0x6C078965 + this.w = s ^ 0x8F1BBCDC +} + +def RandomState::make(s: u32 = 0): RandomState { + let rs: RandomState + rs.seed(s) + return rs +} + +def randint(state: &RandomState=null): u32 { + // Can't do this in default args since they are evaluated at the callsite, + // and `global_random_state` may not be imported there + if not state? then state = &global_random_state + + let t = state.x ^ (state.x << 11) + state.x = state.y + state.y = state.z + state.z = state.w + state.w = (state.w ^ (state.w >> 19)) ^ (t ^ (t >> 8)) + return state.w +} + +def randu32(state: &RandomState=null): u32 => randint(state) +def randi32(state: &RandomState=null): i32 => (randint(state) as i32) + +def randu64(state: &RandomState=null): u64 => (randint(state) as u64) << 32 | randint(state) as u64 +def randf32(state: &RandomState=null): f32 => (randint(state) as f32) / 4294967296.0 +def randf64(state: &RandomState=null): f64 => (randint(state) as f64) / 4294967296.0 \ No newline at end of file diff --git a/std/sdl.oc b/std/sdl.oc index 940b53c..92749b7 100644 --- a/std/sdl.oc +++ b/std/sdl.oc @@ -76,7 +76,7 @@ enum MouseButton extern("u8") { struct Keysym extern("SDL_Keysym") { scancode: Key - sym: i32 + sym: Key mod: i32 } diff --git a/tests/docs.oc b/tests/docs.oc index d649757..78fc680 100644 --- a/tests/docs.oc +++ b/tests/docs.oc @@ -5,15 +5,10 @@ // This is just a dummy file that gets copied into the `compiler` directory and // imports everything so we can generate documentation for it. -import std::{ vector, map, compact_map, heap, deque } -import std::{ buffer, bufferio, image, json, math } -import std::{ value, vec, sdl, glut } +import std::{ vector, map, compact_map, heap, deque, set } +import std::{ buffer, bufferio, image, json, math, complex, fft, random } +import std::{ value, vec, sdl, glut, socket, sort, thread } import std::traits::{ hash, eq, compare } -import std::vector -import std::vector -import std::vector -import std::vector -import std::vector import @parser import @passes::mod // includes all passes diff --git a/tests/import_typedef.oc b/tests/import_typedef.oc new file mode 100644 index 0000000..81e19e3 --- /dev/null +++ b/tests/import_typedef.oc @@ -0,0 +1,23 @@ +/// compile + +struct Bar { + h: i32 +} + +namespace Foo { + struct Bar { + x: i32 + } + + typedef Baz = Bar + typedef InnerBar = Bar +} + +import ::Foo::Baz +typedef OuterBar = Foo::Bar + +def main() { + let x = Baz(5) + let y = OuterBar(10) + let z = Foo::InnerBar(15) +} \ No newline at end of file diff --git a/tests/random.oc b/tests/random.oc new file mode 100644 index 0000000..4c3a8e7 --- /dev/null +++ b/tests/random.oc @@ -0,0 +1,10 @@ +/// out: "2400945431 3444232677 2639662501 3489240994 2627305591" + +import std::random as rng + +def main() { + let rs = rng::RandomState::make(5) + for let i = 0; i < 5; ++i { + print(`{rng::randint(&rs)} `) + } +} \ No newline at end of file diff --git a/tests/vector.oc b/tests/vector.oc index 96a00f3..c9fb444 100644 --- a/tests/vector.oc +++ b/tests/vector.oc @@ -15,4 +15,81 @@ def main() { foo(my_List) println(`{my_List.size}`) -} \ No newline at end of file +} + +//* Mathematical vector library + +import std::math + +//* A type representing a 3D vector +struct Vec3 { + x: T + y: T + z: T +} + +def Vec3::str(this): str => f"{.x} {.y} {.z}" + +def Vec3::add(this, other: Vec3): Vec3 => Vec3(.x + other.x, .y + other.y, .z + other.z) +def Vec3::adds(this, val: T): Vec3 => Vec3(.x + val, .y + val, .z + val) + +def Vec3::sub(this, other: Vec3): Vec3 => Vec3(.x - other.x, .y - other.y, .z - other.z) +def Vec3::subs(this, val: T): Vec3 => Vec3(.x - val, .y - val, .z - val) + +def Vec3::mult(this, other: Vec3): Vec3 => Vec3(.x * other.x, .y * other.y, .z * other.z) +def Vec3::mults(this, val: T): Vec3 => Vec3(.x * val, .y * val, .z * val) + +def Vec3::div(this, other: Vec3): Vec3 => Vec3(.x / other.x, .y / other.y, .z / other.z) +def Vec3::divs(this, val: T): Vec3 => Vec3(.x / val, .y / val, .z / val) + +def Vec3::dot(this, other: Vec3): T => .x * other.x + .y * other.y + .z * other.z + +def Vec3::cross(this, other: Vec3): Vec3 { + return Vec3( + .y * other.z - .z * other.y, + .z * other.x - .x * other.z, + .x * other.y - .y * other.x, + ) +} + + +def Vec3::length_sq(this): T => .x * .x + .y * .y + .z * .z +def Vec3::length(this): f32 => (.length_sq() as f32).sqrt() +def Vec3::normalized(this): Vec3 { + let len = .length() + return Vec3(.x as f32 / len, .y as f32 / len, .z as f32 / len) +} + +def Vec3::reflect(this, normal: Vec3): Vec3 { + return .sub(normal.mults((2 as T) * .dot(normal))); +} + +def Vec3::rotateX(&this, angle: f32): Vec3 { + let c = angle.cos() + let s = angle.sin() + let x = .x as f32 + let y = .y as f32 * c - .z as f32 * s + let z = .y as f32 * s + .z as f32 * c + return Vec3(x, y, z) +} + +def Vec3::rotateY(&this, angle: f32): Vec3 { + let c = angle.cos() + let s = angle.sin() + let x = .z as f32 * s + .x as f32 * c + let y = .y as f32 + let z = .z as f32 * c - .x as f32 * s + return Vec3(x, y, z) +} + +def Vec3::rotateZ(&this, angle: f32): Vec3 { + let c = angle.cos() + let s = angle.sin() + let x = .x as f32 * c - .y as f32 * s + let y = .x as f32 * s + .y as f32 * c + let z = .z as f32 + return Vec3(x, y, z) +} + +typedef Vec3f = Vec3 +typedef Vec3i = Vec3 \ No newline at end of file