diff --git a/tests/c_extern.sl b/c_extern.sl similarity index 73% rename from tests/c_extern.sl rename to c_extern.sl index 82385f3..6fa23e5 100644 --- a/tests/c_extern.sl +++ b/c_extern.sl @@ -1,7 +1,4 @@ -import ../stdlib/io.sl - -# Raw extern (no ABI handling) -extern raw_extern_test +import stdlib/io.sl # C-style externs (auto ABI handling) extern long labs(long n) diff --git a/f.sl b/f.sl new file mode 100644 index 0000000..8c6b2e7 --- /dev/null +++ b/f.sl @@ -0,0 +1,17 @@ +import stdlib/stdlib.sl +import stdlib/io.sl +import stdlib/float.sl + +extern double atan2(double y, double x) + +: main + # Basic math + 1.5 2.5 f+ fputln # Outputs: 4.000000 + + # External math library (libm) + 10.0 10.0 atan2 # Result is pi/4 + 4.0 f* fputln # Outputs: 3.141593 (approx pi) + + 0 +; + diff --git a/main.py b/main.py index 827a7f3..8bfb713 100644 --- a/main.py +++ b/main.py @@ -60,7 +60,7 @@ class Reader: def __init__(self) -> None: self.line = 1 self.column = 0 - self.custom_tokens: Set[str] = {"(", ")", "{", "}", ";", ",", "[", "]", "*"} + self.custom_tokens: Set[str] = {"(", ")", "{", "}", ";", ",", "[", "]"} self._token_order: List[str] = sorted(self.custom_tokens, key=len, reverse=True) def add_tokens(self, tokens: Iterable[str]) -> None: @@ -336,6 +336,7 @@ class Word: is_extern: bool = False # New: mark as extern extern_inputs: int = 0 extern_outputs: int = 0 + extern_signature: Optional[Tuple[List[str], str]] = None # (arg_types, ret_type) @dataclass @@ -509,6 +510,7 @@ class Parser: raise ParseError(f"expected '(' after extern function name '{name}'") inputs = 0 + arg_types: List[str] = [] while True: if self._eof(): raise ParseError("extern unclosed '('") @@ -530,6 +532,7 @@ class Parser: if arg_type != "void": inputs += 1 + arg_types.append(arg_type) # Optional argument name peek_name = self.peek_token() if peek_name and peek_name.lexeme not in (",", ")"): @@ -548,6 +551,7 @@ class Parser: word.is_extern = True word.extern_inputs = inputs word.extern_outputs = outputs + word.extern_signature = (arg_types, ret_type) else: # Raw/Legacy Parsing @@ -839,17 +843,29 @@ class Parser: else: # pragma: no cover - defensive raise ParseError("unknown parse context") - def _try_literal(self, token: Token) -> bool: + def _try_literal(self, token: Token) -> None: try: value = int(token.lexeme, 0) + self._append_node(Literal(value=value)) + return True except ValueError: - string_value = _parse_string_literal(token) - if string_value is None: - return False + pass + + # Try float + try: + if "." in token.lexeme or "e" in token.lexeme.lower(): + value = float(token.lexeme) + self._append_node(Literal(value=value)) + return True + except ValueError: + pass + + string_value = _parse_string_literal(token) + if string_value is not None: self._append_node(Literal(value=string_value)) return True - self._append_node(Literal(value=value)) - return True + + return False def _consume(self) -> Token: self._ensure_tokens(self.pos) @@ -1359,6 +1375,14 @@ class FunctionEmitter: f" mov qword [r12], {value}", ]) + def push_float(self, label: str) -> None: + self.text.extend([ + f" ; push float from {label}", + " sub r12, 8", + f" mov rax, [rel {label}]", + " mov [r12], rax", + ]) + def push_label(self, label: str) -> None: self.text.extend([ f" ; push {label}", @@ -1465,6 +1489,7 @@ class Assembler: self.stack_bytes = 65536 self.io_buffer_bytes = 128 self._string_literals: Dict[str, Tuple[str, int]] = {} + self._float_literals: Dict[float, str] = {} self._data_section: Optional[List[str]] = None def _emit_externs(self, text: List[str]) -> None: @@ -1477,6 +1502,7 @@ class Assembler: self._emit_externs(emission.text) emission.text.extend(self._runtime_prelude()) self._string_literals = {} + self._float_literals = {} self._data_section = emission.data valid_defs = (Definition, AsmDefinition) @@ -1524,6 +1550,21 @@ class Assembler: self._string_literals[value] = (label, len(encoded)) return self._string_literals[value] + def _intern_float_literal(self, value: float) -> str: + if self._data_section is None: + raise CompileError("float literal emission requested without data section") + self._ensure_data_start() + if value in self._float_literals: + return self._float_literals[value] + label = f"flt_{len(self._float_literals)}" + # Use hex representation of double precision float + import struct + hex_val = struct.pack('>d', value).hex() + # NASM expects hex starting with 0x + self._data_section.append(f"{label}: dq 0x{hex_val}") + self._float_literals[value] = label + return label + def _emit_definition(self, definition: Union[Definition, AsmDefinition], text: List[str]) -> None: label = sanitize_label(definition.name) text.append(f"{label}:") @@ -1552,6 +1593,10 @@ class Assembler: if isinstance(node.value, int): builder.push_literal(node.value) return + if isinstance(node.value, float): + label = self._intern_float_literal(node.value) + builder.push_float(label) + return if isinstance(node.value, str): label, length = self._intern_string_literal(node.value) builder.push_label(label) @@ -1591,24 +1636,63 @@ class Assembler: if getattr(word, "is_extern", False): inputs = getattr(word, "extern_inputs", 0) outputs = getattr(word, "extern_outputs", 0) + signature = getattr(word, "extern_signature", None) + if inputs > 0 or outputs > 0: regs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"] - if inputs > 6: - raise CompileError(f"extern '{ref.name}' has too many inputs ({inputs} > 6)") - - # Pop inputs in reverse register order (argN is top of stack -> last reg) - current_regs = regs[:inputs] - for reg in reversed(current_regs): - builder.pop_to(reg) - - # Align RSP and call + xmm_regs = [f"xmm{i}" for i in range(8)] + + arg_types = signature[0] if signature else [] + ret_type = signature[1] if signature else None + + if len(arg_types) != inputs and signature: + raise CompileError(f"extern '{ref.name}' mismatch: {inputs} inputs vs {len(arg_types)} types") + + int_idx = 0 + xmm_idx = 0 + + mapping: List[Tuple[str, str]] = [] # (type, target_reg) + + if not arg_types: + # Legacy/Raw mode: assume all ints + if inputs > 6: + raise CompileError(f"extern '{ref.name}' has too many inputs ({inputs} > 6)") + for i in range(inputs): + mapping.append(("int", regs[i])) + else: + for type_name in arg_types: + if type_name in ("float", "double"): + if xmm_idx >= 8: + raise CompileError(f"extern '{ref.name}' has too many float inputs") + mapping.append(("float", xmm_regs[xmm_idx])) + xmm_idx += 1 + else: + if int_idx >= 6: + raise CompileError(f"extern '{ref.name}' has too many int inputs") + mapping.append(("int", regs[int_idx])) + int_idx += 1 + + for type_name, reg in reversed(mapping): + if type_name == "float": + builder.pop_to("rax") + builder.emit(f" movq {reg}, rax") + else: + builder.pop_to(reg) + builder.emit(" push rbp") builder.emit(" mov rbp, rsp") builder.emit(" and rsp, -16") + builder.emit(f" mov al, {xmm_idx}") builder.emit(f" call {ref.name}") builder.emit(" leave") - - if outputs == 1: + + # Handle Return Value + if ret_type in ("float", "double"): + # Result in xmm0, move to stack + builder.emit(" sub r12, 8") + builder.emit(" movq rax, xmm0") + builder.emit(" mov [r12], rax") + elif outputs == 1: builder.push_from("rax") elif outputs > 1: raise CompileError("extern only supports 0 or 1 output") diff --git a/raw_lib.asm b/raw_lib.asm deleted file mode 100644 index ccf3687..0000000 --- a/raw_lib.asm +++ /dev/null @@ -1,12 +0,0 @@ - -global raw_add -section .text -raw_add: - ; expects args on stack: [r12]=b, [r12+8]=a - ; returns sum on stack - mov rax, [r12] ; b - add r12, 8 - mov rbx, [r12] ; a - add rax, rbx - mov [r12], rax - ret diff --git a/raw_lib.o b/raw_lib.o deleted file mode 100644 index cf8d2ea..0000000 Binary files a/raw_lib.o and /dev/null differ diff --git a/stdlib/float.sl b/stdlib/float.sl new file mode 100644 index 0000000..6e8504f --- /dev/null +++ b/stdlib/float.sl @@ -0,0 +1,95 @@ +# L2 Floating Point Library (Double Precision) + +# Arithmetic +:asm f+ { + movq xmm0, [r12] + add r12, 8 + movq xmm1, [r12] + addsd xmm1, xmm0 + movq [r12], xmm1 +} ; + +:asm f- { + movq xmm0, [r12] + add r12, 8 + movq xmm1, [r12] + subsd xmm1, xmm0 + movq [r12], xmm1 +} ; + +:asm f* { + movq xmm0, [r12] + add r12, 8 + movq xmm1, [r12] + mulsd xmm1, xmm0 + movq [r12], xmm1 +} ; + +:asm f/ { + movq xmm0, [r12] + add r12, 8 + movq xmm1, [r12] + divsd xmm1, xmm0 + movq [r12], xmm1 +} ; + +:asm fneg { + movq xmm0, [r12] + mov rax, 0x8000000000000000 + movq xmm1, rax + xorpd xmm0, xmm1 + movq [r12], xmm0 +} ; + +# Comparison +:asm f== { + movq xmm0, [r12] + add r12, 8 + movq xmm1, [r12] + ucomisd xmm0, xmm1 + mov rax, 0 + setz al + mov [r12], rax +} ; + +:asm f< { + movq xmm0, [r12] ; a + add r12, 8 + movq xmm1, [r12] ; b + ucomisd xmm0, xmm1 + mov rax, 0 + seta al ; Above (a > b) -> b < a + mov [r12], rax +} ; + +:asm f> { + movq xmm0, [r12] ; a + add r12, 8 + movq xmm1, [r12] ; b + ucomisd xmm1, xmm0 + mov rax, 0 + seta al ; b > a + mov [r12], rax +} ; + +# Conversion +:asm int>float { + cvtsi2sd xmm0, [r12] + movq [r12], xmm0 +} ; + +:asm float>int { + cvttsd2si rax, [r12] + mov [r12], rax +} ; + +# Output +extern int printf(char* fmt, double x) + +: fput + "%f" drop swap printf drop +; + +: fputln + "%f\n" drop swap printf drop +; \ No newline at end of file diff --git a/test_raw.sl b/test_raw.sl deleted file mode 100644 index 4713d33..0000000 --- a/test_raw.sl +++ /dev/null @@ -1,8 +0,0 @@ -import stdlib/io.sl - -extern raw_add - -: main - 10 20 raw_add - puti cr -;