added intrinsic interfaces
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 4 Dec 2022 18:43:32 +0000 (12:43 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 4 Dec 2022 18:43:32 +0000 (12:43 -0600)
compiler/include/astnodes.h
compiler/src/astnodes.c
compiler/src/checker.c
compiler/src/parser.c
core/container/iter.onyx
core/intrinsics/type_interfaces.onyx [new file with mode: 0644]
core/misc/arg_parse.onyx
core/std.onyx
tests/aoc-2021/day07.onyx
tests/aoc-2021/day12.onyx
tests/aoc-2021/day13.onyx

index ae5b9d2991e2785ae1bfdc396f391e9f99c030ea..fbfa4acc845f8e5f62fd59431038aa9c52d0e8dd 100644 (file)
@@ -1113,6 +1113,8 @@ struct AstInterface {
 
     bh_arr(InterfaceParam)      params;
     bh_arr(InterfaceConstraint) exprs;
+
+    b32 is_intrinsic: 1;
 };
 
 typedef enum ConstraintPhase {
@@ -1792,6 +1794,8 @@ AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* c
 
 Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed);
 
+b32 resolve_intrinsic_interface_constraint(AstConstraint *constraint);
+
 // NOTE: Useful inlined functions
 static inline b32 is_lval(AstNode* node) {
     node = strip_aliases(node);
index 0c2a72b833edd559763e22ce924b5bb277b831ec..755ae7faa6321111e8c4c86b882a19db707af1ee 100644 (file)
@@ -614,7 +614,11 @@ TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
                 break;
             }
 
-            default: assert(0);
+            // This is not the right thing to return here. In theory it should
+            // try to extract the type of the first element of the array and then
+            // use that as the `array_type`. Then only if there are no elements in the
+            // array should it return TYPE_MATCH_FAILED;
+            default: return TYPE_MATCH_FAILED;
         }
 
         node->type = array_type;
@@ -1587,3 +1591,30 @@ AstPolyCallType* convert_call_to_polycall(AstCall* call) {
 
     return pct;
 }
+
+
+b32 resolve_intrinsic_interface_constraint(AstConstraint *constraint) {
+    AstInterface *interface = constraint->interface;
+    Type* type = type_build_from_ast(context.ast_alloc, constraint->type_args[0]);
+    if (!type) return 0;
+
+    if (!strcmp(interface->name, "type_is_bool"))     return type_is_bool(type);
+    if (!strcmp(interface->name, "type_is_int"))      return type_is_integer(type);
+    if (!strcmp(interface->name, "type_is_float"))    return type->kind == Type_Kind_Basic && (type->Basic.flags & Basic_Flag_Float);
+    if (!strcmp(interface->name, "type_is_number"))   return type->kind == Type_Kind_Basic && (type->Basic.flags & Basic_Flag_Numeric);
+    if (!strcmp(interface->name, "type_is_simd"))     return type->kind == Type_Kind_Basic && (type->Basic.flags & Basic_Flag_SIMD);
+    if (!strcmp(interface->name, "type_is_pointer"))  return type_is_pointer(type) || type_is_rawptr(type);
+    if (!strcmp(interface->name, "type_is_enum"))     return type->kind == Type_Kind_Enum;
+    if (!strcmp(interface->name, "type_is_simple"))   return type->kind == Type_Kind_Basic
+                                                          || type->kind == Type_Kind_Enum
+                                                          || type->kind == Type_Kind_Pointer;
+    if (!strcmp(interface->name, "type_is_array"))    return type->kind == Type_Kind_Array;
+    if (!strcmp(interface->name, "type_is_slice"))    return type->kind == Type_Kind_Slice;
+    if (!strcmp(interface->name, "type_is_struct"))   return type->kind == Type_Kind_Struct;
+    if (!strcmp(interface->name, "type_is_compound")) return type->kind == Type_Kind_Array
+                                                          || type->kind == Type_Kind_Slice
+                                                          || type->kind == Type_Kind_DynArray
+                                                          || type->kind == Type_Kind_Struct;
+
+    return 0;
+}
index edd0427442f38a251c67ace70ee1339118c531de..113682f45811a26baf5339f0b643e1d6045cdbef 100644 (file)
@@ -3011,6 +3011,18 @@ CheckStatus check_constraint(AstConstraint *constraint) {
                     onyx_ast_node_kind_string(constraint->interface->kind));
             }
 
