···11+# TODO
22+33+* Array/Set/Record iteration (`for value in iterable`)
44+* Runtime new atom (beware Ruby symbol problem)
55+* `defer` statement
66+* `test` definitions
77+* Rethink `const` (`slot` and `slot mut`?)
88+* Upgrade `for` to be a folding construct
99+* Fix `break`/`continue` in various areas
1010+* Finish FFI
1111+* Build real standard library
1212+* Fix JIT mode, for "normal" use
+15
testsuite/query-in/main.tri
···11+import "trilogy:debug" use dbg
22+33+proc main!() {
44+ for x in [1, 2, 3] {
55+ dbg!(x)
66+ }
77+88+ for x in [|4, 4, 4, 5, 5|] {
99+ dbg!(x)
1010+ }
1111+1212+ for x in {| 'a => 1, 'b => 2 |} {
1313+ dbg!(x)
1414+ }
1515+}
···263263 trilogy_array_value* closure;
264264} trilogy_module;
265265266266+/**
267267+ * The foreign object works/appears as a module in Trilogy, but internally is
268268+ * backed by some native stuff instead. The contents takes the place of the
269269+ * closure, and is received as the last parameter when calling methods.
270270+ *
271271+ * RAII is not a thing, the contents are possibly unclosed resources,
272272+ * the Trilogy code should correctly open/close them if needed.
273273+ */
274274+typedef struct trilogy_foreign_object {
275275+ /**
276276+ * The reference count for this foreign object instance.
277277+ */
278278+ uint32_t rc;
279279+ /**
280280+ * The number of members in this foreign object.
281281+ */
282282+ size_t len;
283283+ /**
284284+ * The sorted list of IDs for members in this foreign object.
285285+ */
286286+ uint64_t* member_ids;
287287+ /**
288288+ * The actual pointers to contained member accessors.
289289+ */
290290+ void** members;
291291+ /**
292292+ * Pointer to the contained foreign object.
293293+ */
294294+ void* contents;
295295+} trilogy_foreign_object;
296296+266297char* type_name(trilogy_value_tag tag);
+1-1
trilogy-llvm/src/call.rs
···200200 break_to,
201201 continue_to,
202202 next_to,
203203- done_to,
203203+ done_to, // NOTE: done_to may be a variable that gets closed and becomes invalid above
204204 ]);
205205 args.extend_from_slice(arguments);
206206
···140140 let done_function = self.add_continuation("done");
141141 let (done_continuation, done_continuation_point) =
142142 self.capture_current_continuation_as_break(done_function, "for_break");
143143- let next_iteration = self.compile_iterator(&expr.query, done_continuation)?;
143143+ let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?;
144144 self.bind_temporary(next_iteration);
145145 if let Some(value) = self.compile_expression(&expr.value, name) {
146146 let next_iteration = self.use_temporary(next_iteration).unwrap();
···169169 let done_function = self.add_continuation("done");
170170 let (done_continuation, done_continuation_point) =
171171 self.capture_current_continuation_as_break(done_function, "for_break");
172172- let next_iteration = self.compile_iterator(&expr.query, done_continuation)?;
172172+ let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?;
173173 self.bind_temporary(next_iteration);
174174 if let Some(value) = self.compile_expression(&expr.value, "element") {
175175 let arr_val = self.use_temporary(output).unwrap();
···198198 let done_function = self.add_continuation("done");
199199 let (done_continuation, done_continuation_point) =
200200 self.capture_current_continuation_as_break(done_function, "for_break");
201201- let next_iteration = self.compile_iterator(&expr.query, done_continuation)?;
201201+ let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?;
202202 self.bind_temporary(next_iteration);
203203 if let Some(value) = self.compile_expression(&expr.value, "element") {
204204 let set_val = self.use_temporary(output).unwrap();
···227227 let done_function = self.add_continuation("done");
228228 let (done_continuation, done_continuation_point) =
229229 self.capture_current_continuation_as_break(done_function, "for_break");
230230- let next_iteration = self.compile_iterator(&expr.query, done_continuation)?;
230230+ let next_iteration = self.compile_query_iteration(&expr.query, done_continuation)?;
231231 self.bind_temporary(next_iteration);
232232 let Value::Mapping(mapping) = &expr.value.value else {
233233 unreachable!()
···479479 // case. Appears to be some cleanup that is being missed.
480480 let end = self.get_end("");
481481 self.bind_temporary(end);
482482- let next_to = self.compile_iterator(&decl.query, end)?;
482482+ let next_to = self.compile_query_iteration(&decl.query, end)?;
483483 self.push_execution(next_to);
484484 self.compile_expression(&decl.body, name)
485485- },
485485+ }
486486 }
487487 }
488488···564564 let end_false_fn = self.add_continuation("is_false");
565565 let (end_false_cont, end_false_cp) =
566566 self.capture_current_continuation(end_false_fn, "is_false");
567567- let next = self.compile_iterator(query, end_false_cont)?;
567567+ let next = self.compile_query_iteration(query, end_false_cont)?;
568568 self.trilogy_value_destroy(next);
569569 let result = self.allocate_const(self.bool_const(true), "");
570570 self.call_known_continuation(self.use_temporary(merge_to_cont).unwrap(), result);
+133-115
trilogy-llvm/src/query.rs
···44use trilogy_ir::{Id, ir};
5566impl<'ctx> Codegen<'ctx> {
77- pub(crate) fn compile_iterator(
77+ pub(crate) fn compile_query_iteration(
88 &self,
99 query: &ir::Query,
1010 done_to: PointerValue<'ctx>,
···9090 // NOTE: at this time, the lookup expression is not an arbitrary expression, only a
9191 // reference/module path, so branching is not possible here.
9292 let rule = self.compile_expression(&lookup.path, "rule")?;
9393- // NOTE: similarly, the arguments of a query must be syntactically both patterns and
9494- // expressions, so we can again guarantee that branching is not possible.
9595- let arguments = lookup
9696- .patterns
9797- .iter()
9898- .map(|pattern| {
9999- if !pattern.can_evaluate() {
100100- // This pattern is not possibly an expression, e.g. due to containing a wildcard
101101- // or other pattern-only syntax element. It can only be used as an output parameter.
102102- let arg = self.allocate_undefined("out_arg");
103103- self.bind_temporary(arg);
104104- return Some(arg);
105105- }
106106- let variables = pattern
107107- .bindings()
108108- .iter()
109109- .map(|var| self.get_variable(var).unwrap())
110110- .collect::<Vec<_>>();
111111- if variables.is_empty() {
112112- let arg = self.compile_expression(pattern, "in_arg")?;
113113- self.bind_temporary(arg);
114114- return Some(arg);
115115- }
116116- // This pattern is (potentially) fully defined. We must confirm by checking all
117117- // the variables at runtime, and if they are all set, then we can construct
118118- // this pattern.
119119- let out_block = self.context.append_basic_block(self.get_function(), "out");
120120- let original_function = self.get_function();
121121- let snapshot = self.snapshot_function_context();
122122- for variable in variables {
123123- let next_arg =
124124- self.context.append_basic_block(self.get_function(), "next");
125125- self.branch_undefined(variable.ptr(), out_block, next_arg);
126126- self.builder.position_at_end(next_arg);
127127- }
128128- let in_arg = self.compile_expression(pattern, "in_arg")?;
129129- let final_function = self.get_function();
130130- if original_function == final_function {
131131- let merge_block =
132132- self.context.append_basic_block(final_function, "merge_arg");
133133-134134- let in_block = self.builder.get_insert_block().unwrap();
135135- self.builder
136136- .build_unconditional_branch(merge_block)
137137- .unwrap();
138138-139139- self.builder.position_at_end(out_block);
140140- let out_arg = self.allocate_undefined("out_arg");
141141- self.builder
142142- .build_unconditional_branch(merge_block)
143143- .unwrap();
144144-145145- self.builder.position_at_end(merge_block);
146146- let phi = self
147147- .builder
148148- .build_phi(self.context.ptr_type(AddressSpace::default()), "arg")
149149- .unwrap();
150150- phi.add_incoming(&[(&in_arg, in_block), (&out_arg, out_block)]);
151151- let arg = phi.as_basic_value().into_pointer_value();
152152- self.bind_temporary(arg);
153153- Some(arg)
154154- } else {
155155- let arg_continuation = self.add_continuation("arg");
156156- let end = self.continue_in_scope(arg_continuation, in_arg);
157157- self.end_continuation_point_as_close(end);
158158-159159- self.restore_function_context(snapshot);
160160- self.builder.position_at_end(out_block);
161161- let end = self.void_continue_in_scope(arg_continuation);
162162- self.end_continuation_point_as_close(end);
163163-164164- self.begin_next_function(arg_continuation);
165165- let arg = self.get_continuation("arg");
166166- self.bind_temporary(arg);
167167- Some(arg)
168168- }
169169- })
170170- .collect::<Option<Vec<_>>>()?;
171171-172172- let (next_iteration_inner, output_arguments) = self.call_rule(
173173- rule,
174174- &arguments,
175175- self.use_temporary(done_to).unwrap(),
176176- "lookup_next",
177177- );
178178- self.bind_temporary(next_iteration_inner);
179179-180180- // Wrap the next iteration with our own, as a lookup requires some cleanup
181181- // before starting its next internal iteration.
182182- let next_iteration_with_cleanup = self.add_continuation("lookup.next");
183183- let (next_iteration_with_cleanup_continuation, next_iteration_with_cleanup_cp) =
184184- self.capture_current_continuation(next_iteration_with_cleanup, "lookup.next");
185185-186186- let bound_before_lookup = bound_ids.len();
187187- for (pattern, out_value) in lookup.patterns.iter().zip(output_arguments) {
188188- let out_value = self.use_temporary(out_value).unwrap();
189189- self.compile_pattern_match_with_bindings(
190190- pattern,
191191- out_value,
192192- next_iteration_with_cleanup_continuation,
193193- bound_ids,
194194- )?;
195195- }
196196-197197- self.call_known_continuation(
198198- self.use_temporary(next_to).unwrap(),
199199- next_iteration_with_cleanup_continuation,
200200- );
201201-202202- self.become_continuation_point(next_iteration_with_cleanup_cp);
203203- self.begin_next_function(next_iteration_with_cleanup);
204204- self.cleanup_go_next(next_iteration_inner, bound_ids, bound_before_lookup);
9393+ self.perform_lookup(rule, &lookup.patterns, next_to, done_to, bound_ids)?;
20594 }
20695 ir::QueryValue::Disjunction(disj) => {
20796 let disj_second_fn = self.add_continuation("disj.second");
···286175 )?;
287176 self.next_cleanup(done_to, next_to, bound_ids, pre_len, "assign_next");
288177 }
289289- ir::QueryValue::Element(..) => todo!(),
178178+ ir::QueryValue::Element(unification) => {
179179+ let elem = self.elem();
180180+ self.perform_lookup(
181181+ elem,
182182+ [&unification.pattern, &unification.expression],
183183+ next_to,
184184+ done_to,
185185+ bound_ids,
186186+ )?;
187187+ }
290188 ir::QueryValue::Not(query) => {
291189 let go_next_fn = self.add_continuation("not.next");
292190 let (go_next, go_next_cp) =
···328226 keep_ids: usize,
329227 name: &str,
330228 ) {
331331- // Wrap the next iteration with our own, as a lookup requires some cleanup
229229+ // Wrap the next iteration with our own, as a lookup or unification requires some cleanup
332230 // before starting its next internal iteration.
333231 let next_iteration_with_cleanup = self.add_continuation(name);
334232 let (next_iteration_with_cleanup_continuation, next_iteration_with_cleanup_cp) =
···362260 }
363261 let next_iteration = self.use_temporary(next_iteration).unwrap();
364262 self.void_call_continuation(next_iteration);
263263+ }
264264+265265+ fn perform_lookup<'a>(
266266+ &self,
267267+ rule: PointerValue<'ctx>,
268268+ patterns: impl IntoIterator<Item = &'a ir::Expression> + Copy,
269269+ next_to: PointerValue<'ctx>,
270270+ done_to: PointerValue<'ctx>,
271271+ bound_ids: &mut Vec<Id>,
272272+ ) -> Option<()> {
273273+ // NOTE: similarly, the arguments of a query must be syntactically both patterns and
274274+ // expressions, so we can again guarantee that branching is not possible.
275275+ let arguments = patterns
276276+ .into_iter()
277277+ .map(|pattern| {
278278+ if !pattern.can_evaluate() {
279279+ // This pattern is not possibly an expression, e.g. due to containing a wildcard
280280+ // or other pattern-only syntax element. It can only be used as an output parameter.
281281+ let arg = self.allocate_undefined("out_arg");
282282+ self.bind_temporary(arg);
283283+ return Some(arg);
284284+ }
285285+ let variables = pattern
286286+ .bindings()
287287+ .iter()
288288+ .map(|var| self.get_variable(var).unwrap())
289289+ .collect::<Vec<_>>();
290290+ if variables.is_empty() {
291291+ let arg = self.compile_expression(pattern, "in_arg")?;
292292+ self.bind_temporary(arg);
293293+ return Some(arg);
294294+ }
295295+ // This pattern is (potentially) fully defined. We must confirm by checking all
296296+ // the variables at runtime, and if they are all set, then we can construct
297297+ // this pattern.
298298+ let out_block = self.context.append_basic_block(self.get_function(), "out");
299299+ let original_function = self.get_function();
300300+ let snapshot = self.snapshot_function_context();
301301+ for variable in variables {
302302+ let next_arg = self.context.append_basic_block(self.get_function(), "next");
303303+ self.branch_undefined(variable.ptr(), out_block, next_arg);
304304+ self.builder.position_at_end(next_arg);
305305+ }
306306+ let in_arg = self.compile_expression(pattern, "in_arg")?;
307307+ let final_function = self.get_function();
308308+ if original_function == final_function {
309309+ let merge_block = self.context.append_basic_block(final_function, "merge_arg");
310310+311311+ let in_block = self.builder.get_insert_block().unwrap();
312312+ self.builder
313313+ .build_unconditional_branch(merge_block)
314314+ .unwrap();
315315+316316+ self.builder.position_at_end(out_block);
317317+ let out_arg = self.allocate_undefined("out_arg");
318318+ self.builder
319319+ .build_unconditional_branch(merge_block)
320320+ .unwrap();
321321+322322+ self.builder.position_at_end(merge_block);
323323+ let phi = self
324324+ .builder
325325+ .build_phi(self.context.ptr_type(AddressSpace::default()), "arg")
326326+ .unwrap();
327327+ phi.add_incoming(&[(&in_arg, in_block), (&out_arg, out_block)]);
328328+ let arg = phi.as_basic_value().into_pointer_value();
329329+ self.bind_temporary(arg);
330330+ Some(arg)
331331+ } else {
332332+ let arg_continuation = self.add_continuation("arg");
333333+ let end = self.continue_in_scope(arg_continuation, in_arg);
334334+ self.end_continuation_point_as_close(end);
335335+336336+ self.restore_function_context(snapshot);
337337+ self.builder.position_at_end(out_block);
338338+ let end = self.void_continue_in_scope(arg_continuation);
339339+ self.end_continuation_point_as_close(end);
340340+341341+ self.begin_next_function(arg_continuation);
342342+ let arg = self.get_continuation("arg");
343343+ self.bind_temporary(arg);
344344+ Some(arg)
345345+ }
346346+ })
347347+ .collect::<Option<Vec<_>>>()?;
348348+349349+ let (next_iteration_inner, output_arguments) = self.call_rule(
350350+ rule,
351351+ &arguments,
352352+ self.use_temporary(done_to).unwrap(),
353353+ "lookup_next",
354354+ );
355355+ self.bind_temporary(next_iteration_inner);
356356+357357+ // Wrap the next iteration with our own, as a lookup requires some cleanup
358358+ // before starting its next internal iteration.
359359+ let next_iteration_with_cleanup = self.add_continuation("lookup.next");
360360+ let (next_iteration_with_cleanup_continuation, next_iteration_with_cleanup_cp) =
361361+ self.capture_current_continuation(next_iteration_with_cleanup, "lookup.next");
362362+363363+ let bound_before_lookup = bound_ids.len();
364364+ for (pattern, out_value) in patterns.into_iter().zip(output_arguments) {
365365+ let out_value = self.use_temporary(out_value).unwrap();
366366+ self.compile_pattern_match_with_bindings(
367367+ pattern,
368368+ out_value,
369369+ next_iteration_with_cleanup_continuation,
370370+ bound_ids,
371371+ )?;
372372+ }
373373+374374+ self.call_known_continuation(
375375+ self.use_temporary(next_to).unwrap(),
376376+ next_iteration_with_cleanup_continuation,
377377+ );
378378+379379+ self.become_continuation_point(next_iteration_with_cleanup_cp);
380380+ self.begin_next_function(next_iteration_with_cleanup);
381381+ self.cleanup_go_next(next_iteration_inner, bound_ids, bound_before_lookup);
382382+ Some(())
365383 }
366384}
+7-2
trilogy-llvm/src/rule.rs
···7272 let (go_to_next_overload, next_overload_cp) =
7373 self.capture_current_continuation_as_break(next_overload_function, "next_overload");
74747575+ let next_overload_clone = self.allocate_value("next_overload_clone");
7676+ self.trilogy_value_clone_into(next_overload_clone, go_to_next_overload);
7777+ self.bind_temporary(next_overload_clone);
7878+7579 for (param, &value) in overload.parameters.iter().zip(&input_args) {
7680 let original_insert_function = self.get_function();
7781 let original_snapshot = self.snapshot_function_context();
···124128 }
125129 }
126130 // The rule runs the iterator, resulting in new bindings being set.
127127- let Some(next_iteration) = self.compile_iterator(&overload.body, go_to_next_overload)
131131+ let Some(next_iteration) =
132132+ self.compile_query_iteration(&overload.body, go_to_next_overload)
128133 else {
129134 break 'outer;
130135 };
···157162 // If the input and output are both going to be undefined, then go right to the
158163 // next overload.
159164 self.builder.position_at_end(fully_unbound);
160160- let next_overload = self.use_temporary(go_to_next_overload).unwrap();
165165+ let next_overload = self.use_temporary(next_overload_clone).unwrap();
161166 self.void_call_continuation(next_overload);
162167163168 // There's actually no action for the rebind-input case, only to reuse the argument later