From 846666463712e13415bbecd0331c2f41f8c6ccf3 Mon Sep 17 00:00:00 2001 From: IgorCielniak Date: Fri, 9 Jan 2026 11:01:24 +0100 Subject: [PATCH] added inline functions --- main.py | 77 +++++++++++++++++++++++++++++++++++++++++++ tests/inline.expected | 1 + tests/inline.sl | 8 +++++ tests/inline.test | 1 + 4 files changed, 87 insertions(+) create mode 100644 tests/inline.expected create mode 100644 tests/inline.sl create mode 100644 tests/inline.test diff --git a/main.py b/main.py index 9af0c55..a2a0c69 100644 --- a/main.py +++ b/main.py @@ -202,6 +202,7 @@ class Definition: immediate: bool = False compile_only: bool = False terminator: str = "end" + inline: bool = False @dataclass @@ -305,6 +306,7 @@ class Word: extern_inputs: int = 0 extern_outputs: int = 0 extern_signature: Optional[Tuple[List[str], str]] = None # (arg_types, ret_type) + inline: bool = False @dataclass @@ -813,6 +815,7 @@ class Parser: word = self.definition_stack.pop() ctx.immediate = word.immediate ctx.compile_only = word.compile_only + ctx.inline = word.inline if word.compile_only or word.immediate: word.compile_time_override = True word.compile_time_intrinsic = None @@ -1566,6 +1569,8 @@ class Assembler: self._string_literals: Dict[str, Tuple[str, int]] = {} self._float_literals: Dict[float, str] = {} self._data_section: Optional[List[str]] = None + self._inline_stack: List[str] = [] + self._inline_counter: int = 0 def _reachable_runtime_defs(self, runtime_defs: Sequence[Union[Definition, AsmDefinition]]) -> Set[str]: edges: Dict[str, Set[str]] = {} @@ -1618,6 +1623,9 @@ class Assembler: if len(reachable) != len(runtime_defs): runtime_defs = [defn for defn in runtime_defs if defn.name in reachable] + # Inline-only definitions are expanded at call sites; skip emitting standalone labels. + runtime_defs = [defn for defn in runtime_defs if not getattr(defn, "inline", False)] + for definition in runtime_defs: self._emit_definition(definition, emission.text) @@ -1696,6 +1704,56 @@ class Assembler: raise CompileError("unknown definition type") builder.emit(" ret") + def _emit_inline_definition(self, word: Word, builder: FunctionEmitter) -> None: + definition = word.definition + if not isinstance(definition, Definition): + raise CompileError(f"inline word '{word.name}' requires a high-level definition") + + suffix = self._inline_counter + self._inline_counter += 1 + + label_map: Dict[str, str] = {} + + def remap(label: str) -> str: + if label not in label_map: + label_map[label] = f"{label}__inl{suffix}" + return label_map[label] + + for node in definition.body: + kind = node.op + data = node.data + if kind == "label": + mapped = remap(str(data)) + self._emit_node(Op(op="label", data=mapped), builder) + continue + if kind == "jump": + mapped = remap(str(data)) + self._emit_node(Op(op="jump", data=mapped), builder) + continue + if kind == "branch_zero": + mapped = remap(str(data)) + self._emit_node(Op(op="branch_zero", data=mapped), builder) + continue + if kind == "for_begin": + mapped = { + "loop": remap(data["loop"]), + "end": remap(data["end"]), + } + self._emit_node(Op(op="for_begin", data=mapped), builder) + continue + if kind == "for_end": + mapped = { + "loop": remap(data["loop"]), + "end": remap(data["end"]), + } + self._emit_node(Op(op="for_end", data=mapped), builder) + continue + if kind in ("list_begin", "list_end"): + mapped = remap(str(data)) + self._emit_node(Op(op=kind, data=mapped), builder) + continue + self._emit_node(node, builder) + def _emit_asm_body(self, definition: AsmDefinition, builder: FunctionEmitter) -> None: body = definition.body.strip("\n") if not body: @@ -1823,6 +1881,13 @@ class Assembler: raise CompileError(f"unknown word '{name}'") if word.compile_only: raise CompileError(f"word '{name}' is compile-time only") + if getattr(word, "inline", False) and isinstance(word.definition, Definition): + if word.name in self._inline_stack: + raise CompileError(f"recursive inline expansion for '{word.name}'") + self._inline_stack.append(word.name) + self._emit_inline_definition(word, builder) + self._inline_stack.pop() + return if word.intrinsic: word.intrinsic(builder) return @@ -2004,6 +2069,17 @@ def macro_compile_only(ctx: MacroContext) -> Optional[List[Op]]: return None +def macro_inline(ctx: MacroContext) -> Optional[List[Op]]: + parser = ctx.parser + word = parser.most_recent_definition() + if word is None: + raise ParseError("'inline' must follow a definition") + word.inline = True + if word.definition is not None: + word.definition.inline = True + return None + + def macro_compile_time(ctx: MacroContext) -> Optional[List[Op]]: """Run the next word at compile time and still emit it for runtime.""" parser = ctx.parser @@ -2787,6 +2863,7 @@ def bootstrap_dictionary() -> Dictionary: dictionary = Dictionary() dictionary.register(Word(name="immediate", immediate=True, macro=macro_immediate)) dictionary.register(Word(name="compile-only", immediate=True, macro=macro_compile_only)) + dictionary.register(Word(name="inline", immediate=True, macro=macro_inline)) dictionary.register(Word(name="compile-time", immediate=True, macro=macro_compile_time)) dictionary.register(Word(name="here", immediate=True, macro=macro_here)) dictionary.register(Word(name="with", immediate=True, macro=macro_with)) diff --git a/tests/inline.expected b/tests/inline.expected new file mode 100644 index 0000000..b6fc4c6 --- /dev/null +++ b/tests/inline.expected @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/tests/inline.sl b/tests/inline.sl new file mode 100644 index 0000000..0124e70 --- /dev/null +++ b/tests/inline.sl @@ -0,0 +1,8 @@ +word fn + "hello" +end +inline + +word main + 1 fn 3 1 syscall 0 +end diff --git a/tests/inline.test b/tests/inline.test new file mode 100644 index 0000000..ba21251 --- /dev/null +++ b/tests/inline.test @@ -0,0 +1 @@ +python main.py tests/inline.sl -o /tmp/inline > /dev/null && /tmp/inline \ No newline at end of file