+            // #intrinsic interfaces
+            if (constraint->interface->is_intrinsic) {
+                b32 success = resolve_intrinsic_interface_constraint(constraint);
+                if (success) {
+                    *constraint->report_status = Constraint_Check_Status_Success;
+                    return Check_Complete;
+                } else {
+                    *constraint->report_status = Constraint_Check_Status_Failed;
+                    return Check_Failed;
+                }
+            }
+
             bh_arr_new(global_heap_allocator, constraint->exprs, bh_arr_length(constraint->interface->exprs));
             bh_arr_each(InterfaceConstraint, ic, constraint->interface->exprs) {
                 InterfaceConstraint new_ic = {0};
@@ -3154,7 +3166,14 @@ CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFi
                         strncat(constraint_map, "'", 511);
                     }
 
-                    onyx_report_error(constraint->exprs[constraint->expr_idx].expr->token->pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map);
+                    OnyxFilePos error_pos;
+                    if (constraint->exprs) {
+                        error_pos = constraint->exprs[constraint->expr_idx].expr->token->pos;
+                    } else {
+                        error_pos = constraint->interface->token->pos;
+                    }
+
+                    onyx_report_error(error_pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map);
                     onyx_report_error(constraint->token->pos, Error_Critical, "Here is where the interface was used.");
                     onyx_report_error(pos, Error_Critical, "Here is the code that caused this constraint to be checked.");
 
index e79b6acd4b8d383c6d19919796a9bc330c13479e..77dbcddbf6abb187959ea6c86c50cd7fef8e6d20 100644 (file)
@@ -2192,6 +2192,11 @@ static AstInterface* parse_interface(OnyxParser* parser) {
             expect_token(parser, ',');
     }
 
+    if (parse_possible_directive(parser, "intrinsic")) {
+        interface->is_intrinsic = 1;
+        return interface;
+    }
+
     bh_arr_new(global_heap_allocator, interface->exprs, 2);
 
     expect_token(parser, '{');
index 4141f1ff00a7c71d60cb9454313bc8697ba6ae49..77f7bc38e1d2770a90333542b8fbf34662338788 100644 (file)
@@ -453,9 +453,21 @@ enumerate :: (it: Iterator($T), start_index: i32 = 0) -> Iterator(Enumeration_Va
     };
 }
 
+use core.intrinsics.types {type_is_struct}
+
+//
+// `from_array` has two almost identical implementations,
+// but the details are important here. Normally, `from_array`
+// returns an iterator by value, unless the array is of
+// structures, then it returns an iterator by pointer.
+// This seems weird, but in practice it is closer to what
+// you want, as you don't want to have to copy every structure
+// out of the array. While for primitives, you don't want to
+// dereference it everywhere.
+from_array :: #match #local {}
+
 #overload
