struct AstFileContents {
AstTyped_base;
- OnyxToken *filename_token;
+ AstTyped *filename_expr;
char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
u32 addr, size;
--- /dev/null
+package qoi
+
+#package {
+ math :: package core.math
+}
+
+#load "./qoi"
\ No newline at end of file
--- /dev/null
+package qoi
+
+Image :: struct {
+ width: u32;
+ height: u32;
+ channels: u32;
+
+ // This is dynamic so it can be freed.
+ data: [] u8;
+}
+
+#local Header :: struct #pack {
+ magic: [4] u8;
+ be_width: u32;
+ be_height: u32;
+ channels: u8;
+ colorspace: u8;
+}
+
+#local Color :: struct {
+ r, g, b, a: u8;
+}
+
+image_free :: (img: Image) {
+ delete(^img.data);
+}
+
+decode_image :: (buf: [] u8, allocator := context.allocator) -> Image {
+ img: Image;
+
+ pos := 0;
+ header := cast(^Header) buf.data;
+ pos += sizeof Header;
+
+ img.width = swap_endian(header.be_width);
+ img.height = swap_endian(header.be_height);
+ img.channels = ~~header.channels;
+ img.data = make([] u8, img.width * img.height * img.channels, allocator=allocator);
+
+ out_pos := 0;
+
+ prev_pixel := Color.{0, 0, 0, 255};
+ prev_pixels: [64] Color;
+
+ while out_pos < img.data.count {
+ tag := buf[pos];
+ if tag == OP_RGB {
+ prev_pixel.r = buf[pos + 1];
+ prev_pixel.g = buf[pos + 2];
+ prev_pixel.b = buf[pos + 3];
+ output_color(prev_pixel);
+ pos += 4;
+
+ } elseif tag == OP_RGBA {
+ prev_pixel.r = buf[pos + 1];
+ prev_pixel.g = buf[pos + 2];
+ prev_pixel.b = buf[pos + 3];
+ prev_pixel.a = buf[pos + 4];
+ output_color(prev_pixel);
+ pos += 5;
+
+ } elseif tag & OP_MASK == OP_INDEX {
+ index := tag & ~~0x3f;
+
+ p := prev_pixels[index];
+ output_color(p);
+
+ pos += 1;
+ prev_pixel = p;
+
+ } elseif tag & OP_MASK == OP_DIFF {
+ dr := cast(i32) ((tag & ~~0x30) >> 4) - 2;
+ dg := cast(i32) ((tag & ~~0x0c) >> 2) - 2;
+ db := cast(i32) ((tag & ~~0x03) ) - 2;
+
+ p := prev_pixel;
+ p.r = ~~(~~p.r + dr); @TODO // check that these wrap properly
+ p.g = ~~(~~p.g + dg); @TODO // check that these wrap properly
+ p.b = ~~(~~p.b + db); @TODO // check that these wrap properly
+
+ output_color(p);
+
+ pos += 1;
+ prev_pixel = p;
+
+ } elseif tag & OP_MASK == OP_LUMA {
+ dg := cast(i32) ((buf[pos + 0] & ~~0x3f) ) - 32;
+ dr_dg := cast(i32) ((buf[pos + 1] & ~~0xf0) >> 4) - 8;
+ db_dg := cast(i32) ((buf[pos + 1] & ~~0x0f) ) - 8;
+
+ p := prev_pixel;
+ p.r = ~~(~~p.r + dr_dg + dg); @TODO // check that these wrap properly
+ p.g = ~~(~~p.g + dg); @TODO // check that these wrap properly
+ p.b = ~~(~~p.b + db_dg + dg); @TODO // check that these wrap properly
+
+ output_color(p);
+
+ pos += 2;
+ prev_pixel = p;
+
+ } elseif tag & OP_MASK == OP_RUN {
+ len := (tag & ~~0x3f) + 1;
+ for cast(u32) len {
+ output_color(prev_pixel);
+ }
+ pos += 1;
+ }
+
+ prev_pixels[hash_for_index(prev_pixel)] = prev_pixel;
+ }
+
+ return img;
+}
+
+#local {
+ OP_RGB :: cast(u8) 0xfe
+ OP_RGBA :: cast(u8) 0xff
+
+ OP_MASK :: cast(u8) 0xc0
+ OP_INDEX :: cast(u8) 0x00
+ OP_DIFF :: cast(u8) 0x40
+ OP_LUMA :: cast(u8) 0x80
+ OP_RUN :: cast(u8) 0xc0
+}
+
+#local hash_for_index :: macro (c: Color) -> u32 {
+ return ~~ ((c.r * 3 + c.g * 5 + c.b * 7 + c.a * 11) % 64);
+}
+
+#local swap_endian :: macro (x: u32) -> u32 {
+ y := x;
+ return (((y >> 24) & 0xff) )
+ | (((y >> 16) & 0xff) << 8 )
+ | (((y >> 8) & 0xff) << 16)
+ | (((y >> 0) & 0xff) << 24);
+}
+
+#local output_color :: macro (c: Color) {
+ if header.colorspace == 1 {
+ img.data[out_pos+0], img.data[out_pos+1], img.data[out_pos+2] = srgb_to_rgb(c);
+
+ } else {
+ img.data[out_pos + 0] = c.r;
+ img.data[out_pos + 1] = c.g;
+ img.data[out_pos + 2] = c.b;
+ }
+
+ if img.channels == 4 do img.data[out_pos + 3] = c.a;
+ out_pos += img.channels;
+}
+
+#local srgb_to_rgb :: macro (c: Color) -> (r: u8, g: u8, b: u8) {
+ sr := cast(f32) cast(u32) c.r / 255;
+ sg := cast(f32) cast(u32) c.g / 255;
+ sb := cast(f32) cast(u32) c.b / 255;
+
+ r := (sr / 12.92) if sr <= 0.0405 else math.pow((sr + 0.055)/1.055, 2.4);
+ g := (sg / 12.92) if sg <= 0.0405 else math.pow((sg + 0.055)/1.055, 2.4);
+ b := (sb / 12.92) if sb <= 0.0405 else math.pow((sb + 0.055)/1.055, 2.4);
+
+ or := cast(u8) cast(u32) math.floor(r * 255);
+ og := cast(u8) cast(u32) math.floor(g * 255);
+ ob := cast(u8) cast(u32) math.floor(b * 255);
+ return or, og, ob;
+}
Type* node_type = get_expression_type(node);
if (types_are_compatible(node_type, type)) return TYPE_MATCH_SUCCESS;
- i64 any_id = type_build_from_ast(context.ast_alloc, builtin_any_type)->id;
+ Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
+ if (any_type == NULL) return TYPE_MATCH_YIELD;
+ i64 any_id = any_type->id;
+
if (node_type && node_type->id != any_id && type->id == any_id) return TYPE_MATCH_SUCCESS;
// Here are some of the ways you can unify a node with a type if the type of the
case Ast_Kind_Alias:
case Ast_Kind_Code_Block:
case Ast_Kind_Macro:
- case Ast_Kind_File_Contents:
case Ast_Kind_Symbol:
case Ast_Kind_Poly_Struct_Type:
case Ast_Kind_Basic_Type:
C(AstDoBlock, block);
((AstDoBlock *) nn)->type_node = (AstType *) &basic_type_auto_return;
break;
+
+ case Ast_Kind_File_Contents:
+ C(AstFileContents, filename_expr);
+ E(nn);
+ break;
}
clone_depth--;
if (parse_possible_directive(parser, "file_contents")) {
AstFileContents* fc = make_node(AstFileContents, Ast_Kind_File_Contents);
fc->token = parser->prev - 1;
- fc->filename_token = expect_token(parser, Token_Type_Literal_String);
+ fc->filename_expr = parse_expression(parser, 0);
fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
- ENTITY_SUBMIT(fc);
+ if (parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0) {
+ bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) fc);
+
+ } else {
+ ENTITY_SUBMIT(fc);
+ }
retval = (AstTyped *) fc;
break;
// This makes a lot of assumptions about how these nodes are being processed,
// and I don't want to start using this with other nodes without considering
// what the ramifications of that is.
- assert((*node)->kind == Ast_Kind_Static_If);
+ assert((*node)->kind == Ast_Kind_Static_If || (*node)->kind == Ast_Kind_File_Contents);
// Need to curr_scope->parent because curr_scope is the function body scope.
Scope *scope = curr_scope->parent;
return Symres_Goto_Parse;
}
+static SymresStatus symres_file_contents(AstFileContents* fc) {
+ SYMRES(expression, &fc->filename_expr);
+
+ if (fc->filename_expr->kind != Ast_Kind_StrLit) {
+ onyx_report_error(fc->token->pos, Error_Critical, "Expected given expression to be a compile-time stirng literal.");
+ return Symres_Error;
+ }
+
+ return Symres_Success;
+}
+
void symres_entity(Entity* ent) {
if (ent->scope) scope_enter(ent->scope);
case Entity_Type_Load_Path:
case Entity_Type_Load_File: ss = symres_include(ent->include); break;
+ case Entity_Type_File_Contents: ss = symres_file_contents(ent->file_contents); break;
case Entity_Type_Foreign_Function_Header:
case Entity_Type_Temp_Function_Header:
}
static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
- token_toggle_end(fc->filename_token);
-
// INVESTIGATE: I think that filename should always be NULL when this function starts because
// a file contents entity is only processed once and therefore should only go through this step
// once. But somehow filename isn't NULL occasionally so I have to check for that...
if (parent_file == NULL) parent_file = ".";
char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
+
+ OnyxToken *filename_token = fc->filename_expr->token;
- char* temp_fn = bh_alloc_array(global_scratch_allocator, char, fc->filename_token->length);
- i32 temp_fn_len = string_process_escape_seqs(temp_fn, fc->filename_token->text, fc->filename_token->length);
+ token_toggle_end(filename_token);
+ char* temp_fn = bh_alloc_array(global_scratch_allocator, char, filename_token->length);
+ i32 temp_fn_len = string_process_escape_seqs(temp_fn, filename_token->text, filename_token->length);
char* filename = bh_lookup_file(temp_fn, parent_folder, "", 0, NULL, 0);
fc->filename = bh_strdup(global_heap_allocator, filename);
+ token_toggle_end(filename_token);
}
- token_toggle_end(fc->filename_token);
-
i32 index = shgeti(mod->loaded_file_info, fc->filename);
if (index != -1) {
StrLitInfo info = mod->loaded_file_info[index].value;
bh_align(offset, 16);
if (!bh_file_exists(fc->filename)) {
- onyx_report_error(fc->filename_token->pos, Error_Critical,
+ onyx_report_error(fc->token->pos, Error_Critical,
"Unable to open file for reading, '%s'.",
fc->filename);
return;