Actually just three programming languages in a trenchcoat
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

start implementing `in` queries, but found bugs

+296 -126
+12
TODO.md
··· 1 + # TODO 2 + 3 + * Array/Set/Record iteration (`for value in iterable`) 4 + * Runtime new atom (beware Ruby symbol problem) 5 + * `defer` statement 6 + * `test` definitions 7 + * Rethink `const` (`slot` and `slot mut`?) 8 + * Upgrade `for` to be a folding construct 9 + * Fix `break`/`continue` in various areas 10 + * Finish FFI 11 + * Build real standard library 12 + * Fix JIT mode, for "normal" use
+15
testsuite/query-in/main.tri
··· 1 + import "trilogy:debug" use dbg 2 + 3 + proc main!() { 4 + for x in [1, 2, 3] { 5 + dbg!(x) 6 + } 7 + 8 + for x in [|4, 4, 4, 5, 5|] { 9 + dbg!(x) 10 + } 11 + 12 + for x in {| 'a => 1, 'b => 2 |} { 13 + dbg!(x) 14 + } 15 + }
+1
testsuite/query-in/spec.toml
··· 1 + output = "1\n2\n3\n4\n5\na:1\nb:2\n"
+11
testsuite/rule-recursive/main.tri
··· 1 + import "trilogy:debug" use dbg 2 + 3 + rule array_iterate(element, [element, .._]) 4 + rule array_iterate(element, [_, ..rest]) <- array_iterate(element, rest) 5 + rule array_iterate(_, []) <- end 6 + 7 + proc main!() { 8 + for array_iterate(x, [1, 2, 3]) { 9 + dbg!(x) 10 + } 11 + }
+1
testsuite/rule-recursive/spec.toml
··· 1 + output = "1\n2\n3\n"
+11
trilogy-llvm/core/core.c
··· 6 6 #include "trilogy_character.h" 7 7 #include "trilogy_number.h" 8 8 #include "trilogy_record.h" 9 + #include "trilogy_set.h" 9 10 #include "trilogy_string.h" 10 11 #include "trilogy_struct.h" 11 12 #include "trilogy_tuple.h" ··· 425 426 trilogy_string_value* rhs_str = trilogy_string_assume(rhs); 426 427 return trilogy_string_unglue_end(rv, lhs_str, rhs_str); 427 428 } 429 + 430 + void set_to_array(trilogy_value* rv, trilogy_value* set_val) { 431 + trilogy_set_value* set = trilogy_set_untag(set_val); 432 + trilogy_set_to_array(rv, set); 433 + } 434 + 435 + void record_to_array(trilogy_value* rv, trilogy_value* record_val) { 436 + trilogy_record_value* record = trilogy_record_untag(record_val); 437 + trilogy_record_to_array(rv, record); 438 + }
+3
trilogy-llvm/core/core.h
··· 68 68 69 69 void lookup_atom(trilogy_value* rv, trilogy_value* atom); 70 70 void to_string(trilogy_value* rv, trilogy_value* val); 71 + 72 + void set_to_array(trilogy_value* rv, trilogy_value* val); 73 + void record_to_array(trilogy_value* rv, trilogy_value* val);
+15
trilogy-llvm/core/trilogy_record.c
··· 1 1 #include "trilogy_record.h" 2 2 #include "internal.h" 3 + #include "trilogy_array.h" 3 4 #include "trilogy_tuple.h" 4 5 #include "trilogy_value.h" 5 6 #include "types.h" ··· 234 235 } 235 236 return true; 236 237 } 238 + 239 + trilogy_array_value* 240 + trilogy_record_to_array(trilogy_value* tv, trilogy_record_value* record) { 241 + trilogy_array_value* arr = trilogy_array_init_cap(tv, record->len); 242 + for (uint64_t i = 0; i < record->cap; ++i) { 243 + trilogy_tuple_value* entry = &record->contents[i]; 244 + if (entry->fst.tag == TAG_UNDEFINED) continue; 245 + trilogy_value val = trilogy_undefined; 246 + trilogy_tuple_clone_into(&val, entry); 247 + trilogy_array_push(arr, &val); 248 + } 249 + assert(arr->len == record->len); 250 + return arr; 251 + }
+3
trilogy-llvm/core/trilogy_record.h
··· 34 34 ); 35 35 36 36 void trilogy_record_destroy(trilogy_record_value* rec); 37 + 38 + trilogy_array_value* 39 + trilogy_record_to_array(trilogy_value* tv, trilogy_record_value* rec);
+15
trilogy-llvm/core/trilogy_set.c
··· 1 1 #include "trilogy_set.h" 2 2 #include "internal.h" 3 + #include "trilogy_array.h" 3 4 #include "trilogy_value.h" 4 5 #include "types.h" 5 6 #include <assert.h> ··· 201 202 } 202 203 return true; 203 204 } 205 + 206 + trilogy_array_value* 207 + trilogy_set_to_array(trilogy_value* tv, trilogy_set_value* set) { 208 + trilogy_array_value* arr = trilogy_array_init_cap(tv, set->len); 209 + for (uint64_t i = 0; i < set->cap; ++i) { 210 + trilogy_tuple_value* entry = &set->contents[i]; 211 + if (entry->fst.tag == TAG_UNDEFINED) continue; 212 + trilogy_value val = trilogy_undefined; 213 + trilogy_value_clone_into(&val, &entry->fst); 214 + trilogy_array_push(arr, &val); 215 + } 216 + assert(arr->len == set->len); 217 + return arr; 218 + }
+4 -1
trilogy-llvm/core/trilogy_set.h
··· 23 23 trilogy_set_value* trilogy_set_untag(trilogy_value* val); 24 24 trilogy_set_value* trilogy_set_assume(trilogy_value* val); 25 25 26 - void trilogy_set_destroy(trilogy_set_value* arr); 26 + void trilogy_set_destroy(trilogy_set_value* set); 27 + 28 + trilogy_array_value* 29 + trilogy_set_to_array(trilogy_value* tv, trilogy_set_value* set);
+31
trilogy-llvm/core/types.h
··· 263 263 trilogy_array_value* closure; 264 264 } trilogy_module; 265 265 266 + /** 267 + * The foreign object works/appears as a module in Trilogy, but internally is 268 + * backed by some native stuff instead. The contents takes the place of the 269 + * closure, and is received as the last parameter when calling methods. 270 + * 271 + * RAII is not a thing, the contents are possibly unclosed resources, 272 + * the Trilogy code should correctly open/close them if needed. 273 + */ 274 + typedef struct trilogy_foreign_object { 275 + /** 276 + * The reference count for this foreign object instance. 277 + */ 278 + uint32_t rc; 279 + /** 280 + * The number of members in this foreign object. 281 + */ 282 + size_t len; 283 + /** 284 + * The sorted list of IDs for members in this foreign object. 285 + */ 286 + uint64_t* member_ids; 287 + /** 288 + * The actual pointers to contained member accessors. 289 + */ 290 + void** members; 291 + /** 292 + * Pointer to the contained foreign object. 293 + */ 294 + void* contents; 295 + } trilogy_foreign_object; 296 + 266 297 char* type_name(trilogy_value_tag tag);
+1 -1
trilogy-llvm/src/call.rs
··· 200 200 break_to, 201 201 continue_to, 202 202 next_to, 203 - done_to, 203 + done_to, // NOTE: done_to may be a variable that gets closed and becomes invalid above 204 204 ]); 205 205 args.extend_from_slice(arguments); 206 206
+4
trilogy-llvm/src/core.rs
··· 131 131 let f = self.declare_core("bitwise_invert", 1); 132 132 self.call_core(target, f, &[value.into()]); 133 133 } 134 + 135 + pub(crate) fn elem(&self) -> PointerValue<'ctx> { 136 + self.get_core("elem") 137 + } 134 138 }
+7 -7
trilogy-llvm/src/expression/mod.rs
··· 140 140 let done_function = self.add_continuation("done"); 141 141 let (done_continuation, done_continuation_point) = 142 142 self.capture_current_continuation_as_break(done_function, "for_break"); 143 - let next_iteration = self.compile_iterator(&expr.query, done_continuation)?; 143 + let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?; 144 144 self.bind_temporary(next_iteration); 145 145 if let Some(value) = self.compile_expression(&expr.value, name) { 146 146 let next_iteration = self.use_temporary(next_iteration).unwrap(); ··· 169 169 let done_function = self.add_continuation("done"); 170 170 let (done_continuation, done_continuation_point) = 171 171 self.capture_current_continuation_as_break(done_function, "for_break"); 172 - let next_iteration = self.compile_iterator(&expr.query, done_continuation)?; 172 + let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?; 173 173 self.bind_temporary(next_iteration); 174 174 if let Some(value) = self.compile_expression(&expr.value, "element") { 175 175 let arr_val = self.use_temporary(output).unwrap(); ··· 198 198 let done_function = self.add_continuation("done"); 199 199 let (done_continuation, done_continuation_point) = 200 200 self.capture_current_continuation_as_break(done_function, "for_break"); 201 - let next_iteration = self.compile_iterator(&expr.query, done_continuation)?; 201 + let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?; 202 202 self.bind_temporary(next_iteration); 203 203 if let Some(value) = self.compile_expression(&expr.value, "element") { 204 204 let set_val = self.use_temporary(output).unwrap(); ··· 227 227 let done_function = self.add_continuation("done"); 228 228 let (done_continuation, done_continuation_point) = 229 229 self.capture_current_continuation_as_break(done_function, "for_break"); 230 - let next_iteration = self.compile_iterator(&expr.query, done_continuation)?; 230 + let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?; 231 231 self.bind_temporary(next_iteration); 232 232 let Value::Mapping(mapping) = &expr.value.value else { 233 233 unreachable!() ··· 479 479 // case. Appears to be some cleanup that is being missed. 480 480 let end = self.get_end(""); 481 481 self.bind_temporary(end); 482 - let next_to = self.compile_iterator(&decl.query, end)?; 482 + let next_to = self.compile_query_iteration(&decl.query, end)?; 483 483 self.push_execution(next_to); 484 484 self.compile_expression(&decl.body, name) 485 - }, 485 + } 486 486 } 487 487 } 488 488 ··· 564 564 let end_false_fn = self.add_continuation("is_false"); 565 565 let (end_false_cont, end_false_cp) = 566 566 self.capture_current_continuation(end_false_fn, "is_false"); 567 - let next = self.compile_iterator(query, end_false_cont)?; 567 + let next = self.compile_query_iteration(query, end_false_cont)?; 568 568 self.trilogy_value_destroy(next); 569 569 let result = self.allocate_const(self.bool_const(true), ""); 570 570 self.call_known_continuation(self.use_temporary(merge_to_cont).unwrap(), result);
+133 -115
trilogy-llvm/src/query.rs
··· 4 4 use trilogy_ir::{Id, ir}; 5 5 6 6 impl<'ctx> Codegen<'ctx> { 7 - pub(crate) fn compile_iterator( 7 + pub(crate) fn compile_query_iteration( 8 8 &self, 9 9 query: &ir::Query, 10 10 done_to: PointerValue<'ctx>, ··· 90 90 // NOTE: at this time, the lookup expression is not an arbitrary expression, only a 91 91 // reference/module path, so branching is not possible here. 92 92 let rule = self.compile_expression(&lookup.path, "rule")?; 93 - // NOTE: similarly, the arguments of a query must be syntactically both patterns and 94 - // expressions, so we can again guarantee that branching is not possible. 95 - let arguments = lookup 96 - .patterns 97 - .iter() 98 - .map(|pattern| { 99 - if !pattern.can_evaluate() { 100 - // This pattern is not possibly an expression, e.g. due to containing a wildcard 101 - // or other pattern-only syntax element. It can only be used as an output parameter. 102 - let arg = self.allocate_undefined("out_arg"); 103 - self.bind_temporary(arg); 104 - return Some(arg); 105 - } 106 - let variables = pattern 107 - .bindings() 108 - .iter() 109 - .map(|var| self.get_variable(var).unwrap()) 110 - .collect::<Vec<_>>(); 111 - if variables.is_empty() { 112 - let arg = self.compile_expression(pattern, "in_arg")?; 113 - self.bind_temporary(arg); 114 - return Some(arg); 115 - } 116 - // This pattern is (potentially) fully defined. We must confirm by checking all 117 - // the variables at runtime, and if they are all set, then we can construct 118 - // this pattern. 119 - let out_block = self.context.append_basic_block(self.get_function(), "out"); 120 - let original_function = self.get_function(); 121 - let snapshot = self.snapshot_function_context(); 122 - for variable in variables { 123 - let next_arg = 124 - self.context.append_basic_block(self.get_function(), "next"); 125 - self.branch_undefined(variable.ptr(), out_block, next_arg); 126 - self.builder.position_at_end(next_arg); 127 - } 128 - let in_arg = self.compile_expression(pattern, "in_arg")?; 129 - let final_function = self.get_function(); 130 - if original_function == final_function { 131 - let merge_block = 132 - self.context.append_basic_block(final_function, "merge_arg"); 133 - 134 - let in_block = self.builder.get_insert_block().unwrap(); 135 - self.builder 136 - .build_unconditional_branch(merge_block) 137 - .unwrap(); 138 - 139 - self.builder.position_at_end(out_block); 140 - let out_arg = self.allocate_undefined("out_arg"); 141 - self.builder 142 - .build_unconditional_branch(merge_block) 143 - .unwrap(); 144 - 145 - self.builder.position_at_end(merge_block); 146 - let phi = self 147 - .builder 148 - .build_phi(self.context.ptr_type(AddressSpace::default()), "arg") 149 - .unwrap(); 150 - phi.add_incoming(&[(&in_arg, in_block), (&out_arg, out_block)]); 151 - let arg = phi.as_basic_value().into_pointer_value(); 152 - self.bind_temporary(arg); 153 - Some(arg) 154 - } else { 155 - let arg_continuation = self.add_continuation("arg"); 156 - let end = self.continue_in_scope(arg_continuation, in_arg); 157 - self.end_continuation_point_as_close(end); 158 - 159 - self.restore_function_context(snapshot); 160 - self.builder.position_at_end(out_block); 161 - let end = self.void_continue_in_scope(arg_continuation); 162 - self.end_continuation_point_as_close(end); 163 - 164 - self.begin_next_function(arg_continuation); 165 - let arg = self.get_continuation("arg"); 166 - self.bind_temporary(arg); 167 - Some(arg) 168 - } 169 - }) 170 - .collect::<Option<Vec<_>>>()?; 171 - 172 - let (next_iteration_inner, output_arguments) = self.call_rule( 173 - rule, 174 - &arguments, 175 - self.use_temporary(done_to).unwrap(), 176 - "lookup_next", 177 - ); 178 - self.bind_temporary(next_iteration_inner); 179 - 180 - // Wrap the next iteration with our own, as a lookup requires some cleanup 181 - // before starting its next internal iteration. 182 - let next_iteration_with_cleanup = self.add_continuation("lookup.next"); 183 - let (next_iteration_with_cleanup_continuation, next_iteration_with_cleanup_cp) = 184 - self.capture_current_continuation(next_iteration_with_cleanup, "lookup.next"); 185 - 186 - let bound_before_lookup = bound_ids.len(); 187 - for (pattern, out_value) in lookup.patterns.iter().zip(output_arguments) { 188 - let out_value = self.use_temporary(out_value).unwrap(); 189 - self.compile_pattern_match_with_bindings( 190 - pattern, 191 - out_value, 192 - next_iteration_with_cleanup_continuation, 193 - bound_ids, 194 - )?; 195 - } 196 - 197 - self.call_known_continuation( 198 - self.use_temporary(next_to).unwrap(), 199 - next_iteration_with_cleanup_continuation, 200 - ); 201 - 202 - self.become_continuation_point(next_iteration_with_cleanup_cp); 203 - self.begin_next_function(next_iteration_with_cleanup); 204 - self.cleanup_go_next(next_iteration_inner, bound_ids, bound_before_lookup); 93 + self.perform_lookup(rule, &lookup.patterns, next_to, done_to, bound_ids)?; 205 94 } 206 95 ir::QueryValue::Disjunction(disj) => { 207 96 let disj_second_fn = self.add_continuation("disj.second"); ··· 286 175 )?; 287 176 self.next_cleanup(done_to, next_to, bound_ids, pre_len, "assign_next"); 288 177 } 289 - ir::QueryValue::Element(..) => todo!(), 178 + ir::QueryValue::Element(unification) => { 179 + let elem = self.elem(); 180 + self.perform_lookup( 181 + elem, 182 + [&unification.pattern, &unification.expression], 183 + next_to, 184 + done_to, 185 + bound_ids, 186 + )?; 187 + } 290 188 ir::QueryValue::Not(query) => { 291 189 let go_next_fn = self.add_continuation("not.next"); 292 190 let (go_next, go_next_cp) = ··· 328 226 keep_ids: usize, 329 227 name: &str, 330 228 ) { 331 - // Wrap the next iteration with our own, as a lookup requires some cleanup 229 + // Wrap the next iteration with our own, as a lookup or unification requires some cleanup 332 230 // before starting its next internal iteration. 333 231 let next_iteration_with_cleanup = self.add_continuation(name); 334 232 let (next_iteration_with_cleanup_continuation, next_iteration_with_cleanup_cp) = ··· 362 260 } 363 261 let next_iteration = self.use_temporary(next_iteration).unwrap(); 364 262 self.void_call_continuation(next_iteration); 263 + } 264 + 265 + fn perform_lookup<'a>( 266 + &self, 267 + rule: PointerValue<'ctx>, 268 + patterns: impl IntoIterator<Item = &'a ir::Expression> + Copy, 269 + next_to: PointerValue<'ctx>, 270 + done_to: PointerValue<'ctx>, 271 + bound_ids: &mut Vec<Id>, 272 + ) -> Option<()> { 273 + // NOTE: similarly, the arguments of a query must be syntactically both patterns and 274 + // expressions, so we can again guarantee that branching is not possible. 275 + let arguments = patterns 276 + .into_iter() 277 + .map(|pattern| { 278 + if !pattern.can_evaluate() { 279 + // This pattern is not possibly an expression, e.g. due to containing a wildcard 280 + // or other pattern-only syntax element. It can only be used as an output parameter. 281 + let arg = self.allocate_undefined("out_arg"); 282 + self.bind_temporary(arg); 283 + return Some(arg); 284 + } 285 + let variables = pattern 286 + .bindings() 287 + .iter() 288 + .map(|var| self.get_variable(var).unwrap()) 289 + .collect::<Vec<_>>(); 290 + if variables.is_empty() { 291 + let arg = self.compile_expression(pattern, "in_arg")?; 292 + self.bind_temporary(arg); 293 + return Some(arg); 294 + } 295 + // This pattern is (potentially) fully defined. We must confirm by checking all 296 + // the variables at runtime, and if they are all set, then we can construct 297 + // this pattern. 298 + let out_block = self.context.append_basic_block(self.get_function(), "out"); 299 + let original_function = self.get_function(); 300 + let snapshot = self.snapshot_function_context(); 301 + for variable in variables { 302 + let next_arg = self.context.append_basic_block(self.get_function(), "next"); 303 + self.branch_undefined(variable.ptr(), out_block, next_arg); 304 + self.builder.position_at_end(next_arg); 305 + } 306 + let in_arg = self.compile_expression(pattern, "in_arg")?; 307 + let final_function = self.get_function(); 308 + if original_function == final_function { 309 + let merge_block = self.context.append_basic_block(final_function, "merge_arg"); 310 + 311 + let in_block = self.builder.get_insert_block().unwrap(); 312 + self.builder 313 + .build_unconditional_branch(merge_block) 314 + .unwrap(); 315 + 316 + self.builder.position_at_end(out_block); 317 + let out_arg = self.allocate_undefined("out_arg"); 318 + self.builder 319 + .build_unconditional_branch(merge_block) 320 + .unwrap(); 321 + 322 + self.builder.position_at_end(merge_block); 323 + let phi = self 324 + .builder 325 + .build_phi(self.context.ptr_type(AddressSpace::default()), "arg") 326 + .unwrap(); 327 + phi.add_incoming(&[(&in_arg, in_block), (&out_arg, out_block)]); 328 + let arg = phi.as_basic_value().into_pointer_value(); 329 + self.bind_temporary(arg); 330 + Some(arg) 331 + } else { 332 + let arg_continuation = self.add_continuation("arg"); 333 + let end = self.continue_in_scope(arg_continuation, in_arg); 334 + self.end_continuation_point_as_close(end); 335 + 336 + self.restore_function_context(snapshot); 337 + self.builder.position_at_end(out_block); 338 + let end = self.void_continue_in_scope(arg_continuation); 339 + self.end_continuation_point_as_close(end); 340 + 341 + self.begin_next_function(arg_continuation); 342 + let arg = self.get_continuation("arg"); 343 + self.bind_temporary(arg); 344 + Some(arg) 345 + } 346 + }) 347 + .collect::<Option<Vec<_>>>()?; 348 + 349 + let (next_iteration_inner, output_arguments) = self.call_rule( 350 + rule, 351 + &arguments, 352 + self.use_temporary(done_to).unwrap(), 353 + "lookup_next", 354 + ); 355 + self.bind_temporary(next_iteration_inner); 356 + 357 + // Wrap the next iteration with our own, as a lookup requires some cleanup 358 + // before starting its next internal iteration. 359 + let next_iteration_with_cleanup = self.add_continuation("lookup.next"); 360 + let (next_iteration_with_cleanup_continuation, next_iteration_with_cleanup_cp) = 361 + self.capture_current_continuation(next_iteration_with_cleanup, "lookup.next"); 362 + 363 + let bound_before_lookup = bound_ids.len(); 364 + for (pattern, out_value) in patterns.into_iter().zip(output_arguments) { 365 + let out_value = self.use_temporary(out_value).unwrap(); 366 + self.compile_pattern_match_with_bindings( 367 + pattern, 368 + out_value, 369 + next_iteration_with_cleanup_continuation, 370 + bound_ids, 371 + )?; 372 + } 373 + 374 + self.call_known_continuation( 375 + self.use_temporary(next_to).unwrap(), 376 + next_iteration_with_cleanup_continuation, 377 + ); 378 + 379 + self.become_continuation_point(next_iteration_with_cleanup_cp); 380 + self.begin_next_function(next_iteration_with_cleanup); 381 + self.cleanup_go_next(next_iteration_inner, bound_ids, bound_before_lookup); 382 + Some(()) 365 383 } 366 384 }
+7 -2
trilogy-llvm/src/rule.rs
··· 72 72 let (go_to_next_overload, next_overload_cp) = 73 73 self.capture_current_continuation_as_break(next_overload_function, "next_overload"); 74 74 75 + let next_overload_clone = self.allocate_value("next_overload_clone"); 76 + self.trilogy_value_clone_into(next_overload_clone, go_to_next_overload); 77 + self.bind_temporary(next_overload_clone); 78 + 75 79 for (param, &value) in overload.parameters.iter().zip(&input_args) { 76 80 let original_insert_function = self.get_function(); 77 81 let original_snapshot = self.snapshot_function_context(); ··· 124 128 } 125 129 } 126 130 // The rule runs the iterator, resulting in new bindings being set. 127 - let Some(next_iteration) = self.compile_iterator(&overload.body, go_to_next_overload) 131 + let Some(next_iteration) = 132 + self.compile_query_iteration(&overload.body, go_to_next_overload) 128 133 else { 129 134 break 'outer; 130 135 }; ··· 157 162 // If the input and output are both going to be undefined, then go right to the 158 163 // next overload. 159 164 self.builder.position_at_end(fully_unbound); 160 - let next_overload = self.use_temporary(go_to_next_overload).unwrap(); 165 + let next_overload = self.use_temporary(next_overload_clone).unwrap(); 161 166 self.void_call_continuation(next_overload); 162 167 163 168 // There's actually no action for the rebind-input case, only to reuse the argument later
+22
trilogy/src/stdlib/core.tri
··· 126 126 export shift_right_contract 127 127 128 128 extern proc primitive_to_string!(val) 129 + export primitive_to_string 130 + 131 + extern proc set_to_array!(val) 132 + export set_to_array 133 + 134 + extern proc record_to_array!(val) 135 + export record_to_array 129 136 } 130 137 131 138 const print = c::print ··· 229 236 export rem 230 237 func cons lhs rhs = c::cons!(lhs, rhs) 231 238 export cons 239 + 240 + func to_array (arr and typeof 'array) = arr 241 + func to_array (set and typeof 'set) = c::set_to_array!(set) 242 + func to_array (record and typeof 'record) = c::record_to_array!(record) 243 + export to_array 244 + 245 + rule elem(element, [element, .._]) 246 + rule elem(element, [_, ..rest]) <- elem(element, rest) 247 + rule elem(_, []) <- end 248 + rule elem(element, set and typeof 'set) <- 249 + array = c::set_to_array!(set) 250 + and elem(element, array) 251 + rule elem(element, record and typeof 'record) <- array = c::record_to_array!(record) 252 + and elem(element, array) 253 + export elem