-as_iterator :: from_array
-from_array :: (arr: [] $T) => generator(
+from_array :: (arr: [] $T/type_is_struct) => generator(
     ^.{ data = arr.data, count = arr.count, current = 0 },
 
     (use ctx: ^$T) -> (typeof ctx.data, bool) {
@@ -468,6 +480,24 @@ from_array :: (arr: [] $T) => generator(
     }
 );
 
+#overload
+from_array :: (arr: [] $T) => generator(
+    ^.{ data = arr.data, count = arr.count, current = 0 },
+
+    (use ctx: ^$T) -> (typeof *ctx.data, bool) {
+        if current < count {
+            defer current += 1;
+            return data[current], true;
+        }
+
+        return .{}, false;
+    }
+);
+
+
+#overload
+as_iterator :: from_array
+
 #overload
 as_iterator :: (x: ^[..] $T) -> Iterator(T) {
     Context :: struct (T: type_expr) {
diff --git a/core/intrinsics/type_interfaces.onyx b/core/intrinsics/type_interfaces.onyx
new file mode 100644 (file)
index 0000000..c60d59a
--- /dev/null
@@ -0,0 +1,26 @@
+package core.intrinsics.types
+
+//
+// Simple: bool, number, simd, pointer, enum
+// Number: int, float
+// Int: u8, .., u64  i8, .., i64
+// Float: f32 f64
+// Simd: v128 i8x16 i16x8 i32x4 i64x2 f32x4 f64x2
+// Pointer: ^T rawptr
+// Compound: array, slice, struct
+// Array: [$N] $T
+// Slice: [] $T
+
+type_is_bool     :: interface (t: $T) #intrinsic
+type_is_int      :: interface (t: $T) #intrinsic
+type_is_float    :: interface (t: $T) #intrinsic
+type_is_number   :: interface (t: $T) #intrinsic
+type_is_simd     :: interface (t: $T) #intrinsic
+type_is_pointer  :: interface (t: $T) #intrinsic
+type_is_enum     :: interface (t: $T) #intrinsic
+type_is_simple   :: interface (t: $T) #intrinsic
+type_is_array    :: interface (t: $T) #intrinsic
+type_is_slice    :: interface (t: $T) #intrinsic
+type_is_struct   :: interface (t: $T) #intrinsic
+type_is_compound :: interface (t: $T) #intrinsic
+
index cde4b24e37f322b79041c75300683b782bec1257..43dc396f00b20c14a166440b55c08906160085ec 100644 (file)
@@ -4,7 +4,7 @@ use core
 
 arg_parse :: (c_args: [] cstr, output: any) -> bool {
     arg_iter := iter.as_iterator(c_args)
-             |> iter.map((x) => string.from_cstr(*x));
+             |> iter.map(string.from_cstr);
     defer arg_iter.close(arg_iter.data);
 
     use runtime.info;
index 1537974f660b70dd57a95888fa719495e2ff250a..1b03d81123ef2a3f8a437d84402bf87430ef76a6 100644 (file)
@@ -29,6 +29,7 @@ package core
 
 #load "./intrinsics/onyx"
 #load "./intrinsics/wasm"
+#load "./intrinsics/type_interfaces"
 
 #load "./io/io"
 #load "./io/stream"
index 0e96fdaaa5dbee9acd561c53bb295cb8c4429e38..f4fb8c081b8bdc0b127c4d5c771ddb07279e13d2 100644 (file)
@@ -8,7 +8,7 @@ main :: (args) => {
         nums := io.read_all(^reader)
             |> string.split(#char ",")
             |> iter.as_iterator()
-            |> iter.map((x) => cast(i32) conv.str_to_i64(*x))
+            |> iter.map((x) => cast(i32) conv.parse_int(x))
             |> iter.to_array();
 
         min := array.fold(nums, nums[0], math.min);
index 2837716a6c289292c3c5670c1687715e95774cde..51d30757e73de96536ab7112b630709d4bdc3a8e 100644 (file)
@@ -47,11 +47,11 @@ main :: (args) => {
             return iter.concat(
                 iter.as_iterator(edges)
                 |> iter.filter(name_copy, (x, n) => x.a == *n)
-                |> iter.map((x) => x.b),
+                |> iter.map(x => x.b),
 
                 iter.as_iterator(edges)
                 |> iter.filter(name_copy, (x, n) => x.b == *n)
-                |> iter.map((x) => x.a)
+                |> iter.map(x => x.a)
             );
         }
 
index 163339ea454119a3989295c0f813abcd5a5a5be9..ed19deb5ae1d193c1cc9c08df418d65b90f70f11 100644 (file)
@@ -32,9 +32,8 @@ main :: (args) => {
             string.strip_whitespace(^line);
             if line.count == 0 do break;
 
-            parts := string.split(line, #char ",", allocator=context.temp_allocator)
-                    |> iter.as_iterator()
-                    |> iter.map((x) => cast(i32) conv.str_to_i64(*x));
+            parts := string.split_iter(line, #char ",")
+                    |> iter.map(x => cast(i32) conv.parse_int(x));
             
             x, _ := iter.take_one(parts);
             y, _ := iter.take_one(parts);