From 7b0a4c535b86597dffd96d325405d155c6ca55a6 Mon Sep 17 00:00:00 2001 From: JGZ_YES Date: Mon, 10 Aug 2026 21:32:39 +0800 Subject: [PATCH] feat(los4): add pure C compiler targeting Los4 ELF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add src/PazeE.los4/ — a pure C implementation of the PazeE compiler that generates static Los4 ELF executables (x86-64, int 0x80 syscalls). Core components: - Lexer, preprocessor, recursive-descent parser, AST - Tree-walking interpreter (paze run) with breakpoints - x86-64 code generator (SysV AMD64 ABI) - Los4 ELF writer (4 PH layout matching doomlauncher.elf, base 0x400000) - Runtime library as raw machine code: printf, sprintf, puts, malloc, memcpy, memset, strcmp, strlen, exit, etc. Key fixes: - Fix use-after-free: AST paze_str_t slices point into source buffer, so free(src) is deferred until after interpreter/codegen completes - printf/sprintf support %d/%i/%u/%x/%c/%s/%% with rel32 jumps - External function calls resolved via runtime offset lookup All 14 test cases pass for both paze run and paze build. Also includes DebuggerVM.cs and Program.cs updates for the C# compiler. --- src/PazeE.Compiler/DebuggerVM.cs | 1397 +++++++++ src/PazeE.Compiler/Program.cs | 419 ++- src/PazeE.los4/build.ps1 | 48 + src/PazeE.los4/include/paze_ast.h | 349 +++ src/PazeE.los4/include/paze_diagnostics.h | 70 + src/PazeE.los4/include/paze_elf_writer_los4.h | 45 + src/PazeE.los4/include/paze_interpreter.h | 100 + src/PazeE.los4/include/paze_lexer.h | 58 + src/PazeE.los4/include/paze_libc.h | 43 + src/PazeE.los4/include/paze_libc_decls.h | 27 + src/PazeE.los4/include/paze_los4_runtime.h | 78 + src/PazeE.los4/include/paze_object_image.h | 141 + src/PazeE.los4/include/paze_parser.h | 57 + src/PazeE.los4/include/paze_preprocessor.h | 76 + src/PazeE.los4/include/paze_sema.h | 91 + src/PazeE.los4/include/paze_token.h | 179 ++ src/PazeE.los4/include/paze_type.h | 62 + src/PazeE.los4/include/paze_types.h | 182 ++ src/PazeE.los4/include/paze_x64_codegen.h | 31 + src/PazeE.los4/include/paze_x64_emitter.h | 145 + src/PazeE.los4/src/main.c | 259 ++ src/PazeE.los4/src/paze_ast.c | 142 + src/PazeE.los4/src/paze_diagnostics.c | 140 + src/PazeE.los4/src/paze_elf_writer_los4.c | 305 ++ src/PazeE.los4/src/paze_interpreter.c | 2567 +++++++++++++++++ src/PazeE.los4/src/paze_lexer.c | 655 +++++ src/PazeE.los4/src/paze_libc.c | 82 + src/PazeE.los4/src/paze_libc_decls.c | 70 + src/PazeE.los4/src/paze_los4_runtime.c | 1651 +++++++++++ src/PazeE.los4/src/paze_object_image.c | 197 ++ src/PazeE.los4/src/paze_parser.c | 2098 ++++++++++++++ src/PazeE.los4/src/paze_preprocessor.c | 1136 ++++++++ src/PazeE.los4/src/paze_sema.c | 408 +++ src/PazeE.los4/src/paze_token.c | 269 ++ src/PazeE.los4/src/paze_types.c | 165 ++ src/PazeE.los4/src/paze_x64_codegen.c | 1295 +++++++++ src/PazeE.los4/src/paze_x64_emitter.c | 524 ++++ test_comprehensive.pe | 63 + test_simple.pe | 4 + test_typedef.pe | 8 + test_typedef2.pe | 7 + tests/debug_fib.pe | 10 + tests/debug_fordec.pe | 8 + tests/debug_func.pe | 11 + tests/debug_loop.pe | 9 + tests/debug_loop2.pe | 10 + tests/debug_simple.pe | 5 + tests/debug_struct.pe | 12 + tests/debug_test.pe | 9 + tests/debug_while.pe | 10 + tests/localinit.pe | 10 + tests/min_pure.pe | 3 + 52 files changed, 15692 insertions(+), 48 deletions(-) create mode 100644 src/PazeE.Compiler/DebuggerVM.cs create mode 100644 src/PazeE.los4/build.ps1 create mode 100644 src/PazeE.los4/include/paze_ast.h create mode 100644 src/PazeE.los4/include/paze_diagnostics.h create mode 100644 src/PazeE.los4/include/paze_elf_writer_los4.h create mode 100644 src/PazeE.los4/include/paze_interpreter.h create mode 100644 src/PazeE.los4/include/paze_lexer.h create mode 100644 src/PazeE.los4/include/paze_libc.h create mode 100644 src/PazeE.los4/include/paze_libc_decls.h create mode 100644 src/PazeE.los4/include/paze_los4_runtime.h create mode 100644 src/PazeE.los4/include/paze_object_image.h create mode 100644 src/PazeE.los4/include/paze_parser.h create mode 100644 src/PazeE.los4/include/paze_preprocessor.h create mode 100644 src/PazeE.los4/include/paze_sema.h create mode 100644 src/PazeE.los4/include/paze_token.h create mode 100644 src/PazeE.los4/include/paze_type.h create mode 100644 src/PazeE.los4/include/paze_types.h create mode 100644 src/PazeE.los4/include/paze_x64_codegen.h create mode 100644 src/PazeE.los4/include/paze_x64_emitter.h create mode 100644 src/PazeE.los4/src/main.c create mode 100644 src/PazeE.los4/src/paze_ast.c create mode 100644 src/PazeE.los4/src/paze_diagnostics.c create mode 100644 src/PazeE.los4/src/paze_elf_writer_los4.c create mode 100644 src/PazeE.los4/src/paze_interpreter.c create mode 100644 src/PazeE.los4/src/paze_lexer.c create mode 100644 src/PazeE.los4/src/paze_libc.c create mode 100644 src/PazeE.los4/src/paze_libc_decls.c create mode 100644 src/PazeE.los4/src/paze_los4_runtime.c create mode 100644 src/PazeE.los4/src/paze_object_image.c create mode 100644 src/PazeE.los4/src/paze_parser.c create mode 100644 src/PazeE.los4/src/paze_preprocessor.c create mode 100644 src/PazeE.los4/src/paze_sema.c create mode 100644 src/PazeE.los4/src/paze_token.c create mode 100644 src/PazeE.los4/src/paze_types.c create mode 100644 src/PazeE.los4/src/paze_x64_codegen.c create mode 100644 src/PazeE.los4/src/paze_x64_emitter.c create mode 100644 test_comprehensive.pe create mode 100644 test_simple.pe create mode 100644 test_typedef.pe create mode 100644 test_typedef2.pe create mode 100644 tests/debug_fib.pe create mode 100644 tests/debug_fordec.pe create mode 100644 tests/debug_func.pe create mode 100644 tests/debug_loop.pe create mode 100644 tests/debug_loop2.pe create mode 100644 tests/debug_simple.pe create mode 100644 tests/debug_struct.pe create mode 100644 tests/debug_test.pe create mode 100644 tests/debug_while.pe create mode 100644 tests/localinit.pe create mode 100644 tests/min_pure.pe diff --git a/src/PazeE.Compiler/DebuggerVM.cs b/src/PazeE.Compiler/DebuggerVM.cs new file mode 100644 index 0000000..296e5f4 --- /dev/null +++ b/src/PazeE.Compiler/DebuggerVM.cs @@ -0,0 +1,1397 @@ +using System.Linq; +using System.Text; +using PazeE.Compiler.Lexer; +using PazeE.Compiler.Parser; +using PazeE.Compiler.Semantic; + +namespace PazeE.Compiler; + +/// 轻量级树遍历解释器,用于 paze debug 命令。 +/// 使用显式执行栈实现可暂停/恢复的执行模型,支持断点、单步执行、变量查看。 +public sealed class DebuggerVM +{ + private readonly TranslationUnit _unit; + private readonly Sema _sema; + private readonly Diagnostics _diag; + private readonly Dictionary _functions = new(); + private readonly Dictionary _globals = new(); + private readonly Dictionary _heap = new(); + private long _heapPtr = 0x10000000; + private readonly HashSet _breakpoints = new(); + private int _lineOffset; + private string _currentFunc = ""; + + // ---- 执行栈 ---- + private readonly Stack _execStack = new(); + private readonly List _frames = new(); + + // ---- 调试状态 ---- + private bool _finished; + private bool _breakpointHit; + private bool _stepMode; + private bool _stepOverMode; + private int _stepOverDepth; + private int _stepIntoRemaining; + + // ---- 保存/恢复的状态 ---- + private SavedState? _savedState; + + public int ExitCode { get; private set; } + public bool IsFinished => _finished && !_breakpointHit; + public int CurrentLine { get; private set; } + public string CurrentFunc => _currentFunc; + + public DebuggerVM(TranslationUnit unit, Sema sema, Diagnostics diag, int lineOffset = 0) + { + _unit = unit; + _sema = sema; + _diag = diag; + _lineOffset = lineOffset; + BuildFunctionTable(); + InitGlobals(); + } + + private void BuildFunctionTable() + { + foreach (var d in FlattenDecls(_unit.Decls)) + if (d is FunctionDecl f) _functions[f.Name] = f; + } + + private static IEnumerable FlattenDecls(List decls) + { + foreach (var d in decls) + { + if (d is DeclGroup g) foreach (var dd in g.Decls) yield return dd; + else yield return d; + } + } + + private void InitGlobals() + { + foreach (var d in FlattenDecls(_unit.Decls)) + { + if (d is VarDecl vd && vd.Storage != StorageClass.Extern) + { + if (vd.Init != null) + _globals[vd.Name] = EvalExpr(vd.Init); + else + _globals[vd.Name] = DefaultValue(vd.Type); + } + } + } + + private static object DefaultValue(CType t) + { + if (t.IsInteger) return 0L; + if (t.IsPointer) return 0L; + if (t.IsArray) return new byte[t.Size]; + if (t.IsStruct) return new byte[t.Size]; + return 0L; + } + + public void SetBreakpoint(int userLine) => _breakpoints.Add(userLine + _lineOffset); + public void ClearBreakpoint(int userLine) => _breakpoints.Remove(userLine + _lineOffset); + public HashSet GetBreakpoints() + { + var result = new HashSet(); + foreach (var bp in _breakpoints) + result.Add(bp - _lineOffset); + return result; + } + + public void SetLineOffset(int offset) => _lineOffset = offset; + + // ==================== 公共执行接口 ==================== + + public void Run() + { + RestoreInitialState(); + _finished = false; + _breakpointHit = false; + ExitCode = 0; + _stepMode = false; + _stepOverMode = false; + _stepIntoRemaining = 0; + + var main = _functions.GetValueOrDefault("main"); + if (main?.Body == null) { _finished = true; return; } + + _currentFunc = "main"; + PushFrame(main); + PushBlock(main.Body); + + try + { + ExecuteLoop(); + } + catch (BreakpointException bp) + { + _breakpointHit = true; + CurrentLine = bp.Line; + throw; + } + catch (ReturnException ret) + { + HandleTopLevelReturn(ret); + } + } + + public void Continue() + { + if (_savedState != null) + RestoreState(_savedState); + else + { + var main = _functions.GetValueOrDefault("main"); + if (main?.Body == null) { _finished = true; return; } + _currentFunc = "main"; + PushFrame(main); + PushBlock(main.Body); + } + + _finished = false; + _breakpointHit = false; + _stepMode = false; + _stepOverMode = false; + _stepIntoRemaining = 0; + + try + { + ExecuteLoop(); + } + catch (BreakpointException bp) + { + _breakpointHit = true; + CurrentLine = bp.Line; + throw; + } + catch (ReturnException ret) + { + HandleTopLevelReturn(ret); + } + } + + public void StepInto() + { + if (_savedState != null) + RestoreState(_savedState); + else + { + var main = _functions.GetValueOrDefault("main"); + if (main?.Body == null) { _finished = true; return; } + _currentFunc = "main"; + PushFrame(main); + PushBlock(main.Body); + } + + _finished = false; + _breakpointHit = false; + _stepMode = true; + _stepOverMode = false; + _stepIntoRemaining = 1; + + try + { + ExecuteLoop(); + } + catch (BreakpointException bp) + { + _breakpointHit = true; + CurrentLine = bp.Line; + throw; + } + catch (ReturnException ret) + { + HandleTopLevelReturn(ret); + return; + } + + if (_stepIntoRemaining <= 0 && !_stepOverMode) + throw new BreakpointException(CurrentLine, $"单步在第 {CurrentLine - _lineOffset} 行"); + } + + public void StepOver() + { + if (_savedState != null) + RestoreState(_savedState); + else + { + var main = _functions.GetValueOrDefault("main"); + if (main?.Body == null) { _finished = true; return; } + _currentFunc = "main"; + PushFrame(main); + PushBlock(main.Body); + } + + _finished = false; + _breakpointHit = false; + _stepMode = true; + _stepOverMode = true; + _stepOverDepth = _execStack.Count; + _stepIntoRemaining = 1; + + try + { + ExecuteLoop(); + } + catch (BreakpointException bp) + { + _breakpointHit = true; + CurrentLine = bp.Line; + throw; + } + catch (ReturnException ret) + { + HandleTopLevelReturn(ret); + return; + } + + if (_stepOverMode) + throw new BreakpointException(CurrentLine, $"单步在第 {CurrentLine - _lineOffset} 行"); + } + + private void HandleTopLevelReturn(ReturnException ret) + { + ExitCode = Convert.ToInt32(ret.Value); + _finished = true; + _breakpointHit = false; + while (_frames.Count > 0) _frames.RemoveAt(_frames.Count - 1); + _currentFunc = ""; + } + + // ==================== 执行循环 ==================== + + private void ExecuteLoop() + { + while (_execStack.Count > 0 && !_finished) + { + if (_stepMode && _stepIntoRemaining <= 0) + { + AdvanceToNextLine(); + SaveState(); + _stepMode = false; + return; + } + + if (_stepOverMode && _execStack.Count <= _stepOverDepth && _stepIntoRemaining <= 0) + { + AdvanceToNextLine(); + SaveState(); + _stepMode = false; + _stepOverMode = false; + return; + } + + var task = _execStack.Peek(); + + switch (task.Kind) + { + case TaskKind.ExecuteBlock: + ExecTaskExecuteBlock(task); + break; + case TaskKind.ExecStatement: + ExecTaskStatement(task); + break; + case TaskKind.WhileRecheck: + ExecTaskWhileRecheck(task); + break; + case TaskKind.ForRecheck: + ExecTaskForRecheck(task); + break; + case TaskKind.DoWhileCheck: + ExecTaskDoWhileCheck(task); + break; + case TaskKind.SwitchExecute: + ExecTaskSwitch(task); + break; + case TaskKind.IfBranch: + _execStack.Pop(); + break; + case TaskKind.Skip: + _execStack.Pop(); + break; + default: + _execStack.Pop(); + break; + } + } + + if (_execStack.Count == 0 && !_finished) + { + _finished = true; + ExitCode = 0; + } + } + + private void AdvanceToNextLine() + { + if (_execStack.Count > 0 && _execStack.Peek().Kind == TaskKind.ExecuteBlock) + { + var task = _execStack.Peek(); + var block = task.Block!; + int nextIdx = task.NextIndex; + if (nextIdx < block.Items.Count) + { + var item = block.Items[nextIdx]; + int nextLine = item switch + { + Stmt s => s.Range.StartLine, + VarDecl vd => vd.Init?.Range.StartLine ?? vd.Range.StartLine, + _ => CurrentLine + }; + CurrentLine = nextLine; + } + } + } + + private void ExecTaskExecuteBlock(ExecTask task) + { + var block = task.Block!; + + if (task.NextIndex >= block.Items.Count) + { + _execStack.Pop(); + return; + } + + var item = block.Items[task.NextIndex]; + task.NextIndex++; + + if (item is Stmt s) + { + int line = s.Range.StartLine; + CurrentLine = line; + CheckBreakpoint(line); + if (_finished) return; + + _execStack.Push(new ExecTask { Kind = TaskKind.ExecStatement, Stmt = s }); + } + else if (item is VarDecl vd) + { + int line = vd.Init?.Range.StartLine ?? vd.Range.StartLine; + CurrentLine = line; + CheckBreakpoint(line); + if (_finished) return; + + if (vd.Init != null) + { + var val = EvalExpr(vd.Init); + SetLocal(vd.Name, val); + } + else + { + SetLocal(vd.Name, DefaultValue(vd.Type)); + } + if (_stepMode) _stepIntoRemaining--; + } + } + + private void ExecTaskStatement(ExecTask task) + { + _execStack.Pop(); + var s = task.Stmt!; + + switch (s) + { + case BlockStmt b: + PushBlock(b); + break; + case ExprStmt es: + if (es.Expr != null) EvalExpr(es.Expr); + break; + case NullStmt: break; + case IfStmt ifs: + ExecuteIf(ifs); + break; + case WhileStmt ws: + ExecuteWhile(ws); + break; + case DoWhileStmt dw: + ExecuteDoWhile(dw); + break; + case ForStmt fs: + ExecuteFor(fs); + break; + case ReturnStmt rs: + var retVal = rs.Value != null ? EvalExpr(rs.Value) : 0L; + HandleReturn(retVal); + break; + case DeclStmt ds: + var v = ds.Decl; + var val = v.Init != null ? EvalExpr(v.Init) : DefaultValue(v.Type); + SetLocal(v.Name, val); + break; + case BreakStmt: + HandleBreak(); + break; + case ContinueStmt: + HandleContinue(); + break; + case SwitchStmt ss: + PushSwitch(ss); + break; + case CaseStmt cs: + foreach (var b in cs.Body) + _execStack.Push(new ExecTask { Kind = TaskKind.ExecStatement, Stmt = (Stmt)b }); + break; + case LabelStmt ls: + _execStack.Push(new ExecTask { Kind = TaskKind.ExecStatement, Stmt = ls.Body }); + break; + } + + if (_stepMode) _stepIntoRemaining--; + } + + private void ExecuteIf(IfStmt ifs) + { + if (Convert.ToBoolean(EvalExpr(ifs.Cond))) + { + PushBranch(ifs.Then); + } + else if (ifs.Else != null) + { + PushBranch(ifs.Else); + } + } + + private void PushBranch(Stmt branch) + { + if (branch is BlockStmt block) + PushBlock(block); + else + _execStack.Push(new ExecTask { Kind = TaskKind.ExecStatement, Stmt = branch }); + } + + private void ExecuteWhile(WhileStmt ws) + { + if (!Convert.ToBoolean(EvalExpr(ws.Cond))) + return; + + _execStack.Push(new ExecTask { Kind = TaskKind.WhileRecheck, WhileStmt = ws }); + PushBranch(ws.Body); + } + + private void ExecTaskWhileRecheck(ExecTask task) + { + _execStack.Pop(); + var ws = task.WhileStmt!; + + if (!Convert.ToBoolean(EvalExpr(ws.Cond))) + return; + + _execStack.Push(new ExecTask { Kind = TaskKind.WhileRecheck, WhileStmt = ws }); + PushBranch(ws.Body); + } + + private void ExecuteDoWhile(DoWhileStmt dw) + { + _execStack.Push(new ExecTask { Kind = TaskKind.DoWhileCheck, DoWhileStmt = dw }); + PushBranch(dw.Body); + } + + private void ExecTaskDoWhileCheck(ExecTask task) + { + _execStack.Pop(); + var dw = task.DoWhileStmt!; + + if (Convert.ToBoolean(EvalExpr(dw.Cond))) + { + _execStack.Push(new ExecTask { Kind = TaskKind.DoWhileCheck, DoWhileStmt = dw }); + PushBranch(dw.Body); + } + } + + private void ExecuteFor(ForStmt fs) + { + if (fs.Init is VarDecl fv) + SetLocal(fv.Name, fv.Init != null ? EvalExpr(fv.Init) : DefaultValue(fv.Type)); + else if (fs.Init is Expr fe) + EvalExpr(fe); + else if (fs.Init is Stmt fsStmt) + _execStack.Push(new ExecTask { Kind = TaskKind.ExecStatement, Stmt = fsStmt }); + + if (fs.Cond != null && !Convert.ToBoolean(EvalExpr(fs.Cond))) + return; + + _execStack.Push(new ExecTask { Kind = TaskKind.ForRecheck, ForStmt = fs }); + PushBranch(fs.Body); + } + + private void ExecTaskForRecheck(ExecTask task) + { + _execStack.Pop(); + var fs = task.ForStmt!; + + if (fs.Update != null) EvalExpr(fs.Update); + + if (fs.Cond != null && !Convert.ToBoolean(EvalExpr(fs.Cond))) + return; + + _execStack.Push(new ExecTask { Kind = TaskKind.ForRecheck, ForStmt = fs }); + PushBranch(fs.Body); + } + + private void PushSwitch(SwitchStmt ss) + { + var val = EvalExpr(ss.Expr); + long target = Convert.ToInt64(val); + + var matched = false; + int i = 0; + while (i < ss.Cases.Count) + { + var c = ss.Cases[i]; + if (!matched && (c.IsDefault || (c.Value != null && Convert.ToInt64(EvalExpr(c.Value)) == target))) + { + matched = true; + _execStack.Push(new ExecTask { Kind = TaskKind.SwitchExecute, SwitchStmt = ss, StartCase = i }); + return; + } + i++; + } + } + + private void ExecTaskSwitch(ExecTask task) + { + var ss = task.SwitchStmt!; + int idx = task.StartCase; + + if (idx >= ss.Cases.Count) + { + _execStack.Pop(); + return; + } + + var c = ss.Cases[idx]; + task.StartCase = idx + 1; + + foreach (var b in c.Body) + _execStack.Push(new ExecTask { Kind = TaskKind.ExecStatement, Stmt = (Stmt)b }); + + // Push next case as a "continue" point (will be handled by break) + if (idx + 1 < ss.Cases.Count) + { + _execStack.Push(new ExecTask { Kind = TaskKind.Skip }); + // The Skip task will be consumed when we break out + } + } + + private void HandleReturn(object retVal) + { + throw new ReturnException(retVal); + } + + private void HandleBreak() + { + // Pop tasks until we find a loop or switch boundary + int depth = 0; + while (_execStack.Count > 0) + { + var task = _execStack.Pop(); + if (task.Kind is TaskKind.WhileRecheck or TaskKind.ForRecheck or TaskKind.DoWhileCheck) + { + // Found the loop boundary - don't push the recheck back + // Just return, loop ends + return; + } + if (task.Kind == TaskKind.SwitchExecute) + { + // Found switch boundary + return; + } + depth++; + } + } + + private void HandleContinue() + { + // Pop tasks until we find a loop boundary + while (_execStack.Count > 0) + { + var task = _execStack.Pop(); + if (task.Kind == TaskKind.WhileRecheck) + { + // Re-push the recheck and re-execute + _execStack.Push(task); + return; + } + if (task.Kind == TaskKind.ForRecheck) + { + _execStack.Push(task); + return; + } + if (task.Kind == TaskKind.DoWhileCheck) + { + _execStack.Push(task); + return; + } + } + } + + private void CheckBreakpoint(int astLine) + { + if (_breakpoints.Contains(astLine)) + { + SaveState(); + _finished = true; + _breakpointHit = true; + CurrentLine = astLine; + throw new BreakpointException(astLine, $"断点在第 {astLine - _lineOffset} 行"); + } + } + + // ==================== 栈帧管理 ==================== + + private void PushFrame(FunctionDecl func) + { + _frames.Add(new Frame { Func = func, Locals = new Dictionary() }); + } + + private void PopFrame() + { + if (_frames.Count > 0) _frames.RemoveAt(_frames.Count - 1); + _currentFunc = _frames.Count > 0 ? _frames[^1].Func.Name : ""; + } + + private void PushBlock(BlockStmt block) + { + _execStack.Push(new ExecTask + { + Kind = TaskKind.ExecuteBlock, + Block = block, + NextIndex = 0 + }); + } + + // ==================== 状态保存/恢复 ==================== + + private void SaveState() + { + _savedState = new SavedState + { + Frames = _frames.Select(f => new Frame + { + Func = f.Func, + Locals = new Dictionary(f.Locals) + }).ToList(), + Globals = new Dictionary(_globals), + Heap = new Dictionary(_heap), + HeapPtr = _heapPtr, + ExecStack = _execStack.Select(t => t.Clone()).ToList(), + CurrentFunc = _currentFunc, + ExitCode = ExitCode, + Finished = _finished, + StepMode = _stepMode, + StepOverMode = _stepOverMode + }; + } + + private void RestoreState(SavedState state) + { + _frames.Clear(); + foreach (var f in state.Frames) + _frames.Add(new Frame { Func = f.Func, Locals = new Dictionary(f.Locals) }); + + _globals.Clear(); + foreach (var kv in state.Globals) + _globals[kv.Key] = kv.Value; + + _heap.Clear(); + foreach (var kv in state.Heap) + _heap[kv.Key] = kv.Value; + + _heapPtr = state.HeapPtr; + _currentFunc = state.CurrentFunc; + ExitCode = state.ExitCode; + _finished = state.Finished; + _stepMode = state.StepMode; + _stepOverMode = state.StepOverMode; + + _execStack.Clear(); + for (int i = state.ExecStack.Count - 1; i >= 0; i--) + _execStack.Push(state.ExecStack[i].Clone()); + } + + private void RestoreInitialState() + { + _frames.Clear(); + _globals.Clear(); + _heap.Clear(); + _heapPtr = 0x10000000; + _execStack.Clear(); + _currentFunc = ""; + ExitCode = 0; + _finished = false; + _stepMode = false; + _stepOverMode = false; + + InitGlobals(); + } + + // ==================== 表达式求值 ==================== + + private object EvalExpr(Expr e) + { + return e switch + { + IntLiteral il => il.Value, + CharLiteral cl => cl.Value, + StringLiteral sl => AllocString(sl.Value), + IdentifierRef id => GetValue(id.Name), + UnaryExpr u => EvalUnary(u), + BinaryExpr b => EvalBinary(b), + AssignExpr a => EvalAssign(a), + CallExpr c => EvalCall(c), + ConditionalExpr ce => Convert.ToBoolean(EvalExpr(ce.Cond)) ? EvalExpr(ce.Then) : EvalExpr(ce.Else), + CastExpr ce => EvalExpr(ce.Operand), + SizeofExpr sz => sz.SizeOfType != null ? (long)sz.SizeOfType.Size : (long)((CType?)sz.Expr?.Type ?? VoidType.Instance).Size, + CommaExpr cm => EvalComma(cm), + IndexExpr ix => EvalIndex(ix), + MemberExpr m => EvalMember(m), + InitListExpr il => EvalInitList(il), + CompoundLiteralExpr cl => EvalCompoundLiteral(cl), + StringConcatExpr sc => AllocString(string.Concat(sc.Parts.Select(p => p.Value))), + _ => 0L + }; + } + + private object EvalComma(CommaExpr cm) + { + EvalExpr(cm.Left); + return EvalExpr(cm.Right); + } + + private object EvalUnary(UnaryExpr u) + { + long val = Convert.ToInt64(EvalExpr(u.Operand)); + return u.Op switch + { + TokenKind.Minus => -val, + TokenKind.Plus => val, + TokenKind.Tilde => ~val, + TokenKind.Not => val == 0 ? 1L : 0L, + TokenKind.PlusPlus => EvalIncr(u, val), + TokenKind.MinusMinus => EvalDecr(u, val), + TokenKind.Star => Deref(val), + TokenKind.Amp => val, + _ => val + }; + } + + private object EvalIncr(UnaryExpr u, long val) + { + SetValue(u.Operand, val + 1); + return u.Prefix ? val + 1 : val; + } + + private object EvalDecr(UnaryExpr u, long val) + { + SetValue(u.Operand, val - 1); + return u.Prefix ? val - 1 : val; + } + + private object EvalBinary(BinaryExpr b) + { + long l = Convert.ToInt64(EvalExpr(b.Left)); + long r = Convert.ToInt64(EvalExpr(b.Right)); + return b.Op switch + { + TokenKind.Plus => l + r, + TokenKind.Minus => l - r, + TokenKind.Star => l * r, + TokenKind.Slash => r != 0 ? l / r : throw new DivideByZeroException(), + TokenKind.Percent => r != 0 ? l % r : throw new DivideByZeroException(), + TokenKind.Amp => l & r, + TokenKind.Pipe => l | r, + TokenKind.Caret => l ^ r, + TokenKind.Shl => l << (int)r, + TokenKind.Shr => l >> (int)r, + TokenKind.Lt => l < r ? 1L : 0L, + TokenKind.Gt => l > r ? 1L : 0L, + TokenKind.Le => l <= r ? 1L : 0L, + TokenKind.Ge => l >= r ? 1L : 0L, + TokenKind.Eq => l == r ? 1L : 0L, + TokenKind.NotEq => l != r ? 1L : 0L, + TokenKind.AndAnd => (l != 0 && r != 0) ? 1L : 0L, + TokenKind.OrOr => (l != 0 || r != 0) ? 1L : 0L, + _ => 0L + }; + } + + private object EvalAssign(AssignExpr a) + { + long val = Convert.ToInt64(EvalExpr(a.Value)); + if (a.Op == TokenKind.Assign) + { + SetValue(a.Target, val); + return val; + } + long cur = Convert.ToInt64(EvalExpr(a.Target)); + long result = a.Op switch + { + TokenKind.PlusAssign => cur + val, + TokenKind.MinusAssign => cur - val, + TokenKind.StarAssign => cur * val, + TokenKind.SlashAssign => val != 0 ? cur / val : 0, + TokenKind.PercentAssign => val != 0 ? cur % val : 0, + TokenKind.ShlAssign => cur << (int)val, + TokenKind.ShrAssign => cur >> (int)val, + TokenKind.AndAssign => cur & val, + TokenKind.OrAssign => cur | val, + TokenKind.XorAssign => cur ^ val, + _ => val + }; + SetValue(a.Target, result); + return result; + } + + private object EvalCall(CallExpr c) + { + if (c.Callee is IdentifierRef id) + { + string name = id.Name; + var args = c.Args.Select(EvalExpr).ToArray(); + + if (TryBuiltin(name, args, out var result)) + return result; + + if (_functions.TryGetValue(name, out var func)) + return CallFunction(func, args); + + throw new InvalidOperationException($"未定义的函数: {name}"); + } + throw new InvalidOperationException("不支持的函数调用形式"); + } + + private long CallFunction(FunctionDecl func, object[] args) + { + PushFrame(func); + for (int i = 0; i < func.Params.Count && i < args.Length; i++) + SetLocal(func.Params[i].Name, args[i]); + + int baseDepth = _execStack.Count; + + try + { + if (func.Body != null) + { + PushBlock(func.Body); + + while (_execStack.Count > baseDepth && !_finished) + { + if (_stepMode && _stepIntoRemaining <= 0) + { + SaveState(); + _stepMode = false; + break; + } + + if (_stepOverMode && _execStack.Count <= _stepOverDepth && _stepIntoRemaining <= 0) + { + SaveState(); + _stepMode = false; + _stepOverMode = false; + break; + } + + var task = _execStack.Peek(); + switch (task.Kind) + { + case TaskKind.ExecuteBlock: + ExecTaskExecuteBlock(task); + break; + case TaskKind.ExecStatement: + ExecTaskStatement(task); + break; + case TaskKind.WhileRecheck: + ExecTaskWhileRecheck(task); + break; + case TaskKind.ForRecheck: + ExecTaskForRecheck(task); + break; + case TaskKind.DoWhileCheck: + ExecTaskDoWhileCheck(task); + break; + case TaskKind.SwitchExecute: + ExecTaskSwitch(task); + break; + case TaskKind.IfBranch: + _execStack.Pop(); + break; + case TaskKind.Skip: + _execStack.Pop(); + break; + default: + _execStack.Pop(); + break; + } + } + } + + if (!_finished) + { + PopFrame(); + return 0; + } + + return 0; + } + catch (BreakpointException) + { + while (_execStack.Count > baseDepth) + _execStack.Pop(); + PopFrame(); + throw; + } + catch (ReturnException ret) + { + while (_execStack.Count > baseDepth) + _execStack.Pop(); + PopFrame(); + return Convert.ToInt64(ret.Value); + } + } + + private bool TryBuiltin(string name, object[] args, out object result) + { + result = null!; + switch (name) + { + case "printf": result = BuiltinPrintf(args); return true; + case "puts": result = BuiltinPuts(args); return true; + case "putchar": result = BuiltinPutchar(args); return true; + case "getchar": result = BuiltinGetchar(); return true; + case "malloc": result = BuiltinMalloc(args); return true; + case "calloc": result = BuiltinCalloc(args); return true; + case "free": result = BuiltinFree(args); return true; + case "memcpy": result = BuiltinMemcpy(args); return true; + case "memset": result = BuiltinMemset(args); return true; + case "strlen": result = BuiltinStrlen(args); return true; + case "exit": Exit(Convert.ToInt32(args[0])); return true; + case "time_utc": result = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 480 * 60; return true; + case "time_utc_raw": result = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); return true; + case "time_get_utc": result = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 480 * 60; return true; + case "time_get_utc_tz": result = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + Convert.ToInt64(args[0]) * 60; return true; + case "atoi": result = long.TryParse(PtrToString(Convert.ToInt64(args[0])), out var v) ? v : 0L; return true; + case "strcpy": result = BuiltinStrcpy(args); return true; + case "strncpy": result = BuiltinStrncpy(args); return true; + case "strcmp": result = BuiltinStrcmp(args); return true; + case "gui_init": result = 0L; return true; + case "gui_window_create": result = -1L; return true; + case "gui_window_text": result = -1L; return true; + case "gui_window_present": result = 0L; return true; + case "gui_event_poll": result = 0L; return true; + case "gui_event_wait": result = 1L; return true; + case "gui_window_destroy": result = -1L; return true; + case "gui_cleanup": result = 0L; return true; + default: return false; + } + } + + private void Exit(int code) + { + ExitCode = code; + _finished = true; + } + + // ==================== 变量访问 ==================== + + private object GetValue(string name) + { + for (int i = _frames.Count - 1; i >= 0; i--) + if (_frames[i].Locals.TryGetValue(name, out var v)) return v; + if (_globals.TryGetValue(name, out var g)) return g; + throw new InvalidOperationException($"未定义的变量: {name}"); + } + + private void SetLocal(string name, object value) + { + if (_frames.Count > 0) + _frames[^1].Locals[name] = value; + } + + private void SetValue(Expr target, object value) + { + switch (target) + { + case IdentifierRef id: + for (int i = _frames.Count - 1; i >= 0; i--) + if (_frames[i].Locals.ContainsKey(id.Name)) { _frames[i].Locals[id.Name] = value; return; } + if (_globals.ContainsKey(id.Name)) { _globals[id.Name] = value; return; } + SetLocal(id.Name, value); + break; + case UnaryExpr u when u.Op == TokenKind.Star: + DerefWrite(Convert.ToInt64(EvalExpr(u.Operand)), Convert.ToInt64(value)); + break; + case IndexExpr ix: + long arrPtr = Convert.ToInt64(EvalExpr(ix.Array)); + long idx = Convert.ToInt64(EvalExpr(ix.Index)); + PokeByte(arrPtr + idx, (byte)(Convert.ToInt64(value) & 0xFF)); + break; + case MemberExpr m: + long basePtr = Convert.ToInt64(EvalExpr(m.Expr)); + long offset = 0; + if (m.Expr.Type is StructType st) + { + var (f, off) = AstHelpers.FindField(st, m.Name); + offset = off; + } + PokeByte(basePtr + offset, (byte)(Convert.ToInt64(value) & 0xFF)); + break; + } + } + + private object EvalIndex(IndexExpr ix) + { + long arrPtr = Convert.ToInt64(EvalExpr(ix.Array)); + long idx = Convert.ToInt64(EvalExpr(ix.Index)); + return PeekByte(arrPtr + idx); + } + + private object EvalMember(MemberExpr m) + { + long basePtr = Convert.ToInt64(EvalExpr(m.Expr)); + long offset = 0; + if (m.Expr.Type is StructType st) + { + var (f, off) = AstHelpers.FindField(st, m.Name); + if (f != null) offset = off; + } + return PeekByte(basePtr + offset); + } + + private object EvalInitList(InitListExpr il) + { + if (il.Elements.Count == 0) return 0L; + var values = il.Elements.Select(EvalExpr).ToList(); + long ptr = AllocBytes(values.Count); + for (int i = 0; i < values.Count; i++) + PokeByte(ptr + i, (byte)(Convert.ToInt64(values[i]) & 0xFF)); + return ptr; + } + + private object EvalCompoundLiteral(CompoundLiteralExpr cl) => EvalExpr(cl.Init); + + // ==================== 堆内存 ==================== + + private long AllocString(string s) + { + byte[] bytes = Encoding.UTF8.GetBytes(s + "\0"); + long ptr = _heapPtr; + _heapPtr += (long)bytes.Length + 8; + for (int i = 0; i < bytes.Length; i++) + _heap[ptr + i] = bytes[i]; + return ptr; + } + + private long AllocBytes(long count) + { + long ptr = _heapPtr; + _heapPtr += count + 8; + for (long i = 0; i < count; i++) + _heap[ptr + i] = 0; + return ptr; + } + + private byte PeekByte(long addr) => _heap.GetValueOrDefault(addr); + private void PokeByte(long addr, byte val) => _heap[addr] = val; + private long Deref(long addr) + { + long val = 0; + for (int i = 0; i < 8; i++) val |= (long)_heap.GetValueOrDefault(addr + i) << (i * 8); + return val; + } + private void DerefWrite(long addr, long val) + { + for (int i = 0; i < 8; i++) _heap[addr + i] = (byte)((val >> (i * 8)) & 0xFF); + } + + private string PtrToString(long ptr) + { + var sb = new StringBuilder(); + while (true) + { + byte b = PeekByte(ptr++); + if (b == 0) break; + sb.Append((char)b); + } + return sb.ToString(); + } + + // ==================== 内置函数实现 ==================== + + private object BuiltinPrintf(object[] args) + { + if (args.Length == 0) return 0L; + string fmt = PtrToString(Convert.ToInt64(args[0])); + var parts = new List(); + int fi = 1; + for (int i = 0; i < fmt.Length; i++) + { + if (fmt[i] == '%' && i + 1 < fmt.Length) + { + char spec = fmt[i + 1]; + switch (spec) + { + case 'd': case 'i': + parts.Add(fi < args.Length ? Convert.ToString(Convert.ToInt64(args[fi])) : "?"); + fi++; i++; break; + case 'x': case 'X': + parts.Add(fi < args.Length ? Convert.ToString(Convert.ToInt64(args[fi]), 16) : "?"); + fi++; i++; break; + case 's': + parts.Add(fi < args.Length ? PtrToString(Convert.ToInt64(args[fi])) : "?"); + fi++; i++; break; + case 'c': + parts.Add(fi < args.Length ? new string((char)Convert.ToInt32(args[fi]), 1) : "?"); + fi++; i++; break; + case 'l': i++; break; + default: parts.Add(spec.ToString()); i++; break; + } + } + else parts.Add(fmt[i].ToString()); + } + string output = string.Concat(parts); + Console.Write(output); + return output.Length; + } + + private object BuiltinPuts(object[] args) + { + string s = args.Length > 0 ? PtrToString(Convert.ToInt64(args[0])) : ""; + Console.WriteLine(s); + return 0L; + } + + private object BuiltinPutchar(object[] args) + { + int c = args.Length > 0 ? Convert.ToInt32(args[0]) : 0; + Console.Write((char)c); + return (long)c; + } + + private object BuiltinGetchar() + { + int c = Console.Read(); + return c; + } + + private object BuiltinMalloc(object[] args) + { + long size = args.Length > 0 ? Convert.ToInt64(args[0]) : 0; + return AllocBytes(size); + } + + private object BuiltinCalloc(object[] args) + { + long n = args.Length > 0 ? Convert.ToInt64(args[0]) : 0; + long sz = args.Length > 1 ? Convert.ToInt64(args[1]) : 0; + return AllocBytes(n * sz); + } + + private object BuiltinFree(object[] args) + { + if (args.Length > 0) { } + return 0L; + } + + private object BuiltinMemcpy(object[] args) + { + long dst = Convert.ToInt64(args[0]); + long src = Convert.ToInt64(args[1]); + long n = Convert.ToInt64(args[2]); + for (long i = 0; i < n; i++) + PokeByte(dst + i, PeekByte(src + i)); + return dst; + } + + private object BuiltinMemset(object[] args) + { + long dst = Convert.ToInt64(args[0]); + int c = Convert.ToInt32(args[1]); + long n = Convert.ToInt64(args[2]); + for (long i = 0; i < n; i++) + PokeByte(dst + i, (byte)c); + return dst; + } + + private object BuiltinStrlen(object[] args) + { + long ptr = Convert.ToInt64(args[0]); + long len = 0; + while (PeekByte(ptr + len) != 0) len++; + return len; + } + + private object BuiltinStrcpy(object[] args) + { + long dst = Convert.ToInt64(args[0]); + long src = Convert.ToInt64(args[1]); + long i = 0; + byte b; + do { b = PeekByte(src + i); PokeByte(dst + i, b); i++; } while (b != 0); + return dst; + } + + private object BuiltinStrncpy(object[] args) + { + long dst = Convert.ToInt64(args[0]); + long src = Convert.ToInt64(args[1]); + long n = Convert.ToInt64(args[2]); + long i; + for (i = 0; i < n; i++) + { + byte b = PeekByte(src + i); + PokeByte(dst + i, b); + if (b == 0) break; + } + for (; i < n; i++) PokeByte(dst + i, 0); + return dst; + } + + private object BuiltinStrcmp(object[] args) + { + long a = Convert.ToInt64(args[0]); + long b = Convert.ToInt64(args[1]); + long i = 0; + while (true) + { + byte ca = PeekByte(a + i); + byte cb = PeekByte(b + i); + if (ca != cb || ca == 0) return ca - cb; + i++; + } + } + + // ==================== 调试辅助 ==================== + + public string[] GetLocals() + { + var result = new List(); + for (int i = _frames.Count - 1; i >= 0; i--) + foreach (var kv in _frames[i].Locals) + result.Add($"{kv.Key} = {FormatValue(kv.Value)}"); + return result.ToArray(); + } + + public string[] GetGlobals() + { + var result = new List(); + foreach (var kv in _globals) + { + if (kv.Value is long l && l == 0) continue; + if (kv.Value is byte[] arr && arr.All(b => b == 0)) continue; + result.Add($"{kv.Key} = {FormatValue(kv.Value)}"); + } + return result.ToArray(); + } + + private string FormatValue(object v) + { + if (v is long l) + { + if (l > 0x10000000 && l < 0x20000000) + return $"0x{l:X} \"{PtrToStringSafe(l)}\""; + return l.ToString(); + } + if (v is byte[] arr) return $"[{string.Join(",", arr.Select(b => b.ToString()))}]"; + return v?.ToString() ?? "null"; + } + + private string PtrToStringSafe(long ptr) + { + var sb = new StringBuilder(); + for (long i = 0; i < 256; i++) + { + byte b = PeekByte(ptr + i); + if (b == 0) break; + sb.Append((char)b); + } + return sb.ToString(); + } + + // ==================== 内部类型 ==================== + + private enum TaskKind + { + ExecuteBlock, + ExecStatement, + WhileRecheck, + ForRecheck, + DoWhileCheck, + FunctionCall, + FunctionReturn, + SwitchExecute, + IfBranch, + Skip + } + + private class ExecTask + { + public TaskKind Kind { get; set; } + public BlockStmt? Block { get; set; } + public int NextIndex { get; set; } + public Stmt? Stmt { get; set; } + public WhileStmt? WhileStmt { get; set; } + public ForStmt? ForStmt { get; set; } + public DoWhileStmt? DoWhileStmt { get; set; } + public SwitchStmt? SwitchStmt { get; set; } + public int StartCase { get; set; } + public FunctionDecl? Function { get; set; } + public object[]? Args { get; set; } + public long ReturnValue { get; set; } + public int CallerFrameDepth { get; set; } + + public ExecTask Clone() + { + return new ExecTask + { + Kind = Kind, + Block = Block, + NextIndex = NextIndex, + Stmt = Stmt, + WhileStmt = WhileStmt, + ForStmt = ForStmt, + DoWhileStmt = DoWhileStmt, + SwitchStmt = SwitchStmt, + StartCase = StartCase, + Function = Function, + Args = Args?.ToArray(), + ReturnValue = ReturnValue, + CallerFrameDepth = CallerFrameDepth + }; + } + } + + private class Frame + { + public FunctionDecl Func { get; set; } = null!; + public Dictionary Locals { get; set; } = new(); + } + + private class SavedState + { + public List Frames { get; set; } = new(); + public Dictionary Globals { get; set; } = new(); + public Dictionary Heap { get; set; } = new(); + public long HeapPtr { get; set; } + public List ExecStack { get; set; } = new(); + public string CurrentFunc { get; set; } = ""; + public int ExitCode { get; set; } + public bool Finished { get; set; } + public bool StepMode { get; set; } + public bool StepOverMode { get; set; } + } + + public class BreakpointException : Exception + { + public int Line { get; } + public BreakpointException(int line, string message) : base(message) { Line = line; } + } + + private class ReturnException : Exception + { + public object Value { get; } + public ReturnException(object value) { Value = value; } + } +} \ No newline at end of file diff --git a/src/PazeE.Compiler/Program.cs b/src/PazeE.Compiler/Program.cs index 7f41710..4b5580c 100644 --- a/src/PazeE.Compiler/Program.cs +++ b/src/PazeE.Compiler/Program.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; +using System.Text; using PazeE.Compiler.Binary; using PazeE.Compiler.CodeGen; using PazeE.Compiler.CodeGen.Arm64; @@ -13,22 +15,301 @@ internal static class Program { private static int Main(string[] args) { - Console.WriteLine($"paze {BuildInfo.Version} ({BuildInfo.Channel})"); - if (args.Length == 0 || args[0] is "-h" or "--help") { PrintUsage(); return 0; } - if (args[0] is "-v" or "--version") return 0; + if (args[0] is "-v" or "--version") + { + Console.WriteLine($"paze {BuildInfo.Version} ({BuildInfo.Channel})"); + return 0; + } - // 解析参数 + Console.WriteLine($"paze {BuildInfo.Version} ({BuildInfo.Channel})"); + + // ---- 子命令 ---- + if (args[0] == "run") return CmdRun(args); + if (args[0] == "debug") return CmdDebug(args); + if (args[0] == "check") return CmdCheck(args); + + // ---- 编译(原有逻辑)---- + return CmdCompile(args); + } + + // ==================== run ==================== + private static int CmdRun(string[] args) + { + string? srcFile = null; + Platform target = HostPlatform(); + Arch arch = Arch.Amd; + var runArgs = new List(); + + for (int i = 1; i < args.Length; i++) + { + var a = args[i]; + switch (a) + { + case "--target": target = ParsePlatform(args[++i]); break; + case "--arch": arch = ParseArch(args[++i]); break; + case "--": + while (++i < args.Length) runArgs.Add(args[i]); + break; + default: + if (srcFile == null) srcFile = a; + else runArgs.Add(a); + break; + } + } + if (srcFile == null) { Console.Error.WriteLine("用法: paze run [--target platform] [--arch arch] [-- args...]"); return 1; } + + var diag = new Diagnostics(); + var targetInfo = new TargetInfo { Platform = target, Arch = arch }; + + if (!TryParseAndAnalyze(srcFile, target, arch, diag, out var unit, out var sema)) + return 1; + + // 编译到临时文件 + ICodeGenerator cg = arch == Arch.Arm + ? new Arm64CodeGenerator(targetInfo) + : new X64CodeGenerator(targetInfo); + var img = cg.Generate(unit!, sema!); + + IExecutableWriter writer = (target, arch) switch + { + (Platform.Windows, Arch.Arm) => new PeWriterArm64(), + (Platform.Windows, _) => new PeWriter(), + (Platform.Linux, Arch.Arm) => new ElfWriterArm64(), + (Platform.Linux, _) => new ElfWriter(), + (Platform.MacOS, Arch.Arm) => new MachOWriterArm64(), + (Platform.MacOS, _) => new MachOWriter(), + (Platform.Los4, _) => new ElfWriterLos4(), + _ => new PeWriter() + }; + + byte[] exe; + try { exe = writer.Write(img); } + catch (NotImplementedException) { Console.Error.WriteLine($"平台 {target} 的写入器尚未实现"); return 1; } + + // 生成临时文件 + string tmpDir = Path.Combine(Path.GetTempPath(), "paze_run"); + Directory.CreateDirectory(tmpDir); + string baseName = Path.GetFileNameWithoutExtension(srcFile); + string tmpFile = Path.Combine(tmpDir, baseName + (target == Platform.Windows ? ".exe" : "")); + File.WriteAllBytes(tmpFile, exe); + + try + { + if (target != Platform.Windows) + { + try { Process.Start(new ProcessStartInfo("chmod", $"+x \"{tmpFile}\"") { UseShellExecute = true })?.WaitForExit(); } + catch { } + } + + Console.WriteLine($"运行 {tmpFile}..."); + using var proc = new Process(); + proc.StartInfo = new ProcessStartInfo + { + FileName = tmpFile, + Arguments = string.Join(" ", runArgs.Select(a => a.Contains(' ') ? $"\"{a}\"" : a)), + UseShellExecute = false, + RedirectStandardOutput = false, + RedirectStandardError = false + }; + proc.Start(); + proc.WaitForExit(); + int code = proc.ExitCode; + Console.WriteLine($"(退出码: {code})"); + return code; + } + finally + { + try { if (File.Exists(tmpFile)) File.Delete(tmpFile); } catch { } + } + } + + // ==================== debug ==================== + private static int CmdDebug(string[] args) + { + string? srcFile = null; + var breaklines = new HashSet(); + Platform target = HostPlatform(); + Arch arch = Arch.Amd; + + for (int i = 1; i < args.Length; i++) + { + var a = args[i]; + switch (a) + { + case "--target": target = ParsePlatform(args[++i]); break; + case "--arch": arch = ParseArch(args[++i]); break; + case "line": + var nums = args[++i].Split(',', StringSplitOptions.RemoveEmptyEntries); + foreach (var n in nums) + if (int.TryParse(n.Trim(), out int line)) + breaklines.Add(line); + break; + default: + if (srcFile == null) srcFile = a; + break; + } + } + if (srcFile == null) { Console.Error.WriteLine("用法: paze debug [line 1,2,3]"); return 1; } + + var diag = new Diagnostics(); + var targetInfo = new TargetInfo { Platform = target, Arch = arch }; + + if (!TryParseAndAnalyze(srcFile, target, arch, diag, out var unit, out var sema)) + return 1; + + int lineOffset = ComputeLineOffset(target, arch); + var vm = new DebuggerVM(unit!, sema!, diag, lineOffset); + foreach (var bl in breaklines) vm.SetBreakpoint(bl); + + if (breaklines.Count == 0) + { + Console.WriteLine("(无断点,直接运行。使用 'paze debug xxx.pe line 1,2,3' 设置断点)"); + } + else + { + Console.WriteLine($"已设置断点: {string.Join(", ", breaklines.OrderBy(b => b))}"); + } + Console.WriteLine("调试器就绪。命令: s=单步 n=步过 c=继续 p=打印变量 q=退出"); + + // 交互式调试循环 + try + { + vm.Run(); + } + catch (DebuggerVM.BreakpointException ex) + { + Console.WriteLine($"\n{ex.Message}"); + } + + while (!vm.IsFinished) + { + Console.Write($"\n{vm.CurrentFunc} #{vm.CurrentLine - ComputeLineOffset(target, arch)}> "); + var cmd = Console.ReadLine()?.Trim().ToLower(); + if (cmd == null || cmd == "q" || cmd == "quit") break; + try + { + switch (cmd) + { + case "s": case "step": + vm.StepInto(); + break; + case "n": case "next": + vm.StepOver(); + break; + case "c": case "continue": + vm.Continue(); + break; + case "p": case "print": + var locals = vm.GetLocals(); + var globals = vm.GetGlobals(); + if (locals.Length > 0) + { + Console.WriteLine("局部变量:"); + foreach (var l in locals) Console.WriteLine($" {l}"); + } + if (globals.Length > 0) + { + Console.WriteLine("全局变量:"); + foreach (var g in globals) Console.WriteLine($" {g}"); + } + if (locals.Length == 0 && globals.Length == 0) + Console.WriteLine("(无变量)"); + break; + case "l": case "locals": + foreach (var l in vm.GetLocals()) Console.WriteLine(l); + break; + case "g": case "globals": + foreach (var g in vm.GetGlobals()) Console.WriteLine(g); + break; + case "b": case "break": + Console.Write("行号: "); + if (int.TryParse(Console.ReadLine(), out int bl)) + { + vm.SetBreakpoint(bl); + Console.WriteLine($"断点已设置: 第 {bl} 行"); + } + break; + case "bl": case "breaks": + var bs = vm.GetBreakpoints(); + Console.WriteLine(bs.Count > 0 ? $"断点: {string.Join(", ", bs)}" : "无断点"); + break; + case "cb": case "clearbreak": + Console.Write("要清除的断点行号: "); + if (int.TryParse(Console.ReadLine(), out int cb)) + { + vm.ClearBreakpoint(cb); + Console.WriteLine($"断点已清除: 第 {cb} 行"); + } + break; + case "r": case "run": + vm.Run(); + break; + default: + Console.WriteLine("未知命令。可用: s(单步) n(步过) c(继续) p(变量) g(全局) b(断点) bl(断点列表) cb(清除断点) r(重运行) q(退出)"); + break; + } + } + catch (DebuggerVM.BreakpointException ex) + { + Console.WriteLine($"\n{ex.Message}"); + } + catch (Exception ex) + { + Console.WriteLine($"错误: {ex.Message}"); + break; + } + } + + Console.WriteLine($"程序退出码: {vm.ExitCode}"); + return vm.ExitCode; + } + + // ==================== check ==================== + private static int CmdCheck(string[] args) + { + string? srcFile = null; + Platform target = HostPlatform(); + Arch arch = Arch.Amd; + + for (int i = 1; i < args.Length; i++) + { + var a = args[i]; + switch (a) + { + case "--target": target = ParsePlatform(args[++i]); break; + case "--arch": arch = ParseArch(args[++i]); break; + default: + if (srcFile == null) srcFile = a; + break; + } + } + if (srcFile == null) { Console.Error.WriteLine("用法: paze check "); return 1; } + + var diag = new Diagnostics(); + var targetInfo = new TargetInfo { Platform = target, Arch = arch }; + + if (!TryParseAndAnalyze(srcFile, target, arch, diag, out _, out _)) + return 1; + + Console.WriteLine("检查通过 ✓"); + return 0; + } + + // ==================== 编译 ==================== + private static int CmdCompile(string[] args) + { string? srcFile = null; string? outFile = null; Platform target = HostPlatform(); Arch arch = Arch.Amd; string emit = "exe"; bool printAst = false; + for (int i = 0; i < args.Length; i++) { var a = args[i]; @@ -37,9 +318,7 @@ internal static class Program case "-o": outFile = args[++i]; break; case "--target": target = ParsePlatform(args[++i]); break; case "-arch": - case "--arch": - arch = ParseArch(args[++i]); - break; + case "--arch": arch = ParseArch(args[++i]); break; case "--emit": emit = args[++i]; break; case "--print-ast": printAst = true; break; default: @@ -53,53 +332,19 @@ internal static class Program var diag = new Diagnostics(); var targetInfo = new TargetInfo { Platform = target, Arch = arch }; - // 预处理(自动注入 paze.h) - string source; - try { source = File.ReadAllText(srcFile); } - catch (Exception e) { Console.Error.WriteLine($"无法读取源文件 '{srcFile}': {e.Message}"); return 1; } - - var srcDir = Path.GetDirectoryName(Path.GetFullPath(srcFile)) ?? ""; - var pp = new Preprocessor(diag, new[] { srcDir }, name => name == "paze.h" ? LibcDecls.PazeHeader : null); - // 按目标平台注入预定义宏,让 paze.h 用 #ifdef 切换平台实现 - string platformDefs = target switch - { - Platform.Windows => "#define _WIN32 1\n#define _WIN64 1\n", - Platform.Linux => "#define __linux__ 1\n#define __linux 1\n", - Platform.MacOS => "#define __APPLE__ 1\n#define __MACH__ 1\n", - Platform.Los4 => "#define __leonos__ 1\n#define __los4__ 1\n", - _ => "" - }; - platformDefs += arch switch - { - Arch.Arm => "#define __arm64__ 1\n#define __aarch64__ 1\n", - _ => "#define __x86_64__ 1\n#define __amd64__ 1\n" - }; - var tokens = pp.Run(platformDefs + "#include \n" + source, srcFile); - - if (diag.HasErrors) { diag.Print(Console.Error); return 1; } - - // 解析 - var parser = new PazeE.Compiler.Parser.Parser(tokens, diag, targetInfo); - var unit = parser.Parse(); - if (diag.HasErrors) { diag.Print(Console.Error); return 1; } - - if (printAst) PrintAst(unit); - - // 语义分析 - var sema = new Sema(diag, targetInfo); - if (!sema.Analyze(unit)) { diag.Print(Console.Error); return 1; } + if (!TryParseAndAnalyze(srcFile, target, arch, diag, out var unit, out var sema)) + return 1; + if (printAst && unit != null) PrintAst(unit); if (emit == "ast") return 0; - // 代码生成 ICodeGenerator cg = arch == Arch.Arm ? new Arm64CodeGenerator(targetInfo) : new X64CodeGenerator(targetInfo); - var img = cg.Generate(unit, sema); + var img = cg.Generate(unit!, sema!); if (emit == "asm") { DumpAsm(img); return 0; } - // 写入可执行文件 IExecutableWriter writer = (target, arch) switch { (Platform.Windows, Arch.Arm) => new PeWriterArm64(), @@ -123,6 +368,65 @@ internal static class Program return 0; } + // ==================== 公共辅助 ==================== + private static bool TryParseAndAnalyze(string srcFile, Platform target, Arch arch, + Diagnostics diag, out TranslationUnit? unit, out Sema? sema) + { + unit = null; sema = null; + var targetInfo = new TargetInfo { Platform = target, Arch = arch }; + + string source; + try { source = File.ReadAllText(srcFile); } + catch (Exception e) { Console.Error.WriteLine($"无法读取源文件 '{srcFile}': {e.Message}"); return false; } + + var srcDir = Path.GetDirectoryName(Path.GetFullPath(srcFile)) ?? ""; + var pp = new Preprocessor(diag, new[] { srcDir }, name => name == "paze.h" ? LibcDecls.PazeHeader : null); + string platformDefs = target switch + { + Platform.Windows => "#define _WIN32 1\n#define _WIN64 1\n", + Platform.Linux => "#define __linux__ 1\n#define __linux 1\n", + Platform.MacOS => "#define __APPLE__ 1\n#define __MACH__ 1\n", + Platform.Los4 => "#define __leonos__ 1\n#define __los4__ 1\n", + _ => "" + }; + platformDefs += arch switch + { + Arch.Arm => "#define __arm64__ 1\n#define __aarch64__ 1\n", + _ => "#define __x86_64__ 1\n#define __amd64__ 1\n" + }; + var tokens = pp.Run(platformDefs + "#include \n" + source, srcFile); + if (diag.HasErrors) { diag.Print(Console.Error); return false; } + + var parser = new PazeE.Compiler.Parser.Parser(tokens, diag, targetInfo); + unit = parser.Parse(); + if (diag.HasErrors) { diag.Print(Console.Error); return false; } + + sema = new Sema(diag, targetInfo); + if (!sema.Analyze(unit)) { diag.Print(Console.Error); return false; } + + return true; + } + + private static int ComputeLineOffset(Platform target, Arch arch) + { + string platformDefs = target switch + { + Platform.Windows => "#define _WIN32 1\n#define _WIN64 1\n", + Platform.Linux => "#define __linux__ 1\n#define __linux 1\n", + Platform.MacOS => "#define __APPLE__ 1\n#define __MACH__ 1\n", + Platform.Los4 => "#define __leonos__ 1\n#define __los4__ 1\n", + _ => "" + }; + platformDefs += arch switch + { + Arch.Arm => "#define __arm64__ 1\n#define __aarch64__ 1\n", + _ => "#define __x86_64__ 1\n#define __amd64__ 1\n" + }; + + int CountLines(string s) => s.Split('\n').Length; + return CountLines(platformDefs); + } + private static Platform HostPlatform() => Environment.OSVersion.Platform == PlatformID.Unix ? (Directory.Exists("/System/Library/Frameworks") ? Platform.MacOS : Platform.Linux) : Platform.Windows; @@ -151,13 +455,32 @@ internal static class Program private static void PrintUsage() { - Console.WriteLine("用法: paze [选项]"); + Console.WriteLine("用法:"); + Console.WriteLine(" paze [选项] 编译为可执行文件"); + Console.WriteLine(" paze run [-- args] 编译并直接运行"); + Console.WriteLine(" paze debug [line 1,2] 调试运行(断点)"); + Console.WriteLine(" paze check 语法/语义检查"); + Console.WriteLine(""); + Console.WriteLine("编译选项:"); Console.WriteLine(" -o 指定输出文件名"); Console.WriteLine(" --target win|linux|macos|los4(默认宿主)"); - Console.WriteLine(" --arch amd|arm(默认 amd=x86-64;arm=AArch64 三平台)"); + Console.WriteLine(" --arch amd|arm(默认 amd)"); Console.WriteLine(" --emit ast|asm|exe(默认 exe)"); Console.WriteLine(" --print-ast 打印 AST"); Console.WriteLine(" -v / --version 显示版本"); + Console.WriteLine(" -h / --help 帮助"); + Console.WriteLine(""); + Console.WriteLine("调试命令:"); + Console.WriteLine(" s / step 单步进入"); + Console.WriteLine(" n / next 单步跳过"); + Console.WriteLine(" c / continue 继续运行"); + Console.WriteLine(" p / print 打印变量(局部+全局)"); + Console.WriteLine(" g / globals 打印非零全局变量"); + Console.WriteLine(" b / break 设置断点"); + Console.WriteLine(" bl 列出断点"); + Console.WriteLine(" cb 清除断点"); + Console.WriteLine(" r / run 重新运行"); + Console.WriteLine(" q / quit 退出调试"); } private static void PrintAst(TranslationUnit unit) @@ -180,4 +503,4 @@ internal static class Program Console.WriteLine($"符号: {img.Symbols.Count} 外部: {string.Join(", ", img.Externals)}"); Console.WriteLine($"重定位: {img.Fixups.Count}"); } -} +} \ No newline at end of file diff --git a/src/PazeE.los4/build.ps1 b/src/PazeE.los4/build.ps1 new file mode 100644 index 0000000..7d313a0 --- /dev/null +++ b/src/PazeE.los4/build.ps1 @@ -0,0 +1,48 @@ +# Build script for the pure-C PazeE compiler (Los4 target). +# Usage: +# ./build.ps1 # build bin/paze.exe +# ./build.ps1 -Clean # remove bin/ and rebuild +param([switch]$Clean) + +$root = Split-Path -Parent $MyInvocation.MyCommand.Path +$src = Join-Path $root "src" +$inc = Join-Path $root "include" +$bin = Join-Path $root "bin" + +if ($Clean -and (Test-Path $bin)) { + Remove-Item -Recurse -Force $bin +} + +New-Item -ItemType Directory -Force -Path $bin | Out-Null + +$sources = Get-ChildItem -Path $src -Filter "*.c" | ForEach-Object { $_.FullName } + +# Pick the first available C compiler (gcc preferred, then clang). +$cc = $null +foreach ($c in @("gcc", "clang")) { + $found = Get-Command $c -ErrorAction SilentlyContinue + if ($found) { $cc = $found.Source; break } +} +if (-not $cc) { + Write-Error "No C compiler found (gcc/clang). Install MinGW or clang." + exit 1 +} + +$out = Join-Path $bin "paze.exe" + +Write-Host "Compiler : $cc" +Write-Host "Sources : $($sources.Count) file(s)" +Write-Host "Output : $out" +Write-Host "" + +& $cc -O2 -std=c11 -Wall -Wextra -Wno-unused-parameter -Wno-unused-function ` + -Wno-unused-variable -Wno-sign-compare ` + "-I$inc" $sources -o $out 2>&1 | ForEach-Object { Write-Host $_ } + +if ($LASTEXITCODE -eq 0) { + Write-Host "" + Write-Host "Build OK: $out" -ForegroundColor Green +} else { + Write-Error "Build failed (exit $LASTEXITCODE)" + exit $LASTEXITCODE +} diff --git a/src/PazeE.los4/include/paze_ast.h b/src/PazeE.los4/include/paze_ast.h new file mode 100644 index 0000000..52e5bb4 --- /dev/null +++ b/src/PazeE.los4/include/paze_ast.h @@ -0,0 +1,349 @@ +#ifndef PAZE_AST_H +#define PAZE_AST_H + +#include "paze_types.h" +#include "paze_token.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration - defined in paze_type system */ +typedef struct paze_type_t paze_type_t; + +/* ======================================================================== + * Node Kind Enum + * ======================================================================== */ + +typedef enum { + /* Declarations */ + PAZE_NODE_TRANSLATION_UNIT, + PAZE_NODE_FUNCTION_DECL, + PAZE_NODE_VAR_DECL, + PAZE_NODE_PARAM, + PAZE_NODE_STRUCT_DECL, + PAZE_NODE_UNION_DECL, + PAZE_NODE_TYPEDEF_DECL, + PAZE_NODE_ENUM_DECL, + PAZE_NODE_DECL_GROUP, + + /* Statements */ + PAZE_NODE_BLOCK_STMT, + PAZE_NODE_EXPR_STMT, + PAZE_NODE_NULL_STMT, + PAZE_NODE_IF_STMT, + PAZE_NODE_WHILE_STMT, + PAZE_NODE_DO_WHILE_STMT, + PAZE_NODE_FOR_STMT, + PAZE_NODE_SWITCH_STMT, + PAZE_NODE_CASE_STMT, + PAZE_NODE_BREAK_STMT, + PAZE_NODE_CONTINUE_STMT, + PAZE_NODE_RETURN_STMT, + PAZE_NODE_DECL_STMT, + PAZE_NODE_GOTO_STMT, + PAZE_NODE_LABEL_STMT, + + /* Expressions */ + PAZE_NODE_INT_LITERAL, + PAZE_NODE_CHAR_LITERAL, + PAZE_NODE_STRING_LITERAL, + PAZE_NODE_IDENTIFIER_REF, + PAZE_NODE_UNARY_EXPR, + PAZE_NODE_BINARY_EXPR, + PAZE_NODE_ASSIGN_EXPR, + PAZE_NODE_CONDITIONAL_EXPR, + PAZE_NODE_CALL_EXPR, + PAZE_NODE_INDEX_EXPR, + PAZE_NODE_MEMBER_EXPR, + PAZE_NODE_CAST_EXPR, + PAZE_NODE_SIZEOF_EXPR, + PAZE_NODE_COMMA_EXPR, + PAZE_NODE_INIT_LIST_EXPR, + PAZE_NODE_COMPOUND_LITERAL_EXPR, + PAZE_NODE_STRING_CONCAT_EXPR, + PAZE_NODE_STMT_EXPR, + + PAZE_NODE_COUNT +} paze_node_kind_t; + +/* ======================================================================== + * Dynamic Array of AST Nodes + * ======================================================================== */ + +PAZE_DYNARR_DEF(struct paze_ast_node_t *, paze_ast_node_arr_t); + +/* ======================================================================== + * AST Node (tagged union) + * ======================================================================== */ + +typedef struct paze_ast_node_t { + paze_node_kind_t kind; + paze_loc_t loc; + paze_type_t *type; + + union { + /* ----- TranslationUnit ----- */ + struct { + paze_ast_node_arr_t decls; + } translation_unit; + + /* ----- FunctionDecl ----- */ + struct { + paze_str_t name; + paze_type_t *return_type; + paze_ast_node_arr_t params; + struct paze_ast_node_t *body; + bool is_variadic; + bool is_static; + bool is_extern; + } function_decl; + + /* ----- VarDecl ----- */ + struct { + paze_str_t name; + paze_type_t *var_type; + struct paze_ast_node_t *init_expr; + bool is_static; + bool is_extern; + int frame_offset; /* set by layout pass; used by codegen */ + } var_decl; + + /* ----- Param ----- */ + struct { + paze_str_t name; + paze_type_t *param_type; + } param; + + /* ----- StructDecl / UnionDecl ----- */ + struct { + paze_str_t name; + paze_ast_node_arr_t fields; + bool is_anonymous; + bool is_union; + } struct_or_union_decl; + + /* ----- TypedefDecl ----- */ + struct { + paze_str_t name; + paze_type_t *underlying_type; + } typedef_decl; + + /* ----- EnumDecl ----- */ + struct { + paze_str_t name; + paze_ast_node_arr_t constants; + } enum_decl; + + /* ----- DeclGroup ----- */ + struct { + paze_ast_node_arr_t decls; + } decl_group; + + /* ----- BlockStmt ----- */ + struct { + paze_ast_node_arr_t stmts; + } block_stmt; + + /* ----- ExprStmt ----- */ + struct { + struct paze_ast_node_t *expr; + } expr_stmt; + + /* ----- IfStmt ----- */ + struct { + struct paze_ast_node_t *condition; + struct paze_ast_node_t *then_branch; + struct paze_ast_node_t *else_branch; + } if_stmt; + + /* ----- WhileStmt ----- */ + struct { + struct paze_ast_node_t *condition; + struct paze_ast_node_t *body; + } while_stmt; + + /* ----- DoWhileStmt ----- */ + struct { + struct paze_ast_node_t *body; + struct paze_ast_node_t *condition; + } do_while_stmt; + + /* ----- ForStmt ----- */ + struct { + struct paze_ast_node_t *init; + struct paze_ast_node_t *condition; + struct paze_ast_node_t *increment; + struct paze_ast_node_t *body; + } for_stmt; + + /* ----- SwitchStmt ----- */ + struct { + struct paze_ast_node_t *expr; + paze_ast_node_arr_t cases; + } switch_stmt; + + /* ----- CaseStmt ----- */ + struct { + struct paze_ast_node_t *value; + paze_ast_node_arr_t stmts; + bool is_default; + } case_stmt; + + /* ----- ReturnStmt ----- */ + struct { + struct paze_ast_node_t *value; + } return_stmt; + + /* ----- GotoStmt ----- */ + struct { + paze_str_t label; + } goto_stmt; + + /* ----- LabelStmt ----- */ + struct { + paze_str_t label; + struct paze_ast_node_t *stmt; + } label_stmt; + + /* ----- DeclStmt ----- */ + struct { + struct paze_ast_node_t *decl; + } decl_stmt; + + /* ----- IntLiteral ----- */ + struct { + long value; + } int_literal; + + /* ----- CharLiteral ----- */ + struct { + char value; + } char_literal; + + /* ----- StringLiteral ----- */ + struct { + paze_str_t value; + } string_literal; + + /* ----- IdentifierRef ----- */ + struct { + paze_str_t name; + } identifier_ref; + + /* ----- UnaryExpr ----- */ + struct { + paze_tk_t op; + struct paze_ast_node_t *operand; + bool is_postfix; + } unary_expr; + + /* ----- BinaryExpr ----- */ + struct { + paze_tk_t op; + struct paze_ast_node_t *left; + struct paze_ast_node_t *right; + } binary_expr; + + /* ----- AssignExpr ----- */ + struct { + paze_tk_t op; + struct paze_ast_node_t *target; + struct paze_ast_node_t *value; + } assign_expr; + + /* ----- ConditionalExpr ----- */ + struct { + struct paze_ast_node_t *condition; + struct paze_ast_node_t *then_expr; + struct paze_ast_node_t *else_expr; + } conditional_expr; + + /* ----- CallExpr ----- */ + struct { + struct paze_ast_node_t *function; + paze_ast_node_arr_t args; + } call_expr; + + /* ----- IndexExpr ----- */ + struct { + struct paze_ast_node_t *array; + struct paze_ast_node_t *index; + } index_expr; + + /* ----- MemberExpr ----- */ + struct { + struct paze_ast_node_t *object; + paze_str_t member; + bool is_arrow; + } member_expr; + + /* ----- CastExpr ----- */ + struct { + paze_type_t *target_type; + struct paze_ast_node_t *expr; + } cast_expr; + + /* ----- SizeofExpr ----- */ + struct { + bool is_type; + paze_type_t *size_type; + struct paze_ast_node_t *expr; + } sizeof_expr; + + /* ----- CommaExpr ----- */ + struct { + struct paze_ast_node_t *left; + struct paze_ast_node_t *right; + } comma_expr; + + /* ----- InitListExpr ----- */ + struct { + paze_ast_node_arr_t elements; + } init_list_expr; + + /* ----- CompoundLiteralExpr ----- */ + struct { + paze_type_t *target_type; + struct paze_ast_node_t *init; + } compound_literal_expr; + + /* ----- StringConcatExpr ----- */ + struct { + paze_ast_node_arr_t parts; + } string_concat_expr; + + /* ----- StmtExpr ----- */ + struct { + struct paze_ast_node_t *body; + } stmt_expr; + } data; +} paze_ast_node_t; + +/* ======================================================================== + * AST Construction Helpers + * ======================================================================== */ + +/* Allocate a new AST node from the arena with the given kind and location. + * The node's data union is zero-initialized. */ +paze_ast_node_t *paze_ast_new(paze_arena_t *arena, paze_node_kind_t kind, + paze_loc_t loc); + +/* Return true if the node kind is an expression kind. */ +bool paze_ast_is_expression(paze_node_kind_t kind); + +/* Return true if the node kind is a statement kind. */ +bool paze_ast_is_statement(paze_node_kind_t kind); + +/* Return true if the node kind is a declaration kind. */ +bool paze_ast_is_declaration(paze_node_kind_t kind); + +/* Return a human-readable name for a node kind. */ +const char *paze_ast_kind_name(paze_node_kind_t kind); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_AST_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_diagnostics.h b/src/PazeE.los4/include/paze_diagnostics.h new file mode 100644 index 0000000..cacb292 --- /dev/null +++ b/src/PazeE.los4/include/paze_diagnostics.h @@ -0,0 +1,70 @@ +#ifndef PAZE_DIAGNOSTICS_H +#define PAZE_DIAGNOSTICS_H + +#include "paze_types.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Diagnostic Severity + * ======================================================================== */ + +typedef enum { + PAZE_DIAG_ERROR = 0, + PAZE_DIAG_WARNING = 1, +} paze_diag_severity_t; + +/* ======================================================================== + * Diagnostic Entry + * ======================================================================== */ + +typedef struct paze_diagnostic_t { + paze_diag_severity_t severity; + paze_loc_t loc; + char *message; /* owned heap-allocated string */ +} paze_diagnostic_t; + +/* ======================================================================== + * Diagnostics Collector + * ======================================================================== */ + +typedef struct paze_diagnostics_t { + paze_diagnostic_t *items; + size_t count; + size_t capacity; + size_t error_count; +} paze_diagnostics_t; + +/* Create a new diagnostics collector. */ +paze_diagnostics_t *paze_diagnostics_create(void); + +/* Destroy the collector and free all stored diagnostics. */ +void paze_diagnostics_destroy(paze_diagnostics_t *diag); + +/* Add an error diagnostic. + * `fmt` is a printf-style format string; arguments follow. */ +void paze_diagnostics_error(paze_diagnostics_t *diag, paze_loc_t loc, + const char *fmt, ...); + +/* Add a warning diagnostic. */ +void paze_diagnostics_warning(paze_diagnostics_t *diag, paze_loc_t loc, + const char *fmt, ...); + +/* True if any error-level diagnostic has been added. */ +bool paze_diagnostics_has_errors(const paze_diagnostics_t *diag); + +/* Return the number of error diagnostics. */ +size_t paze_diagnostics_error_count(const paze_diagnostics_t *diag); + +/* Print all diagnostics to the given file stream (e.g. stderr). + * Format: severity file(line,col): message */ +void paze_diagnostics_print(const paze_diagnostics_t *diag, FILE *out); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_DIAGNOSTICS_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_elf_writer_los4.h b/src/PazeE.los4/include/paze_elf_writer_los4.h new file mode 100644 index 0000000..5197088 --- /dev/null +++ b/src/PazeE.los4/include/paze_elf_writer_los4.h @@ -0,0 +1,45 @@ +#ifndef PAZE_ELF_WRITER_LOS4_H +#define PAZE_ELF_WRITER_LOS4_H + +#include "paze_object_image.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Los4 ELF64 Writer + * + * Produces a static ELF64 executable for LeonOS 4 (x86-64). + * + * Layout (matching doomlauncher.elf): + * - Base address: 0x400000 + * - ELF header (64 bytes) + 4 program headers (56 bytes each) + * - PT_LOAD R-X: .text (page-aligned, includes _start + runtime) + * - PT_LOAD R--: .rodata (page-aligned) + * - PT_LOAD RW-: .data + .bss (page-aligned, bss has no file content) + * - PT_GNU_STACK RW-: non-executable stack + * + * No dynamic linking (no PLT/GOT/.dynsym/.dynamic/PT_INTERP). + * Runtime functions are statically injected into .text. + * System calls use int 0x80 (0xCD 0x80), Linux x86-64 ABI. + * ======================================================================== */ + +/* Write a Los4 ELF64 executable from the given ObjectImage. + * + * Parameters: + * img - the object image (text/rdata/data/bss, symbols, fixups) + * has_argc_argv - if true, _start passes argc/argv to main + * out_path - output file path + * + * Returns: true on success, false on error. */ +bool paze_elf_writer_los4_write(paze_object_image_t *img, + bool has_argc_argv, + const char *out_path); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_ELF_WRITER_LOS4_H */ diff --git a/src/PazeE.los4/include/paze_interpreter.h b/src/PazeE.los4/include/paze_interpreter.h new file mode 100644 index 0000000..96995a2 --- /dev/null +++ b/src/PazeE.los4/include/paze_interpreter.h @@ -0,0 +1,100 @@ +#ifndef PAZE_INTERPRETER_H +#define PAZE_INTERPRETER_H + +#include "paze_types.h" +#include "paze_ast.h" +#include "paze_diagnostics.h" +#include "paze_type.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Opaque Interpreter + * ======================================================================== */ + +typedef struct paze_interpreter_t paze_interpreter_t; + +/* ======================================================================== + * Variable Info (for debugger inspection) + * ======================================================================== */ + +typedef struct { + const char *name; + const char *value_str; + bool is_constant; +} paze_var_info_t; + +/* ======================================================================== + * Interpreter API + * ======================================================================== */ + +/* Create an interpreter from a parsed translation unit. + * `unit` is the root AST node (PAZE_NODE_TRANSLATION_UNIT). + * `diag` is the diagnostics collector (may be NULL). */ +paze_interpreter_t *paze_interpreter_create(paze_ast_node_t *unit, + paze_diagnostics_t *diag); + +/* Destroy the interpreter and free all resources. */ +void paze_interpreter_destroy(paze_interpreter_t *interp); + +/* Run the program from main(). Returns exit code. */ +int paze_interpreter_run(paze_interpreter_t *interp); + +/* Continue execution from a breakpoint. Returns exit code. */ +int paze_interpreter_continue(paze_interpreter_t *interp); + +/* Execute a single step into (step into function calls). + * Returns exit code if program finishes, -1 if stopped at breakpoint. */ +int paze_interpreter_step_into(paze_interpreter_t *interp); + +/* Step over current line (step over function calls). + * Returns exit code if program finishes, -1 if stopped at breakpoint. */ +int paze_interpreter_step_over(paze_interpreter_t *interp); + +/* Set a breakpoint at the given source line number. + * Returns true if successful. */ +bool paze_interpreter_set_breakpoint(paze_interpreter_t *interp, int line); + +/* Clear a breakpoint at the given source line number. + * Returns true if breakpoint was found and removed. */ +bool paze_interpreter_clear_breakpoint(paze_interpreter_t *interp, int line); + +/* Get all breakpoint line numbers. + * `count_out` receives the number of breakpoints. + * Returns a heap-allocated array (caller must free). */ +int *paze_interpreter_get_breakpoints(paze_interpreter_t *interp, + int *count_out); + +/* Get local variables in the current frame. + * `count_out` receives the number of variables. + * Returns a heap-allocated array (caller must free). */ +paze_var_info_t *paze_interpreter_get_locals(paze_interpreter_t *interp, + int *count_out); + +/* Get global variables. + * `count_out` receives the number of variables. + * Returns a heap-allocated array (caller must free). */ +paze_var_info_t *paze_interpreter_get_globals(paze_interpreter_t *interp, + int *count_out); + +/* Get the current source line number being executed. */ +int paze_interpreter_current_line(paze_interpreter_t *interp); + +/* Get the name of the function currently being executed. + * Returns a string view (not owned). */ +paze_str_t paze_interpreter_current_func(paze_interpreter_t *interp); + +/* Check if the program has finished execution. */ +bool paze_interpreter_is_finished(paze_interpreter_t *interp); + +/* Get the exit code of the finished program. */ +int paze_interpreter_exit_code(paze_interpreter_t *interp); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_INTERPRETER_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_lexer.h b/src/PazeE.los4/include/paze_lexer.h new file mode 100644 index 0000000..bd36c8f --- /dev/null +++ b/src/PazeE.los4/include/paze_lexer.h @@ -0,0 +1,58 @@ +#ifndef PAZE_LEXER_H +#define PAZE_LEXER_H + +#include "paze_types.h" +#include "paze_token.h" +#include "paze_diagnostics.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Dynamic Array of Tokens + * ======================================================================== */ + +PAZE_DYNARR_DEF(paze_token_t, paze_token_arr_t); + +/* ======================================================================== + * Lexer + * ======================================================================== */ + +typedef struct paze_lexer_t { + const char *source; /* source text (not owned) */ + size_t source_len; /* total length of source */ + size_t pos; /* current byte position in source */ + int line; /* 1-based line number */ + int col; /* 1-based column number */ + const char *source_file; /* optional file path (not owned) */ + bool has_peeked; /* true if peeked_token is valid */ + paze_token_t peeked_token; /* cached peeked token */ + bool at_line_start;/* true after a newline or on first token */ +} paze_lexer_t; + +/* Create a lexer from source text. + * `source` must remain valid for the lifetime of the lexer. + * `source_file` is optional (may be NULL). */ +paze_lexer_t *paze_lexer_create(const char *source, const char *source_file); + +/* Destroy the lexer. */ +void paze_lexer_destroy(paze_lexer_t *lexer); + +/* Get the next token from the lexer. */ +paze_token_t paze_lexer_next_token(paze_lexer_t *lexer, paze_diagnostics_t *diag); + +/* Peek at the next token without consuming it. + * The next call to next_token will return the same token. */ +paze_token_t paze_lexer_peek_token(paze_lexer_t *lexer, paze_diagnostics_t *diag); + +/* Tokenize the entire source, returning a dynamic array of tokens. + * Caller must call paze_token_arr_t_free() on the result. */ +paze_token_arr_t paze_lexer_tokenize(paze_lexer_t *lexer, paze_diagnostics_t *diag); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_LEXER_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_libc.h b/src/PazeE.los4/include/paze_libc.h new file mode 100644 index 0000000..7d02a7f --- /dev/null +++ b/src/PazeE.los4/include/paze_libc.h @@ -0,0 +1,43 @@ +#ifndef PAZE_LIBC_H +#define PAZE_LIBC_H + +#include "paze_types.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Built-in Type Definitions + * ======================================================================== */ + +typedef int64_t paze_int64_t; +typedef uint64_t paze_uint64_t; +typedef uint32_t paze_uint32_t; +typedef uint16_t paze_uint16_t; +typedef uint8_t paze_uint8_t; + +/* ======================================================================== + * Built-in Function Declarations + * ======================================================================== */ + +paze_int64_t paze_builtin_printf(const char *fmt, ...); +paze_int64_t paze_builtin_puts(const char *str); +int paze_builtin_getchar(void); +void *paze_builtin_malloc(size_t size); +void paze_builtin_free(void *ptr); +void *paze_builtin_memcpy(void *dst, const void *src, size_t n); +void paze_builtin_memset(void *s, int c, size_t n); +size_t paze_builtin_strlen(const char *s); +char *paze_builtin_strcpy(char *dst, const char *src); +char *paze_builtin_strcat(char *dst, const char *src); +int paze_builtin_strcmp(const char *a, const char *b); +int paze_builtin_sprintf(char *buf, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_LIBC_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_libc_decls.h b/src/PazeE.los4/include/paze_libc_decls.h new file mode 100644 index 0000000..bc4b683 --- /dev/null +++ b/src/PazeE.los4/include/paze_libc_decls.h @@ -0,0 +1,27 @@ +#ifndef PAZE_LIBC_DECLS_H +#define PAZE_LIBC_DECLS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Returns true if `func` is a known variadic libc function. + * (On System V ABI a variadic call must set AL=0 before the call.) */ +bool paze_libc_is_variadic(const char *func); + +/* Returns the Windows DLL that exports `func`. + * Default: "msvcrt.dll". ExitProcess/GetStdHandle/... -> "kernel32.dll", + * Win32 GUI functions -> "user32.dll"/"gdi32.dll"/"dwmapi.dll". */ +const char *paze_libc_dll_of(const char *func); + +/* Returns true if importing `func` implies a GUI subsystem program + * (Win32 windowing/drawing functions). */ +bool paze_libc_is_gui_import(const char *func); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_LIBC_DECLS_H */ diff --git a/src/PazeE.los4/include/paze_los4_runtime.h b/src/PazeE.los4/include/paze_los4_runtime.h new file mode 100644 index 0000000..13c11fa --- /dev/null +++ b/src/PazeE.los4/include/paze_los4_runtime.h @@ -0,0 +1,78 @@ +#ifndef PAZE_LOS4_RUNTIME_H +#define PAZE_LOS4_RUNTIME_H + +#include "paze_object_image.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Los4 Static Runtime + * + * Generates raw x86-64 machine code for standard C library functions + * that use LeonOS 4 system calls (int 0x80, Linux x86-64 ABI). + * + * All functions use the System V AMD64 calling convention. + * The generated code is appended to the .text section by the ELF writer. + * ======================================================================== */ + +/* Syscall numbers (from leonos/syscall.h) */ +#define LOS4_SYS_read 0 +#define LOS4_SYS_write 1 +#define LOS4_SYS_open 2 +#define LOS4_SYS_close 3 +#define LOS4_SYS_mmap 9 +#define LOS4_SYS_munmap 11 +#define LOS4_SYS_exit 60 + +/* mmap constants */ +#define LOS4_PROT_READ 0x1 +#define LOS4_PROT_WRITE 0x2 +#define LOS4_MAP_PRIVATE 0x02 +#define LOS4_MAP_ANONYMOUS 0x20 + +/* Heap pool size for the bump allocator (BSS) */ +#define LOS4_HEAP_POOL_SIZE (1024 * 1024) /* 1 MB */ + +/* Runtime BSS layout: heap_pool + heap_ptr */ +typedef struct { + int heap_pool_off; /* offset in BSS for heap pool */ + int heap_ptr_off; /* offset in BSS for heap pointer */ + int bss_size; /* total BSS size used by runtime */ +} paze_los4_rt_bss_t; + +/* Generate all runtime functions and append them to the ObjectImage's + * .text section. Returns the base offset of runtime code in .text and + * fills `offsets` with the offset of each function name within the + * runtime code block. + * + * Parameters: + * img - the object image to append runtime code to + * offsets - array of (name, offset) pairs, caller-allocated + * count - number of entries in offsets (output) + * bss_info - BSS layout info (output) + * + * Returns: base offset of runtime code in .text section. */ +size_t paze_los4_runtime_generate(paze_object_image_t *img, + /* output: */ paze_los4_rt_bss_t *bss_info); + +/* Look up a runtime function offset by name. + * Returns -1 if not found. */ +int paze_los4_runtime_find_offset(const char *name); + +/* Get the total number of runtime functions. */ +size_t paze_los4_runtime_func_count(void); + +/* Get the name of the i-th runtime function. */ +const char *paze_los4_runtime_func_name(size_t i); + +/* Get the offset of the i-th runtime function (relative to runtime base). */ +int paze_los4_runtime_func_offset(size_t i); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_LOS4_RUNTIME_H */ diff --git a/src/PazeE.los4/include/paze_object_image.h b/src/PazeE.los4/include/paze_object_image.h new file mode 100644 index 0000000..2c47501 --- /dev/null +++ b/src/PazeE.los4/include/paze_object_image.h @@ -0,0 +1,141 @@ +#ifndef PAZE_OBJECT_IMAGE_H +#define PAZE_OBJECT_IMAGE_H + +#include "paze_types.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Section Flags + * ======================================================================== */ + +typedef enum { + PAZE_SEC_NONE = 0, + PAZE_SEC_WRITE = 1, + PAZE_SEC_EXEC = 2, + PAZE_SEC_BSS = 4, +} paze_sec_flags_t; + +/* ======================================================================== + * Image Section (dynamic byte buffer) + * ======================================================================== */ + +typedef struct { + char name[16]; + paze_sec_flags_t flags; + uint8_t *data; /* BSS section: NULL */ + size_t len; + size_t cap; + size_t bss_size; /* BSS only */ +} paze_image_section_t; + +void paze_section_init(paze_image_section_t *s, const char *name, + paze_sec_flags_t flags); +void paze_section_free(paze_image_section_t *s); + +/* Append bytes to the section. Returns the offset where data was written. */ +size_t paze_section_append(paze_image_section_t *s, const void *bytes, size_t n); + +/* Append a single byte. */ +size_t paze_section_append_byte(paze_image_section_t *s, uint8_t b); + +/* Reserve n bytes (zero-filled) in BSS. Returns offset. */ +size_t paze_section_reserve_bss(paze_image_section_t *s, size_t n); + +/* Write bytes at a specific offset (must be within current length). */ +void paze_section_write_at(paze_image_section_t *s, size_t off, + const void *bytes, size_t n); + +/* ======================================================================== + * Fixup + * ======================================================================== */ + +typedef enum { + PAZE_FIXUP_REL32, /* PC-relative 32-bit: local symbol call / RIP-relative mem */ + PAZE_FIXUP_EXT_SLOT32, /* PC-relative 32-bit -> external thunk (FF 25 disp32) */ + PAZE_FIXUP_ABS64, /* 64-bit absolute address (.data pointer init) */ +} paze_fixup_kind_t; + +typedef struct { + paze_image_section_t *section; + size_t offset; + paze_fixup_kind_t kind; + char *symbol; /* malloc'd */ + int64_t addend; +} paze_fixup_t; + +/* ======================================================================== + * Defined Symbol + * ======================================================================== */ + +typedef enum { + PAZE_SYM_TEXT, PAZE_SYM_RDATA, PAZE_SYM_DATA, PAZE_SYM_BSS, +} paze_sym_section_t; + +typedef struct { + char *name; /* malloc'd */ + paze_sym_section_t section; + size_t offset; + bool global; + bool is_function; + char *string_value; /* string literals only; NULL otherwise */ + size_t size; +} paze_defined_symbol_t; + +/* ======================================================================== + * Object Image + * ======================================================================== */ + +typedef struct { + paze_image_section_t text; + paze_image_section_t rdata; + paze_image_section_t data; + paze_image_section_t bss; + + paze_defined_symbol_t *symbols; + size_t symbols_len, symbols_cap; + + char **externals; /* in order of first appearance */ + size_t externals_len, externals_cap; + + paze_fixup_t *fixups; + size_t fixups_len, fixups_cap; + + char *entry_symbol; /* default "main" */ + bool has_argc_argv; +} paze_object_image_t; + +paze_object_image_t *paze_object_image_create(void); +void paze_object_image_destroy(paze_object_image_t *img); + +/* Add an external symbol (deduplicated, preserves order). */ +void paze_object_image_add_external(paze_object_image_t *img, const char *name); + +/* Add a string literal to .rdata (deduplicated, NUL-terminated). + * Returns the .rdata offset. Also registers a $str.N symbol. */ +size_t paze_object_image_add_string(paze_object_image_t *img, paze_str_t value); + +/* Define a symbol. Returns pointer to the symbol entry (owned by image). */ +paze_defined_symbol_t *paze_object_image_define_symbol( + paze_object_image_t *img, const char *name, + paze_sym_section_t sec, size_t offset, bool global, bool is_function); + +/* Find a defined symbol by name. Returns NULL if not found. */ +paze_defined_symbol_t *paze_object_image_find_symbol(paze_object_image_t *img, + const char *name); + +/* Add a fixup. The symbol string is copied (malloc'd). */ +void paze_object_image_add_fixup(paze_object_image_t *img, + paze_image_section_t *sec, size_t off, + paze_fixup_kind_t kind, const char *sym, + int64_t addend); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_OBJECT_IMAGE_H */ diff --git a/src/PazeE.los4/include/paze_parser.h b/src/PazeE.los4/include/paze_parser.h new file mode 100644 index 0000000..6e2558f --- /dev/null +++ b/src/PazeE.los4/include/paze_parser.h @@ -0,0 +1,57 @@ +#ifndef PAZE_PARSER_H +#define PAZE_PARSER_H + +#include "paze_types.h" +#include "paze_token.h" +#include "paze_diagnostics.h" +#include "paze_ast.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Parser + * ======================================================================== */ + +typedef struct paze_parser_t { + const paze_token_t *tokens; + size_t token_count; + size_t pos; + paze_diagnostics_t *diag; + paze_arena_t *arena; + bool arena_owned; + bool had_error; + /* Typedef name registry */ + paze_str_t *typedef_names; + size_t typedef_count; + size_t typedef_cap; + /* Struct/union type registry: name → type (for lookups without body) */ + paze_str_t *struct_names; + paze_type_t **struct_types; + size_t struct_count; + size_t struct_cap; +} paze_parser_t; + +/* Create a parser from a token array. + * `tokens` must remain valid for the lifetime of the parser. + * `diag` is the diagnostics collector (may be NULL). + * `arena` is the arena for AST allocations (may be NULL, in which case + * an internal arena is created and destroyed with the parser). */ +paze_parser_t *paze_parser_create(const paze_token_t *tokens, + size_t token_count, + paze_diagnostics_t *diag, + paze_arena_t *arena); + +/* Destroy the parser. If an internal arena was created, it is destroyed. */ +void paze_parser_destroy(paze_parser_t *parser); + +/* Parse the entire token stream as a translation unit. + * Returns the root AST node (PAZE_NODE_TRANSLATION_UNIT), or NULL on failure. */ +paze_ast_node_t *paze_parser_parse(paze_parser_t *parser); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_PARSER_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_preprocessor.h b/src/PazeE.los4/include/paze_preprocessor.h new file mode 100644 index 0000000..7304b32 --- /dev/null +++ b/src/PazeE.los4/include/paze_preprocessor.h @@ -0,0 +1,76 @@ +#ifndef PAZE_PREPROCESSOR_H +#define PAZE_PREPROCESSOR_H + +#include "paze_types.h" +#include "paze_token.h" +#include "paze_diagnostics.h" +#include "paze_lexer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Preprocessor + * ======================================================================== */ + +typedef struct paze_macro_def_t { + paze_str_t name; /* macro name (non-owning view) */ + paze_str_t replacement; /* replacement text (non-owning view) */ + bool is_function; /* true for function-like macros */ + paze_str_t *params; /* parameter names (array of paze_str_t) */ + size_t param_count; /* number of parameters */ + bool is_predefined;/* true for built-in/platform macros */ +} paze_macro_def_t; + +typedef struct paze_macro_table_t { + paze_macro_def_t *data; + size_t len; + size_t cap; +} paze_macro_table_t; + +typedef struct paze_if_state_t { + bool active; /* true if this block is currently active */ + bool was_active; /* true if any branch in this block was taken */ +} paze_if_state_t; + +typedef struct paze_if_stack_t { + paze_if_state_t *data; + size_t len; + size_t cap; +} paze_if_stack_t; + +typedef struct paze_preprocessor_t { + paze_arena_t *arena; /* arena for allocations */ + paze_macro_table_t macros; /* macro definitions */ + paze_if_stack_t if_stack; /* conditional compilation stack */ + int expand_depth; /* current macro expansion depth */ + int max_expand; /* max expansion depth (recursion limit) */ +} paze_preprocessor_t; + +/* Create a new preprocessor with its own arena. */ +paze_preprocessor_t *paze_preprocessor_create(void); + +/* Destroy the preprocessor and free all resources. */ +void paze_preprocessor_destroy(paze_preprocessor_t *pp); + +/* Preprocess source text. + * `source` is the source text to preprocess. + * `source_file` is the optional file path (for diagnostics). + * `platform_defs` is an array of macro names to predefine (e.g. "_WIN32"). + * `platform_def_count` is the number of platform definitions. + * `diag` is the diagnostics collector. + * Returns a dynamic array of tokens (caller must free with paze_token_arr_t_free). */ +paze_token_arr_t paze_preprocessor_run( + paze_preprocessor_t *pp, + const char *source, + const char *source_file, + const paze_str_t *platform_defs, + size_t platform_def_count, + paze_diagnostics_t *diag); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_PREPROCESSOR_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_sema.h b/src/PazeE.los4/include/paze_sema.h new file mode 100644 index 0000000..26acbd5 --- /dev/null +++ b/src/PazeE.los4/include/paze_sema.h @@ -0,0 +1,91 @@ +#ifndef PAZE_SEMA_H +#define PAZE_SEMA_H + +#include "paze_types.h" +#include "paze_type.h" +#include "paze_ast.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Type Size / Alignment (mirror of parser's type_size_of / type_align_of) + * ======================================================================== */ + +size_t paze_cg_sizeof(paze_type_t *t); +size_t paze_cg_alignof(paze_type_t *t); +bool paze_cg_is_unsigned(paze_type_t *t); +bool paze_cg_is_integer(paze_type_t *t); +bool paze_cg_is_ptr_like(paze_type_t *t); +paze_type_t *paze_cg_resolve_typedef(paze_type_t *t); +paze_type_t *paze_cg_element_type(paze_type_t *t); + +/* Find a struct field by name (recursive for anonymous members). + * Returns the field pointer or NULL. out_total_offset receives the + * accumulated byte offset. */ +paze_struct_field_t *paze_cg_find_field(paze_type_t *struct_type, const char *name, + int *out_total_offset); + +/* ======================================================================== + * Symbol Table (scoped) + * ======================================================================== */ + +typedef enum { + PAZE_CG_SYM_VAR, + PAZE_CG_SYM_FUNC, + PAZE_CG_SYM_ENUM_CONST, +} paze_cg_sym_kind_t; + +typedef struct { + char *name; /* malloc'd, NUL-terminated */ + paze_type_t *type; + paze_cg_sym_kind_t kind; + bool is_global; + bool is_extern; + bool is_defined; + int offset; /* local/param: relative to rbp (negative) */ + int64_t enum_value; +} paze_cg_sym_t; + +typedef struct paze_cg_scope { + paze_cg_sym_t **items; + size_t count; + size_t cap; + struct paze_cg_scope *parent; +} paze_cg_scope_t; + +typedef struct { + paze_cg_scope_t *global_scope; + paze_cg_scope_t *stack[64]; + int top; +} paze_cg_sym_table_t; + +paze_cg_sym_table_t *paze_cg_sym_table_create(void); +void paze_cg_sym_table_destroy(paze_cg_sym_table_t *tbl); + +void paze_cg_push_scope(paze_cg_sym_table_t *tbl); +void paze_cg_pop_scope(paze_cg_sym_table_t *tbl); + +/* Add a symbol (copies name to malloc'd NUL-terminated string). */ +void paze_cg_scope_add(paze_cg_sym_table_t *tbl, paze_cg_sym_t sym); + +/* Find a symbol by name, searching from innermost scope outward. */ +paze_cg_sym_t *paze_cg_scope_find(paze_cg_sym_table_t *tbl, const char *name); + +/* ======================================================================== + * Constant Evaluation + * ======================================================================== */ + +/* Try to evaluate an expression as a compile-time constant. + * Returns true on success and writes the value to *out_val. */ +bool paze_cg_try_const(paze_ast_node_t *e, int64_t *out_val, + paze_cg_sym_table_t *syms); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_SEMA_H */ diff --git a/src/PazeE.los4/include/paze_token.h b/src/PazeE.los4/include/paze_token.h new file mode 100644 index 0000000..08c75d0 --- /dev/null +++ b/src/PazeE.los4/include/paze_token.h @@ -0,0 +1,179 @@ +#ifndef PAZE_TOKEN_H +#define PAZE_TOKEN_H + +#include "paze_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Token Kind Enum + * Mirrors the C# TokenKind enumeration. + * ======================================================================== */ + +typedef enum { + /* Special */ + PAZE_TK_EOF = 0, + PAZE_TK_UNKNOWN, + + /* Literals */ + PAZE_TK_INT_LITERAL, + PAZE_TK_CHAR_LITERAL, + PAZE_TK_STRING_LITERAL, + + /* Identifier */ + PAZE_TK_IDENTIFIER, + + /* Keywords – type / storage-class */ + PAZE_TK_KW_VOID, + PAZE_TK_KW_CHAR, + PAZE_TK_KW_SHORT, + PAZE_TK_KW_INT, + PAZE_TK_KW_LONG, + PAZE_TK_KW_UNSIGNED, + PAZE_TK_KW_SIGNED, + PAZE_TK_KW_CONST, + PAZE_TK_KW_STATIC, + PAZE_TK_KW_EXTERN, + PAZE_TK_KW_REGISTER, + PAZE_TK_KW_VOLATILE, + PAZE_TK_KW_AUTO, + PAZE_TK_KW_BOOL, + + /* Keywords – declarators / statements */ + PAZE_TK_KW_STRUCT, + PAZE_TK_KW_UNION, + PAZE_TK_KW_ENUM, + PAZE_TK_KW_TYPEDEF, + PAZE_TK_KW_SIZEOF, + PAZE_TK_KW_TYPEOF, + PAZE_TK_KW_RETURN, + PAZE_TK_KW_IF, + PAZE_TK_KW_ELSE, + PAZE_TK_KW_WHILE, + PAZE_TK_KW_DO, + PAZE_TK_KW_FOR, + PAZE_TK_KW_SWITCH, + PAZE_TK_KW_CASE, + PAZE_TK_KW_DEFAULT, + PAZE_TK_KW_BREAK, + PAZE_TK_KW_CONTINUE, + PAZE_TK_KW_GOTO, + + /* Operators */ + PAZE_TK_PLUS, + PAZE_TK_MINUS, + PAZE_TK_STAR, + PAZE_TK_SLASH, + PAZE_TK_PERCENT, + + PAZE_TK_PLUS_PLUS, + PAZE_TK_MINUS_MINUS, + + PAZE_TK_ASSIGN, + PAZE_TK_PLUS_ASSIGN, + PAZE_TK_MINUS_ASSIGN, + PAZE_TK_STAR_ASSIGN, + PAZE_TK_SLASH_ASSIGN, + PAZE_TK_PERCENT_ASSIGN, + PAZE_TK_SHL_ASSIGN, + PAZE_TK_SHR_ASSIGN, + PAZE_TK_AND_ASSIGN, + PAZE_TK_OR_ASSIGN, + PAZE_TK_XOR_ASSIGN, + + PAZE_TK_EQ, + PAZE_TK_NOT_EQ, + PAZE_TK_LT, + PAZE_TK_LE, + PAZE_TK_GT, + PAZE_TK_GE, + + PAZE_TK_AND_AND, + PAZE_TK_OR_OR, + PAZE_TK_NOT, + + PAZE_TK_AMP, + PAZE_TK_PIPE, + PAZE_TK_CARET, + PAZE_TK_TILDE, + + PAZE_TK_SHL, + PAZE_TK_SHR, + + /* Punctuation */ + PAZE_TK_QUESTION, + PAZE_TK_COLON, + PAZE_TK_SEMICOLON, + PAZE_TK_COMMA, + PAZE_TK_DOT, + PAZE_TK_ARROW, + + PAZE_TK_LPAREN, + PAZE_TK_RPAREN, + PAZE_TK_LBRACKET, + PAZE_TK_RBRACKET, + PAZE_TK_LBRACE, + PAZE_TK_RBRACE, + + /* Preprocessor */ + PAZE_TK_HASH, + + /* Sentinel */ + PAZE_TK_COUNT +} paze_tk_t; + +/* ======================================================================== + * Token Struct + * ======================================================================== */ + +typedef struct paze_token_t { + paze_tk_t kind; + paze_str_t text; /* lexeme text (non-owning view) */ + paze_loc_t loc; /* source location */ + long int_value; /* integer / char literal value */ + paze_str_t str_value; /* string literal *decoded* content */ + bool at_line_start; /* true if at beginning of a preprocessor line */ +} paze_token_t; + +/* ======================================================================== + * Keyword Lookup + * Returns the keyword TokenKind for the given identifier, + * or PAZE_TK_IDENTIFIER if the string is not a keyword. + * ======================================================================== */ + +paze_tk_t paze_keyword_lookup(paze_str_t ident); + +/* ======================================================================== + * Helper Predicates + * ======================================================================== */ + +/* True for type keywords: void, char, short, int, long, unsigned, + * signed, const, struct, union, enum, typedef, _Bool / bool, typeof. */ +bool paze_tk_is_type_keyword(paze_tk_t kind); + +/* True for assignment operators: =, +=, -=, *=, /=, %=, <<=, >>=, &=, |=, ^= */ +bool paze_tk_is_assignment(paze_tk_t kind); + +/* ======================================================================== + * Convenience + * ======================================================================== */ + +static inline bool paze_tk_is(paze_tk_t kind, paze_tk_t expected) +{ + return kind == expected; +} + +/* Return true if `kind` matches any of the `count` kinds following. + * Usage: paze_tk_is_one_of(tk, 3, PAZE_TK_PLUS, PAZE_TK_MINUS, PAZE_TK_STAR); */ +bool paze_tk_is_one_of(paze_tk_t kind, size_t count, ...); + +/* Return a human-readable name for a token kind (for diagnostics). */ +const char *paze_tk_name(paze_tk_t kind); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_TOKEN_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_type.h b/src/PazeE.los4/include/paze_type.h new file mode 100644 index 0000000..88397dc --- /dev/null +++ b/src/PazeE.los4/include/paze_type.h @@ -0,0 +1,62 @@ +#ifndef PAZE_TYPE_H +#define PAZE_TYPE_H + +#include "paze_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declarations so paze_struct_field_t can reference paze_type_t + * and paze_type_t can reference paze_ast_node_t (for typeof). */ +typedef struct paze_type_t paze_type_t; +typedef struct paze_ast_node_t paze_ast_node_t; + +typedef enum { + PAZE_TYPE_VOID = 0, + PAZE_TYPE_CHAR, + PAZE_TYPE_SHORT, + PAZE_TYPE_INT, + PAZE_TYPE_LONG, + PAZE_TYPE_UNSIGNED, + PAZE_TYPE_SIGNED, + PAZE_TYPE_BOOL, + PAZE_TYPE_POINTER, + PAZE_TYPE_ARRAY, + PAZE_TYPE_FUNCTION, + PAZE_TYPE_STRUCT, + PAZE_TYPE_UNION, + PAZE_TYPE_ENUM, + PAZE_TYPE_TYPEDEF, + PAZE_TYPE_TYPEOF, + PAZE_TYPE_ERROR, +} paze_type_kind_t; + +typedef struct paze_struct_field_t { + paze_str_t name; + paze_type_t *type; + int offset; + size_t size; +} paze_struct_field_t; + +struct paze_type_t { + paze_type_kind_t kind; + paze_type_t *base; + long array_size; + paze_str_t name; + paze_str_t typedef_name; + paze_ast_node_t *typeof_expr; + bool is_const; + bool is_volatile; + paze_arena_t *arena; + int struct_field_count; + int struct_field_cap; + paze_struct_field_t *struct_fields; + size_t struct_size; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_TYPE_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_types.h b/src/PazeE.los4/include/paze_types.h new file mode 100644 index 0000000..7108ada --- /dev/null +++ b/src/PazeE.los4/include/paze_types.h @@ -0,0 +1,182 @@ +#ifndef PAZE_TYPES_H +#define PAZE_TYPES_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Dynamic Array Macro + * Usage: PAZE_DYNARR_DEF(int, IntArr); + * Then use IntArr_push(), IntArr_free(), IntArr_count(), IntArr_data(). + * ======================================================================== */ + +#define PAZE_DYNARR_DEF(type_t, name_t) \ + typedef struct name_t { \ + type_t *data; \ + size_t len; \ + size_t cap; \ + } name_t; \ + \ + static inline void name_t##_init(name_t *a) \ + { \ + a->data = NULL; \ + a->len = 0; \ + a->cap = 0; \ + } \ + \ + static inline void name_t##_free(name_t *a) \ + { \ + free(a->data); \ + a->data = NULL; \ + a->len = 0; \ + a->cap = 0; \ + } \ + \ + static inline size_t name_t##_count(const name_t *a) \ + { \ + return a->len; \ + } \ + \ + static inline type_t *name_t##_data(const name_t *a) \ + { \ + return a->data; \ + } \ + \ + static inline void name_t##_push(name_t *a, type_t value) \ + { \ + if (a->len >= a->cap) { \ + size_t new_cap = a->cap == 0 ? 8 : a->cap * 2; \ + a->data = (type_t *)realloc(a->data, new_cap * sizeof(type_t)); \ + a->cap = new_cap; \ + } \ + a->data[a->len++] = value; \ + } + +/* ======================================================================== + * String Slice (non-owning, not null-terminated) + * ======================================================================== */ + +typedef struct paze_str_t { + const char *data; + size_t len; +} paze_str_t; + +#define PAZE_STR_LIT(lit) ((paze_str_t){ (lit), sizeof(lit) - 1 }) +#define PAZE_STR_EMPTY ((paze_str_t){ NULL, 0 }) + +/* Create a paze_str_t from a C string literal (or buffer). + * The returned struct does NOT own the memory. */ +static inline paze_str_t paze_str_from_cstr(const char *cstr) +{ + paze_str_t s; + s.data = cstr; + s.len = cstr ? strlen(cstr) : 0; + return s; +} + +/* Duplicate a string slice into a newly malloc'd null-terminated buffer. + * Caller must free() the returned pointer. */ +char *paze_str_dup(paze_str_t s); + +/* Compare two string slices. Returns 0 if equal, non-zero otherwise. */ +int paze_str_cmp(paze_str_t a, paze_str_t b); + +/* ======================================================================== + * Arena Allocator + * Bump-pointer allocator for AST nodes. O(1) allocation, O(1) free-all. + * ======================================================================== */ + +#define PAZE_ARENA_DEFAULT_SIZE (1024 * 1024) /* 1 MB per block */ + +typedef struct paze_arena_block { + struct paze_arena_block *next; + size_t used; + size_t capacity; + /* flexible array member – must be last */ + char memory[]; +} paze_arena_block_t; + +typedef struct paze_arena_t { + paze_arena_block_t *head; +} paze_arena_t; + +/* Create an arena. Returns NULL on failure. */ +paze_arena_t *paze_arena_create(void); + +/* Destroy an arena and free all memory. */ +void paze_arena_destroy(paze_arena_t *arena); + +/* Allocate `size` bytes from the arena. + * The returned pointer is aligned to the maximum useful alignment (16). + * Returns NULL on failure. */ +void *paze_arena_alloc(paze_arena_t *arena, size_t size); + +/* Allocate and zero-initialize `size` bytes from the arena. */ +void *paze_arena_calloc(paze_arena_t *arena, size_t size); + +/* Duplicate a string slice into arena-owned memory. + * Returns a null-terminated string. Free when arena is destroyed. */ +char *paze_arena_strdup(paze_arena_t *arena, paze_str_t s); + +/* ======================================================================== + * Source Location + * ======================================================================== */ + +typedef struct paze_loc_t { + const char *file; /* source file path (owned by caller / compilation context) */ + int line; /* 1-based line number */ + int col; /* 1-based column number */ +} paze_loc_t; + +#define PAZE_LOC_EMPTY ((paze_loc_t){ NULL, 0, 0 }) + +static inline bool paze_loc_is_empty(paze_loc_t loc) +{ + return loc.file == NULL || loc.line <= 0; +} + +/* ======================================================================== + * Platform & Architecture Enums + * ======================================================================== */ + +typedef enum { + PAZE_PLATFORM_WINDOWS = 0, + PAZE_PLATFORM_LINUX = 1, + PAZE_PLATFORM_MACOS = 2, + PAZE_PLATFORM_LOS4 = 3, +} paze_platform_t; + +typedef enum { + PAZE_ARCH_AMD = 0, /* x86-64 */ + PAZE_ARCH_ARM = 1, /* AArch64 */ +} paze_arch_t; + +typedef enum { + PAZE_ABI_WIN64 = 0, + PAZE_ABI_SYSV = 1, +} paze_abi_t; + +/* ======================================================================== + * Memory Allocation Helpers + * ======================================================================== */ + +/* Allocate `size` bytes, abort on failure. */ +void *paze_malloc(size_t size); + +/* Allocate and zero-initialize, abort on failure. */ +void *paze_calloc(size_t count, size_t size); + +/* Reallocate, abort on failure. */ +void *paze_realloc(void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_TYPES_H */ \ No newline at end of file diff --git a/src/PazeE.los4/include/paze_x64_codegen.h b/src/PazeE.los4/include/paze_x64_codegen.h new file mode 100644 index 0000000..9065b6a --- /dev/null +++ b/src/PazeE.los4/include/paze_x64_codegen.h @@ -0,0 +1,31 @@ +#ifndef PAZE_X64_CODEGEN_H +#define PAZE_X64_CODEGEN_H + +#include "paze_ast.h" +#include "paze_object_image.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * x86-64 Code Generator (System V AMD64 ABI) + * + * Generates x86-64 machine code from an AST into an ObjectImage. + * Uses the System V AMD64 calling convention: + * - Integer args: RDI, RSI, RDX, RCX, R8, R9 (up to 6) + * - Return value: RAX + * - Stack: 16-byte aligned before call, no shadow space + * - Callee-saved: RBX, RBP, R12-R15 + * ======================================================================== */ + +/* Generate code from a translation unit AST into the given ObjectImage. + * Returns true on success. */ +bool paze_x64_codegen_generate(paze_ast_node_t *tu, paze_object_image_t *img); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_X64_CODEGEN_H */ diff --git a/src/PazeE.los4/include/paze_x64_emitter.h b/src/PazeE.los4/include/paze_x64_emitter.h new file mode 100644 index 0000000..738bbef --- /dev/null +++ b/src/PazeE.los4/include/paze_x64_emitter.h @@ -0,0 +1,145 @@ +#ifndef PAZE_X64_EMITTER_H +#define PAZE_X64_EMITTER_H + +#include "paze_object_image.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ======================================================================== + * Register + * ======================================================================== */ + +typedef struct { uint8_t index; uint8_t size; } paze_x64_reg_t; + +#define PAZE_R64(i) ((paze_x64_reg_t){ (uint8_t)(i), 64 }) +#define PAZE_R32(i) ((paze_x64_reg_t){ (uint8_t)(i), 32 }) +#define PAZE_R8REG(i) ((paze_x64_reg_t){ (uint8_t)(i), 8 }) + +/* Register indices: RAX=0 RCX=1 RDX=2 RBX=3 RSP=4 RBP=5 RSI=6 RDI=7 + * R8=8 R9=9 R10=10 R11=11 R12=12 R13=13 R14=14 R15=15 */ +extern const paze_x64_reg_t PAZE_X64_RAX, PAZE_X64_RBX, PAZE_X64_RCX, PAZE_X64_RDX; +extern const paze_x64_reg_t PAZE_X64_RSP, PAZE_X64_RBP, PAZE_X64_RSI, PAZE_X64_RDI; +extern const paze_x64_reg_t PAZE_X64_R8, PAZE_X64_R9, PAZE_X64_R10, PAZE_X64_R11; +extern const paze_x64_reg_t PAZE_X64_R12, PAZE_X64_R13, PAZE_X64_R14, PAZE_X64_R15; +extern const paze_x64_reg_t PAZE_X64_EAX, PAZE_X64_AL, PAZE_X64_CL; + +/* ======================================================================== + * Memory Operand + * ======================================================================== */ + +typedef struct { + bool has_base; + paze_x64_reg_t base; + bool has_index; + paze_x64_reg_t index; + uint8_t scale; /* 1, 2, 4, 8 */ + int64_t disp; + bool rip_relative; + const char *symbol; /* non-owning */ + int64_t sym_addend; +} paze_x64_mem_t; + +paze_x64_mem_t paze_x64_mem_rip(const char *sym); +paze_x64_mem_t paze_x64_mem_base_disp(paze_x64_reg_t b, int64_t d); +paze_x64_mem_t paze_x64_mem_base_index_disp(paze_x64_reg_t b, paze_x64_reg_t i, + uint8_t s, int64_t d); + +/* ======================================================================== + * Condition Codes + * ======================================================================== */ + +typedef enum { + PAZE_COND_O=0, PAZE_COND_NO=1, PAZE_COND_B=2, PAZE_COND_AE=3, + PAZE_COND_E=4, PAZE_COND_NE=5, PAZE_COND_BE=6, PAZE_COND_A=7, + PAZE_COND_S=8, PAZE_COND_NS=9, PAZE_COND_L=12, PAZE_COND_GE=13, + PAZE_COND_LE=14, PAZE_COND_G=15, +} paze_x64_cond_t; + +/* ======================================================================== + * Emitter + * ======================================================================== */ + +typedef struct paze_x64_emitter_t paze_x64_emitter_t; + +paze_x64_emitter_t *paze_x64_emitter_create(paze_image_section_t *target); +void paze_x64_emitter_destroy(paze_x64_emitter_t *e); +size_t paze_x64_emitter_position(const paze_x64_emitter_t *e); + +/* Basic writes */ +void paze_x64_emit8(paze_x64_emitter_t *e, uint8_t b); +void paze_x64_emit32(paze_x64_emitter_t *e, uint32_t v); +void paze_x64_emit64(paze_x64_emitter_t *e, uint64_t v); + +/* Labels */ +int paze_x64_new_label(paze_x64_emitter_t *e); +void paze_x64_mark_label(paze_x64_emitter_t *e, int label); +void paze_x64_emitter_finish(paze_x64_emitter_t *e); + +/* MOV */ +void paze_x64_mov_rr(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_mov_from_mem(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m); +void paze_x64_mov_to_mem(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src); +void paze_x64_lea(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m); +void paze_x64_mov_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm); + +/* Arithmetic / Logic */ +void paze_x64_add(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_sub(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_and(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_or (paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_xor(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_cmp(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_test(paze_x64_emitter_t *e, paze_x64_reg_t a, paze_x64_reg_t b); +void paze_x64_add_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm); +void paze_x64_sub_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm); +void paze_x64_cmp_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm); +void paze_x64_imul(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_idiv(paze_x64_emitter_t *e, paze_x64_reg_t src); +void paze_x64_div (paze_x64_emitter_t *e, paze_x64_reg_t src); +void paze_x64_cqo(paze_x64_emitter_t *e); +void paze_x64_neg(paze_x64_emitter_t *e, paze_x64_reg_t r); +void paze_x64_not(paze_x64_emitter_t *e, paze_x64_reg_t r); + +/* Shifts (by CL) */ +void paze_x64_shl_cl(paze_x64_emitter_t *e, paze_x64_reg_t r); +void paze_x64_shr_cl(paze_x64_emitter_t *e, paze_x64_reg_t r); +void paze_x64_sar_cl(paze_x64_emitter_t *e, paze_x64_reg_t r); + +/* Extend load / store */ +void paze_x64_movzx8(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_movsx8(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_movsx32(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src); +void paze_x64_movzx8_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m); +void paze_x64_movsx8_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m); +void paze_x64_movzx16_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m); +void paze_x64_movsx16_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m); +void paze_x64_mov64_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m); +void paze_x64_store8(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src); +void paze_x64_store16(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src); +void paze_x64_store32(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src); +void paze_x64_store64(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src); + +/* Stack */ +void paze_x64_push(paze_x64_emitter_t *e, paze_x64_reg_t r); +void paze_x64_pop (paze_x64_emitter_t *e, paze_x64_reg_t r); + +/* Control flow */ +void paze_x64_ret(paze_x64_emitter_t *e); +void paze_x64_leave(paze_x64_emitter_t *e); +void paze_x64_jmp(paze_x64_emitter_t *e, int label); +void paze_x64_jcc(paze_x64_emitter_t *e, paze_x64_cond_t cc, int label); +void paze_x64_setcc(paze_x64_emitter_t *e, paze_x64_cond_t cc, paze_x64_reg_t r); + +/* Call */ +size_t paze_x64_call_rel(paze_x64_emitter_t *e); /* returns rel32 field offset */ +void paze_x64_call_reg(paze_x64_emitter_t *e, paze_x64_reg_t r); + +#ifdef __cplusplus +} +#endif + +#endif /* PAZE_X64_EMITTER_H */ diff --git a/src/PazeE.los4/src/main.c b/src/PazeE.los4/src/main.c new file mode 100644 index 0000000..5973f6d --- /dev/null +++ b/src/PazeE.los4/src/main.c @@ -0,0 +1,259 @@ +#include "../include/paze_lexer.h" +#include "../include/paze_preprocessor.h" +#include "../include/paze_parser.h" +#include "../include/paze_interpreter.h" +#include "../include/paze_diagnostics.h" +#include "../include/paze_object_image.h" +#include "../include/paze_x64_codegen.h" +#include "../include/paze_elf_writer_los4.h" +#include +#include +#include + +#define PAZE_VERSION "0.1.1-alpha (los4)" + +static void print_help(void) { + printf("paze %s - PazeE Language Compiler (Pure C, Los4 target)\n\n", PAZE_VERSION); + printf("Usage:\n"); + printf(" paze run Run a .pe file (interpreted)\n"); + printf(" paze debug [lines] Debug a .pe file at specified breakpoints\n"); + printf(" paze check Check syntax without running\n"); + printf(" paze build [output] Compile to Los4 ELF executable\n"); + printf(" paze -v, --version Show version\n"); + printf(" paze -h, --help Show this help\n"); +} + +/* Read a file into a malloc'd buffer. Returns NULL on failure. */ +static char *read_file(const char *path, size_t *out_len) { + FILE *fp = fopen(path, "rb"); + if (!fp) return NULL; + fseek(fp, 0, SEEK_END); + long sz = ftell(fp); + fseek(fp, 0, SEEK_SET); + char *buf = (char *)malloc(sz + 1); + if (!buf) { fclose(fp); return NULL; } + fread(buf, 1, sz, fp); + buf[sz] = '\0'; + fclose(fp); + if (out_len) *out_len = (size_t)sz; + return buf; +} + +/* Full pipeline: source → preprocess (tokenizes) → parse → AST. + * The preprocessor runs the lexer internally and returns a token array. + * *out_arena receives the arena that owns the AST nodes; the caller must + * keep it alive while using the AST and destroy it with paze_arena_destroy(). */ +static paze_ast_node_t *parse_source(const char *source, paze_diagnostics_t *diag, + paze_arena_t **out_arena) { + *out_arena = paze_arena_create(); + if (!*out_arena) return NULL; + + paze_preprocessor_t *pp = paze_preprocessor_create(); + paze_token_arr_t toks = paze_preprocessor_run(pp, source, NULL, NULL, 0, diag); + paze_preprocessor_destroy(pp); + + paze_parser_t *parser = paze_parser_create(toks.data, toks.len, diag, *out_arena); + paze_ast_node_t *tu = paze_parser_parse(parser); + paze_parser_destroy(parser); /* arena is external — not freed here */ + paze_token_arr_t_free(&toks); + + return tu; +} + +static int cmd_run(const char *file) { + size_t len; + char *src = read_file(file, &len); + if (!src) { fprintf(stderr, "error: cannot read '%s'\n", file); return 1; } + + paze_diagnostics_t *diag = paze_diagnostics_create(); + paze_arena_t *arena = NULL; + paze_ast_node_t *tu = parse_source(src, diag, &arena); + if (!tu) { + paze_diagnostics_print(diag, stderr); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return 1; + } + + /* NOTE: src must stay alive while the AST is in use — paze_str_t slices + * inside AST nodes point directly into the source buffer. */ + paze_interpreter_t *interp = paze_interpreter_create(tu, diag); + int code = paze_interpreter_run(interp); + paze_interpreter_destroy(interp); + paze_arena_destroy(arena); + free(src); + paze_diagnostics_destroy(diag); + return code; +} + +static int cmd_check(const char *file) { + size_t len; + char *src = read_file(file, &len); + if (!src) { fprintf(stderr, "error: cannot read '%s'\n", file); return 1; } + + paze_diagnostics_t *diag = paze_diagnostics_create(); + paze_arena_t *arena = NULL; + paze_ast_node_t *tu = parse_source(src, diag, &arena); + if (!tu) { + paze_diagnostics_print(diag, stderr); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return 1; + } + + if (paze_diagnostics_has_errors(diag)) { + paze_diagnostics_print(diag, stderr); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return 1; + } + + printf("OK: no syntax errors in '%s'\n", file); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return 0; +} + +static int cmd_build(const char *file, const char *output) { + size_t len; + char *src = read_file(file, &len); + if (!src) { fprintf(stderr, "error: cannot read '%s'\n", file); return 1; } + + paze_diagnostics_t *diag = paze_diagnostics_create(); + paze_arena_t *arena = NULL; + paze_ast_node_t *tu = parse_source(src, diag, &arena); + if (!tu) { + paze_diagnostics_print(diag, stderr); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return 1; + } + + /* Generate code — AST paze_str_t slices point into src, keep it alive. */ + paze_object_image_t *img = paze_object_image_create(); + if (!paze_x64_codegen_generate(tu, img)) { + fprintf(stderr, "error: code generation failed\n"); + paze_object_image_destroy(img); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return 1; + } + + /* Write ELF */ + bool ok = paze_elf_writer_los4_write(img, false, output); + paze_object_image_destroy(img); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + + if (!ok) { + fprintf(stderr, "error: failed to write ELF\n"); + return 1; + } + printf("Built: %s\n", output); + return 0; +} + +static int cmd_debug(const char *file, const int *lines, int line_count) { + size_t len; + char *src = read_file(file, &len); + if (!src) { fprintf(stderr, "error: cannot read '%s'\n", file); return 1; } + + paze_diagnostics_t *diag = paze_diagnostics_create(); + paze_arena_t *arena = NULL; + paze_ast_node_t *tu = parse_source(src, diag, &arena); + if (!tu) { + paze_diagnostics_print(diag, stderr); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return 1; + } + + /* AST paze_str_t slices point into src — keep src alive during debug. */ + paze_interpreter_t *interp = paze_interpreter_create(tu, diag); + + /* Set breakpoints */ + for (int i = 0; i < line_count; i++) + paze_interpreter_set_breakpoint(interp, lines[i]); + + /* Run until first breakpoint or completion */ + int code = paze_interpreter_run(interp); + + /* Interactive debug loop */ + if (!paze_interpreter_is_finished(interp)) { + printf("(paze-debug) stopped at line %d\n", paze_interpreter_current_line(interp)); + /* Simple: just continue to end */ + code = paze_interpreter_continue(interp); + } + + paze_interpreter_destroy(interp); + free(src); + paze_arena_destroy(arena); + paze_diagnostics_destroy(diag); + return code; +} + +int main(int argc, char **argv) { + if (argc < 2) { + print_help(); + return 1; + } + + const char *command = argv[1]; + + if (strcmp(command, "-v") == 0 || strcmp(command, "--version") == 0) { + printf("paze %s\n", PAZE_VERSION); + return 0; + } + if (strcmp(command, "-h") == 0 || strcmp(command, "--help") == 0) { + print_help(); + return 0; + } + + if (strcmp(command, "run") == 0) { + if (argc < 3) { fprintf(stderr, "usage: paze run \n"); return 1; } + return cmd_run(argv[2]); + } + if (strcmp(command, "check") == 0) { + if (argc < 3) { fprintf(stderr, "usage: paze check \n"); return 1; } + return cmd_check(argv[2]); + } + if (strcmp(command, "build") == 0) { + if (argc < 3) { fprintf(stderr, "usage: paze build [output]\n"); return 1; } + /* Default output: same name without .pe extension */ + char default_out[512]; + snprintf(default_out, sizeof(default_out), "%s", argv[2]); + /* Remove .pe extension */ + size_t slen = strlen(default_out); + if (slen > 3 && strcmp(default_out + slen - 3, ".pe") == 0) + default_out[slen - 3] = '\0'; + const char *output = (argc >= 4) ? argv[3] : default_out; + return cmd_build(argv[2], output); + } + if (strcmp(command, "debug") == 0) { + if (argc < 3) { fprintf(stderr, "usage: paze debug [line1,line2,...]\n"); return 1; } + /* Parse line numbers */ + int lines[64]; + int line_count = 0; + if (argc >= 4) { + char *s = argv[3]; + while (*s && line_count < 64) { + lines[line_count++] = atoi(s); + while (*s && *s != ',') s++; + if (*s == ',') s++; + } + } + return cmd_debug(argv[2], lines, line_count); + } + + fprintf(stderr, "Unknown command: %s\n", command); + print_help(); + return 1; +} diff --git a/src/PazeE.los4/src/paze_ast.c b/src/PazeE.los4/src/paze_ast.c new file mode 100644 index 0000000..fcf2b1c --- /dev/null +++ b/src/PazeE.los4/src/paze_ast.c @@ -0,0 +1,142 @@ +#include "../include/paze_ast.h" +#include + +/* ======================================================================== + * AST Creation + * ======================================================================== */ + +paze_ast_node_t *paze_ast_new(paze_arena_t *arena, paze_node_kind_t kind, + paze_loc_t loc) +{ + paze_ast_node_t *node = (paze_ast_node_t *)paze_arena_calloc( + arena, sizeof(paze_ast_node_t)); + if (!node) return NULL; + node->kind = kind; + node->loc = loc; + node->type = NULL; + return node; +} + +/* ======================================================================== + * Node Kind Queries + * ======================================================================== */ + +bool paze_ast_is_expression(paze_node_kind_t kind) +{ + switch (kind) { + case PAZE_NODE_INT_LITERAL: + case PAZE_NODE_CHAR_LITERAL: + case PAZE_NODE_STRING_LITERAL: + case PAZE_NODE_IDENTIFIER_REF: + case PAZE_NODE_UNARY_EXPR: + case PAZE_NODE_BINARY_EXPR: + case PAZE_NODE_ASSIGN_EXPR: + case PAZE_NODE_CONDITIONAL_EXPR: + case PAZE_NODE_CALL_EXPR: + case PAZE_NODE_INDEX_EXPR: + case PAZE_NODE_MEMBER_EXPR: + case PAZE_NODE_CAST_EXPR: + case PAZE_NODE_SIZEOF_EXPR: + case PAZE_NODE_COMMA_EXPR: + case PAZE_NODE_INIT_LIST_EXPR: + case PAZE_NODE_COMPOUND_LITERAL_EXPR: + case PAZE_NODE_STRING_CONCAT_EXPR: + case PAZE_NODE_STMT_EXPR: + return true; + default: + return false; + } +} + +bool paze_ast_is_statement(paze_node_kind_t kind) +{ + switch (kind) { + case PAZE_NODE_BLOCK_STMT: + case PAZE_NODE_EXPR_STMT: + case PAZE_NODE_NULL_STMT: + case PAZE_NODE_IF_STMT: + case PAZE_NODE_WHILE_STMT: + case PAZE_NODE_DO_WHILE_STMT: + case PAZE_NODE_FOR_STMT: + case PAZE_NODE_SWITCH_STMT: + case PAZE_NODE_CASE_STMT: + case PAZE_NODE_BREAK_STMT: + case PAZE_NODE_CONTINUE_STMT: + case PAZE_NODE_RETURN_STMT: + case PAZE_NODE_DECL_STMT: + case PAZE_NODE_GOTO_STMT: + case PAZE_NODE_LABEL_STMT: + return true; + default: + return false; + } +} + +bool paze_ast_is_declaration(paze_node_kind_t kind) +{ + switch (kind) { + case PAZE_NODE_FUNCTION_DECL: + case PAZE_NODE_VAR_DECL: + case PAZE_NODE_STRUCT_DECL: + case PAZE_NODE_UNION_DECL: + case PAZE_NODE_TYPEDEF_DECL: + case PAZE_NODE_ENUM_DECL: + case PAZE_NODE_DECL_GROUP: + return true; + default: + return false; + } +} + +/* ======================================================================== + * Node Kind Names (for diagnostics / debugging) + * ======================================================================== */ + +const char *paze_ast_kind_name(paze_node_kind_t kind) +{ + switch (kind) { + case PAZE_NODE_TRANSLATION_UNIT: return "TranslationUnit"; + case PAZE_NODE_FUNCTION_DECL: return "FunctionDecl"; + case PAZE_NODE_VAR_DECL: return "VarDecl"; + case PAZE_NODE_PARAM: return "Param"; + case PAZE_NODE_STRUCT_DECL: return "StructDecl"; + case PAZE_NODE_UNION_DECL: return "UnionDecl"; + case PAZE_NODE_TYPEDEF_DECL: return "TypedefDecl"; + case PAZE_NODE_ENUM_DECL: return "EnumDecl"; + case PAZE_NODE_DECL_GROUP: return "DeclGroup"; + case PAZE_NODE_BLOCK_STMT: return "BlockStmt"; + case PAZE_NODE_EXPR_STMT: return "ExprStmt"; + case PAZE_NODE_NULL_STMT: return "NullStmt"; + case PAZE_NODE_IF_STMT: return "IfStmt"; + case PAZE_NODE_WHILE_STMT: return "WhileStmt"; + case PAZE_NODE_DO_WHILE_STMT: return "DoWhileStmt"; + case PAZE_NODE_FOR_STMT: return "ForStmt"; + case PAZE_NODE_SWITCH_STMT: return "SwitchStmt"; + case PAZE_NODE_CASE_STMT: return "CaseStmt"; + case PAZE_NODE_BREAK_STMT: return "BreakStmt"; + case PAZE_NODE_CONTINUE_STMT: return "ContinueStmt"; + case PAZE_NODE_RETURN_STMT: return "ReturnStmt"; + case PAZE_NODE_DECL_STMT: return "DeclStmt"; + case PAZE_NODE_GOTO_STMT: return "GotoStmt"; + case PAZE_NODE_LABEL_STMT: return "LabelStmt"; + case PAZE_NODE_INT_LITERAL: return "IntLiteral"; + case PAZE_NODE_CHAR_LITERAL: return "CharLiteral"; + case PAZE_NODE_STRING_LITERAL: return "StringLiteral"; + case PAZE_NODE_IDENTIFIER_REF: return "IdentifierRef"; + case PAZE_NODE_UNARY_EXPR: return "UnaryExpr"; + case PAZE_NODE_BINARY_EXPR: return "BinaryExpr"; + case PAZE_NODE_ASSIGN_EXPR: return "AssignExpr"; + case PAZE_NODE_CONDITIONAL_EXPR: return "ConditionalExpr"; + case PAZE_NODE_CALL_EXPR: return "CallExpr"; + case PAZE_NODE_INDEX_EXPR: return "IndexExpr"; + case PAZE_NODE_MEMBER_EXPR: return "MemberExpr"; + case PAZE_NODE_CAST_EXPR: return "CastExpr"; + case PAZE_NODE_SIZEOF_EXPR: return "SizeofExpr"; + case PAZE_NODE_COMMA_EXPR: return "CommaExpr"; + case PAZE_NODE_INIT_LIST_EXPR: return "InitListExpr"; + case PAZE_NODE_COMPOUND_LITERAL_EXPR: return "CompoundLiteralExpr"; + case PAZE_NODE_STRING_CONCAT_EXPR: return "StringConcatExpr"; + case PAZE_NODE_STMT_EXPR: return "StmtExpr"; + default: return "Unknown"; + } +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_diagnostics.c b/src/PazeE.los4/src/paze_diagnostics.c new file mode 100644 index 0000000..8a637ce --- /dev/null +++ b/src/PazeE.los4/src/paze_diagnostics.c @@ -0,0 +1,140 @@ +#include "../include/paze_diagnostics.h" + +#include +#include +#include +#include + +/* ======================================================================== + * Internal helpers + * ======================================================================== */ + +static char *diag_strdup(const char *s) +{ + if (!s) return NULL; + size_t len = strlen(s); + char *dup = (char *)malloc(len + 1); + if (dup) { + memcpy(dup, s, len + 1); + } + return dup; +} + +static void diag_ensure_capacity(paze_diagnostics_t *diag) +{ + if (diag->count < diag->capacity) return; + size_t new_cap = diag->capacity == 0 ? 8 : diag->capacity * 2; + diag->items = (paze_diagnostic_t *)realloc( + diag->items, new_cap * sizeof(paze_diagnostic_t)); + diag->capacity = new_cap; +} + +static void diag_add(paze_diagnostics_t *diag, paze_diag_severity_t severity, + paze_loc_t loc, const char *fmt, va_list ap) +{ + diag_ensure_capacity(diag); + + /* Format the message string. */ + char buf[1024]; + va_list ap_copy; + va_copy(ap_copy, ap); + int needed = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap_copy); + + char *message; + if (needed < 0) { + message = diag_strdup("(diagnostic formatting error)"); + } else if ((size_t)needed < sizeof(buf)) { + message = diag_strdup(buf); + } else { + message = (char *)malloc((size_t)needed + 1); + if (message) { + va_list ap_copy2; + va_copy(ap_copy2, ap); + vsnprintf(message, (size_t)needed + 1, fmt, ap_copy2); + va_end(ap_copy2); + } + } + + paze_diagnostic_t *entry = &diag->items[diag->count++]; + entry->severity = severity; + entry->loc = loc; + entry->message = message; + + if (severity == PAZE_DIAG_ERROR) { + diag->error_count++; + } +} + +/* ======================================================================== + * Public API + * ======================================================================== */ + +paze_diagnostics_t *paze_diagnostics_create(void) +{ + paze_diagnostics_t *diag = (paze_diagnostics_t *)calloc( + 1, sizeof(paze_diagnostics_t)); + return diag; +} + +void paze_diagnostics_destroy(paze_diagnostics_t *diag) +{ + if (!diag) return; + for (size_t i = 0; i < diag->count; i++) { + free(diag->items[i].message); + } + free(diag->items); + free(diag); +} + +void paze_diagnostics_error(paze_diagnostics_t *diag, paze_loc_t loc, + const char *fmt, ...) +{ + if (!diag) return; + va_list ap; + va_start(ap, fmt); + diag_add(diag, PAZE_DIAG_ERROR, loc, fmt, ap); + va_end(ap); +} + +void paze_diagnostics_warning(paze_diagnostics_t *diag, paze_loc_t loc, + const char *fmt, ...) +{ + if (!diag) return; + va_list ap; + va_start(ap, fmt); + diag_add(diag, PAZE_DIAG_WARNING, loc, fmt, ap); + va_end(ap); +} + +bool paze_diagnostics_has_errors(const paze_diagnostics_t *diag) +{ + return diag && diag->error_count > 0; +} + +size_t paze_diagnostics_error_count(const paze_diagnostics_t *diag) +{ + return diag ? diag->error_count : 0; +} + +void paze_diagnostics_print(const paze_diagnostics_t *diag, FILE *out) +{ + if (!diag || !out) return; + + for (size_t i = 0; i < diag->count; i++) { + const paze_diagnostic_t *d = &diag->items[i]; + const char *sev_str = + d->severity == PAZE_DIAG_ERROR ? "error" : "warning"; + + if (paze_loc_is_empty(d->loc)) { + fprintf(out, "%s: %s\n", sev_str, + d->message ? d->message : "(null)"); + } else { + fprintf(out, "%s(%d,%d): %s: %s\n", + d->loc.file ? d->loc.file : "", + d->loc.line, d->loc.col, + sev_str, + d->message ? d->message : "(null)"); + } + } +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_elf_writer_los4.c b/src/PazeE.los4/src/paze_elf_writer_los4.c new file mode 100644 index 0000000..a19b08a --- /dev/null +++ b/src/PazeE.los4/src/paze_elf_writer_los4.c @@ -0,0 +1,305 @@ +#include "../include/paze_elf_writer_los4.h" +#include "../include/paze_los4_runtime.h" +#include "../include/paze_object_image.h" +#include "../include/paze_libc_decls.h" +#include +#include +#include + +/* ======================================================================== + * Los4 ELF64 Writer — static executable for LeonOS 4 (x86-64) + * + * Layout (matching doomlauncher.elf): + * ELF header (64) + 4 program headers (56 each) + * PT_LOAD R-X: .text (stub + user code + runtime, page-aligned) + * PT_LOAD R--: .rodata + * PT_LOAD RW-: .data + .bss + * PT_GNU_STACK RW- + * Base: 0x400000. Syscalls: int 0x80 (0xCD 0x80). + * ======================================================================== */ + +#define LOS4_BASE_ADDR 0x400000L +#define LOS4_PAGE 0x1000 + +#define ET_EXEC 2 +#define EM_X86_64 62 +#define PT_LOAD 1 +#define PT_GNU_STACK 0x6474e551 +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 + +/* ---- Byte buffer ---- */ + +typedef struct { uint8_t *data; size_t len, cap; } byte_buf_t; + +static void buf_ensure(byte_buf_t *b, size_t need) { + if (b->len + need <= b->cap) return; + while (b->len + need > b->cap) b->cap = b->cap ? b->cap * 2 : 4096; + b->data = (uint8_t *)realloc(b->data, b->cap); +} +static void buf_add(byte_buf_t *b, uint8_t v) { buf_ensure(b,1); b->data[b->len++]=v; } +static void buf_add_n(byte_buf_t *b, const void *p, size_t n) { + buf_ensure(b,n); memcpy(b->data+b->len,p,n); b->len+=n; +} +static void buf_pad_to(byte_buf_t *b, size_t target) { + while (b->len < target) buf_add(b,0); +} +static void buf_w16(byte_buf_t *b, size_t o, uint16_t v) { + b->data[o]=(uint8_t)v; b->data[o+1]=(uint8_t)(v>>8); } +static void buf_w32(byte_buf_t *b, size_t o, uint32_t v) { + b->data[o]=(uint8_t)v; b->data[o+1]=(uint8_t)(v>>8); + b->data[o+2]=(uint8_t)(v>>16); b->data[o+3]=(uint8_t)(v>>24); } +static void buf_w64(byte_buf_t *b, size_t o, uint64_t v) { + for (int i=0;i<8;i++) b->data[o+i]=(uint8_t)(v>>(i*8)); } +static void buf_a16(byte_buf_t *b, uint16_t v) { size_t o=b->len; buf_add_n(b,(uint8_t[2]){0,0},2); buf_w16(b,o,v); } +static void buf_a32(byte_buf_t *b, uint32_t v) { size_t o=b->len; buf_add_n(b,(uint8_t[4]){0,0,0,0},4); buf_w32(b,o,v); } +static void buf_a64(byte_buf_t *b, uint64_t v) { size_t o=b->len; buf_add_n(b,(uint8_t[8]){0,0,0,0,0,0,0,0},8); buf_w64(b,o,v); } + +static int64_t align_up(int64_t v, int64_t a) { + return a <= 1 ? v : (v + a - 1) & ~(a - 1); +} + +/* ---- Symbol resolution ---- */ + +static int64_t sym_vaddr(const char *sym, paze_object_image_t *img, + int64_t text_vaddr, int64_t rodata_vaddr, int64_t data_vaddr, int64_t bss_vaddr) +{ + paze_defined_symbol_t *ds = paze_object_image_find_symbol(img, sym); + if (ds) { + int64_t base = 0; + switch (ds->section) { + case PAZE_SYM_TEXT: base = text_vaddr; break; + case PAZE_SYM_RDATA: base = rodata_vaddr; break; + case PAZE_SYM_DATA: base = data_vaddr; break; + case PAZE_SYM_BSS: base = bss_vaddr; break; + } + return base + (int64_t)ds->offset; + } + /* Check runtime functions (offsets relative to runtime base in img->text) */ + int rt_off = paze_los4_runtime_find_offset(sym); + if (rt_off >= 0) { + /* Runtime code is appended to img->text. The caller passes text_vaddr + * that already accounts for the stub prefix. We need to add rt_base + * and rt_off. But we don't know rt_base here... + * The caller must pass text_vaddr = stub_vaddr + stub_sz + img_text_offset + * Actually, let's pass the runtime base vaddr via a different mechanism. + * For simplicity, we encode it: text_vaddr already = base + stub_sz, + * so runtime vaddr = text_vaddr + rt_base + rt_off. + * But we need rt_base. Let's use a global. */ + extern size_t g_los4_rt_base_in_text; /* set by the writer */ + return text_vaddr + (int64_t)g_los4_rt_base_in_text + (int64_t)rt_off; + } + return text_vaddr; /* fallback */ +} + +/* Global: offset of runtime code within img->text (set during write) */ +size_t g_los4_rt_base_in_text = 0; + +/* ---- _start stub ---- */ + +typedef struct { int call_rel32_pos; } stub_info_t; + +static void gen_start_stub(byte_buf_t *text, bool has_argc_argv, stub_info_t *si) +{ + /* xor rbp, rbp */ + buf_add_n(text, (uint8_t[3]){0x48,0x31,0xED}, 3); + if (has_argc_argv) { + buf_add_n(text, (uint8_t[4]){0x48,0x8B,0x3C,0x24}, 4); /* mov rdi, [rsp] */ + buf_add_n(text, (uint8_t[5]){0x48,0x8D,0x74,0x24,0x08}, 5); /* lea rsi, [rsp+8] */ + } + /* call main (rel32) */ + si->call_rel32_pos = (int)text->len; + buf_add(text, 0xE8); + buf_add_n(text, (uint8_t[4]){0,0,0,0}, 4); + /* mov edi, eax */ + buf_add_n(text, (uint8_t[2]){0x89,0xC7}, 2); + /* mov eax, 60 (SYS_exit) */ + buf_add_n(text, (uint8_t[5]){0xB8,0x3C,0x00,0x00,0x00}, 5); + /* int 0x80 */ + buf_add_n(text, (uint8_t[2]){0xCD,0x80}, 2); +} + +/* ---- Program header ---- */ + +static void write_phdr(byte_buf_t *f, uint32_t type, uint32_t flags, + uint64_t off, uint64_t vaddr, uint64_t filesz, uint64_t memsz, uint64_t align) +{ + buf_a32(f, type); + buf_a32(f, flags); + buf_a64(f, off); + buf_a64(f, vaddr); + buf_a64(f, vaddr); /* p_paddr = p_vaddr */ + buf_a64(f, filesz); + buf_a64(f, memsz); + buf_a64(f, align); +} + +/* ======================================================================== + * Main entry point + * ======================================================================== */ + +bool paze_elf_writer_los4_write(paze_object_image_t *img, + bool has_argc_argv, const char *out_path) +{ + /* ---- Step 1: Generate runtime (appends to img->text, reserves BSS) ---- */ + paze_los4_rt_bss_t bss_info; + g_los4_rt_base_in_text = paze_los4_runtime_generate(img, &bss_info); + /* Now img->text = [user code][runtime code] */ + + /* ---- Step 2: Build final .text = [_start stub] + img->text ---- */ + byte_buf_t text; text.data = NULL; text.len = 0; text.cap = 0; + stub_info_t si; + gen_start_stub(&text, has_argc_argv, &si); + size_t stub_sz = text.len; + buf_add_n(&text, img->text.data, img->text.len); + + size_t user_bss = img->bss.bss_size - bss_info.bss_size; + size_t total_bss = img->bss.bss_size; + + /* ---- Step 3: Segment layout ---- */ + int64_t text_vaddr = LOS4_BASE_ADDR; + int64_t entry_vaddr = text_vaddr; /* _start at offset 0 */ + int64_t seg1_end = text_vaddr + (int64_t)text.len; + + int64_t rodata_vaddr = align_up(seg1_end, LOS4_PAGE); + int64_t rodata_off = rodata_vaddr - LOS4_BASE_ADDR + LOS4_PAGE; + int64_t seg2_end = rodata_vaddr + (int64_t)img->rdata.len; + + int64_t data_vaddr = align_up(seg2_end, LOS4_PAGE); + int64_t data_off = data_vaddr - LOS4_BASE_ADDR + LOS4_PAGE; + int64_t bss_vaddr = data_vaddr + (int64_t)img->data.len; + int64_t seg3_mem_end = bss_vaddr + (int64_t)total_bss; + + /* For symbol resolution: user symbols are at text_vaddr + stub_sz + offset + * Runtime symbols are at text_vaddr + stub_sz + rt_base + rt_off */ + int64_t user_text_vaddr = text_vaddr + (int64_t)stub_sz; + + /* ---- Step 4: Patch _start's call to main ---- */ + { + paze_defined_symbol_t *main_sym = paze_object_image_find_symbol(img, "main"); + int64_t main_vaddr = main_sym + ? (user_text_vaddr + (int64_t)main_sym->offset) + : user_text_vaddr; + int64_t call_vaddr = text_vaddr + (int64_t)si.call_rel32_pos; + int32_t rel = (int32_t)(main_vaddr - (call_vaddr + 5)); + buf_w32(&text, (size_t)si.call_rel32_pos + 1, (uint32_t)rel); + } + + /* ---- Step 5: Patch all user fixups ---- */ + for (size_t i = 0; i < img->fixups_len; i++) { + paze_fixup_t *fx = &img->fixups[i]; + int64_t fix_vaddr = 0; + size_t text_off = 0; + bool in_text = (fx->section == &img->text); + bool in_rdata = (fx->section == &img->rdata); + bool in_data = (fx->section == &img->data); + if (!in_text && !in_rdata && !in_data) continue; + + if (in_text) { + text_off = stub_sz + fx->offset; + fix_vaddr = text_vaddr + (int64_t)text_off; + } else if (in_rdata) { + fix_vaddr = rodata_vaddr + (int64_t)fx->offset; + } else { + fix_vaddr = data_vaddr + (int64_t)fx->offset; + } + + if (fx->kind == PAZE_FIXUP_REL32 || fx->kind == PAZE_FIXUP_EXT_SLOT32) { + int64_t target; + if (fx->kind == PAZE_FIXUP_EXT_SLOT32) { + /* External function → runtime */ + int rt_off = paze_los4_runtime_find_offset(fx->symbol); + target = (rt_off >= 0) + ? (user_text_vaddr + (int64_t)g_los4_rt_base_in_text + (int64_t)rt_off) + : user_text_vaddr; + } else { + target = sym_vaddr(fx->symbol, img, + user_text_vaddr, rodata_vaddr, data_vaddr, bss_vaddr); + } + int32_t rel = (int32_t)(target - (fix_vaddr + 4)); + if (in_text) buf_w32(&text, text_off, (uint32_t)rel); + else if (in_rdata) paze_section_write_at(&img->rdata, fx->offset, &rel, 4); + else paze_section_write_at(&img->data, fx->offset, &rel, 4); + } else if (fx->kind == PAZE_FIXUP_ABS64) { + int64_t target = sym_vaddr(fx->symbol, img, + user_text_vaddr, rodata_vaddr, data_vaddr, bss_vaddr); + target += fx->addend; + if (in_text) buf_w64(&text, text_off, (uint64_t)target); + else if (in_rdata) paze_section_write_at(&img->rdata, fx->offset, &target, 8); + else paze_section_write_at(&img->data, fx->offset, &target, 8); + } + } + + /* ---- Step 6: Patch runtime BSS refs (malloc heap_ptr address) ---- */ + { + int malloc_off = paze_los4_runtime_find_offset("malloc"); + if (malloc_off >= 0) { + int64_t heap_ptr_vaddr = bss_vaddr + (int64_t)user_bss + + (int64_t)bss_info.heap_ptr_off; + /* Scan malloc function for mov rax, 0 (48 B8 + 8 zero bytes) */ + size_t base = stub_sz + g_los4_rt_base_in_text + (size_t)malloc_off; + for (int scan = 0; scan < 80; scan++) { + size_t pos = base + (size_t)scan; + if (pos + 10 > text.len) break; + if (text.data[pos] == 0x48 && text.data[pos+1] == 0xB8) { + bool zero = true; + for (int j = 2; j < 10 && zero; j++) + if (text.data[pos+j] != 0) zero = false; + if (zero) + buf_w64(&text, pos + 2, (uint64_t)heap_ptr_vaddr); + } + } + } + } + + /* ---- Step 7: Assemble ELF ---- */ + byte_buf_t elf; elf.data = NULL; elf.len = 0; elf.cap = 0; + + /* ELF header (64 bytes) */ + buf_add_n(&elf, (uint8_t[16]){0x7F,'E','L','F',2,1,1,0,0,0,0,0,0,0,0,0}, 16); + buf_a16(&elf, ET_EXEC); + buf_a16(&elf, EM_X86_64); + buf_a32(&elf, 1); + buf_a64(&elf, (uint64_t)entry_vaddr); + buf_a64(&elf, 64); /* e_phoff */ + buf_a64(&elf, 0); /* e_shoff */ + buf_a32(&elf, 0); /* e_flags */ + buf_a16(&elf, 64); /* e_ehsize */ + buf_a16(&elf, 56); /* e_phentsize */ + buf_a16(&elf, 4); /* e_phnum */ + buf_a16(&elf, 64); /* e_shentsize */ + buf_a16(&elf, 0); /* e_shnum */ + buf_a16(&elf, 0); /* e_shstrndx */ + + /* Program headers */ + write_phdr(&elf, PT_LOAD, PF_R|PF_X, LOS4_PAGE, (uint64_t)text_vaddr, + (uint64_t)text.len, (uint64_t)text.len, LOS4_PAGE); + write_phdr(&elf, PT_LOAD, PF_R, (uint64_t)rodata_off, (uint64_t)rodata_vaddr, + (uint64_t)img->rdata.len, (uint64_t)img->rdata.len, LOS4_PAGE); + write_phdr(&elf, PT_LOAD, PF_R|PF_W, (uint64_t)data_off, (uint64_t)data_vaddr, + (uint64_t)img->data.len, (uint64_t)(seg3_mem_end - data_vaddr), LOS4_PAGE); + write_phdr(&elf, PT_GNU_STACK, PF_R|PF_W, 0, 0, 0, 0, LOS4_PAGE); + + /* Segment data */ + buf_pad_to(&elf, (size_t)LOS4_PAGE); + buf_add_n(&elf, text.data, text.len); + buf_pad_to(&elf, (size_t)rodata_off); + buf_add_n(&elf, img->rdata.data, img->rdata.len); + buf_pad_to(&elf, (size_t)data_off); + buf_add_n(&elf, img->data.data, img->data.len); + + /* ---- Write file ---- */ + FILE *fp = fopen(out_path, "wb"); + if (!fp) { + fprintf(stderr, "error: cannot open '%s'\n", out_path); + free(text.data); free(elf.data); + return false; + } + fwrite(elf.data, 1, elf.len, fp); + fclose(fp); + + free(text.data); + free(elf.data); + return true; +} diff --git a/src/PazeE.los4/src/paze_interpreter.c b/src/PazeE.los4/src/paze_interpreter.c new file mode 100644 index 0000000..f9a037b --- /dev/null +++ b/src/PazeE.los4/src/paze_interpreter.c @@ -0,0 +1,2567 @@ +#include "../include/paze_interpreter.h" +#include "../include/paze_libc.h" +#include +#include +#include +#include +#include +#include + +#define PAZE_MAX_HEAP_SIZE (64 * 1024 * 1024) + +typedef enum { + PAZE_VAL_VOID = 0, + PAZE_VAL_INT, + PAZE_VAL_DOUBLE, + PAZE_VAL_PTR, + PAZE_VAL_STRUCT, +} paze_val_kind_t; + +typedef struct paze_val_t { + paze_val_kind_t kind; + union { + int64_t int_val; + double float_val; + void *ptr_val; + struct { uint8_t *data; size_t size; paze_type_t *type; } bytes_val; + } u; +} paze_val_t; + +static paze_val_t paze_val_void(void) { paze_val_t r; r.kind = PAZE_VAL_VOID; r.u.int_val = 0; return r; } +static paze_val_t paze_val_int(int64_t v) { paze_val_t r; r.kind = PAZE_VAL_INT; r.u.int_val = v; return r; } +static paze_val_t paze_val_dbl(double v) { paze_val_t r; r.kind = PAZE_VAL_DOUBLE; r.u.float_val = v; return r; } +static paze_val_t paze_val_ptr(void *v) { paze_val_t r; r.kind = PAZE_VAL_PTR; r.u.ptr_val = v; return r; } +static paze_val_t paze_val_struct(uint8_t *data, size_t size) { + paze_val_t r; r.kind = PAZE_VAL_STRUCT; + r.u.bytes_val.data = data; r.u.bytes_val.size = size; r.u.bytes_val.type = NULL; return r; +} +static paze_val_t paze_val_struct_typed(uint8_t *data, size_t size, paze_type_t *type) { + paze_val_t r; r.kind = PAZE_VAL_STRUCT; + r.u.bytes_val.data = data; r.u.bytes_val.size = size; r.u.bytes_val.type = type; return r; +} + +static int64_t paze_val_as_int(paze_val_t v) { + switch (v.kind) { + case PAZE_VAL_INT: return v.u.int_val; + case PAZE_VAL_DOUBLE: return (int64_t)v.u.float_val; + case PAZE_VAL_PTR: return (int64_t)(intptr_t)v.u.ptr_val; + default: return 0; + } +} +static double paze_val_as_double(paze_val_t v) { + switch (v.kind) { + case PAZE_VAL_DOUBLE: return v.u.float_val; + case PAZE_VAL_INT: return (double)v.u.int_val; + default: return 0.0; + } +} +static void *paze_val_as_ptr(paze_val_t v) { + switch (v.kind) { + case PAZE_VAL_PTR: return v.u.ptr_val; + case PAZE_VAL_INT: return (void *)(intptr_t)v.u.int_val; + default: return NULL; + } +} +static bool paze_val_is_truthy(paze_val_t v) { + switch (v.kind) { + case PAZE_VAL_INT: return v.u.int_val != 0; + case PAZE_VAL_DOUBLE: return v.u.float_val != 0.0; + case PAZE_VAL_PTR: return v.u.ptr_val != NULL; + case PAZE_VAL_STRUCT: return true; + default: return false; + } +} +static void paze_val_free(paze_val_t v) { + if (v.kind == PAZE_VAL_STRUCT && v.u.bytes_val.data) free(v.u.bytes_val.data); +} + +typedef struct paze_frame_t { + char **local_names; + paze_val_t *local_vals; + paze_type_t **local_types; + int local_count; + int local_cap; + paze_ast_node_t *func_decl; + int caller_frame_index; +} paze_frame_t; + +typedef enum { + PAZE_SIGNAL_NONE = 0, + PAZE_SIGNAL_BREAK, + PAZE_SIGNAL_CONTINUE, + PAZE_SIGNAL_RETURN, + PAZE_SIGNAL_GOTO, +} paze_signal_t; + +typedef struct paze_heap_entry_t { void *ptr; size_t size; } paze_heap_entry_t; + +struct paze_interpreter_t { + paze_ast_node_t *unit; + paze_diagnostics_t *diag; + struct { paze_str_t name; paze_ast_node_t *decl; bool is_builtin; + paze_val_t (*builtin_func)(struct paze_interpreter_t *, paze_val_t *, int); + } *functions; + int func_count, func_cap; + struct { paze_str_t name; paze_val_t value; paze_type_t *type; bool is_initialized; bool is_constant; } *globals; + int global_count, global_cap; + paze_frame_t *frames; + int frame_count, frame_cap; + int *breakpoints; + int breakpoint_count, breakpoint_cap; + bool is_running, is_finished; + int exit_code; + bool single_step, step_pending; + int step_target_depth, current_line; + paze_signal_t signal; + paze_val_t return_value; + paze_str_t goto_label; + paze_heap_entry_t *heap_entries; + int heap_entry_count, heap_entry_cap; + size_t heap_total_allocated; + struct { paze_str_t label; paze_ast_node_t *target_stmt; } *labels; + int label_count, label_cap; + paze_arena_t *arena; + bool had_error; +}; + +static paze_val_t eval_expr(struct paze_interpreter_t *interp, paze_ast_node_t *node); +static void exec_stmt(struct paze_interpreter_t *interp, paze_ast_node_t *node); +static void exec_block(struct paze_interpreter_t *interp, paze_ast_node_t *block); +static void push_frame(struct paze_interpreter_t *interp, paze_ast_node_t *func_decl, int caller_frame_index); +static void pop_frame(struct paze_interpreter_t *interp); +static void set_local(struct paze_interpreter_t *interp, paze_str_t name, paze_val_t value, paze_type_t *type); +static paze_val_t get_local(struct paze_interpreter_t *interp, paze_str_t name, bool *found); +static paze_type_t *get_local_type(struct paze_interpreter_t *interp, paze_str_t name); +static paze_val_t get_global(struct paze_interpreter_t *interp, paze_str_t name, bool *found); +static paze_type_t *get_global_type(struct paze_interpreter_t *interp, paze_str_t name); +static void set_global(struct paze_interpreter_t *interp, paze_str_t name, paze_val_t value, paze_type_t *type); +static size_t get_type_size(paze_type_t *type); +static paze_struct_field_t *find_struct_field(paze_type_t *type, paze_str_t name); +static int get_field_offset(paze_type_t *type, paze_str_t name); +static size_t get_field_size(paze_type_t *type, paze_str_t name); +static bool is_at_breakpoint(struct paze_interpreter_t *interp, int line); +static void check_breakpoint(struct paze_interpreter_t *interp, paze_ast_node_t *node); +static void build_function_table(struct paze_interpreter_t *interp); +static void init_globals(struct paze_interpreter_t *interp); +static void register_builtin(struct paze_interpreter_t *interp, const char *name, + paze_val_t (*func)(struct paze_interpreter_t *, paze_val_t *, int)); +static void register_builtins(struct paze_interpreter_t *interp); +static void grow_func_table(struct paze_interpreter_t *interp); +static paze_type_t *find_pointer_base_type(struct paze_interpreter_t *interp, paze_str_t var_name); +static paze_type_t *resolve_struct_type(struct paze_interpreter_t *interp, paze_type_t *type); +static paze_val_t call_function(struct paze_interpreter_t *interp, paze_ast_node_t *func_decl, paze_val_t *args, int arg_count); +static paze_val_t exec_builtin_printf(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_puts(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_getchar(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_malloc(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_free(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_memcpy(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_memset(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_strlen(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_strcpy(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_strcat(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_strcmp(struct paze_interpreter_t *, paze_val_t *, int); +static paze_val_t exec_builtin_sprintf(struct paze_interpreter_t *, paze_val_t *, int); +static char *val_to_str(paze_val_t v); +static void *heap_alloc(struct paze_interpreter_t *interp, size_t size); + +static char *val_to_str(paze_val_t v) { + char buf[256]; + switch (v.kind) { + case PAZE_VAL_VOID: snprintf(buf, sizeof(buf), "void"); break; + case PAZE_VAL_INT: snprintf(buf, sizeof(buf), "%lld", (long long)v.u.int_val); break; + case PAZE_VAL_DOUBLE: snprintf(buf, sizeof(buf), "%f", v.u.float_val); break; + case PAZE_VAL_PTR: + if (v.u.ptr_val == NULL) { snprintf(buf, sizeof(buf), "NULL"); } + else { snprintf(buf, sizeof(buf), "%p", v.u.ptr_val); } + break; + case PAZE_VAL_STRUCT: snprintf(buf, sizeof(buf), "", v.u.bytes_val.size); break; + } + char *r = (char *)malloc(strlen(buf) + 1); + if (r) { memcpy(r, buf, strlen(buf) + 1); } + return r; +} + +static size_t get_type_size(paze_type_t *type) { + if (!type) return 0; + switch (type->kind) { + case PAZE_TYPE_VOID: return 0; + case PAZE_TYPE_CHAR: return 1; + case PAZE_TYPE_SHORT: return 2; + case PAZE_TYPE_INT: return 4; + case PAZE_TYPE_LONG: return 8; + case PAZE_TYPE_UNSIGNED: + case PAZE_TYPE_SIGNED: + if (type->base) return get_type_size(type->base); + return 4; + case PAZE_TYPE_BOOL: return 1; + case PAZE_TYPE_POINTER: return 8; + case PAZE_TYPE_ARRAY: + if (type->array_size >= 0 && type->base) + return get_type_size(type->base) * (size_t)type->array_size; + return 0; + case PAZE_TYPE_FUNCTION: return 8; + case PAZE_TYPE_STRUCT: + case PAZE_TYPE_UNION: return type->struct_size; + case PAZE_TYPE_TYPEDEF: + if (type->base) return get_type_size(type->base); + return 0; + case PAZE_TYPE_TYPEOF: + if (type->typeof_expr && type->typeof_expr->type) + return get_type_size(type->typeof_expr->type); + return 0; + default: return 0; + } +} + +static paze_struct_field_t *find_struct_field(paze_type_t *type, paze_str_t name) { + if (!type || (type->kind != PAZE_TYPE_STRUCT && type->kind != PAZE_TYPE_UNION)) + return NULL; + for (int i = 0; i < type->struct_field_count; i++) { + if (type->struct_fields && paze_str_cmp(type->struct_fields[i].name, name) == 0) { + return &type->struct_fields[i]; + } + } + return NULL; +} + +static int get_field_offset(paze_type_t *type, paze_str_t name) { + paze_struct_field_t *f = find_struct_field(type, name); + return f ? f->offset : -1; +} + +static size_t get_field_size(paze_type_t *type, paze_str_t name) { + paze_struct_field_t *f = find_struct_field(type, name); + return f ? f->size : 0; +} + +static paze_type_t *find_pointer_base_type(struct paze_interpreter_t *interp, paze_str_t var_name) { + paze_type_t *type = get_local_type(interp, var_name); + if (!type) type = get_global_type(interp, var_name); + if (!type) return NULL; + if (type->kind == PAZE_TYPE_POINTER && type->base) return type->base; + if (type->kind == PAZE_TYPE_ARRAY && type->base) return type->base; + return type; +} + +static paze_type_t *resolve_struct_type(struct paze_interpreter_t *interp, paze_type_t *type) { + if (!type) return NULL; + if (type->kind != PAZE_TYPE_STRUCT && type->kind != PAZE_TYPE_UNION) return type; + if (type->struct_field_count > 0) return type; + paze_ast_node_t *unit = interp->unit; + if (!unit || unit->kind != PAZE_NODE_TRANSLATION_UNIT) return type; + paze_ast_node_arr_t *decls = &unit->data.translation_unit.decls; + for (size_t i = 0; i < decls->len; i++) { + paze_ast_node_t *decl = decls->data[i]; + if (!decl) continue; + if ((decl->kind == PAZE_NODE_STRUCT_DECL || decl->kind == PAZE_NODE_UNION_DECL) && + paze_str_cmp(decl->data.struct_or_union_decl.name, type->name) == 0) { + if (decl->data.struct_or_union_decl.fields.len > 0) { + type->struct_fields = (paze_struct_field_t *)calloc( + decl->data.struct_or_union_decl.fields.len, sizeof(paze_struct_field_t)); + type->struct_field_count = (int)decl->data.struct_or_union_decl.fields.len; + type->struct_field_cap = (int)decl->data.struct_or_union_decl.fields.len; + size_t offset = 0; + size_t max_align = 1; + bool is_union = (decl->kind == PAZE_NODE_UNION_DECL); + for (size_t fi = 0; fi < decl->data.struct_or_union_decl.fields.len; fi++) { + paze_ast_node_t *field_node = decl->data.struct_or_union_decl.fields.data[fi]; + if (!field_node) continue; + paze_str_t fname = field_node->data.var_decl.name; + paze_type_t *ftype = field_node->data.var_decl.var_type; + if (ftype && (ftype->kind == PAZE_TYPE_STRUCT || ftype->kind == PAZE_TYPE_UNION)) { + ftype = resolve_struct_type(interp, ftype); + } + size_t fsize = ftype ? get_type_size(ftype) : 0; + size_t falign = 1; + if (ftype) { + switch (ftype->kind) { + case PAZE_TYPE_CHAR: falign = 1; break; + case PAZE_TYPE_SHORT: falign = 2; break; + case PAZE_TYPE_INT: falign = 4; break; + case PAZE_TYPE_LONG: falign = 8; break; + case PAZE_TYPE_POINTER: falign = 8; break; + default: falign = 4; break; + } + } + if (falign > max_align) max_align = falign; + if (!is_union) { + offset = (offset + falign - 1) & ~(falign - 1); + } + type->struct_fields[fi].name = fname; + type->struct_fields[fi].type = ftype; + type->struct_fields[fi].offset = (int)offset; + type->struct_fields[fi].size = fsize; + if (!is_union) { + offset += fsize; + } + if (fsize > type->struct_size) { + type->struct_size = fsize; + } + } + if (!is_union) { + offset = (offset + max_align - 1) & ~(max_align - 1); + type->struct_size = offset; + } + return type; + } + } + } + return type; +} + +static bool is_at_breakpoint(struct paze_interpreter_t *interp, int line) { + for (int i = 0; i < interp->breakpoint_count; i++) { + if (interp->breakpoints[i] == line) return true; + } + return false; +} + +static void check_breakpoint(struct paze_interpreter_t *interp, paze_ast_node_t *node) { + if (interp->had_error) return; + interp->current_line = node->loc.line; + if (interp->single_step) { + interp->single_step = false; + interp->step_pending = true; + return; + } + if (interp->step_pending) { + if (interp->frame_count <= interp->step_target_depth) { + interp->step_pending = false; + return; + } + } + if (is_at_breakpoint(interp, node->loc.line)) { + interp->is_running = false; + } +} + +static void grow_func_table(struct paze_interpreter_t *interp) { + if (interp->func_count >= interp->func_cap) { + interp->func_cap = interp->func_cap ? interp->func_cap * 2 : 64; + interp->functions = realloc(interp->functions, interp->func_cap * sizeof(*interp->functions)); + } +} + +static void build_function_table(struct paze_interpreter_t *interp) { + paze_ast_node_t *unit = interp->unit; + if (!unit || unit->kind != PAZE_NODE_TRANSLATION_UNIT) return; + paze_ast_node_arr_t *decls = &unit->data.translation_unit.decls; + for (size_t i = 0; i < decls->len; i++) { + paze_ast_node_t *decl = decls->data[i]; + if (!decl || decl->kind != PAZE_NODE_FUNCTION_DECL) continue; + grow_func_table(interp); + interp->functions[interp->func_count].name = decl->data.function_decl.name; + interp->functions[interp->func_count].decl = decl; + interp->functions[interp->func_count].is_builtin = false; + interp->functions[interp->func_count].builtin_func = NULL; + interp->func_count++; + } +} + +static void register_builtin(struct paze_interpreter_t *interp, const char *name, + paze_val_t (*func)(struct paze_interpreter_t *, paze_val_t *, int)) { + grow_func_table(interp); + interp->functions[interp->func_count].name = paze_str_from_cstr(name); + interp->functions[interp->func_count].decl = NULL; + interp->functions[interp->func_count].is_builtin = true; + interp->functions[interp->func_count].builtin_func = func; + interp->func_count++; +} + +static void register_builtins(struct paze_interpreter_t *interp) { + register_builtin(interp, "printf", exec_builtin_printf); + register_builtin(interp, "puts", exec_builtin_puts); + register_builtin(interp, "getchar", exec_builtin_getchar); + register_builtin(interp, "malloc", exec_builtin_malloc); + register_builtin(interp, "free", exec_builtin_free); + register_builtin(interp, "memcpy", exec_builtin_memcpy); + register_builtin(interp, "memset", exec_builtin_memset); + register_builtin(interp, "strlen", exec_builtin_strlen); + register_builtin(interp, "strcpy", exec_builtin_strcpy); + register_builtin(interp, "strcat", exec_builtin_strcat); + register_builtin(interp, "strcmp", exec_builtin_strcmp); + register_builtin(interp, "sprintf", exec_builtin_sprintf); +} + +static void init_globals(struct paze_interpreter_t *interp) { + paze_ast_node_t *unit = interp->unit; + if (!unit || unit->kind != PAZE_NODE_TRANSLATION_UNIT) return; + paze_ast_node_arr_t *decls = &unit->data.translation_unit.decls; + for (size_t i = 0; i < decls->len; i++) { + paze_ast_node_t *decl = decls->data[i]; + if (!decl || decl->kind != PAZE_NODE_VAR_DECL) continue; + paze_str_t name = decl->data.var_decl.name; + paze_type_t *type = decl->data.var_decl.var_type; + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + type = resolve_struct_type(interp, type); + } + if (interp->global_count >= interp->global_cap) { + interp->global_cap = interp->global_cap ? interp->global_cap * 2 : 64; + interp->globals = realloc(interp->globals, interp->global_cap * sizeof(*interp->globals)); + } + interp->globals[interp->global_count].name = name; + interp->globals[interp->global_count].type = type; + interp->globals[interp->global_count].is_initialized = false; + interp->globals[interp->global_count].is_constant = + decl->data.var_decl.is_static || decl->data.var_decl.is_extern; + if (decl->data.var_decl.init_expr) { + interp->globals[interp->global_count].value = + eval_expr(interp, decl->data.var_decl.init_expr); + if (type && type->kind == PAZE_TYPE_ARRAY && + interp->globals[interp->global_count].value.kind == PAZE_VAL_STRUCT) { + size_t elem_sz = type->base ? get_type_size(type->base) : sizeof(int); + if (elem_sz == 0) elem_sz = sizeof(int); + if (decl->data.var_decl.init_expr->kind == PAZE_NODE_INIT_LIST_EXPR) { + paze_ast_node_arr_t *elems = &decl->data.var_decl.init_expr->data.init_list_expr.elements; + size_t total = elem_sz * elems->len; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total > 0 ? total : 1); + memset(buf, 0, total > 0 ? total : 1); + for (size_t ei = 0; ei < elems->len; ei++) { + paze_val_t v = eval_expr(interp, elems->data[ei]); + memcpy(buf + ei * elem_sz, &v.u.int_val, elem_sz < sizeof(int64_t) ? elem_sz : sizeof(int64_t)); + } + paze_val_free(interp->globals[interp->global_count].value); + interp->globals[interp->global_count].value = paze_val_ptr(buf); + } + } + interp->globals[interp->global_count].is_initialized = true; + } else { + interp->globals[interp->global_count].value = paze_val_int(0); + interp->globals[interp->global_count].is_initialized = true; + } + interp->global_count++; + } +} + +static void push_frame(struct paze_interpreter_t *interp, paze_ast_node_t *func_decl, int caller_frame_index) { + if (interp->frame_count >= interp->frame_cap) { + interp->frame_cap = interp->frame_cap ? interp->frame_cap * 2 : 32; + interp->frames = realloc(interp->frames, interp->frame_cap * sizeof(*interp->frames)); + } + paze_frame_t *f = &interp->frames[interp->frame_count]; + f->local_names = NULL; + f->local_vals = NULL; + f->local_types = NULL; + f->local_count = 0; + f->local_cap = 0; + f->func_decl = func_decl; + f->caller_frame_index = caller_frame_index; + interp->frame_count++; +} + +static void pop_frame(struct paze_interpreter_t *interp) { + if (interp->frame_count <= 0) return; + paze_frame_t *f = &interp->frames[interp->frame_count - 1]; + for (int i = 0; i < f->local_count; i++) { + paze_val_free(f->local_vals[i]); + free(f->local_names[i]); + } + free(f->local_names); + free(f->local_vals); + free(f->local_types); + interp->frame_count--; +} + +static void set_local(struct paze_interpreter_t *interp, paze_str_t name, paze_val_t value, paze_type_t *type) { + if (interp->frame_count <= 0) return; + paze_frame_t *f = &interp->frames[interp->frame_count - 1]; + for (int i = 0; i < f->local_count; i++) { + if (f->local_names[i] && + strlen(f->local_names[i]) == name.len && + strncmp(f->local_names[i], name.data, name.len) == 0) { + paze_val_free(f->local_vals[i]); + f->local_vals[i] = value; + f->local_types[i] = type; + return; + } + } + if (f->local_count >= f->local_cap) { + f->local_cap = f->local_cap ? f->local_cap * 2 : 16; + f->local_names = realloc(f->local_names, f->local_cap * sizeof(*f->local_names)); + f->local_vals = realloc(f->local_vals, f->local_cap * sizeof(*f->local_vals)); + f->local_types = realloc(f->local_types, f->local_cap * sizeof(*f->local_types)); + } + char *nc = (char *)malloc(name.len + 1); + if (nc) { memcpy(nc, name.data, name.len); nc[name.len] = '\0'; } + f->local_names[f->local_count] = nc; + f->local_vals[f->local_count] = value; + f->local_types[f->local_count] = type; + f->local_count++; +} + +static paze_val_t get_local(struct paze_interpreter_t *interp, paze_str_t name, bool *found) { + *found = false; + if (interp->frame_count <= 0) return paze_val_void(); + for (int i = interp->frame_count - 1; i >= 0; i--) { + paze_frame_t *f = &interp->frames[i]; + for (int j = f->local_count - 1; j >= 0; j--) { + if (f->local_names[j] && + strlen(f->local_names[j]) == name.len && + strncmp(f->local_names[j], name.data, name.len) == 0) { + *found = true; + return f->local_vals[j]; + } + } + } + return paze_val_void(); +} + +static paze_type_t *get_local_type(struct paze_interpreter_t *interp, paze_str_t name) { + if (interp->frame_count <= 0) return NULL; + for (int i = interp->frame_count - 1; i >= 0; i--) { + paze_frame_t *f = &interp->frames[i]; + for (int j = f->local_count - 1; j >= 0; j--) { + if (f->local_names[j] && + strlen(f->local_names[j]) == name.len && + strncmp(f->local_names[j], name.data, name.len) == 0) { + return f->local_types[j]; + } + } + } + return NULL; +} + +static paze_val_t get_global(struct paze_interpreter_t *interp, paze_str_t name, bool *found) { + *found = false; + for (int i = 0; i < interp->global_count; i++) { + if (interp->globals[i].name.len == name.len && + strncmp(interp->globals[i].name.data, name.data, name.len) == 0) { + *found = true; + return interp->globals[i].value; + } + } + return paze_val_void(); +} + +static void set_global(struct paze_interpreter_t *interp, paze_str_t name, paze_val_t value, paze_type_t *type) { + for (int i = 0; i < interp->global_count; i++) { + if (interp->globals[i].name.len == name.len && + strncmp(interp->globals[i].name.data, name.data, name.len) == 0) { + if (!interp->globals[i].is_constant) { + paze_val_free(interp->globals[i].value); + interp->globals[i].value = value; + interp->globals[i].type = type; + } + return; + } + } + if (interp->global_count >= interp->global_cap) { + interp->global_cap = interp->global_cap ? interp->global_cap * 2 : 64; + interp->globals = realloc(interp->globals, interp->global_cap * sizeof(*interp->globals)); + } + interp->globals[interp->global_count].name = name; + interp->globals[interp->global_count].value = value; + interp->globals[interp->global_count].type = type; + interp->globals[interp->global_count].is_initialized = true; + interp->globals[interp->global_count].is_constant = false; + interp->global_count++; +} + +static paze_type_t *get_global_type(struct paze_interpreter_t *interp, paze_str_t name) { + for (int i = 0; i < interp->global_count; i++) { + if (interp->globals[i].name.len == name.len && + strncmp(interp->globals[i].name.data, name.data, name.len) == 0) { + return interp->globals[i].type; + } + } + return NULL; +} + +static void add_label(struct paze_interpreter_t *interp, paze_str_t label, paze_ast_node_t *target) { + if (interp->label_count >= interp->label_cap) { + interp->label_cap = interp->label_cap ? interp->label_cap * 2 : 16; + interp->labels = realloc(interp->labels, interp->label_cap * sizeof(*interp->labels)); + } + interp->labels[interp->label_count].label = label; + interp->labels[interp->label_count].target_stmt = target; + interp->label_count++; +} + +static void *heap_alloc(struct paze_interpreter_t *interp, size_t size) { + if (interp->heap_total_allocated + size > PAZE_MAX_HEAP_SIZE) { + if (interp->diag) { + paze_diagnostics_error(interp->diag, PAZE_LOC_EMPTY, + "heap exhausted: requested %zu bytes", size); + } + return NULL; + } + void *ptr = malloc(size); + if (!ptr) return NULL; + if (interp->heap_entry_count >= interp->heap_entry_cap) { + interp->heap_entry_cap = interp->heap_entry_cap ? interp->heap_entry_cap * 2 : 64; + interp->heap_entries = realloc(interp->heap_entries, + interp->heap_entry_cap * sizeof(*interp->heap_entries)); + } + interp->heap_entries[interp->heap_entry_count].ptr = ptr; + interp->heap_entries[interp->heap_entry_count].size = size; + interp->heap_entry_count++; + interp->heap_total_allocated += size; + return ptr; +} + +static void heap_free(struct paze_interpreter_t *interp, void *ptr) { + if (!ptr) return; + for (int i = 0; i < interp->heap_entry_count; i++) { + if (interp->heap_entries[i].ptr == ptr) { + interp->heap_total_allocated -= interp->heap_entries[i].size; + interp->heap_entries[i].ptr = NULL; + interp->heap_entries[i].size = 0; + break; + } + } + free(ptr); +} + +static int find_function(struct paze_interpreter_t *interp, paze_str_t name) { + /* Prefer builtins over user-defined functions */ + for (int i = 0; i < interp->func_count; i++) { + if (interp->functions[i].is_builtin && + interp->functions[i].name.len == name.len && + strncmp(interp->functions[i].name.data, name.data, name.len) == 0) + return i; + } + for (int i = 0; i < interp->func_count; i++) { + if (!interp->functions[i].is_builtin && + interp->functions[i].name.len == name.len && + strncmp(interp->functions[i].name.data, name.data, name.len) == 0) + return i; + } + return -1; +} + +static paze_val_t exec_builtin_printf(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 1 || args[0].kind != PAZE_VAL_PTR) return paze_val_int(-1); + const char *fmt = (const char *)args[0].u.ptr_val; + if (!fmt) return paze_val_int(-1); + int arg_idx = 1; + int total = 0; + char ch; + while ((ch = *fmt) != '\0') { + if (ch == '%' && fmt[1] != '\0') { + fmt++; + int width = 0; + int precision = -1; + bool zero_pad = false; + bool left_align = false; + if (*fmt == '-') { left_align = true; fmt++; } + if (*fmt == '0') { zero_pad = true; fmt++; } + while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } + if (*fmt == '.') { fmt++; precision = 0; while (*fmt >= '0' && *fmt <= '9') { precision = precision * 10 + (*fmt - '0'); fmt++; } } + + if (*fmt == 'l' && fmt[1] == 'l') { fmt += 2; } + else if (*fmt == 'l') { fmt++; } + else if (*fmt == 'h' && fmt[1] == 'h') { fmt += 2; } + else if (*fmt == 'h') { fmt++; } + + char spec = *fmt; + switch (spec) { + case 'd': case 'i': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + char buf[64]; + snprintf(buf, sizeof(buf), "%lld", (long long)v); + int slen = (int)strlen(buf); + if (!left_align && width > 0) { + char pad = zero_pad ? '0' : ' '; + while (slen < width) { putchar(pad); total++; slen++; } + } + fputs(buf, stdout); total += slen; + if (left_align && width > 0) { + while (slen < width) { putchar(' '); total++; slen++; } + } + break; + } + case 'x': case 'X': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + char buf[64]; + snprintf(buf, sizeof(buf), spec == 'X' ? "%llX" : "%llx", (unsigned long long)v); + int slen = (int)strlen(buf); + if (!left_align && width > 0) { + char pad = zero_pad ? '0' : ' '; + while (slen < width) { putchar(pad); total++; slen++; } + } + fputs(buf, stdout); total += slen; + if (left_align && width > 0) { + while (slen < width) { putchar(' '); total++; slen++; } + } + break; + } + case 'o': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + char buf[64]; + snprintf(buf, sizeof(buf), "%llo", (unsigned long long)v); + int slen = (int)strlen(buf); + if (!left_align && width > 0) { + char pad = zero_pad ? '0' : ' '; + while (slen < width) { putchar(pad); total++; slen++; } + } + fputs(buf, stdout); total += slen; + if (left_align && width > 0) { + while (slen < width) { putchar(' '); total++; slen++; } + } + break; + } + case 'u': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + char buf[64]; + snprintf(buf, sizeof(buf), "%llu", (unsigned long long)v); + int slen = (int)strlen(buf); + if (!left_align && width > 0) { + char pad = zero_pad ? '0' : ' '; + while (slen < width) { putchar(pad); total++; slen++; } + } + fputs(buf, stdout); total += slen; + if (left_align && width > 0) { + while (slen < width) { putchar(' '); total++; slen++; } + } + break; + } + case 'f': { + double v = (arg_idx < arg_count) ? paze_val_as_double(args[arg_idx++]) : 0.0; + char buf[64]; + if (precision >= 0) snprintf(buf, sizeof(buf), "%.*f", precision, v); + else snprintf(buf, sizeof(buf), "%f", v); + int slen = (int)strlen(buf); + if (!left_align && width > 0) { + char pad = zero_pad ? '0' : ' '; + while (slen < width) { putchar(pad); total++; slen++; } + } + fputs(buf, stdout); total += slen; + if (left_align && width > 0) { + while (slen < width) { putchar(' '); total++; slen++; } + } + break; + } + case 'c': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + putchar((char)v); total++; + break; + } + case 's': { + if (arg_idx < arg_count && args[arg_idx].kind == PAZE_VAL_PTR) { + const char *s = (const char *)args[arg_idx++].u.ptr_val; + int slen = 0; + if (s) slen = (int)strlen(s); + if (!left_align && width > 0) { + while (slen < width) { putchar(' '); total++; slen++; } + } + if (s) { fputs(s, stdout); total += (int)strlen(s); } + else { fputs("(null)", stdout); total += 6; if (slen == 0) slen = 6; } + if (left_align && width > 0) { + while (slen < width) { putchar(' '); total++; slen++; } + } + } else { arg_idx++; fputs("(null)", stdout); total += 6; } + break; + } + case 'p': { + if (arg_idx < arg_count) { + void *p = paze_val_as_ptr(args[arg_idx++]); + char buf[64]; + snprintf(buf, sizeof(buf), "%p", p); + fputs(buf, stdout); total += (int)strlen(buf); + } + break; + } + case '%': + putchar('%'); total++; + break; + default: + putchar('%'); putchar(spec); total += 2; + break; + } + fmt++; + } else { + putchar(ch); + total++; + fmt++; + } + } + (void)interp; + return paze_val_int(total); +} + +static paze_val_t exec_builtin_puts(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 1 || args[0].kind != PAZE_VAL_PTR) return paze_val_int(-1); + const char *str = (const char *)args[0].u.ptr_val; + if (!str) return paze_val_int(-1); + int result = puts(str); + (void)interp; + return paze_val_int(result); +} + +static paze_val_t exec_builtin_getchar(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + (void)interp; (void)args; (void)arg_count; + return paze_val_int(getchar()); +} + +static paze_val_t exec_builtin_malloc(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 1) return paze_val_ptr(NULL); + size_t size = (size_t)paze_val_as_int(args[0]); + return paze_val_ptr(heap_alloc(interp, size)); +} + +static paze_val_t exec_builtin_free(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 1) return paze_val_void(); + heap_free(interp, paze_val_as_ptr(args[0])); + return paze_val_void(); +} + +static paze_val_t exec_builtin_memcpy(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 3) return paze_val_ptr(NULL); + void *dst = paze_val_as_ptr(args[0]); + void *src = paze_val_as_ptr(args[1]); + size_t n = (size_t)paze_val_as_int(args[2]); + if (dst && src && n > 0) memcpy(dst, src, n); + (void)interp; + return paze_val_ptr(dst); +} + +static paze_val_t exec_builtin_memset(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 3) return paze_val_ptr(NULL); + void *s = paze_val_as_ptr(args[0]); + int c = (int)paze_val_as_int(args[1]); + size_t n = (size_t)paze_val_as_int(args[2]); + if (s && n > 0) memset(s, c, n); + (void)interp; + return paze_val_ptr(s); +} + +static paze_val_t exec_builtin_strlen(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 1 || args[0].kind != PAZE_VAL_PTR) return paze_val_int(0); + const char *s = (const char *)args[0].u.ptr_val; + (void)interp; + if (!s) return paze_val_int(0); + return paze_val_int(strlen(s)); +} + +static paze_val_t exec_builtin_strcpy(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 2) return paze_val_ptr(NULL); + void *dst = paze_val_as_ptr(args[0]); + const char *src = NULL; + if (args[1].kind == PAZE_VAL_PTR) src = (const char *)args[1].u.ptr_val; + if (args[1].kind == PAZE_VAL_STRUCT && args[1].u.bytes_val.data) src = (const char *)args[1].u.bytes_val.data; + if (dst && src) strcpy((char *)dst, src); + (void)interp; + return paze_val_ptr(dst); +} + +static paze_val_t exec_builtin_strcat(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 2) return paze_val_ptr(NULL); + void *dst = paze_val_as_ptr(args[0]); + const char *src = NULL; + if (args[1].kind == PAZE_VAL_PTR) src = (const char *)args[1].u.ptr_val; + if (args[1].kind == PAZE_VAL_STRUCT && args[1].u.bytes_val.data) src = (const char *)args[1].u.bytes_val.data; + if (dst && src) strcat((char *)dst, src); + (void)interp; + return paze_val_ptr(dst); +} + +static paze_val_t exec_builtin_strcmp(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 2) return paze_val_int(0); + const char *a = NULL, *b = NULL; + if (args[0].kind == PAZE_VAL_PTR) a = (const char *)args[0].u.ptr_val; + if (args[0].kind == PAZE_VAL_STRUCT && args[0].u.bytes_val.data) a = (const char *)args[0].u.bytes_val.data; + if (args[1].kind == PAZE_VAL_PTR) b = (const char *)args[1].u.ptr_val; + if (args[1].kind == PAZE_VAL_STRUCT && args[1].u.bytes_val.data) b = (const char *)args[1].u.bytes_val.data; + (void)interp; + if (!a || !b) return paze_val_int(a == b ? 0 : (a ? 1 : -1)); + return paze_val_int(strcmp(a, b)); +} + +static char *val_sprintf(const char *fmt, paze_val_t *args, int arg_count) { + if (!fmt) return NULL; + size_t cap = 256; + size_t len = 0; + char *buf = (char *)malloc(cap); + if (!buf) return NULL; + int arg_idx = 0; + char ch; + while ((ch = *fmt) != '\0') { + if (ch == '%' && fmt[1] != '\0') { + fmt++; + int width = 0; + int precision = -1; + bool zero_pad = false; + bool left_align = false; + if (*fmt == '-') { left_align = true; fmt++; } + if (*fmt == '0') { zero_pad = true; fmt++; } + while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } + if (*fmt == '.') { fmt++; precision = 0; while (*fmt >= '0' && *fmt <= '9') { precision = precision * 10 + (*fmt - '0'); fmt++; } } + if (*fmt == 'l' && fmt[1] == 'l') { fmt += 2; } + else if (*fmt == 'l') { fmt++; } + else if (*fmt == 'h' && fmt[1] == 'h') { fmt += 2; } + else if (*fmt == 'h') { fmt++; } + + char spec = *fmt; + char tmp[128]; + int tmp_len = 0; + switch (spec) { + case 'd': case 'i': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + tmp_len = snprintf(tmp, sizeof(tmp), "%lld", (long long)v); + break; + } + case 'x': case 'X': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + tmp_len = snprintf(tmp, sizeof(tmp), spec == 'X' ? "%llX" : "%llx", (unsigned long long)v); + break; + } + case 'o': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + tmp_len = snprintf(tmp, sizeof(tmp), "%llo", (unsigned long long)v); + break; + } + case 'u': { + int64_t v = (arg_idx < arg_count) ? paze_val_as_int(args[arg_idx++]) : 0; + tmp_len = snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)v); + break; + } + case 'f': { + double v = (arg_idx < arg_count) ? paze_val_as_double(args[arg_idx++]) : 0.0; + if (precision >= 0) tmp_len = snprintf(tmp, sizeof(tmp), "%.*f", precision, v); + else tmp_len = snprintf(tmp, sizeof(tmp), "%f", v); + break; + } + case 'c': { + char cv = (arg_idx < arg_count) ? (char)paze_val_as_int(args[arg_idx++]) : 0; + tmp[0] = cv; tmp[1] = '\0'; tmp_len = 1; + break; + } + case 's': { + const char *sv = NULL; + if (arg_idx < arg_count) { + if (args[arg_idx].kind == PAZE_VAL_PTR) sv = (const char *)args[arg_idx].u.ptr_val; + else if (args[arg_idx].kind == PAZE_VAL_STRUCT && args[arg_idx].u.bytes_val.data) sv = (const char *)args[arg_idx].u.bytes_val.data; + arg_idx++; + } + if (!sv) sv = "(null)"; + tmp_len = snprintf(tmp, sizeof(tmp), "%s", sv); + break; + } + case 'p': { + void *pv = (arg_idx < arg_count) ? paze_val_as_ptr(args[arg_idx++]) : NULL; + tmp_len = snprintf(tmp, sizeof(tmp), "%p", pv); + break; + } + case '%': { + tmp[0] = '%'; tmp[1] = '\0'; tmp_len = 1; + break; + } + default: { + tmp[0] = '%'; tmp[1] = spec; tmp[2] = '\0'; tmp_len = 2; + break; + } + } + while ((size_t)tmp_len + len + 1 > cap) { cap *= 2; buf = (char *)realloc(buf, cap); if (!buf) return NULL; } + if (!left_align && width > 0) { + int slen = tmp_len; + char pad = zero_pad ? '0' : ' '; + while (slen < width) { buf[len++] = pad; slen++; } + } + memcpy(buf + len, tmp, tmp_len); len += tmp_len; + if (left_align && width > 0) { + int slen = tmp_len; + while (slen < width) { buf[len++] = ' '; slen++; } + } + } else { + while (len + 2 > cap) { cap *= 2; buf = (char *)realloc(buf, cap); if (!buf) return NULL; } + buf[len++] = ch; + } + fmt++; + } + buf[len] = '\0'; + return buf; +} + +static paze_val_t exec_builtin_sprintf(struct paze_interpreter_t *interp, paze_val_t *args, int arg_count) { + if (arg_count < 2) return paze_val_int(-1); + void *buf = paze_val_as_ptr(args[0]); + const char *fmt = NULL; + if (args[1].kind == PAZE_VAL_PTR) fmt = (const char *)args[1].u.ptr_val; + if (args[1].kind == PAZE_VAL_STRUCT && args[1].u.bytes_val.data) fmt = (const char *)args[1].u.bytes_val.data; + if (!buf || !fmt) return paze_val_int(-1); + + char *result = val_sprintf(fmt, args + 2, arg_count - 2); + if (result) { + strcpy((char *)buf, result); + int len = (int)strlen(result); + free(result); + (void)interp; + return paze_val_int(len); + } + (void)interp; + return paze_val_int(-1); +} + +static paze_val_t call_function(struct paze_interpreter_t *interp, + paze_ast_node_t *func_decl, + paze_val_t *args, int arg_count) { + if (!func_decl) { + if (interp->diag) { + paze_diagnostics_error(interp->diag, PAZE_LOC_EMPTY, + "null function declaration"); + } + return paze_val_void(); + } + push_frame(interp, func_decl, interp->frame_count > 0 ? interp->frame_count - 1 : 0); + int func_frame_idx = interp->frame_count; + if (func_decl->kind == PAZE_NODE_FUNCTION_DECL) { + paze_ast_node_arr_t *params = &func_decl->data.function_decl.params; + for (size_t i = 0; i < params->len; i++) { + paze_ast_node_t *param = params->data[i]; + paze_val_t val = (i < (size_t)arg_count) ? args[i] : paze_val_int(0); + paze_type_t *ptype = param->data.param.param_type; + if (ptype && (ptype->kind == PAZE_TYPE_STRUCT || ptype->kind == PAZE_TYPE_UNION)) { + ptype = resolve_struct_type(interp, ptype); + } + if (val.kind == PAZE_VAL_STRUCT && val.u.bytes_val.data && ptype) { + size_t sz = get_type_size(ptype); + if (sz == 0) sz = val.u.bytes_val.size; + if (sz > 0) { + uint8_t *copy = (uint8_t *)heap_alloc(interp, sz); + if (copy) { + memcpy(copy, val.u.bytes_val.data, sz); + val = paze_val_struct_typed(copy, sz, ptype); + } + } + } + set_local(interp, param->data.param.name, val, ptype); + } + if (func_decl->data.function_decl.body) { + exec_stmt(interp, func_decl->data.function_decl.body); + if (interp->signal == PAZE_SIGNAL_RETURN) { + paze_val_t ret = interp->return_value; + interp->signal = PAZE_SIGNAL_NONE; + while (interp->frame_count > func_frame_idx) pop_frame(interp); + pop_frame(interp); + return ret; + } + } + interp->signal = PAZE_SIGNAL_NONE; + while (interp->frame_count > func_frame_idx) pop_frame(interp); + pop_frame(interp); + return paze_val_int(0); + } + interp->signal = PAZE_SIGNAL_NONE; + while (interp->frame_count > func_frame_idx) pop_frame(interp); + pop_frame(interp); + return paze_val_void(); +} + +/* ======================================================================== + * Expression Evaluation + * ======================================================================== */ + +static paze_ast_node_t *unwrap_expr_stmt(paze_ast_node_t *node) { + if (node && node->kind == PAZE_NODE_EXPR_STMT) + return node->data.expr_stmt.expr; + return node; +} + +static void resolve_array_elem_type(struct paze_interpreter_t *interp, + paze_ast_node_t *arr_node, size_t *out_sz, paze_type_t **out_type) { + *out_sz = sizeof(int); + *out_type = NULL; + if (!arr_node) return; + paze_type_t *arr_type = NULL; + if (arr_node->type && arr_node->type->kind == PAZE_TYPE_ARRAY && arr_node->type->base) { + arr_type = arr_node->type; + } else if (arr_node->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = arr_node->data.identifier_ref.name; + arr_type = get_local_type(interp, name); + if (!arr_type) arr_type = get_global_type(interp, name); + } + if (arr_type && arr_type->kind == PAZE_TYPE_ARRAY && arr_type->base) { + *out_type = arr_type->base; + *out_sz = get_type_size(arr_type->base); + if (*out_sz == 0) *out_sz = sizeof(int); + } +} + +static paze_val_t eval_expr(struct paze_interpreter_t *interp, paze_ast_node_t *node) { + if (!node) return paze_val_void(); + if (interp->had_error) return paze_val_void(); + + node = unwrap_expr_stmt(node); + if (!node) return paze_val_void(); + + switch (node->kind) { + + case PAZE_NODE_INT_LITERAL: + return paze_val_int((int64_t)node->data.int_literal.value); + + case PAZE_NODE_CHAR_LITERAL: + return paze_val_int((int64_t)(unsigned char)node->data.char_literal.value); + + case PAZE_NODE_STRING_LITERAL: { + paze_str_t s = node->data.string_literal.value; + char *buf = (char *)heap_alloc(interp, s.len + 1); + if (!buf) return paze_val_ptr(NULL); + if (s.len > 0) memcpy(buf, s.data, s.len); + buf[s.len] = '\0'; + return paze_val_ptr(buf); + } + + case PAZE_NODE_IDENTIFIER_REF: { + paze_str_t name = node->data.identifier_ref.name; + bool found = false; + paze_val_t val = get_local(interp, name, &found); + if (found) return val; + val = get_global(interp, name, &found); + if (found) return val; + if (interp->diag) { + paze_diagnostics_error(interp->diag, node->loc, + "undeclared variable: %.*s", (int)name.len, name.data); + } + interp->had_error = true; + return paze_val_void(); + } + + case PAZE_NODE_UNARY_EXPR: { + paze_tk_t op = node->data.unary_expr.op; + paze_ast_node_t *operand = node->data.unary_expr.operand; + bool is_postfix = node->data.unary_expr.is_postfix; + + switch (op) { + case PAZE_TK_MINUS: { + paze_val_t v = eval_expr(interp, operand); + if (v.kind == PAZE_VAL_DOUBLE) return paze_val_dbl(-v.u.float_val); + if (v.kind == PAZE_VAL_INT) return paze_val_int(-v.u.int_val); + return paze_val_int(0); + } + case PAZE_TK_PLUS: { + paze_val_t v = eval_expr(interp, operand); + if (v.kind == PAZE_VAL_DOUBLE) return v; + if (v.kind == PAZE_VAL_INT) return v; + return paze_val_int(paze_val_as_int(v)); + } + case PAZE_TK_NOT: { + paze_val_t v = eval_expr(interp, operand); + return paze_val_int(!paze_val_is_truthy(v)); + } + case PAZE_TK_TILDE: { + paze_val_t v = eval_expr(interp, operand); + return paze_val_int(~paze_val_as_int(v)); + } + case PAZE_TK_STAR: { + paze_val_t v = eval_expr(interp, operand); + if (v.kind == PAZE_VAL_PTR && v.u.ptr_val) { + paze_type_t *type = NULL; + if (operand->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = operand->data.identifier_ref.name; + type = get_local_type(interp, name); + if (!type) type = get_global_type(interp, name); + if (type && type->kind == PAZE_TYPE_POINTER && type->base) { + type = type->base; + } else if (type && type->kind == PAZE_TYPE_ARRAY && type->base) { + type = type->base; + } + } else if (operand->kind == PAZE_NODE_BINARY_EXPR && + (operand->data.binary_expr.op == PAZE_TK_PLUS || + operand->data.binary_expr.op == PAZE_TK_MINUS)) { + paze_ast_node_t *left = operand->data.binary_expr.left; + if (left && left->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = left->data.identifier_ref.name; + paze_type_t *ptype = get_local_type(interp, name); + if (!ptype) ptype = get_global_type(interp, name); + if (ptype && ptype->kind == PAZE_TYPE_POINTER && ptype->base) { + type = ptype->base; + } else if (ptype && ptype->kind == PAZE_TYPE_ARRAY && ptype->base) { + type = ptype->base; + } + } + } + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + type = resolve_struct_type(interp, type); + size_t sz = get_type_size(type); + if (sz > 0) { + return paze_val_struct_typed((uint8_t *)v.u.ptr_val, sz, type); + } + } + if (type && type->kind == PAZE_TYPE_ARRAY && type->base) { + size_t elem_sz = get_type_size(type->base); + if (elem_sz > 0) { + return paze_val_struct((uint8_t *)v.u.ptr_val, elem_sz); + } + } + size_t sz = type ? get_type_size(type) : sizeof(int64_t); + if (sz == 0) sz = sizeof(int64_t); + if (sz >= sizeof(int64_t)) return paze_val_int(*(int64_t *)v.u.ptr_val); + else if (sz == sizeof(int)) return paze_val_int((int64_t)*(int32_t *)v.u.ptr_val); + else if (sz == sizeof(short)) return paze_val_int((int64_t)*(int16_t *)v.u.ptr_val); + else if (sz == sizeof(char)) return paze_val_int((int64_t)*(int8_t *)v.u.ptr_val); + else if (sz == sizeof(void *)) return paze_val_ptr(*(void **)v.u.ptr_val); + else return paze_val_int(*(int64_t *)v.u.ptr_val); + } + return paze_val_int(0); + } + case PAZE_TK_AMP: { + if (operand->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = operand->data.identifier_ref.name; + bool found = false; + paze_val_t v = get_local(interp, name, &found); + if (!found) v = get_global(interp, name, &found); + if (found) { + if (v.kind == PAZE_VAL_STRUCT && v.u.bytes_val.data) { + return paze_val_ptr(v.u.bytes_val.data); + } + if (v.kind == PAZE_VAL_PTR && v.u.ptr_val) { + return v; + } + paze_type_t *type = get_local_type(interp, name); + if (!type) type = get_global_type(interp, name); + size_t sz = type ? get_type_size(type) : sizeof(int64_t); + if (sz == 0) sz = sizeof(int64_t); + void *buf = heap_alloc(interp, sz); + if (buf) { + memcpy(buf, &v.u.int_val, sz < sizeof(int64_t) ? sz : sizeof(int64_t)); + } + return paze_val_ptr(buf); + } + } + paze_val_t v = eval_expr(interp, operand); + return paze_val_ptr((void *)(intptr_t)paze_val_as_int(v)); + } + case PAZE_TK_PLUS_PLUS: + case PAZE_TK_MINUS_MINUS: { + paze_val_t old_val = eval_expr(interp, operand); + paze_val_t new_val; + int64_t delta = (op == PAZE_TK_PLUS_PLUS) ? 1 : -1; + if (old_val.kind == PAZE_VAL_PTR) { + int64_t elem_sz = 1; + if (operand->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = operand->data.identifier_ref.name; + paze_type_t *ptype = get_local_type(interp, name); + if (!ptype) ptype = get_global_type(interp, name); + if (ptype && ptype->kind == PAZE_TYPE_POINTER && ptype->base) { + elem_sz = (int64_t)get_type_size(ptype->base); + if (elem_sz == 0) elem_sz = 1; + } else if (ptype && ptype->kind == PAZE_TYPE_ARRAY && ptype->base) { + elem_sz = (int64_t)get_type_size(ptype->base); + if (elem_sz == 0) elem_sz = 1; + } + } + new_val = paze_val_ptr((char *)old_val.u.ptr_val + delta * elem_sz); + } else if (old_val.kind == PAZE_VAL_DOUBLE) { + new_val = paze_val_dbl(old_val.u.float_val + (double)delta); + } else { + new_val = paze_val_int(paze_val_as_int(old_val) + delta); + } + if (operand->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = operand->data.identifier_ref.name; + bool found = false; + paze_type_t *type = get_local_type(interp, name); + if (!type) type = get_global_type(interp, name); + get_local(interp, name, &found); + if (found) set_local(interp, name, new_val, type); + else set_global(interp, name, new_val, type); + } + return is_postfix ? old_val : new_val; + } + default: + break; + } + return paze_val_void(); + } + + case PAZE_NODE_BINARY_EXPR: { + paze_tk_t op = node->data.binary_expr.op; + + if (op == PAZE_TK_AND_AND) { + paze_val_t left = eval_expr(interp, node->data.binary_expr.left); + if (!paze_val_is_truthy(left)) return paze_val_int(0); + paze_val_t right = eval_expr(interp, node->data.binary_expr.right); + return paze_val_int(paze_val_is_truthy(right) ? 1 : 0); + } + if (op == PAZE_TK_OR_OR) { + paze_val_t left = eval_expr(interp, node->data.binary_expr.left); + if (paze_val_is_truthy(left)) return paze_val_int(1); + paze_val_t right = eval_expr(interp, node->data.binary_expr.right); + return paze_val_int(paze_val_is_truthy(right) ? 1 : 0); + } + + paze_val_t left = eval_expr(interp, node->data.binary_expr.left); + paze_val_t right = eval_expr(interp, node->data.binary_expr.right); + + if (left.kind == PAZE_VAL_PTR || right.kind == PAZE_VAL_PTR) { + int64_t l_int = paze_val_as_int(left); + int64_t r_int = paze_val_as_int(right); + int64_t elem_sz = 1; + if (node->data.binary_expr.left && node->data.binary_expr.left->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = node->data.binary_expr.left->data.identifier_ref.name; + paze_type_t *ptype = get_local_type(interp, name); + if (!ptype) ptype = get_global_type(interp, name); + if (ptype && ptype->kind == PAZE_TYPE_POINTER && ptype->base) { + elem_sz = (int64_t)get_type_size(ptype->base); + if (elem_sz == 0) elem_sz = 1; + } else if (ptype && ptype->kind == PAZE_TYPE_ARRAY && ptype->base) { + elem_sz = (int64_t)get_type_size(ptype->base); + if (elem_sz == 0) elem_sz = 1; + } + } + if (left.kind == PAZE_VAL_PTR && right.kind == PAZE_VAL_INT) { + char *p = (char *)left.u.ptr_val; + switch (op) { + case PAZE_TK_PLUS: return paze_val_ptr(p + r_int * elem_sz); + case PAZE_TK_MINUS: return paze_val_ptr(p - r_int * elem_sz); + default: break; + } + } + if (left.kind == PAZE_VAL_INT && right.kind == PAZE_VAL_PTR) { + char *p = (char *)right.u.ptr_val; + switch (op) { + case PAZE_TK_PLUS: return paze_val_ptr(p + l_int * elem_sz); + default: break; + } + } + if (left.kind == PAZE_VAL_PTR && right.kind == PAZE_VAL_PTR) { + char *lp = (char *)left.u.ptr_val; + char *rp = (char *)right.u.ptr_val; + switch (op) { + case PAZE_TK_MINUS: return paze_val_int((int64_t)((lp - rp) / elem_sz)); + default: break; + } + } + } + + int64_t l_int = paze_val_as_int(left); + int64_t r_int = paze_val_as_int(right); + + bool is_float = (left.kind == PAZE_VAL_DOUBLE || right.kind == PAZE_VAL_DOUBLE); + if (is_float) { + double l = paze_val_as_double(left); + double r = paze_val_as_double(right); + switch (op) { + case PAZE_TK_PLUS: return paze_val_dbl(l + r); + case PAZE_TK_MINUS: return paze_val_dbl(l - r); + case PAZE_TK_STAR: return paze_val_dbl(l * r); + case PAZE_TK_SLASH: return paze_val_dbl(r != 0.0 ? l / r : 0.0); + case PAZE_TK_PERCENT: return paze_val_dbl(fmod(l, r)); + case PAZE_TK_EQ: return paze_val_int(l == r); + case PAZE_TK_NOT_EQ: return paze_val_int(l != r); + case PAZE_TK_LT: return paze_val_int(l < r); + case PAZE_TK_LE: return paze_val_int(l <= r); + case PAZE_TK_GT: return paze_val_int(l > r); + case PAZE_TK_GE: return paze_val_int(l >= r); + default: break; + } + } else { + switch (op) { + case PAZE_TK_PLUS: return paze_val_int(l_int + r_int); + case PAZE_TK_MINUS: return paze_val_int(l_int - r_int); + case PAZE_TK_STAR: return paze_val_int(l_int * r_int); + case PAZE_TK_SLASH: return paze_val_int(r_int != 0 ? l_int / r_int : 0); + case PAZE_TK_PERCENT: return paze_val_int(r_int != 0 ? l_int % r_int : 0); + case PAZE_TK_AMP: return paze_val_int(l_int & r_int); + case PAZE_TK_PIPE: return paze_val_int(l_int | r_int); + case PAZE_TK_CARET: return paze_val_int(l_int ^ r_int); + case PAZE_TK_SHL: return paze_val_int(l_int << (r_int & 63)); + case PAZE_TK_SHR: return paze_val_int(l_int >> (r_int & 63)); + case PAZE_TK_EQ: return paze_val_int(l_int == r_int); + case PAZE_TK_NOT_EQ: return paze_val_int(l_int != r_int); + case PAZE_TK_LT: return paze_val_int(l_int < r_int); + case PAZE_TK_LE: return paze_val_int(l_int <= r_int); + case PAZE_TK_GT: return paze_val_int(l_int > r_int); + case PAZE_TK_GE: return paze_val_int(l_int >= r_int); + default: break; + } + } + return paze_val_int(0); + } + + case PAZE_NODE_ASSIGN_EXPR: { + paze_ast_node_t *target = node->data.assign_expr.target; + paze_val_t val = eval_expr(interp, node->data.assign_expr.value); + paze_tk_t op = node->data.assign_expr.op; + + if (op != PAZE_TK_ASSIGN) { + paze_val_t cur = eval_expr(interp, target); + bool is_float = (cur.kind == PAZE_VAL_DOUBLE || val.kind == PAZE_VAL_DOUBLE); + if (is_float) { + double c = paze_val_as_double(cur); + double v = paze_val_as_double(val); + double r = c; + switch (op) { + case PAZE_TK_PLUS_ASSIGN: r = c + v; break; + case PAZE_TK_MINUS_ASSIGN: r = c - v; break; + case PAZE_TK_STAR_ASSIGN: r = c * v; break; + case PAZE_TK_SLASH_ASSIGN: r = v != 0.0 ? c / v : 0.0; break; + case PAZE_TK_PERCENT_ASSIGN: r = fmod(c, v); break; + default: break; + } + val = paze_val_dbl(r); + } else { + int64_t c = paze_val_as_int(cur); + int64_t v = paze_val_as_int(val); + int64_t r = c; + switch (op) { + case PAZE_TK_PLUS_ASSIGN: r = c + v; break; + case PAZE_TK_MINUS_ASSIGN: r = c - v; break; + case PAZE_TK_STAR_ASSIGN: r = c * v; break; + case PAZE_TK_SLASH_ASSIGN: r = v != 0 ? c / v : 0; break; + case PAZE_TK_PERCENT_ASSIGN: r = v != 0 ? c % v : 0; break; + case PAZE_TK_SHL_ASSIGN: r = c << (v & 63); break; + case PAZE_TK_SHR_ASSIGN: r = c >> (v & 63); break; + case PAZE_TK_AND_ASSIGN: r = c & v; break; + case PAZE_TK_OR_ASSIGN: r = c | v; break; + case PAZE_TK_XOR_ASSIGN: r = c ^ v; break; + default: break; + } + val = paze_val_int(r); + } + } + + if (target->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = target->data.identifier_ref.name; + bool found = false; + paze_type_t *type = get_local_type(interp, name); + if (!type) type = get_global_type(interp, name); + get_local(interp, name, &found); + if (found) { set_local(interp, name, val, type); return val; } + get_global(interp, name, &found); + if (found) { set_global(interp, name, val, type); return val; } + set_global(interp, name, val, NULL); + return val; + } + if (target->kind == PAZE_NODE_INDEX_EXPR) { + paze_val_t arr = eval_expr(interp, target->data.index_expr.array); + paze_val_t idx = eval_expr(interp, target->data.index_expr.index); + if (arr.kind == PAZE_VAL_PTR && arr.u.ptr_val) { + int64_t i = paze_val_as_int(idx); + size_t elem_sz; + paze_type_t *elem_type; + resolve_array_elem_type(interp, target->data.index_expr.array, &elem_sz, &elem_type); + memcpy((char *)arr.u.ptr_val + i * elem_sz, &val.u.int_val, elem_sz < sizeof(int64_t) ? elem_sz : sizeof(int64_t)); + } + return val; + } + if (target->kind == PAZE_NODE_MEMBER_EXPR) { + paze_val_t obj = eval_expr(interp, target->data.member_expr.object); + paze_str_t member = target->data.member_expr.member; + bool is_arrow = target->data.member_expr.is_arrow; + (void)is_arrow; + + if (obj.kind == PAZE_VAL_STRUCT && obj.u.bytes_val.data) { + paze_type_t *struct_type = obj.u.bytes_val.type; + if (!struct_type) { + if (target->data.member_expr.object->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t obj_name = target->data.member_expr.object->data.identifier_ref.name; + struct_type = get_local_type(interp, obj_name); + if (!struct_type) struct_type = get_global_type(interp, obj_name); + } + } + if (struct_type) struct_type = resolve_struct_type(interp, struct_type); + int offset = -1; + if (struct_type) { + offset = get_field_offset(struct_type, member); + } + if (offset >= 0) { + size_t field_sz = get_field_size(struct_type, member); + if (field_sz == 0) field_sz = sizeof(int64_t); + memcpy(obj.u.bytes_val.data + offset, &val.u.int_val, + field_sz < sizeof(int64_t) ? field_sz : sizeof(int64_t)); + } + } else if (obj.kind == PAZE_VAL_PTR && obj.u.ptr_val) { + paze_type_t *struct_type = NULL; + if (target->data.member_expr.object->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t obj_name = target->data.member_expr.object->data.identifier_ref.name; + struct_type = find_pointer_base_type(interp, obj_name); + } + if (!struct_type) { + struct_type = find_pointer_base_type(interp, PAZE_STR_EMPTY); + } + int offset = -1; + if (struct_type) { + struct_type = resolve_struct_type(interp, struct_type); + offset = get_field_offset(struct_type, member); + } + if (offset >= 0) { + size_t field_sz = get_field_size(struct_type, member); + if (field_sz == 0) field_sz = sizeof(int64_t); + memcpy((char *)obj.u.ptr_val + offset, &val.u.int_val, + field_sz < sizeof(int64_t) ? field_sz : sizeof(int64_t)); + } + } + return val; + } + return val; + } + + case PAZE_NODE_CONDITIONAL_EXPR: { + paze_val_t cond = eval_expr(interp, node->data.conditional_expr.condition); + if (paze_val_is_truthy(cond)) { + return eval_expr(interp, node->data.conditional_expr.then_expr); + } else { + return eval_expr(interp, node->data.conditional_expr.else_expr); + } + } + + case PAZE_NODE_CALL_EXPR: { + paze_ast_node_t *func_node = node->data.call_expr.function; + paze_ast_node_arr_t *args_arr = &node->data.call_expr.args; + + int arg_count = (int)args_arr->len; + paze_val_t *args = NULL; + if (arg_count > 0) { + args = (paze_val_t *)malloc((size_t)arg_count * sizeof(paze_val_t)); + if (!args) return paze_val_void(); + for (int i = 0; i < arg_count; i++) { + args[i] = eval_expr(interp, args_arr->data[i]); + if (interp->had_error) { free(args); return paze_val_void(); } + } + } + + if (func_node->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t name = func_node->data.identifier_ref.name; + int idx = find_function(interp, name); + if (idx >= 0) { + paze_val_t result; + if (interp->functions[idx].is_builtin && interp->functions[idx].builtin_func) { + result = interp->functions[idx].builtin_func(interp, args, arg_count); + } else { + result = call_function(interp, interp->functions[idx].decl, args, arg_count); + } + free(args); + return result; + } + if (interp->diag) { + paze_diagnostics_error(interp->diag, node->loc, + "undeclared function: %.*s", (int)name.len, name.data); + } + interp->had_error = true; + free(args); + return paze_val_void(); + } + + if (func_node->kind == PAZE_NODE_MEMBER_EXPR) { + paze_val_t obj = eval_expr(interp, func_node->data.member_expr.object); + (void)obj; + } + + if (interp->diag) { + paze_diagnostics_error(interp->diag, node->loc, + "unsupported call expression"); + } + interp->had_error = true; + free(args); + return paze_val_void(); + } + + case PAZE_NODE_INDEX_EXPR: { + paze_val_t arr = eval_expr(interp, node->data.index_expr.array); + paze_val_t idx = eval_expr(interp, node->data.index_expr.index); + int64_t i = paze_val_as_int(idx); + size_t elem_sz; + paze_type_t *elem_type; + resolve_array_elem_type(interp, node->data.index_expr.array, &elem_sz, &elem_type); + if (arr.kind == PAZE_VAL_PTR && arr.u.ptr_val) { + void *p = (char *)arr.u.ptr_val + i * elem_sz; + if (elem_type && (elem_type->kind == PAZE_TYPE_STRUCT || elem_type->kind == PAZE_TYPE_UNION || + elem_type->kind == PAZE_TYPE_ARRAY)) { + if (elem_type->kind == PAZE_TYPE_STRUCT || elem_type->kind == PAZE_TYPE_UNION) + elem_type = resolve_struct_type(interp, elem_type); + return paze_val_struct_typed((uint8_t *)p, elem_sz, elem_type); + } + if (elem_sz >= sizeof(int64_t)) { + return paze_val_int(*(int64_t *)p); + } else if (elem_sz == sizeof(int)) { + return paze_val_int((int64_t)*(int32_t *)p); + } else if (elem_sz == sizeof(short)) { + return paze_val_int((int64_t)*(int16_t *)p); + } else if (elem_sz == sizeof(char)) { + return paze_val_int((int64_t)*(int8_t *)p); + } else if (elem_sz == sizeof(void *)) { + return paze_val_ptr(*(void **)p); + } else { + return paze_val_int(*(int64_t *)p); + } + } + if (arr.kind == PAZE_VAL_STRUCT && arr.u.bytes_val.data) { + size_t stride = elem_sz; + if (stride == 0) stride = sizeof(int64_t); + if (i >= 0 && (size_t)(i * stride + stride) <= arr.u.bytes_val.size) { + void *p = arr.u.bytes_val.data + i * stride; + if (elem_type && (elem_type->kind == PAZE_TYPE_STRUCT || elem_type->kind == PAZE_TYPE_UNION || + elem_type->kind == PAZE_TYPE_ARRAY)) { + if (elem_type->kind == PAZE_TYPE_STRUCT || elem_type->kind == PAZE_TYPE_UNION) + elem_type = resolve_struct_type(interp, elem_type); + return paze_val_struct_typed((uint8_t *)p, stride, elem_type); + } + if (stride >= sizeof(int64_t)) { + return paze_val_int(*(int64_t *)p); + } else if (stride == sizeof(int)) { + return paze_val_int((int64_t)*(int32_t *)p); + } else if (stride == sizeof(short)) { + return paze_val_int((int64_t)*(int16_t *)p); + } else if (stride == sizeof(char)) { + return paze_val_int((int64_t)*(int8_t *)p); + } else { + return paze_val_int(*(int64_t *)p); + } + } + } + return paze_val_int(0); + } + + case PAZE_NODE_MEMBER_EXPR: { + paze_val_t obj = eval_expr(interp, node->data.member_expr.object); + paze_str_t member = node->data.member_expr.member; + + if (obj.kind == PAZE_VAL_STRUCT && obj.u.bytes_val.data) { + paze_type_t *struct_type = obj.u.bytes_val.type; + if (!struct_type) { + if (node->data.member_expr.object->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t obj_name = node->data.member_expr.object->data.identifier_ref.name; + struct_type = get_local_type(interp, obj_name); + if (!struct_type) struct_type = get_global_type(interp, obj_name); + } + } + if (struct_type) struct_type = resolve_struct_type(interp, struct_type); + int offset = -1; + if (struct_type) { + offset = get_field_offset(struct_type, member); + } + if (offset >= 0 && struct_type) { + size_t field_sz = get_field_size(struct_type, member); + if (field_sz == 0) field_sz = sizeof(int64_t); + paze_struct_field_t *fld = find_struct_field(struct_type, member); + paze_type_t *field_type = fld ? fld->type : NULL; + if (field_type && (field_type->kind == PAZE_TYPE_STRUCT || field_type->kind == PAZE_TYPE_UNION || + field_type->kind == PAZE_TYPE_ARRAY)) { + if ((size_t)(offset + (int)field_sz) <= obj.u.bytes_val.size) { + return paze_val_struct_typed(obj.u.bytes_val.data + offset, field_sz, field_type); + } + } + if ((size_t)(offset + (int)field_sz) <= obj.u.bytes_val.size) { + if (field_sz >= sizeof(int64_t)) { + return paze_val_int(*(int64_t *)(obj.u.bytes_val.data + offset)); + } else if (field_sz == sizeof(int)) { + return paze_val_int((int64_t)*(int32_t *)(obj.u.bytes_val.data + offset)); + } else if (field_sz == sizeof(short)) { + return paze_val_int((int64_t)*(int16_t *)(obj.u.bytes_val.data + offset)); + } else if (field_sz == sizeof(char)) { + return paze_val_int((int64_t)*(int8_t *)(obj.u.bytes_val.data + offset)); + } else if (field_sz == sizeof(void *)) { + return paze_val_ptr(*(void **)(obj.u.bytes_val.data + offset)); + } else { + return paze_val_int(*(int64_t *)(obj.u.bytes_val.data + offset)); + } + } + } + return paze_val_int(0); + } + + if (obj.kind == PAZE_VAL_PTR && obj.u.ptr_val) { + paze_type_t *struct_type = NULL; + if (node->data.member_expr.object->kind == PAZE_NODE_IDENTIFIER_REF) { + paze_str_t obj_name = node->data.member_expr.object->data.identifier_ref.name; + struct_type = find_pointer_base_type(interp, obj_name); + } + int offset = -1; + if (struct_type) { + struct_type = resolve_struct_type(interp, struct_type); + offset = get_field_offset(struct_type, member); + } + if (offset >= 0 && struct_type) { + size_t field_sz = get_field_size(struct_type, member); + if (field_sz == 0) field_sz = sizeof(int64_t); + paze_struct_field_t *fld2 = find_struct_field(struct_type, member); + paze_type_t *field_type = fld2 ? fld2->type : NULL; + if (field_type && (field_type->kind == PAZE_TYPE_STRUCT || field_type->kind == PAZE_TYPE_UNION || + field_type->kind == PAZE_TYPE_ARRAY)) { + return paze_val_struct_typed((uint8_t *)obj.u.ptr_val + offset, field_sz, field_type); + } + if (field_sz >= sizeof(int64_t)) { + return paze_val_int(*(int64_t *)((char *)obj.u.ptr_val + offset)); + } else if (field_sz == sizeof(int)) { + return paze_val_int((int64_t)*(int32_t *)((char *)obj.u.ptr_val + offset)); + } else if (field_sz == sizeof(short)) { + return paze_val_int((int64_t)*(int16_t *)((char *)obj.u.ptr_val + offset)); + } else if (field_sz == sizeof(char)) { + return paze_val_int((int64_t)*(int8_t *)((char *)obj.u.ptr_val + offset)); + } else if (field_sz == sizeof(void *)) { + return paze_val_ptr(*(void **)((char *)obj.u.ptr_val + offset)); + } else { + return paze_val_int(*(int64_t *)((char *)obj.u.ptr_val + offset)); + } + } + return paze_val_int(0); + } + return paze_val_int(0); + } + + case PAZE_NODE_CAST_EXPR: { + paze_val_t v = eval_expr(interp, node->data.cast_expr.expr); + paze_type_t *target = node->data.cast_expr.target_type; + if (!target) return v; + switch (target->kind) { + case PAZE_TYPE_VOID: return paze_val_void(); + case PAZE_TYPE_CHAR: + case PAZE_TYPE_SHORT: + case PAZE_TYPE_INT: + case PAZE_TYPE_LONG: + case PAZE_TYPE_SIGNED: + case PAZE_TYPE_UNSIGNED: + case PAZE_TYPE_BOOL: + return paze_val_int(paze_val_as_int(v)); + case PAZE_TYPE_POINTER: + if (v.kind == PAZE_VAL_PTR) return v; + return paze_val_ptr((void *)(intptr_t)paze_val_as_int(v)); + case PAZE_TYPE_ARRAY: + case PAZE_TYPE_STRUCT: + case PAZE_TYPE_UNION: + return v; + default: + break; + } + return v; + } + + case PAZE_NODE_SIZEOF_EXPR: { + if (node->data.sizeof_expr.is_type) { + return paze_val_int((int64_t)get_type_size(node->data.sizeof_expr.size_type)); + } + if (node->data.sizeof_expr.expr && node->data.sizeof_expr.expr->type) { + return paze_val_int((int64_t)get_type_size(node->data.sizeof_expr.expr->type)); + } + return paze_val_int(8); + } + + case PAZE_NODE_COMMA_EXPR: { + eval_expr(interp, node->data.comma_expr.left); + return eval_expr(interp, node->data.comma_expr.right); + } + + case PAZE_NODE_INIT_LIST_EXPR: { + paze_ast_node_arr_t *elems = &node->data.init_list_expr.elements; + if (elems->len == 0) return paze_val_void(); + if (node->type && node->type->kind == PAZE_TYPE_STRUCT) { + paze_type_t *stype = resolve_struct_type(interp, node->type); + size_t sz = get_type_size(stype); + if (sz == 0) sz = 1; + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz); + if (!buf) return paze_val_void(); + memset(buf, 0, sz); + for (size_t i = 0; i < elems->len && i < (size_t)stype->struct_field_count; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + int offset = stype->struct_fields[i].offset; + size_t field_sz = stype->struct_fields[i].size; + if (field_sz == 0) field_sz = sizeof(int64_t); + if ((size_t)offset + field_sz <= sz) { + memcpy(buf + offset, &v.u.int_val, + field_sz < sizeof(int64_t) ? field_sz : sizeof(int64_t)); + } + } + return paze_val_struct_typed(buf, sz, stype); + } + size_t elem_size = sizeof(int64_t); + if (node->type && node->type->kind == PAZE_TYPE_ARRAY && node->type->base) { + elem_size = get_type_size(node->type->base); + } + if (elem_size == 0) elem_size = sizeof(int64_t); + size_t total = elem_size * elems->len; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total > 0 ? total : 1); + if (!buf) return paze_val_void(); + memset(buf, 0, total > 0 ? total : 1); + for (size_t i = 0; i < elems->len; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + if (i * elem_size + elem_size <= total) { + memcpy(buf + i * elem_size, &v.u.int_val, elem_size < sizeof(int64_t) ? elem_size : sizeof(int64_t)); + } + } + return paze_val_struct(buf, total > 0 ? total : 0); + } + + case PAZE_NODE_STRING_CONCAT_EXPR: { + paze_ast_node_arr_t *parts = &node->data.string_concat_expr.parts; + size_t total_len = 0; + char **strings = NULL; + int count = 0; + if (parts->len > 0) { + strings = (char **)malloc(parts->len * sizeof(char *)); + count = 0; + } + for (size_t i = 0; i < parts->len; i++) { + paze_ast_node_t *part = parts->data[i]; + if (part->kind == PAZE_NODE_STRING_LITERAL) { + paze_str_t s = part->data.string_literal.value; + char *buf = (char *)heap_alloc(interp, s.len + 1); + if (buf) { + if (s.len > 0) memcpy(buf, s.data, s.len); + buf[s.len] = '\0'; + strings[count++] = buf; + total_len += s.len; + } + } + } + char *result = (char *)heap_alloc(interp, total_len + 1); + if (result) { + result[0] = '\0'; + size_t pos = 0; + for (int i = 0; i < count; i++) { + size_t l = strlen(strings[i]); + memcpy(result + pos, strings[i], l); + pos += l; + } + result[pos] = '\0'; + } + free(strings); + return paze_val_ptr(result); + } + + case PAZE_NODE_STMT_EXPR: { + paze_ast_node_t *body = node->data.stmt_expr.body; + paze_val_t last = paze_val_void(); + if (body && body->kind == PAZE_NODE_BLOCK_STMT) { + paze_ast_node_arr_t *stmts = &body->data.block_stmt.stmts; + for (size_t i = 0; i < stmts->len; i++) { + if (stmts->data[i]->kind == PAZE_NODE_EXPR_STMT) { + last = eval_expr(interp, stmts->data[i]->data.expr_stmt.expr); + } else if (stmts->data[i]->kind == PAZE_NODE_RETURN_STMT) { + last = eval_expr(interp, stmts->data[i]->data.return_stmt.value); + } else { + exec_stmt(interp, stmts->data[i]); + } + } + } + return last; + } + + case PAZE_NODE_COMPOUND_LITERAL_EXPR: { + paze_ast_node_t *init = node->data.compound_literal_expr.init; + paze_type_t *target = node->data.compound_literal_expr.target_type; + if (target && (target->kind == PAZE_TYPE_STRUCT || target->kind == PAZE_TYPE_UNION)) { + target = resolve_struct_type(interp, target); + } + size_t sz = target ? get_type_size(target) : sizeof(int64_t); + if (sz == 0) sz = sizeof(int64_t); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz); + if (!buf) return paze_val_void(); + memset(buf, 0, sz); + if (init && init->kind == PAZE_NODE_INIT_LIST_EXPR && target && + (target->kind == PAZE_TYPE_STRUCT || target->kind == PAZE_TYPE_UNION)) { + paze_ast_node_arr_t *elems = &init->data.init_list_expr.elements; + for (size_t i = 0; i < elems->len && i < (size_t)target->struct_field_count; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + int offset = target->struct_fields[i].offset; + size_t field_sz = target->struct_fields[i].size; + if (field_sz == 0) field_sz = sizeof(int64_t); + if ((size_t)offset + field_sz <= sz) { + memcpy(buf + offset, &v.u.int_val, + field_sz < sizeof(int64_t) ? field_sz : sizeof(int64_t)); + } + } + } else if (init && init->kind == PAZE_NODE_INIT_LIST_EXPR) { + paze_ast_node_arr_t *elems = &init->data.init_list_expr.elements; + size_t elem_sz = (target && target->base) ? get_type_size(target->base) : sizeof(int64_t); + if (elem_sz == 0) elem_sz = sizeof(int64_t); + for (size_t i = 0; i < elems->len && i * elem_sz < sz; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + memcpy(buf + i * elem_sz, &v.u.int_val, + elem_sz < sizeof(int64_t) ? elem_sz : sizeof(int64_t)); + } + } + return paze_val_struct_typed(buf, sz, target); + } + + case PAZE_NODE_NULL_STMT: + return paze_val_void(); + + default: + break; + } + return paze_val_void(); +} + +/* ======================================================================== + * Statement Execution + * ======================================================================== */ + +static void exec_block(struct paze_interpreter_t *interp, paze_ast_node_t *block) { + if (!block || block->kind != PAZE_NODE_BLOCK_STMT) return; + paze_ast_node_arr_t *stmts = &block->data.block_stmt.stmts; + for (size_t i = 0; i < stmts->len; i++) { + if (interp->signal == PAZE_SIGNAL_RETURN || + interp->signal == PAZE_SIGNAL_BREAK || + interp->signal == PAZE_SIGNAL_CONTINUE) { + break; + } + if (interp->signal == PAZE_SIGNAL_GOTO) { + bool found = false; + for (int j = 0; j < interp->label_count; j++) { + if (interp->labels[j].label.len == interp->goto_label.len && + strncmp(interp->labels[j].label.data, interp->goto_label.data, + interp->goto_label.len) == 0) { + interp->signal = PAZE_SIGNAL_NONE; + interp->goto_label = PAZE_STR_EMPTY; + paze_ast_node_t *target = interp->labels[j].target_stmt; + if (target) exec_stmt(interp, target); + found = true; + break; + } + } + if (!found) break; + continue; + } + exec_stmt(interp, stmts->data[i]); + } +} + +static void exec_stmt(struct paze_interpreter_t *interp, paze_ast_node_t *node) { + if (!node) return; + if (interp->had_error) return; + + check_breakpoint(interp, node); + if (!interp->is_running && interp->step_pending) return; + if (interp->had_error) return; + + switch (node->kind) { + + case PAZE_NODE_BLOCK_STMT: + exec_block(interp, node); + break; + + case PAZE_NODE_EXPR_STMT: + if (node->data.expr_stmt.expr) { + eval_expr(interp, node->data.expr_stmt.expr); + } + break; + + case PAZE_NODE_NULL_STMT: + break; + + case PAZE_NODE_IF_STMT: { + paze_val_t cond = eval_expr(interp, node->data.if_stmt.condition); + if (paze_val_is_truthy(cond)) { + if (node->data.if_stmt.then_branch) + exec_stmt(interp, node->data.if_stmt.then_branch); + } else { + if (node->data.if_stmt.else_branch) + exec_stmt(interp, node->data.if_stmt.else_branch); + } + break; + } + + case PAZE_NODE_WHILE_STMT: { + int break_depth = 0; + while (true) { + paze_val_t cond = eval_expr(interp, node->data.while_stmt.condition); + if (!paze_val_is_truthy(cond)) break; + if (interp->had_error) break; + exec_stmt(interp, node->data.while_stmt.body); + if (interp->signal == PAZE_SIGNAL_BREAK) { + interp->signal = PAZE_SIGNAL_NONE; + break; + } + if (interp->signal == PAZE_SIGNAL_CONTINUE) { + interp->signal = PAZE_SIGNAL_NONE; + continue; + } + if (interp->signal == PAZE_SIGNAL_RETURN) break; + if (interp->signal == PAZE_SIGNAL_GOTO) break; + if (interp->had_error) break; + break_depth++; + if (break_depth > 1000000) break; + } + break; + } + + case PAZE_NODE_DO_WHILE_STMT: { + int break_depth = 0; + do { + exec_stmt(interp, node->data.do_while_stmt.body); + if (interp->signal == PAZE_SIGNAL_BREAK) { + interp->signal = PAZE_SIGNAL_NONE; + break; + } + if (interp->signal == PAZE_SIGNAL_CONTINUE) { + interp->signal = PAZE_SIGNAL_NONE; + } + if (interp->signal == PAZE_SIGNAL_RETURN) break; + if (interp->signal == PAZE_SIGNAL_GOTO) break; + if (interp->had_error) break; + paze_val_t cond = eval_expr(interp, node->data.do_while_stmt.condition); + if (!paze_val_is_truthy(cond)) break; + break_depth++; + if (break_depth > 1000000) break; + } while (true); + break; + } + + case PAZE_NODE_FOR_STMT: { + if (node->data.for_stmt.init) { + if (node->data.for_stmt.init->kind == PAZE_NODE_DECL_STMT) { + exec_stmt(interp, node->data.for_stmt.init); + } else if (node->data.for_stmt.init->kind == PAZE_NODE_VAR_DECL) { + paze_ast_node_t *decl = node->data.for_stmt.init; + paze_str_t name = decl->data.var_decl.name; + paze_type_t *type = decl->data.var_decl.var_type; + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + type = resolve_struct_type(interp, type); + } + paze_val_t val; + if (decl->data.var_decl.init_expr) { + val = eval_expr(interp, decl->data.var_decl.init_expr); + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION) && + val.kind != PAZE_VAL_STRUCT) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } + if (type && type->kind == PAZE_TYPE_ARRAY && val.kind == PAZE_VAL_STRUCT) { + size_t elem_sz = type->base ? get_type_size(type->base) : sizeof(int); + if (elem_sz == 0) elem_sz = sizeof(int); + if (decl->data.var_decl.init_expr->kind == PAZE_NODE_INIT_LIST_EXPR) { + paze_ast_node_arr_t *elems = &decl->data.var_decl.init_expr->data.init_list_expr.elements; + size_t total = elem_sz * elems->len; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total > 0 ? total : 1); + memset(buf, 0, total > 0 ? total : 1); + for (size_t i = 0; i < elems->len; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + memcpy(buf + i * elem_sz, &v.u.int_val, elem_sz < sizeof(int64_t) ? elem_sz : sizeof(int64_t)); + } + paze_val_free(val); + val = paze_val_ptr(buf); + } + } + } else if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } else if (type && type->kind == PAZE_TYPE_ARRAY) { + if (type->base && (type->base->kind == PAZE_TYPE_STRUCT || type->base->kind == PAZE_TYPE_UNION)) { + type->base = resolve_struct_type(interp, type->base); + } + size_t elem_sz = type->base ? get_type_size(type->base) : 4; + size_t total = elem_sz * (size_t)(type->array_size > 0 ? type->array_size : 0); + if (total == 0) total = 1; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total); + memset(buf, 0, total); + val = paze_val_ptr(buf); + } else if (type && type->kind == PAZE_TYPE_POINTER) { + val = paze_val_ptr(NULL); + } else { + val = paze_val_int(0); + } + if (interp->frame_count > 0) { + set_local(interp, name, val, type); + } else { + set_global(interp, name, val, type); + } + } else if (node->data.for_stmt.init->kind == PAZE_NODE_DECL_GROUP) { + paze_ast_node_t *decl = node->data.for_stmt.init; + paze_ast_node_arr_t *gdecls = &decl->data.decl_group.decls; + for (size_t gi = 0; gi < gdecls->len; gi++) { + paze_ast_node_t *d = gdecls->data[gi]; + if (d && d->kind == PAZE_NODE_VAR_DECL) { + paze_str_t name = d->data.var_decl.name; + paze_type_t *type = d->data.var_decl.var_type; + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + type = resolve_struct_type(interp, type); + } + paze_val_t val; + if (d->data.var_decl.init_expr) { + val = eval_expr(interp, d->data.var_decl.init_expr); + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION) && + val.kind != PAZE_VAL_STRUCT) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } + if (type && type->kind == PAZE_TYPE_ARRAY && val.kind == PAZE_VAL_STRUCT) { + size_t elem_sz = type->base ? get_type_size(type->base) : sizeof(int); + if (elem_sz == 0) elem_sz = sizeof(int); + if (d->data.var_decl.init_expr->kind == PAZE_NODE_INIT_LIST_EXPR) { + paze_ast_node_arr_t *elems = &d->data.var_decl.init_expr->data.init_list_expr.elements; + size_t total = elem_sz * elems->len; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total > 0 ? total : 1); + memset(buf, 0, total > 0 ? total : 1); + for (size_t i = 0; i < elems->len; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + memcpy(buf + i * elem_sz, &v.u.int_val, elem_sz < sizeof(int64_t) ? elem_sz : sizeof(int64_t)); + } + paze_val_free(val); + val = paze_val_ptr(buf); + } + } + } else if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } else if (type && type->kind == PAZE_TYPE_ARRAY) { + if (type->base && (type->base->kind == PAZE_TYPE_STRUCT || type->base->kind == PAZE_TYPE_UNION)) { + type->base = resolve_struct_type(interp, type->base); + } + size_t elem_sz = type->base ? get_type_size(type->base) : 4; + size_t total = elem_sz * (size_t)(type->array_size > 0 ? type->array_size : 0); + if (total == 0) total = 1; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total); + memset(buf, 0, total); + val = paze_val_ptr(buf); + } else if (type && type->kind == PAZE_TYPE_POINTER) { + val = paze_val_ptr(NULL); + } else { + val = paze_val_int(0); + } + if (interp->frame_count > 0) { + set_local(interp, name, val, type); + } else { + set_global(interp, name, val, type); + } + } + } + } else { + eval_expr(interp, node->data.for_stmt.init); + } + } + int break_depth = 0; + while (true) { + if (node->data.for_stmt.condition) { + paze_val_t cond = eval_expr(interp, node->data.for_stmt.condition); + if (!paze_val_is_truthy(cond)) break; + } + if (interp->had_error) break; + exec_stmt(interp, node->data.for_stmt.body); + if (interp->signal == PAZE_SIGNAL_BREAK) { + interp->signal = PAZE_SIGNAL_NONE; + break; + } + if (interp->signal == PAZE_SIGNAL_CONTINUE) { + interp->signal = PAZE_SIGNAL_NONE; + } + if (interp->signal == PAZE_SIGNAL_RETURN) break; + if (interp->signal == PAZE_SIGNAL_GOTO) break; + if (interp->had_error) break; + if (node->data.for_stmt.increment) { + eval_expr(interp, node->data.for_stmt.increment); + } + break_depth++; + if (break_depth > 1000000) break; + } + break; + } + + case PAZE_NODE_SWITCH_STMT: { + paze_val_t switch_val = eval_expr(interp, node->data.switch_stmt.expr); + int64_t sv = paze_val_as_int(switch_val); + paze_ast_node_arr_t *cases = &node->data.switch_stmt.cases; + bool matched = false; + bool case_matched = false; + for (size_t i = 0; i < cases->len; i++) { + paze_ast_node_t *c = cases->data[i]; + if (!c || c->kind != PAZE_NODE_CASE_STMT) continue; + if (!matched && !c->data.case_stmt.is_default && c->data.case_stmt.value) { + paze_val_t cv = eval_expr(interp, c->data.case_stmt.value); + if (paze_val_as_int(cv) == sv) { + matched = true; + case_matched = true; + } + } + if (!matched && c->data.case_stmt.is_default) { + matched = true; + case_matched = true; + } + if (case_matched) { + paze_ast_node_arr_t *stmts = &c->data.case_stmt.stmts; + for (size_t j = 0; j < stmts->len; j++) { + exec_stmt(interp, stmts->data[j]); + if (interp->signal == PAZE_SIGNAL_BREAK) { + interp->signal = PAZE_SIGNAL_NONE; + return; + } + if (interp->signal == PAZE_SIGNAL_RETURN) return; + if (interp->signal == PAZE_SIGNAL_GOTO) return; + if (interp->had_error) return; + } + case_matched = false; + } + } + break; + } + + case PAZE_NODE_BREAK_STMT: + interp->signal = PAZE_SIGNAL_BREAK; + break; + + case PAZE_NODE_CONTINUE_STMT: + interp->signal = PAZE_SIGNAL_CONTINUE; + break; + + case PAZE_NODE_RETURN_STMT: + if (node->data.return_stmt.value) { + interp->return_value = eval_expr(interp, node->data.return_stmt.value); + } else { + interp->return_value = paze_val_int(0); + } + interp->signal = PAZE_SIGNAL_RETURN; + break; + + case PAZE_NODE_DECL_STMT: { + paze_ast_node_t *decl = node->data.decl_stmt.decl; + if (!decl) break; + if (decl->kind == PAZE_NODE_VAR_DECL) { + paze_str_t name = decl->data.var_decl.name; + paze_type_t *type = decl->data.var_decl.var_type; + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + type = resolve_struct_type(interp, type); + } + paze_val_t val; + if (decl->data.var_decl.init_expr) { + val = eval_expr(interp, decl->data.var_decl.init_expr); + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION) && + val.kind != PAZE_VAL_STRUCT) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } + if (type && type->kind == PAZE_TYPE_ARRAY && val.kind == PAZE_VAL_STRUCT) { + size_t elem_sz = type->base ? get_type_size(type->base) : sizeof(int); + if (elem_sz == 0) elem_sz = sizeof(int); + if (decl->data.var_decl.init_expr->kind == PAZE_NODE_INIT_LIST_EXPR) { + paze_ast_node_arr_t *elems = &decl->data.var_decl.init_expr->data.init_list_expr.elements; + size_t total = elem_sz * elems->len; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total > 0 ? total : 1); + memset(buf, 0, total > 0 ? total : 1); + for (size_t i = 0; i < elems->len; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + memcpy(buf + i * elem_sz, &v.u.int_val, elem_sz < sizeof(int64_t) ? elem_sz : sizeof(int64_t)); + } + paze_val_free(val); + val = paze_val_ptr(buf); + } + } + } else if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } else if (type && type->kind == PAZE_TYPE_ARRAY) { + if (type->base && (type->base->kind == PAZE_TYPE_STRUCT || type->base->kind == PAZE_TYPE_UNION)) { + type->base = resolve_struct_type(interp, type->base); + } + size_t elem_sz = type->base ? get_type_size(type->base) : 4; + size_t total = elem_sz * (size_t)(type->array_size > 0 ? type->array_size : 0); + if (total == 0) total = 1; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total); + memset(buf, 0, total); + val = paze_val_ptr(buf); + } else if (type && type->kind == PAZE_TYPE_POINTER) { + val = paze_val_ptr(NULL); + } else { + val = paze_val_int(0); + } + if (interp->frame_count > 0) { + set_local(interp, name, val, type); + } else { + set_global(interp, name, val, type); + } + } else if (decl->kind == PAZE_NODE_DECL_GROUP) { + paze_ast_node_arr_t *gdecls = &decl->data.decl_group.decls; + for (size_t gi = 0; gi < gdecls->len; gi++) { + paze_ast_node_t *d = gdecls->data[gi]; + if (d && d->kind == PAZE_NODE_VAR_DECL) { + paze_str_t name = d->data.var_decl.name; + paze_type_t *type = d->data.var_decl.var_type; + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + type = resolve_struct_type(interp, type); + } + paze_val_t val; + if (d->data.var_decl.init_expr) { + val = eval_expr(interp, d->data.var_decl.init_expr); + if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION) && + val.kind != PAZE_VAL_STRUCT) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } + if (type && type->kind == PAZE_TYPE_ARRAY && val.kind == PAZE_VAL_STRUCT) { + size_t elem_sz = type->base ? get_type_size(type->base) : sizeof(int); + if (elem_sz == 0) elem_sz = sizeof(int); + if (d->data.var_decl.init_expr->kind == PAZE_NODE_INIT_LIST_EXPR) { + paze_ast_node_arr_t *elems = &d->data.var_decl.init_expr->data.init_list_expr.elements; + size_t total = elem_sz * elems->len; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total > 0 ? total : 1); + memset(buf, 0, total > 0 ? total : 1); + for (size_t i = 0; i < elems->len; i++) { + paze_val_t v = eval_expr(interp, elems->data[i]); + memcpy(buf + i * elem_sz, &v.u.int_val, elem_sz < sizeof(int64_t) ? elem_sz : sizeof(int64_t)); + } + paze_val_free(val); + val = paze_val_ptr(buf); + } + } + } else if (type && (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION)) { + size_t sz = get_type_size(type); + uint8_t *buf = (uint8_t *)heap_alloc(interp, sz > 0 ? sz : 1); + memset(buf, 0, sz > 0 ? sz : 1); + val = paze_val_struct_typed(buf, sz > 0 ? sz : 0, type); + } else if (type && type->kind == PAZE_TYPE_ARRAY) { + if (type->base && (type->base->kind == PAZE_TYPE_STRUCT || type->base->kind == PAZE_TYPE_UNION)) { + type->base = resolve_struct_type(interp, type->base); + } + size_t elem_sz = type->base ? get_type_size(type->base) : 4; + size_t total = elem_sz * (size_t)(type->array_size > 0 ? type->array_size : 0); + if (total == 0) total = 1; + uint8_t *buf = (uint8_t *)heap_alloc(interp, total); + memset(buf, 0, total); + val = paze_val_ptr(buf); + } else if (type && type->kind == PAZE_TYPE_POINTER) { + val = paze_val_ptr(NULL); + } else { + val = paze_val_int(0); + } + if (interp->frame_count > 0) { + set_local(interp, name, val, type); + } else { + set_global(interp, name, val, type); + } + } + } + } + break; + } + + case PAZE_NODE_GOTO_STMT: + interp->signal = PAZE_SIGNAL_GOTO; + interp->goto_label = node->data.goto_stmt.label; + break; + + case PAZE_NODE_LABEL_STMT: + add_label(interp, node->data.label_stmt.label, node->data.label_stmt.stmt); + if (node->data.label_stmt.stmt) + exec_stmt(interp, node->data.label_stmt.stmt); + break; + + default: + if (paze_ast_is_expression(node->kind)) { + eval_expr(interp, node); + } + break; + } +} + +/* ======================================================================== + * Public API Implementation + * ======================================================================== */ + +paze_interpreter_t *paze_interpreter_create(paze_ast_node_t *unit, + paze_diagnostics_t *diag) { + paze_interpreter_t *interp = (paze_interpreter_t *)calloc(1, sizeof(paze_interpreter_t)); + if (!interp) return NULL; + interp->unit = unit; + interp->diag = diag; + interp->is_running = false; + interp->is_finished = false; + interp->exit_code = 0; + interp->signal = PAZE_SIGNAL_NONE; + interp->had_error = false; + interp->arena = paze_arena_create(); + build_function_table(interp); + register_builtins(interp); + init_globals(interp); + return interp; +} + +void paze_interpreter_destroy(paze_interpreter_t *interp) { + if (!interp) return; + for (int i = 0; i < interp->func_count; i++) { + if (interp->functions[i].is_builtin) { + free((void *)interp->functions[i].name.data); + } + } + free(interp->functions); + for (int i = 0; i < interp->global_count; i++) { + paze_val_free(interp->globals[i].value); + } + free(interp->globals); + for (int i = 0; i < interp->frame_count; i++) { + paze_frame_t *f = &interp->frames[i]; + for (int j = 0; j < f->local_count; j++) { + paze_val_free(f->local_vals[j]); + free(f->local_names[j]); + } + free(f->local_names); + free(f->local_vals); + } + free(interp->frames); + free(interp->breakpoints); + for (int i = 0; i < interp->heap_entry_count; i++) { + if (interp->heap_entries[i].ptr) { + free(interp->heap_entries[i].ptr); + } + } + free(interp->heap_entries); + free(interp->labels); + if (interp->arena) paze_arena_destroy(interp->arena); + free(interp); +} + +static int run_program(struct paze_interpreter_t *interp) { + if (!interp->unit || interp->had_error) return interp->exit_code; + int main_idx = find_function(interp, paze_str_from_cstr("main")); + if (main_idx < 0) { + if (interp->diag) { + paze_diagnostics_error(interp->diag, PAZE_LOC_EMPTY, + "no main function found"); + } + interp->had_error = true; + return -1; + } + interp->is_running = true; + interp->is_finished = false; + interp->signal = PAZE_SIGNAL_NONE; + interp->exit_code = 0; + interp->frame_count = 0; + interp->current_line = 0; + + paze_val_t result = call_function(interp, interp->functions[main_idx].decl, NULL, 0); + interp->exit_code = paze_val_as_int(result); + interp->is_finished = true; + interp->is_running = false; + return interp->exit_code; +} + +int paze_interpreter_run(paze_interpreter_t *interp) { + if (!interp) return -1; + return run_program(interp); +} + +int paze_interpreter_continue(paze_interpreter_t *interp) { + if (!interp) return -1; + interp->is_running = true; + interp->step_pending = false; + interp->signal = PAZE_SIGNAL_NONE; + return run_program(interp); +} + +int paze_interpreter_step_into(paze_interpreter_t *interp) { + if (!interp) return -1; + interp->is_running = true; + interp->single_step = true; + interp->step_pending = false; + return run_program(interp); +} + +int paze_interpreter_step_over(paze_interpreter_t *interp) { + if (!interp) return -1; + interp->is_running = true; + interp->step_pending = true; + interp->step_target_depth = interp->frame_count; + interp->single_step = false; + return run_program(interp); +} + +bool paze_interpreter_set_breakpoint(paze_interpreter_t *interp, int line) { + if (!interp || line <= 0) return false; + for (int i = 0; i < interp->breakpoint_count; i++) { + if (interp->breakpoints[i] == line) return true; + } + if (interp->breakpoint_count >= interp->breakpoint_cap) { + interp->breakpoint_cap = interp->breakpoint_cap ? interp->breakpoint_cap * 2 : 16; + interp->breakpoints = (int *)realloc(interp->breakpoints, + interp->breakpoint_cap * sizeof(int)); + } + interp->breakpoints[interp->breakpoint_count++] = line; + return true; +} + +bool paze_interpreter_clear_breakpoint(paze_interpreter_t *interp, int line) { + if (!interp) return false; + for (int i = 0; i < interp->breakpoint_count; i++) { + if (interp->breakpoints[i] == line) { + for (int j = i; j < interp->breakpoint_count - 1; j++) + interp->breakpoints[j] = interp->breakpoints[j + 1]; + interp->breakpoint_count--; + return true; + } + } + return false; +} + +int *paze_interpreter_get_breakpoints(paze_interpreter_t *interp, int *count_out) { + if (!interp || !count_out) return NULL; + *count_out = interp->breakpoint_count; + if (interp->breakpoint_count == 0) return NULL; + int *result = (int *)malloc((size_t)interp->breakpoint_count * sizeof(int)); + if (!result) return NULL; + memcpy(result, interp->breakpoints, (size_t)interp->breakpoint_count * sizeof(int)); + return result; +} + +paze_var_info_t *paze_interpreter_get_locals(paze_interpreter_t *interp, int *count_out) { + if (!interp || !count_out) return NULL; + *count_out = 0; + if (interp->frame_count <= 0) return NULL; + paze_frame_t *f = &interp->frames[interp->frame_count - 1]; + if (f->local_count <= 0) return NULL; + paze_var_info_t *result = (paze_var_info_t *)malloc((size_t)f->local_count * sizeof(paze_var_info_t)); + if (!result) return NULL; + int count = 0; + for (int i = 0; i < f->local_count; i++) { + result[count].name = f->local_names[i]; + result[count].value_str = val_to_str(f->local_vals[i]); + result[count].is_constant = false; + count++; + } + *count_out = count; + return result; +} + +paze_var_info_t *paze_interpreter_get_globals(paze_interpreter_t *interp, int *count_out) { + if (!interp || !count_out) return NULL; + *count_out = 0; + if (interp->global_count <= 0) return NULL; + paze_var_info_t *result = (paze_var_info_t *)malloc((size_t)interp->global_count * sizeof(paze_var_info_t)); + if (!result) return NULL; + int count = 0; + for (int i = 0; i < interp->global_count; i++) { + char *name_str = (char *)malloc(interp->globals[i].name.len + 1); + if (name_str) { + if (interp->globals[i].name.len > 0) + memcpy(name_str, interp->globals[i].name.data, interp->globals[i].name.len); + name_str[interp->globals[i].name.len] = '\0'; + } + result[count].name = name_str; + result[count].value_str = val_to_str(interp->globals[i].value); + result[count].is_constant = interp->globals[i].is_constant; + count++; + } + *count_out = count; + return result; +} + +int paze_interpreter_current_line(paze_interpreter_t *interp) { + if (!interp) return 0; + return interp->current_line; +} + +paze_str_t paze_interpreter_current_func(paze_interpreter_t *interp) { + if (!interp || interp->frame_count <= 0) return PAZE_STR_EMPTY; + paze_frame_t *f = &interp->frames[interp->frame_count - 1]; + if (f->func_decl && f->func_decl->kind == PAZE_NODE_FUNCTION_DECL) { + return f->func_decl->data.function_decl.name; + } + if (interp->frame_count >= 2) { + paze_frame_t *pf = &interp->frames[interp->frame_count - 2]; + if (pf->func_decl && pf->func_decl->kind == PAZE_NODE_FUNCTION_DECL) { + return pf->func_decl->data.function_decl.name; + } + } + return PAZE_STR_EMPTY; +} + +bool paze_interpreter_is_finished(paze_interpreter_t *interp) { + if (!interp) return true; + return interp->is_finished; +} + +int paze_interpreter_exit_code(paze_interpreter_t *interp) { + if (!interp) return -1; + return interp->exit_code; +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_lexer.c b/src/PazeE.los4/src/paze_lexer.c new file mode 100644 index 0000000..e9d2225 --- /dev/null +++ b/src/PazeE.los4/src/paze_lexer.c @@ -0,0 +1,655 @@ +#include "paze_lexer.h" + +#include +#include +#include + +/* ======================================================================== + * Internal Helpers + * ======================================================================== */ + +static char peek_char(paze_lexer_t *lex) +{ + if (lex->pos >= lex->source_len) return '\0'; + return lex->source[lex->pos]; +} + +static char peek_next_char(paze_lexer_t *lex) +{ + if (lex->pos + 1 >= lex->source_len) return '\0'; + return lex->source[lex->pos + 1]; +} + +static char next_char(paze_lexer_t *lex) +{ + if (lex->pos >= lex->source_len) return '\0'; + char c = lex->source[lex->pos]; + lex->pos++; + if (c == '\n') { + lex->line++; + lex->col = 1; + } else { + lex->col++; + } + return c; +} + +static bool at_end(paze_lexer_t *lex) +{ + return lex->pos >= lex->source_len; +} + +static paze_loc_t make_loc(paze_lexer_t *lex, int line, int col) +{ + paze_loc_t loc; + loc.file = lex->source_file; + loc.line = line; + loc.col = col; + return loc; +} + +static paze_token_t make_token(paze_lexer_t *lex, paze_tk_t kind, + const char *start, const char *end, + paze_loc_t loc) +{ + paze_token_t tk; + memset(&tk, 0, sizeof(tk)); + tk.kind = kind; + tk.text.data = start; + tk.text.len = (size_t)(end - start); + tk.loc = loc; + tk.at_line_start = lex->at_line_start; + lex->at_line_start = false; + return tk; +} + +static void skip_whitespace_and_comments(paze_lexer_t *lex, paze_diagnostics_t *diag) +{ + while (!at_end(lex)) { + char c = peek_char(lex); + + switch (c) { + case ' ': + case '\t': + case '\r': + next_char(lex); + break; + case '\n': + next_char(lex); + lex->at_line_start = true; + break; + case '/': + if (peek_next_char(lex) == '/') { + next_char(lex); + next_char(lex); + while (!at_end(lex) && peek_char(lex) != '\n') { + next_char(lex); + } + break; + } + if (peek_next_char(lex) == '*') { + int start_line = lex->line; + int start_col = lex->col; + next_char(lex); + next_char(lex); + while (!at_end(lex)) { + if (peek_char(lex) == '*' && peek_next_char(lex) == '/') { + next_char(lex); + next_char(lex); + break; + } + next_char(lex); + } + if (at_end(lex)) { + paze_loc_t loc = make_loc(lex, start_line, start_col); + paze_diagnostics_error(diag, loc, + "unterminated block comment"); + } + break; + } + return; + default: + return; + } + } +} + +/* ======================================================================== + * Number Literals + * ======================================================================== */ + +static bool is_digit(char c) { return c >= '0' && c <= '9'; } +static bool is_hex_digit(char c) +{ + return (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); +} +static bool is_octal_digit(char c) { return c >= '0' && c <= '7'; } + +static paze_token_t lex_number(paze_lexer_t *lex, paze_diagnostics_t *diag) +{ + int start_line = lex->line; + int start_col = lex->col; + const char *start = lex->source + lex->pos; + paze_loc_t loc = make_loc(lex, start_line, start_col); + + long value = 0; + bool is_hex = false; + bool is_octal = false; + + if (peek_char(lex) == '0') { + char next = peek_next_char(lex); + if (next == 'x' || next == 'X') { + is_hex = true; + next_char(lex); + next_char(lex); + if (!is_hex_digit(peek_char(lex))) { + paze_diagnostics_error(diag, loc, + "invalid hexadecimal literal"); + } + while (!at_end(lex) && is_hex_digit(peek_char(lex))) { + char c = next_char(lex); + int digit; + if (c >= '0' && c <= '9') digit = c - '0'; + else if (c >= 'a' && c <= 'f') digit = c - 'a' + 10; + else digit = c - 'A' + 10; + value = value * 16 + digit; + } + } else if (is_octal_digit(next)) { + is_octal = true; + next_char(lex); + value = 0; + while (!at_end(lex) && is_octal_digit(peek_char(lex))) { + char c = next_char(lex); + value = value * 8 + (c - '0'); + } + if (is_digit(peek_char(lex)) && !is_hex_digit(peek_char(lex))) { + /* decimal digits 8 and 9 are invalid in octal, + * but in C this would be a decimal literal starting + * with 0. We'll treat 0 followed by 8/9 as decimal. */ + } + } + } + + if (!is_hex && !is_octal) { + while (!at_end(lex) && is_digit(peek_char(lex))) { + char c = next_char(lex); + value = value * 10 + (c - '0'); + } + } + + /* Optional suffix: L, LL, U, UL, ULL */ + while (!at_end(lex)) { + char c = peek_char(lex); + if (c == 'l' || c == 'L' || c == 'u' || c == 'U') { + next_char(lex); + } else { + break; + } + } + + const char *end = lex->source + lex->pos; + paze_token_t tk = make_token(lex, PAZE_TK_INT_LITERAL, start, end, loc); + tk.int_value = value; + return tk; +} + +/* ======================================================================== + * Character Literals + * ======================================================================== */ + +static int parse_escape_char(paze_lexer_t *lex, paze_diagnostics_t *diag, paze_loc_t loc) +{ + next_char(lex); /* skip backslash */ + if (at_end(lex)) { + paze_diagnostics_error(diag, loc, "unexpected end of input in escape sequence"); + return '\\'; + } + char c = next_char(lex); + switch (c) { + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; + case '\\': return '\\'; + case '\'': return '\''; + case '\"': return '\"'; + case '0': return '\0'; + case 'a': return '\a'; + case 'b': return '\b'; + case 'f': return '\f'; + case 'v': return '\v'; + case 'x': { + int val = 0; + bool found = false; + while (!at_end(lex) && is_hex_digit(peek_char(lex))) { + found = true; + char h = next_char(lex); + int digit; + if (h >= '0' && h <= '9') digit = h - '0'; + else if (h >= 'a' && h <= 'f') digit = h - 'a' + 10; + else digit = h - 'A' + 10; + val = val * 16 + digit; + } + if (!found) { + paze_diagnostics_error(diag, loc, "invalid hex escape sequence"); + } + return val; + } + default: + if (is_octal_digit(c)) { + int val = c - '0'; + for (int i = 0; i < 2 && !at_end(lex) && is_octal_digit(peek_char(lex)); i++) { + char o = next_char(lex); + val = val * 8 + (o - '0'); + } + return val; + } + paze_diagnostics_error(diag, loc, "unknown escape sequence '\\%c'", c); + return c; + } +} + +static paze_token_t lex_char_literal(paze_lexer_t *lex, paze_diagnostics_t *diag) +{ + int start_line = lex->line; + int start_col = lex->col; + const char *start = lex->source + lex->pos; + paze_loc_t loc = make_loc(lex, start_line, start_col); + + next_char(lex); /* skip opening quote */ + + int value; + if (peek_char(lex) == '\\') { + value = parse_escape_char(lex, diag, loc); + } else if (at_end(lex) || peek_char(lex) == '\'') { + paze_diagnostics_error(diag, loc, "empty character literal"); + value = 0; + } else { + value = next_char(lex); + } + + if (at_end(lex) || peek_char(lex) != '\'') { + paze_diagnostics_error(diag, loc, "unterminated character literal"); + } else { + next_char(lex); /* skip closing quote */ + } + + const char *end = lex->source + lex->pos; + paze_token_t tk = make_token(lex, PAZE_TK_CHAR_LITERAL, start, end, loc); + tk.int_value = value; + return tk; +} + +/* ======================================================================== + * String Literals + * ======================================================================== */ + +static char *decode_string(paze_lexer_t *lex, paze_diagnostics_t *diag, + const char *start, const char *end, paze_loc_t loc) +{ + (void)lex; + size_t len = (size_t)(end - start); + char *buf = (char *)paze_malloc(len + 1); + size_t out = 0; + + for (const char *p = start; p < end; ) { + if (*p == '\\') { + p++; + if (p >= end) { + buf[out++] = '\\'; + break; + } + char c = *p++; + switch (c) { + case 'n': buf[out++] = '\n'; break; + case 't': buf[out++] = '\t'; break; + case 'r': buf[out++] = '\r'; break; + case '\\': buf[out++] = '\\'; break; + case '\'': buf[out++] = '\''; break; + case '\"': buf[out++] = '\"'; break; + case '0': buf[out++] = '\0'; break; + case 'a': buf[out++] = '\a'; break; + case 'b': buf[out++] = '\b'; break; + case 'f': buf[out++] = '\f'; break; + case 'v': buf[out++] = '\v'; break; + case 'x': { + int val = 0; + bool found = false; + while (p < end && is_hex_digit(*p)) { + found = true; + char h = *p++; + int digit; + if (h >= '0' && h <= '9') digit = h - '0'; + else if (h >= 'a' && h <= 'f') digit = h - 'a' + 10; + else digit = h - 'A' + 10; + val = val * 16 + digit; + } + if (!found) { + paze_diagnostics_error(diag, loc, + "invalid hex escape in string literal"); + } + buf[out++] = (char)val; + break; + } + default: + if (is_octal_digit(c)) { + int val = c - '0'; + for (int i = 0; i < 2 && p < end && is_octal_digit(*p); i++) { + char o = *p++; + val = val * 8 + (o - '0'); + } + buf[out++] = (char)val; + } else { + paze_diagnostics_error(diag, loc, + "unknown escape sequence '\\%c'", c); + buf[out++] = c; + } + break; + } + } else { + buf[out++] = *p++; + } + } + + buf[out] = '\0'; + return buf; +} + +static paze_token_t lex_string_literal(paze_lexer_t *lex, paze_diagnostics_t *diag) +{ + int start_line = lex->line; + int start_col = lex->col; + const char *start = lex->source + lex->pos; + paze_loc_t loc = make_loc(lex, start_line, start_col); + + next_char(lex); /* skip opening quote */ + + while (!at_end(lex) && peek_char(lex) != '"') { + if (peek_char(lex) == '\\') { + next_char(lex); + if (!at_end(lex)) next_char(lex); + } else { + next_char(lex); + } + } + + if (at_end(lex)) { + paze_diagnostics_error(diag, loc, "unterminated string literal"); + } else { + next_char(lex); /* skip closing quote */ + } + + const char *end = lex->source + lex->pos; + + /* Decode the string content (between quotes) */ + const char *content_start = start + 1; + const char *content_end = end - 1; + char *decoded = decode_string(lex, diag, content_start, content_end, loc); + + paze_token_t tk = make_token(lex, PAZE_TK_STRING_LITERAL, start, end, loc); + tk.str_value.data = decoded; + tk.str_value.len = strlen(decoded); + return tk; +} + +/* ======================================================================== + * Identifiers & Keywords + * ======================================================================== */ + +static bool is_ident_start(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +} + +static bool is_ident_part(char c) +{ + return is_ident_start(c) || is_digit(c); +} + +static paze_token_t lex_identifier(paze_lexer_t *lex, paze_diagnostics_t *diag) +{ + (void)diag; + int start_line = lex->line; + int start_col = lex->col; + const char *start = lex->source + lex->pos; + paze_loc_t loc = make_loc(lex, start_line, start_col); + + while (!at_end(lex) && is_ident_part(peek_char(lex))) { + next_char(lex); + } + + const char *end = lex->source + lex->pos; + paze_str_t ident; + ident.data = start; + ident.len = (size_t)(end - start); + + paze_tk_t kind = paze_keyword_lookup(ident); + paze_token_t tk = make_token(lex, kind, start, end, loc); + return tk; +} + +/* ======================================================================== + * Operators & Punctuation + * ======================================================================== */ + +static paze_token_t lex_operator_or_punct(paze_lexer_t *lex, paze_diagnostics_t *diag) +{ + (void)diag; + int start_line = lex->line; + int start_col = lex->col; + const char *start = lex->source + lex->pos; + paze_loc_t loc = make_loc(lex, start_line, start_col); + + char c = next_char(lex); + + switch (c) { + case '+': + if (peek_char(lex) == '+') { next_char(lex); return make_token(lex, PAZE_TK_PLUS_PLUS, start, lex->source + lex->pos, loc); } + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_PLUS_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_PLUS, start, lex->source + lex->pos, loc); + + case '-': + if (peek_char(lex) == '-') { next_char(lex); return make_token(lex, PAZE_TK_MINUS_MINUS, start, lex->source + lex->pos, loc); } + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_MINUS_ASSIGN, start, lex->source + lex->pos, loc); } + if (peek_char(lex) == '>') { next_char(lex); return make_token(lex, PAZE_TK_ARROW, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_MINUS, start, lex->source + lex->pos, loc); + + case '*': + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_STAR_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_STAR, start, lex->source + lex->pos, loc); + + case '/': + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_SLASH_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_SLASH, start, lex->source + lex->pos, loc); + + case '%': + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_PERCENT_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_PERCENT, start, lex->source + lex->pos, loc); + + case '=': + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_EQ, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_ASSIGN, start, lex->source + lex->pos, loc); + + case '!': + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_NOT_EQ, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_NOT, start, lex->source + lex->pos, loc); + + case '<': + if (peek_char(lex) == '<') { + next_char(lex); + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_SHL_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_SHL, start, lex->source + lex->pos, loc); + } + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_LE, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_LT, start, lex->source + lex->pos, loc); + + case '>': + if (peek_char(lex) == '>') { + next_char(lex); + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_SHR_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_SHR, start, lex->source + lex->pos, loc); + } + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_GE, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_GT, start, lex->source + lex->pos, loc); + + case '&': + if (peek_char(lex) == '&') { next_char(lex); return make_token(lex, PAZE_TK_AND_AND, start, lex->source + lex->pos, loc); } + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_AND_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_AMP, start, lex->source + lex->pos, loc); + + case '|': + if (peek_char(lex) == '|') { next_char(lex); return make_token(lex, PAZE_TK_OR_OR, start, lex->source + lex->pos, loc); } + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_OR_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_PIPE, start, lex->source + lex->pos, loc); + + case '^': + if (peek_char(lex) == '=') { next_char(lex); return make_token(lex, PAZE_TK_XOR_ASSIGN, start, lex->source + lex->pos, loc); } + return make_token(lex, PAZE_TK_CARET, start, lex->source + lex->pos, loc); + + case '~': + return make_token(lex, PAZE_TK_TILDE, start, lex->source + lex->pos, loc); + + case '?': + return make_token(lex, PAZE_TK_QUESTION, start, lex->source + lex->pos, loc); + + case ':': + return make_token(lex, PAZE_TK_COLON, start, lex->source + lex->pos, loc); + + case ';': + return make_token(lex, PAZE_TK_SEMICOLON, start, lex->source + lex->pos, loc); + + case ',': + return make_token(lex, PAZE_TK_COMMA, start, lex->source + lex->pos, loc); + + case '.': + return make_token(lex, PAZE_TK_DOT, start, lex->source + lex->pos, loc); + + case '(': + return make_token(lex, PAZE_TK_LPAREN, start, lex->source + lex->pos, loc); + + case ')': + return make_token(lex, PAZE_TK_RPAREN, start, lex->source + lex->pos, loc); + + case '[': + return make_token(lex, PAZE_TK_LBRACKET, start, lex->source + lex->pos, loc); + + case ']': + return make_token(lex, PAZE_TK_RBRACKET, start, lex->source + lex->pos, loc); + + case '{': + return make_token(lex, PAZE_TK_LBRACE, start, lex->source + lex->pos, loc); + + case '}': + return make_token(lex, PAZE_TK_RBRACE, start, lex->source + lex->pos, loc); + + case '#': + { + return make_token(lex, PAZE_TK_HASH, start, lex->source + lex->pos, loc); + } + + default: + return make_token(lex, PAZE_TK_UNKNOWN, start, lex->source + lex->pos, loc); + } +} + +/* ======================================================================== + * Public API + * ======================================================================== */ + +paze_lexer_t *paze_lexer_create(const char *source, const char *source_file) +{ + paze_lexer_t *lex = (paze_lexer_t *)paze_calloc(1, sizeof(paze_lexer_t)); + if (!source) return NULL; + lex->source = source; + lex->source_len = strlen(source); + lex->pos = 0; + lex->line = 1; + lex->col = 1; + lex->source_file = source_file; + lex->has_peeked = false; + lex->at_line_start = true; + return lex; +} + +void paze_lexer_destroy(paze_lexer_t *lexer) +{ + if (!lexer) return; + free(lexer); +} + +paze_token_t paze_lexer_next_token(paze_lexer_t *lexer, paze_diagnostics_t *diag) +{ + if (!lexer) { + paze_token_t tk; + memset(&tk, 0, sizeof(tk)); + tk.kind = PAZE_TK_EOF; + return tk; + } + + if (lexer->has_peeked) { + lexer->has_peeked = false; + return lexer->peeked_token; + } + + skip_whitespace_and_comments(lexer, diag); + + if (at_end(lexer)) { + paze_token_t tk; + memset(&tk, 0, sizeof(tk)); + tk.kind = PAZE_TK_EOF; + tk.loc = make_loc(lexer, lexer->line, lexer->col); + tk.at_line_start = lexer->at_line_start; + lexer->at_line_start = false; + return tk; + } + + char c = peek_char(lexer); + + if (is_digit(c)) { + return lex_number(lexer, diag); + } + + if (is_ident_start(c)) { + return lex_identifier(lexer, diag); + } + + if (c == '\'') { + return lex_char_literal(lexer, diag); + } + + if (c == '"') { + return lex_string_literal(lexer, diag); + } + + return lex_operator_or_punct(lexer, diag); +} + +paze_token_t paze_lexer_peek_token(paze_lexer_t *lexer, paze_diagnostics_t *diag) +{ + if (!lexer) { + paze_token_t tk; + memset(&tk, 0, sizeof(tk)); + tk.kind = PAZE_TK_EOF; + return tk; + } + if (!lexer->has_peeked) { + lexer->peeked_token = paze_lexer_next_token(lexer, diag); + lexer->has_peeked = true; + } + return lexer->peeked_token; +} + +paze_token_arr_t paze_lexer_tokenize(paze_lexer_t *lexer, paze_diagnostics_t *diag) +{ + paze_token_arr_t arr; + paze_token_arr_t_init(&arr); + + while (true) { + paze_token_t tk = paze_lexer_next_token(lexer, diag); + paze_token_arr_t_push(&arr, tk); + if (tk.kind == PAZE_TK_EOF) break; + } + + return arr; +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_libc.c b/src/PazeE.los4/src/paze_libc.c new file mode 100644 index 0000000..a2d66de --- /dev/null +++ b/src/PazeE.los4/src/paze_libc.c @@ -0,0 +1,82 @@ +#include "paze_libc.h" +#include +#include +#include +#include + +paze_int64_t paze_builtin_printf(const char *fmt, ...) +{ + if (!fmt) return -1; + + va_list ap; + va_start(ap, fmt); + int result = vprintf(fmt, ap); + va_end(ap); + return result; +} + +paze_int64_t paze_builtin_puts(const char *str) +{ + if (!str) return -1; + int result = puts(str); + return result; +} + +int paze_builtin_getchar(void) +{ + return getchar(); +} + +void *paze_builtin_malloc(size_t size) +{ + return malloc(size); +} + +void paze_builtin_free(void *ptr) +{ + free(ptr); +} + +void *paze_builtin_memcpy(void *dst, const void *src, size_t n) +{ + return memcpy(dst, src, n); +} + +void paze_builtin_memset(void *s, int c, size_t n) +{ + memset(s, c, n); +} + +size_t paze_builtin_strlen(const char *s) +{ + if (!s) return 0; + return strlen(s); +} + +char *paze_builtin_strcpy(char *dst, const char *src) +{ + if (!dst || !src) return dst; + return strcpy(dst, src); +} + +char *paze_builtin_strcat(char *dst, const char *src) +{ + if (!dst || !src) return dst; + return strcat(dst, src); +} + +int paze_builtin_strcmp(const char *a, const char *b) +{ + if (!a || !b) return a == b ? 0 : (a ? 1 : -1); + return strcmp(a, b); +} + +int paze_builtin_sprintf(char *buf, const char *fmt, ...) +{ + if (!buf || !fmt) return -1; + va_list ap; + va_start(ap, fmt); + int result = vsprintf(buf, fmt, ap); + va_end(ap); + return result; +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_libc_decls.c b/src/PazeE.los4/src/paze_libc_decls.c new file mode 100644 index 0000000..558eb1a --- /dev/null +++ b/src/PazeE.los4/src/paze_libc_decls.c @@ -0,0 +1,70 @@ +#include "../include/paze_libc_decls.h" +#include + +/* ======================================================================== + * Variadic functions (must zero AL before call on System V ABI) + * ======================================================================== */ +static const char *const kVariadic[] = { + "printf", "fprintf", "sprintf", "scanf", "sscanf", NULL +}; + +bool paze_libc_is_variadic(const char *func) +{ + if (!func) return false; + for (int i = 0; kVariadic[i]; i++) { + if (strcmp(kVariadic[i], func) == 0) return true; + } + return false; +} + +/* ======================================================================== + * Windows DLL mapping + * ======================================================================== */ + +static bool name_in_list(const char *func, const char *const *list) +{ + for (int i = 0; list[i]; i++) { + if (strcmp(list[i], func) == 0) return true; + } + return false; +} + +static const char *const kKernel32[] = { + "ExitProcess", "GetCommandLineA", "GetStdHandle", + "WriteConsoleA", "WriteFile", NULL +}; + +static const char *const kUser32[] = { + "RegisterClassA", "CreateWindowExA", "ShowWindow", "UpdateWindow", + "GetMessageA", "PeekMessageA", "TranslateMessage", "DispatchMessageA", + "DefWindowProcA", "DestroyWindow", "PostQuitMessage", + "BeginPaint", "EndPaint", "GetDC", "ReleaseDC", "InvalidateRect", + "SetWindowTextA", NULL +}; + +static const char *const kGdi32[] = { + "SetTextColor", "SetBkColor", "TextOutA", "CreateSolidBrush", NULL +}; + +static const char *const kDwmapi[] = { + "DwmSetWindowAttribute", NULL +}; + +const char *paze_libc_dll_of(const char *func) +{ + if (!func) return "msvcrt.dll"; + if (name_in_list(func, kKernel32)) return "kernel32.dll"; + if (name_in_list(func, kUser32)) return "user32.dll"; + if (name_in_list(func, kGdi32)) return "gdi32.dll"; + if (name_in_list(func, kDwmapi)) return "dwmapi.dll"; + return "msvcrt.dll"; +} + +bool paze_libc_is_gui_import(const char *func) +{ + if (!func) return false; + const char *dll = paze_libc_dll_of(func); + return strcmp(dll, "user32.dll") == 0 || + strcmp(dll, "gdi32.dll") == 0 || + strcmp(dll, "dwmapi.dll") == 0; +} diff --git a/src/PazeE.los4/src/paze_los4_runtime.c b/src/PazeE.los4/src/paze_los4_runtime.c new file mode 100644 index 0000000..be302f4 --- /dev/null +++ b/src/PazeE.los4/src/paze_los4_runtime.c @@ -0,0 +1,1651 @@ +#include "../include/paze_los4_runtime.h" +#include "../include/paze_x64_emitter.h" +#include +#include +#include + +/* ======================================================================== + * Los4 Static Runtime — x86-64 machine code generator + * + * Each function is emitted as raw bytes and appended to the ObjectImage's + * .text section. Functions use the System V AMD64 ABI and LeonOS 4 + * system calls (int 0x80 = 0xCD 0x80, register conventions same as + * Linux x86-64: rax=sysno, rdi/rsi/rdx/r10/r8/r9=args). + * ======================================================================== */ + +/* x86-64 register indices in the emitter (from paze_x64_emitter.h): + * RAX=0, RCX=1, RDX=2, RBX=3, RSP=4, RBP=5, RSI=6, RDI=7 + * R8=8, R9=9, R10=10, R11=11, R12=12, R13=13, R14=14, R15=15 + */ + +/* Runtime function registry: name → offset within runtime block. */ +typedef struct { + const char *name; + int offset; /* filled in during generation */ +} rt_func_entry_t; + +static rt_func_entry_t g_rt_funcs[32]; +static int g_rt_func_count = 0; + +static void rt_register(const char *name, int offset) +{ + if (g_rt_func_count >= 32) return; + g_rt_funcs[g_rt_func_count].name = name; + g_rt_funcs[g_rt_func_count].offset = offset; + g_rt_func_count++; +} + +int paze_los4_runtime_find_offset(const char *name) +{ + for (int i = 0; i < g_rt_func_count; i++) { + if (strcmp(g_rt_funcs[i].name, name) == 0) + return g_rt_funcs[i].offset; + } + return -1; +} + +size_t paze_los4_runtime_func_count(void) { return (size_t)g_rt_func_count; } + +const char *paze_los4_runtime_func_name(size_t i) +{ + if (i >= (size_t)g_rt_func_count) return NULL; + return g_rt_funcs[i].name; +} + +int paze_los4_runtime_func_offset(size_t i) +{ + if (i >= (size_t)g_rt_func_count) return -1; + return g_rt_funcs[i].offset; +} + +/* ======================================================================== + * Byte emit helpers — write raw x86-64 bytes to a growable buffer. + * The buffer is later copied into the ObjectImage's .text section. + * ======================================================================== */ + +typedef struct { + uint8_t *data; + size_t len; + size_t cap; +} rt_buf_t; + +static void rt_init(rt_buf_t *b) { b->data = NULL; b->len = 0; b->cap = 0; } + +static void rt_emit(rt_buf_t *b, uint8_t byte) +{ + if (b->len >= b->cap) { + b->cap = b->cap ? b->cap * 2 : 4096; + b->data = (uint8_t *)realloc(b->data, b->cap); + } + b->data[b->len++] = byte; +} + +static void rt_emit_n(rt_buf_t *b, const uint8_t *bytes, size_t n) +{ + for (size_t i = 0; i < n; i++) rt_emit(b, bytes[i]); +} + +static int rt_pos(rt_buf_t *b) { return (int)b->len; } + +/* ---- Instruction emitters (raw byte sequences) ---- */ + +/* push rbp ; mov rbp, rsp */ +static void rt_prologue(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x55, 0x48, 0x89, 0xE5}, 4); +} + +/* leave ; ret */ +static void rt_epilogue(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0xC9, 0xC3}, 2); +} + +/* ret */ +static void rt_ret(rt_buf_t *b) { rt_emit(b, 0xC3); } + +/* int 0x80 */ +static void rt_syscall(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0xCD, 0x80}, 2); +} + +/* mov eax, imm32 */ +static void rt_mov_eax_imm32(rt_buf_t *b, uint32_t val) +{ + rt_emit(b, 0xB8); + rt_emit(b, (uint8_t)(val)); + rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); + rt_emit(b, (uint8_t)(val >> 24)); +} + +/* mov edi, imm32 */ +static void rt_mov_edi_imm32(rt_buf_t *b, uint32_t val) +{ + rt_emit_n(b, (const uint8_t[]){0xBF}, 1); + rt_emit(b, (uint8_t)(val)); + rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); + rt_emit(b, (uint8_t)(val >> 24)); +} + +/* mov rdi, imm64 (REX.W + mov) */ +static void rt_mov_rdi_imm64(rt_buf_t *b, uint64_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xBF}, 2); + for (int i = 0; i < 8; i++) + rt_emit(b, (uint8_t)(val >> (i * 8))); +} + +/* mov rax, imm64 */ +static void rt_mov_rax_imm64(rt_buf_t *b, uint64_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xB8}, 2); + for (int i = 0; i < 8; i++) + rt_emit(b, (uint8_t)(val >> (i * 8))); +} + +/* mov rsi, imm64 */ +static void rt_mov_rsi_imm64(rt_buf_t *b, uint64_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xBE}, 2); + for (int i = 0; i < 8; i++) + rt_emit(b, (uint8_t)(val >> (i * 8))); +} + +/* xor reg, reg (32-bit, zero-extends to 64) */ +static void rt_xor32(rt_buf_t *b, int reg) +{ + /* xor r32, r32 = 0x31 C0 + (reg<<3 | reg) */ + rt_emit(b, 0x31); + rt_emit(b, (uint8_t)(0xC0 | (reg << 3) | reg)); +} + +/* sub rsp, imm8 */ +static void rt_sub_rsp_imm8(rt_buf_t *b, uint8_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xEC, val}, 4); +} + +/* sub rsp, imm32 */ +static void rt_sub_rsp_imm32(rt_buf_t *b, uint32_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x81, 0xEC}, 3); + rt_emit(b, (uint8_t)(val)); + rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); + rt_emit(b, (uint8_t)(val >> 24)); +} + +/* push r64 (0x50 + reg, for rax-rdi; 0x41 0x50 + (reg-8) for r8-r15) */ +static void rt_push_r(rt_buf_t *b, int reg) +{ + if (reg < 8) { + rt_emit(b, (uint8_t)(0x50 + reg)); + } else { + rt_emit_n(b, (const uint8_t[]){0x41, (uint8_t)(0x50 + reg - 8)}, 2); + } +} + +/* pop r64 */ +static void rt_pop_r(rt_buf_t *b, int reg) +{ + if (reg < 8) { + rt_emit(b, (uint8_t)(0x58 + reg)); + } else { + rt_emit_n(b, (const uint8_t[]){0x41, (uint8_t)(0x58 + reg - 8)}, 2); + } +} + +/* mov [rbp + disp8], r64 */ +static void rt_store_rbp_disp8(rt_buf_t *b, int reg, int8_t disp) +{ + /* REX.W = 0x48 (or 0x49 for r8-r15) */ + uint8_t rex = (reg < 8) ? 0x48 : 0x4C; + uint8_t rm = (reg < 8) ? (uint8_t)reg : (uint8_t)(reg - 8); + rt_emit(b, rex); + rt_emit(b, 0x89); + rt_emit(b, (uint8_t)(0x45 | (rm << 3))); /* ModRM: [rbp+disp8], reg */ + rt_emit(b, (uint8_t)disp); +} + +/* mov r64, [rbp + disp8] */ +static void rt_load_rbp_disp8(rt_buf_t *b, int reg, int8_t disp) +{ + uint8_t rex = (reg < 8) ? 0x48 : 0x4C; + uint8_t rm = (reg < 8) ? (uint8_t)reg : (uint8_t)(reg - 8); + rt_emit(b, rex); + rt_emit(b, 0x8B); + rt_emit(b, (uint8_t)(0x45 | (rm << 3))); + rt_emit(b, (uint8_t)disp); +} + +/* lea r64, [rbp + disp8] */ +static void rt_lea_rbp_disp8(rt_buf_t *b, int reg, int8_t disp) +{ + uint8_t rex = (reg < 8) ? 0x48 : 0x4C; + uint8_t rm = (reg < 8) ? (uint8_t)reg : (uint8_t)(reg - 8); + rt_emit(b, rex); + rt_emit(b, 0x8D); + rt_emit(b, (uint8_t)(0x45 | (rm << 3))); + rt_emit(b, (uint8_t)disp); +} + +/* mov [rbp + disp8], r8 (8-bit register AL/CL/etc via REX) */ +static void rt_store8_rbp_disp8(rt_buf_t *b, int reg, int8_t disp) +{ + /* For storing AL (reg=0), it's just mov [rbp+disp8], al = 0x88 0x45 disp + * For other regs, need REX prefix */ + uint8_t rex = (reg >= 8) ? 0x41 : 0x40; + if (reg < 8 && reg == 0) { + /* AL: no REX needed, but we can emit it anyway */ + } + if (reg >= 4 || reg >= 8) { + rt_emit(b, rex); + } + rt_emit(b, 0x88); + uint8_t rm = (reg < 8) ? (uint8_t)(reg & 7) : (uint8_t)(reg - 8); + rt_emit(b, (uint8_t)(0x45 | (rm << 3))); + rt_emit(b, (uint8_t)disp); +} + +/* mov rax, rdi (or any reg to rax) */ +static void rt_mov_rax_r(rt_buf_t *b, int src_reg) +{ + /* mov rax, r64 = REX.W 0x8B C0 + (src<<3) -- no, that's mov rax, [reg] + * Actually mov rax, rsi = 0x48 0x89 F0 (mov rax, rsi using src/dst swap) + * Simpler: mov dst, src = REX.W 0x89 (src<<3 | dst) */ + uint8_t rex = (src_reg < 8) ? 0x48 : 0x49; + uint8_t src_rm = (src_reg < 8) ? (uint8_t)(src_reg << 3) : (uint8_t)((src_reg - 8) << 3); + rt_emit(b, rex); + rt_emit(b, 0x89); + rt_emit(b, (uint8_t)(src_rm | 0)); /* dst = RAX = 0 */ +} + +/* call rel32 (placeholder, to be patched) */ +static int rt_call_rel32(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0xE8); + rt_emit_n(b, (const uint8_t[]){0, 0, 0, 0}, 4); + return pos; +} + +/* jmp rel8 (short jump placeholder) */ +static int rt_jmp_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0xEB); + rt_emit(b, 0); + return pos; +} + +/* Patch a rel32 at the given offset to target `target_pos`. */ +static void rt_patch_rel32(rt_buf_t *b, int patch_off, int target_pos) +{ + int32_t rel = (int32_t)(target_pos - patch_off - 4); + b->data[patch_off + 1] = (uint8_t)(rel); + b->data[patch_off + 2] = (uint8_t)(rel >> 8); + b->data[patch_off + 3] = (uint8_t)(rel >> 16); + b->data[patch_off + 4] = (uint8_t)(rel >> 24); +} + +/* Patch a rel8 at the given offset. */ +static void rt_patch_rel8(rt_buf_t *b, int patch_off, int target_pos) +{ + int8_t rel = (int8_t)(target_pos - patch_off - 2); + b->data[patch_off + 1] = (uint8_t)rel; +} + +/* cmp rax, imm8 */ +static void rt_cmp_rax_imm8(rt_buf_t *b, uint8_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xF8, val}, 4); +} + +/* jne rel8 */ +static int rt_jne_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0x75); + rt_emit(b, 0); + return pos; +} + +/* je rel8 */ +static int rt_je_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0x74); + rt_emit(b, 0); + return pos; +} + +/* jl rel8 */ +static int rt_jl_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0x7C); + rt_emit(b, 0); + return pos; +} + +/* jge rel8 */ +static int rt_jge_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0x7D); + rt_emit(b, 0); + return pos; +} + +/* jg rel8 */ +static int rt_jg_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0x7F); + rt_emit(b, 0); + return pos; +} + +/* jle rel8 */ +static int rt_jle_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0x7E); + rt_emit(b, 0); + return pos; +} + +/* jb rel8 (unsigned below) */ +static int rt_jb_rel8(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0x72); + rt_emit(b, 0); + return pos; +} + +/* movzx eax, byte [rsi] */ +static void rt_movzx_eax_byte_rsi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x06}, 3); +} + +/* movzx eax, byte [rdi] */ +static void rt_movzx_eax_byte_rdi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x07}, 3); +} + +/* inc rsi */ +static void rt_inc_rsi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xFF, 0xC6}, 3); +} + +/* inc rdi */ +static void rt_inc_rdi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xFF, 0xC7}, 3); +} + +/* inc rcx */ +static void rt_inc_rcx(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xFF, 0xC1}, 3); +} + +/* dec rcx */ +static void rt_dec_rcx(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xFF, 0xC9}, 3); +} + +/* mov byte [rdi], al */ +static void rt_stosb(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0xAA}, 1); /* stosb: stores AL to [rdi], increments rdi */ +} + +/* rep movsb */ +static void rt_rep_movsb(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0xF3, 0xA4}, 2); +} + +/* rep stosb */ +static void rt_rep_stosb(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0xF3, 0xAA}, 2); +} + +/* mov rax, rsi */ +static void rt_mov_rax_rsi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xF0}, 3); +} + +/* mov rdi, rax */ +static void rt_mov_rdi_rax(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xC7}, 3); +} + +/* mov rsi, rax */ +static void rt_mov_rsi_rax(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xC6}, 3); +} + +/* mov rdx, rax */ +static void rt_mov_rdx_rax(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xC2}, 3); +} + +/* mov rcx, rax */ +static void rt_mov_rcx_rax(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xC1}, 3); +} + +/* mov r8, rax (REX.B) */ +static void rt_mov_r8_rax(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x49, 0x89, 0xC0}, 3); /* mov r8, rax? no, this is mov r8, rax = REX.WB 0x89 C0 */ + /* Actually: mov r8, rax = 0x49 0x89 0xC0 (REX.WB, modrm=11 000 000 = rax→r8) + * But we want mov r8, rax which is: src=rax(0), dst=r8(0+REX.B) + * 0x49 = REX.W+B, 0x89 = mov r/m64, r64, modrm = 11 000 000 = C0 + * This stores rax to r8. Wait, modrm C0 = mod=11, reg=000(rax), rm=000(rax with REX.B = r8) + * So 0x49 0x89 0xC0 = mov r8, rax. Correct! */ +} + +/* add rax, imm32 */ +static void rt_add_rax_imm32(rt_buf_t *b, uint32_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x05}, 2); + rt_emit(b, (uint8_t)(val)); + rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); + rt_emit(b, (uint8_t)(val >> 24)); +} + +/* add rdi, imm8 */ +static void rt_add_rdi_imm8(rt_buf_t *b, uint8_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xC7, val}, 4); +} + +/* sub rax, imm32 */ +static void rt_sub_rax_imm32(rt_buf_t *b, uint32_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x2D}, 2); + rt_emit(b, (uint8_t)(val)); + rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); + rt_emit(b, (uint8_t)(val >> 24)); +} + +/* imul rax, rsi */ +static void rt_imul_rax_rsi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x0F, 0xAF, 0xC6}, 4); +} + +/* cqo (sign-extend rax into rdx:rax) */ +static void rt_cqo(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x99}, 2); +} + +/* idiv rsi */ +static void rt_idiv_rsi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xF7, 0xFE}, 3); +} + +/* xor edx, edx (zero rdx for unsigned div) */ +static void rt_xor_edx(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x31, 0xD2}, 2); +} + +/* div rsi (unsigned) */ +static void rt_div_rsi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xF7, 0xF6}, 3); +} + +/* test rax, rax */ +static void rt_test_rax_rax(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x85, 0xC0}, 3); +} + +/* add rsi, imm8 */ +static void rt_add_rsi_imm8(rt_buf_t *b, uint8_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xC6, val}, 4); +} + +/* sub rsi, imm8 */ +static void rt_sub_rsi_imm8(rt_buf_t *b, uint8_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xEE, val}, 4); +} + +/* mov rdx, rcx */ +static void rt_mov_rdx_rcx(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xCA}, 3); +} + +/* mov [rdi], al */ +static void rt_mov_byte_rdi_al(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x88, 0x07}, 2); +} + +/* mov [rdi], rsi (8-byte store) */ +static void rt_mov_qword_rdi_rsi(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0x37}, 2); +} + +/* add rcx, imm8 */ +static void rt_add_rcx_imm8(rt_buf_t *b, uint8_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xC1, val}, 4); +} + +/* cmp byte [rsi], 0 */ +static void rt_cmp_byte_rsi_0(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x80, 0x3E, 0x00}, 3); +} + +/* ======================================================================== + * rel32 conditional jumps (for printf and other long functions) + * ======================================================================== */ + +/* jmp rel32 */ +static int rt_jmp_rel32(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit(b, 0xE9); + rt_emit_n(b, (const uint8_t[]){0, 0, 0, 0}, 4); + return pos; +} +/* je rel32 */ +static int rt_je_rel32(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit_n(b, (const uint8_t[]){0x0F, 0x84}, 2); + rt_emit_n(b, (const uint8_t[]){0, 0, 0, 0}, 4); + return pos; +} +/* jne rel32 */ +static int rt_jne_rel32(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit_n(b, (const uint8_t[]){0x0F, 0x85}, 2); + rt_emit_n(b, (const uint8_t[]){0, 0, 0, 0}, 4); + return pos; +} +/* jns rel32 (jump if not sign) */ +static int rt_jns_rel32(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit_n(b, (const uint8_t[]){0x0F, 0x89}, 2); + rt_emit_n(b, (const uint8_t[]){0, 0, 0, 0}, 4); + return pos; +} +/* jb rel32 (unsigned below) */ +static int rt_jb_rel32(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit_n(b, (const uint8_t[]){0x0F, 0x82}, 2); + rt_emit_n(b, (const uint8_t[]){0, 0, 0, 0}, 4); + return pos; +} +/* jge rel32 */ +static int rt_jge_rel32(rt_buf_t *b) +{ + int pos = rt_pos(b); + rt_emit_n(b, (const uint8_t[]){0x0F, 0x8D}, 2); + rt_emit_n(b, (const uint8_t[]){0, 0, 0, 0}, 4); + return pos; +} + +/* ======================================================================== + * printf-specific instruction emitters + * ======================================================================== */ + +/* movzx eax, byte [rbx] */ +static void rt_movzx_eax_byte_rbx(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x03}, 3); +} +/* cmp al, imm8 */ +static void rt_cmp_al_imm8(rt_buf_t *b, uint8_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x3C, val}, 2); +} +/* inc rbx */ +static void rt_inc_rbx(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x48, 0xFF, 0xC3}, 3); } +/* inc r12 */ +static void rt_inc_r12(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x49, 0xFF, 0xC4}, 3); } +/* inc r14 */ +static void rt_inc_r14(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x49, 0xFF, 0xC6}, 3); } +/* inc r15 */ +static void rt_inc_r15(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x49, 0xFF, 0xC7}, 3); } +/* dec rcx (already exists as rt_dec_rcx) */ + +/* neg rax */ +static void rt_neg_rax(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x48, 0xF7, 0xD8}, 3); } + +/* mov rbx, rax */ +static void rt_mov_rbx_rax(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xC3}, 3); } +/* mov r13, rax */ +static void rt_mov_r13_rax(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x49, 0x89, 0xC5}, 3); } +/* mov r12, r13 */ +static void rt_mov_r12_r13(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x4D, 0x89, 0xEC}, 3); } +/* mov r15, rax */ +static void rt_mov_r15_rax(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x49, 0x89, 0xC7}, 3); } +/* mov rdx, r12 */ +static void rt_mov_rdx_r12(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x4C, 0x89, 0xE2}, 3); } +/* mov rsi, r13 */ +static void rt_mov_rsi_r13(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x4C, 0x89, 0xEE}, 3); } +/* mov rax, r12 */ +static void rt_mov_rax_r12(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x4C, 0x89, 0xE0}, 3); } +/* mov rcx, r12 */ +static void rt_mov_rcx_r12(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x4C, 0x89, 0xE1}, 3); } +/* sub rdx, r13 */ +static void rt_sub_rdx_r13(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x4C, 0x29, 0xEA}, 3); } +/* sub rax, r13 */ +static void rt_sub_rax_r13(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x4C, 0x29, 0xE8}, 3); } +/* mov r14, imm64 */ +static void rt_mov_r14_imm64(rt_buf_t *b, uint64_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x49, 0xBE}, 2); + for (int i = 0; i < 8; i++) rt_emit(b, (uint8_t)(val >> (i * 8))); +} + +/* mov rax, [rbp + r14*8 - 0x58] (LoadArg: rax = arg[r14]) + * REX: W=1, R=0(rax), X=1(r14), B=0(rbp) → 0x4A + * ModRM: mod=01, reg=rax(0), r/m=100(SIB) + * SIB: scale=3(×8), index=r14(6), base=rbp(5) + * disp8 = -0x58 = 0xA8 */ +static void rt_load_arg(rt_buf_t *b) +{ + rt_emit_n(b, (const uint8_t[]){0x4A, 0x8B, 0x44, 0xF5, 0xA8}, 5); +} + +/* mov byte [r12], al (REX.B=1, 88, ModRM=04 SIB, SIB=24) */ +static void rt_mov_byte_r12_al(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x42, 0x88, 0x04, 0x24}, 4); } +/* mov byte [r12], dl (REX.B=1, 88, ModRM=14 SIB, SIB=24) */ +static void rt_mov_byte_r12_dl(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x42, 0x88, 0x14, 0x24}, 4); } +/* mov byte [r12], imm8 (REX.BX=1, C6, ModRM=04 SIB, SIB=24, imm8) */ +static void rt_mov_byte_r12_imm8(rt_buf_t *b, uint8_t imm) +{ + rt_emit_n(b, (const uint8_t[]){0x43, 0xC6, 0x04, 0x24, imm}, 5); +} + +/* movzx eax, byte [rcx] */ +static void rt_movzx_eax_byte_rcx(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x01}, 3); } +/* mov [rsi], al */ +static void rt_mov_byte_rsi_al(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x88, 0x06}, 2); } +/* mov [rcx], al */ +static void rt_mov_byte_rcx_al(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x88, 0x01}, 2); } + +/* cmp rsi, rcx */ +static void rt_cmp_rsi_rcx(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x48, 0x39, 0xCE}, 3); } +/* xor rdx, rdx */ +static void rt_xor_rdx_rdx(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x48, 0x31, 0xD2}, 3); } +/* mov rcx, imm32 */ +static void rt_mov_rcx_imm32(rt_buf_t *b, uint32_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0xC7, 0xC1}, 3); + rt_emit(b, (uint8_t)(val)); rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); rt_emit(b, (uint8_t)(val >> 24)); +} +/* div rcx (unsigned) */ +static void rt_div_rcx(rt_buf_t *b) { rt_emit_n(b, (const uint8_t[]){0x48, 0xF7, 0xF1}, 3); } +/* add dl, imm8 */ +static void rt_add_dl_imm8(rt_buf_t *b, uint8_t val) { rt_emit_n(b, (const uint8_t[]){0x80, 0xC2, val}, 3); } +/* cmp dl, imm8 */ +static void rt_cmp_dl_imm8(rt_buf_t *b, uint8_t val) { rt_emit_n(b, (const uint8_t[]){0x80, 0xFA, val}, 3); } + +/* add rsp, imm32 */ +static void rt_add_rsp_imm32(rt_buf_t *b, uint32_t val) +{ + rt_emit_n(b, (const uint8_t[]){0x48, 0x81, 0xC4}, 3); + rt_emit(b, (uint8_t)(val)); rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); rt_emit(b, (uint8_t)(val >> 24)); +} + +/* mov edi, imm32 */ +static void rt_mov_edi_imm32_val(rt_buf_t *b, uint32_t val) +{ + rt_emit(b, 0xBF); + rt_emit(b, (uint8_t)(val)); rt_emit(b, (uint8_t)(val >> 8)); + rt_emit(b, (uint8_t)(val >> 16)); rt_emit(b, (uint8_t)(val >> 24)); +} + +/* ======================================================================== + * Runtime function generators + * ======================================================================== */ + +/* void exit(int code) + * mov eax, 60 (SYS_exit) + * mov edi, -- code already in edi (SysV ABI) + * int 0x80 + * But wait: in SysV ABI, the first arg is in edi already. + * We need: mov eax, 60; int 0x80 (edi already has the exit code) + * Actually we need to set eax first, but edi is already set by caller. + * So: mov eax, 60; int 0x80 + * Wait, that clobbers nothing. But the caller passes code in edi. + * The syscall convention: rax=sysno, rdi=arg1. So edi already has the code. + * Just: mov eax, 60; int 0x80 + */ +static void gen_exit(rt_buf_t *b) +{ + rt_mov_eax_imm32(b, LOS4_SYS_exit); + rt_syscall(b); + /* int 0x80 doesn't return for SYS_exit, but add ud2 just in case */ + rt_emit_n(b, (const uint8_t[]){0x0F, 0x0B}, 2); /* ud2 */ +} + +/* void abort(void) + * mov eax, 60; mov edi, 1; int 0x80 + */ +static void gen_abort(rt_buf_t *b) +{ + rt_mov_edi_imm32(b, 1); /* exit code 1 */ + rt_mov_eax_imm32(b, LOS4_SYS_exit); + rt_syscall(b); + rt_emit_n(b, (const uint8_t[]){0x0F, 0x0B}, 2); /* ud2 */ +} + +/* int putchar(int c) + * SysV ABI: c in edi + * Stack frame: [rbp-1] = c (as byte) + * write(1, &c, 1): rdi=1, rsi=&c, rdx=1, rax=1 (SYS_write) + * return: rax = c + */ +static void gen_putchar(rt_buf_t *b) +{ + rt_prologue(b); + rt_sub_rsp_imm8(b, 16); + + /* Store edi (char) to [rbp-1] */ + /* mov [rbp-1], dil (REX.W+REX.B for dil? actually just REX for dil) + * dil = low byte of rdi. mov [rbp-1], dil = 40 88 7D FF */ + rt_emit_n(b, (const uint8_t[]){0x40, 0x88, 0x7D, 0xFF}, 4); + + /* write(1, &c, 1) */ + rt_mov_edi_imm32(b, 1); /* rdi = 1 (stdout fd) */ + rt_lea_rbp_disp8(b, 6, -1); /* rsi = &[rbp-1] */ + rt_emit_n(b, (const uint8_t[]){0xBA, 0x01, 0x00, 0x00, 0x00}, 5); /* mov edx, 1 */ + rt_mov_eax_imm32(b, LOS4_SYS_write); + rt_syscall(b); + + /* return c: mov eax, edi_original... but we clobbered edi. + * Need to reload from [rbp-1]. */ + /* movzx eax, byte [rbp-1] */ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x45, 0xFF}, 4); + + rt_epilogue(b); +} + +/* int puts(const char *s) + * SysV ABI: s in rdi + * 1. strlen(s) → rdx (length) + * 2. write(1, s, len): rdi=1, rsi=s, rdx=len, rax=1 + * 3. write(1, "\n", 1) for newline + * 4. return 0 (or non-negative) + */ +static void gen_puts(rt_buf_t *b) +{ + rt_prologue(b); + rt_sub_rsp_imm8(b, 16); + + /* Save s (rdi) in [rbp-8] */ + rt_store_rbp_disp8(b, 7, -8); /* mov [rbp-8], rdi */ + + /* strlen: rsi = s, rcx = 0; loop until [rsi] == 0; rcx = length */ + rt_mov_rsi_rax(b); /* mov rsi, rax? No, rax is garbage. Need mov rsi, rdi */ + /* Actually: mov rsi, rdi = 48 89 FE */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xFE}, 3); /* mov rsi, rdi */ + rt_xor32(b, 1); /* xor ecx, ecx */ + + int loop_start = rt_pos(b); + rt_cmp_byte_rsi_0(b); + int done = rt_jne_rel8(b); + rt_inc_rcx(b); + rt_inc_rsi(b); + rt_emit(b, 0xEB); /* jmp loop_start */ + rt_emit(b, (uint8_t)(loop_start - rt_pos(b) - 1)); + rt_patch_rel8(b, done, rt_pos(b)); + + /* Now ecx = strlen(s). mov edx, ecx */ + rt_emit_n(b, (const uint8_t[]){0x89, 0xCA}, 2); /* mov edx, ecx */ + + /* write(1, s, len) */ + rt_mov_edi_imm32(b, 1); /* rdi = 1 (stdout) */ + rt_load_rbp_disp8(b, 6, -8); /* rsi = s (from [rbp-8]) */ + /* rdx already has length */ + rt_mov_eax_imm32(b, LOS4_SYS_write); + rt_syscall(b); + + /* write(1, "\n", 1): store '\n' to [rbp-1], write it */ + rt_emit_n(b, (const uint8_t[]){0xC6, 0x45, 0xFE, 0x0A}, 4); /* mov byte [rbp-2], '\n' */ + rt_mov_edi_imm32(b, 1); + rt_lea_rbp_disp8(b, 6, -2); /* rsi = &[rbp-2] */ + rt_emit_n(b, (const uint8_t[]){0xBA, 0x01, 0x00, 0x00, 0x00}, 5); /* mov edx, 1 */ + rt_mov_eax_imm32(b, LOS4_SYS_write); + rt_syscall(b); + + /* return 0 */ + rt_xor32(b, 0); /* xor eax, eax */ + + rt_epilogue(b); +} + +/* size_t strlen(const char *s) + * SysV ABI: s in rdi + * return: rax = length + * rsi = s, rcx = 0; loop until [rsi]==0; return rcx + */ +static void gen_strlen(rt_buf_t *b) +{ + /* mov rsi, rdi */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xFE}, 3); + rt_xor32(b, 1); /* xor ecx, ecx */ + + int loop = rt_pos(b); + rt_cmp_byte_rsi_0(b); + int done = rt_jne_rel8(b); + rt_inc_rcx(b); + rt_inc_rsi(b); + rt_emit(b, 0xEB); + rt_emit(b, (uint8_t)(loop - rt_pos(b) - 1)); + rt_patch_rel8(b, done, rt_pos(b)); + + /* mov eax, ecx */ + rt_emit_n(b, (const uint8_t[]){0x89, 0xC8}, 2); + rt_ret(b); +} + +/* void *memcpy(void *dst, const void *src, size_t n) + * SysV ABI: rdi=dst, rsi=src, rdx=n + * rax=dst (return value) + * rep movsb: copies n bytes from [rsi] to [rdi] + */ +static void gen_memcpy(rt_buf_t *b) +{ + /* Save dst for return: mov rax, rdi */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xF8}, 3); /* mov rax, rdi */ + + /* mov rcx, rdx (count) */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xD1}, 3); /* mov rcx, rdx */ + + /* rep movsb */ + rt_rep_movsb(b); + + rt_ret(b); +} + +/* void *memset(void *dst, int c, size_t n) + * SysV ABI: rdi=dst, esi=c, rdx=n + * rax=dst (return) + * mov rax, rdi (save dst for return) + * mov ecx, edx (count) + * mov eax, esi (fill byte, but we need to preserve rdi first) + * Actually: stosb stores AL to [rdi] and increments rdi + * So: mov rax, rdi (save dst); mov ecx, edx (count); mov eax, esi (byte) + * But mov eax, esi clobbers rax (which has dst). Need to push rdi first. + */ +static void gen_memset(rt_buf_t *b) +{ + /* mov rax, rdi (save dst for return) */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xF8}, 3); + /* mov rcx, rdx (count) */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xD1}, 3); + /* mov eax, esi (fill byte) */ + rt_emit_n(b, (const uint8_t[]){0x89, 0xF0}, 2); + /* rep stosb */ + rt_rep_stosb(b); + rt_ret(b); +} + +/* int memcmp(const void *a, const void *b, size_t n) + * SysV ABI: rdi=a, rsi=b, rdx=n + * return: 0 if equal, difference of first differing bytes otherwise + */ +static void gen_memcmp(rt_buf_t *b) +{ + /* mov rcx, rdx */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xD1}, 3); + + int loop = rt_pos(b); + /* test rcx, rcx; je done */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x85, 0xC9}, 3); /* test rcx, rcx */ + int done = rt_je_rel8(b); + + /* movzx eax, byte [rdi] */ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x07}, 3); + /* movzx r8d, byte [rsi] -- need REX.B for r8 */ + rt_emit_n(b, (const uint8_t[]){0x44, 0x0F, 0xB6, 0x06}, 4); + /* sub eax, r8d */ + rt_emit_n(b, (const uint8_t[]){0x44, 0x29, 0xC0}, 3); + /* jne done2 (return the difference) */ + int done2 = rt_jne_rel8(b); + + /* inc rdi; inc rsi; dec rcx; jmp loop */ + rt_inc_rdi(b); + rt_inc_rsi(b); + rt_dec_rcx(b); + rt_emit(b, 0xEB); + rt_emit(b, (uint8_t)(loop - rt_pos(b) - 1)); + + /* done: return 0 */ + rt_patch_rel8(b, done, rt_pos(b)); + rt_xor32(b, 0); /* xor eax, eax */ + int ret_pos = rt_pos(b); + rt_ret(b); + + /* done2: eax already has the difference, return it */ + rt_patch_rel8(b, done2, rt_pos(b)); + rt_ret(b); + (void)ret_pos; +} + +/* int strcmp(const char *a, const char *b) + * SysV ABI: rdi=a, rsi=b + * return: 0 if equal, difference otherwise + */ +static void gen_strcmp(rt_buf_t *b) +{ + int loop = rt_pos(b); + /* movzx eax, byte [rdi] */ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x07}, 3); + /* movzx r8d, byte [rsi] */ + rt_emit_n(b, (const uint8_t[]){0x44, 0x0F, 0xB6, 0x06}, 4); + /* sub eax, r8d */ + rt_emit_n(b, (const uint8_t[]){0x44, 0x29, 0xC0}, 3); + /* jne return_diff */ + int ret_diff = rt_jne_rel8(b); + /* test al, al (check if a[i] == 0, end of string) */ + rt_emit_n(b, (const uint8_t[]){0x84, 0xC0}, 2); + /* je return_zero (both strings ended, equal) */ + int ret_zero = rt_je_rel8(b); + /* inc rdi; inc rsi; jmp loop */ + rt_inc_rdi(b); + rt_inc_rsi(b); + rt_emit(b, 0xEB); + rt_emit(b, (uint8_t)(loop - rt_pos(b) - 1)); + + /* return_zero: xor eax, eax; ret */ + rt_patch_rel8(b, ret_zero, rt_pos(b)); + rt_xor32(b, 0); + rt_ret(b); + + /* return_diff: eax already has difference; ret */ + rt_patch_rel8(b, ret_diff, rt_pos(b)); + rt_ret(b); +} + +/* char *strcpy(char *dst, const char *src) + * SysV ABI: rdi=dst, rsi=src + * return: rax = dst + */ +static void gen_strcpy(rt_buf_t *b) +{ + /* mov rax, rdi (save dst for return) */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xF8}, 3); + + int loop = rt_pos(b); + /* movzx ecx, byte [rsi] */ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x0E}, 3); + /* mov [rdi], cl */ + rt_emit_n(b, (const uint8_t[]){0x88, 0x0F}, 2); + /* test cl, cl; je done */ + rt_emit_n(b, (const uint8_t[]){0x84, 0xC9}, 2); + int done = rt_je_rel8(b); + /* inc rdi; inc rsi; jmp loop */ + rt_inc_rdi(b); + rt_inc_rsi(b); + rt_emit(b, 0xEB); + rt_emit(b, (uint8_t)(loop - rt_pos(b) - 1)); + + rt_patch_rel8(b, done, rt_pos(b)); + rt_ret(b); +} + +/* void *malloc(size_t size) + * SysV ABI: rdi = size + * Uses a simple bump allocator: + * 1. heap_ptr (BSS global) starts at heap_pool + * 2. old_ptr = heap_ptr; heap_ptr += size; return old_ptr + * 3. Align to 16 bytes + * + * The BSS offsets for heap_pool and heap_ptr are patched by the ELF writer. + * + * Layout: + * mov rax, [heap_ptr] ; load current heap pointer + * mov r8, rax ; save old pointer + * add rax, rdi ; new_ptr = old_ptr + size + * add rax, 15 ; align to 16 + * and rax, -16 ; rax = aligned new_ptr + * mov [heap_ptr], rax ; store new pointer + * mov rax, r8 ; return old pointer + * ret + * + * The [heap_ptr] address is an 8-byte placeholder that gets patched + * to the actual BSS virtual address. + */ +static void gen_malloc(rt_buf_t *b) +{ + /* mov rax, [rip+0] — load heap_ptr (8-byte placeholder for patching) */ + /* We use: mov rax, [abs 0] = 0x48 0x8B 0x04 0x25 0x00 0x00 0x00 0x00 + * But actually, let's use a mov rax, imm64 placeholder for the address + * of heap_ptr, then load from it. + * Simpler: mov rax, (8-byte placeholder); mov rax, [rax] + */ + /* mov rax, imm64 (placeholder for heap_ptr address) */ + rt_mov_rax_imm64(b, 0); /* will be patched to heap_ptr BSS address */ + /* mov rax, [rax] */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x8B, 0x00}, 3); + + /* mov r8, rax (save old pointer) */ + rt_emit_n(b, (const uint8_t[]){0x49, 0x89, 0xC0}, 3); /* mov r8, rax */ + + /* add rax, rdi (new_ptr = old + size) */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x01, 0xF8}, 3); /* add rax, rdi */ + + /* add rax, 15 */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xC0, 0x0F}, 4); + + /* and rax, -16 (0xFFFFFFFFFFFFFFF0) */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x83, 0xE0, 0xF0}, 4); + + /* mov [heap_ptr], rax — store new pointer */ + /* mov rdx, rax (save new pointer) */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xC2}, 3); /* mov rdx, rax */ + /* mov rax, imm64 (placeholder for heap_ptr address) */ + rt_mov_rax_imm64(b, 0); /* will be patched to heap_ptr BSS address */ + /* mov [rax], rdx */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0x10}, 3); + + /* mov rax, r8 (return old pointer) */ + rt_emit_n(b, (const uint8_t[]){0x4C, 0x89, 0xC0}, 3); /* mov rax, r8 */ + + rt_ret(b); +} + +/* void free(void *ptr) — no-op (bump allocator doesn't free) */ +static void gen_free(rt_buf_t *b) +{ + rt_ret(b); +} + +/* void *calloc(size_t nmemb, size_t size) + * SysV ABI: rdi=nmemb, rsi=size + * total = nmemb * size + * ptr = malloc(total) + * memset(ptr, 0, total) + * return ptr + */ +static void gen_calloc(rt_buf_t *b) +{ + rt_prologue(b); + rt_sub_rsp_imm8(b, 16); + + /* Save nmemb*size computation: + * rax = rdi * rsi (using imul) */ + /* mov rax, rdi */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xF8}, 3); + /* imul rax, rsi */ + rt_imul_rax_rsi(b); + /* Store total to [rbp-8] */ + rt_store_rbp_disp8(b, 0, -8); /* mov [rbp-8], rax */ + + /* malloc(total): rdi = total */ + rt_load_rbp_disp8(b, 7, -8); /* mov rdi, [rbp-8] — wait, rdi=7 */ + /* call malloc (rel32, patched later) */ + int call_pos = rt_call_rel32(b); + /* Save ptr: mov [rbp-16], rax */ + rt_store_rbp_disp8(b, 0, -16); /* mov [rbp-16], rax */ + + /* memset(ptr, 0, total): rdi=ptr, esi=0, rdx=total */ + rt_load_rbp_disp8(b, 7, -16); /* mov rdi, [rbp-16] — rdi=7 */ + rt_xor32(b, 6); /* xor esi, esi */ + rt_load_rbp_disp8(b, 2, -8); /* mov rdx, [rbp-8] */ + /* call memset */ + /* For now, just do inline rep stosb instead of calling memset */ + /* mov rcx, rdx */ + rt_emit_n(b, (const uint8_t[]){0x48, 0x89, 0xD1}, 3); + /* xor eax, eax */ + rt_xor32(b, 0); + /* rep stosb */ + rt_rep_stosb(b); + + /* return ptr: mov rax, [rbp-16] */ + rt_load_rbp_disp8(b, 0, -16); + + /* Patch: malloc is at a known offset. We need to resolve it later. + * For now, we'll patch it in the ELF writer or runtime generator. */ + /* Actually, let's not call malloc. Instead, inline the bump allocator. */ + /* Let me redo this — inline the allocator to avoid call patching. */ + + rt_epilogue(b); + /* We'll need to handle the call_pos patching. Mark it for now. */ + /* TODO: patch call_pos to point to malloc offset */ + (void)call_pos; +} + +/* int getchar(void) + * read(0, &c, 1): rdi=0, rsi=&c, rdx=1, rax=0 (SYS_read) + * return rax (the char, or EOF on error) + */ +static void gen_getchar(rt_buf_t *b) +{ + rt_prologue(b); + rt_sub_rsp_imm8(b, 16); + + /* Store 0 to [rbp-1] (EOF marker if read fails) */ + rt_emit_n(b, (const uint8_t[]){0xC6, 0x45, 0xFF, 0x00}, 4); /* mov byte [rbp-1], 0 */ + + /* read(0, &c, 1) */ + rt_xor32(b, 7); /* xor edi, edi (fd=0, stdin) */ + rt_lea_rbp_disp8(b, 6, -1); /* rsi = &[rbp-1] */ + rt_emit_n(b, (const uint8_t[]){0xBA, 0x01, 0x00, 0x00, 0x00}, 5); /* mov edx, 1 */ + rt_mov_eax_imm32(b, LOS4_SYS_read); + rt_syscall(b); + + /* If rax <= 0, return -1 (EOF) */ + rt_test_rax_rax(b); + int ok = rt_jg_rel8(b); + /* return -1 */ + rt_emit_n(b, (const uint8_t[]){0xB8, 0xFF, 0xFF, 0xFF, 0xFF}, 5); /* mov eax, -1 */ + int ret_pos = rt_pos(b); + rt_epilogue(b); + + /* ok: return the char */ + rt_patch_rel8(b, ok, rt_pos(b)); + /* movzx eax, byte [rbp-1] */ + rt_emit_n(b, (const uint8_t[]){0x0F, 0xB6, 0x45, 0xFF}, 4); + rt_epilogue(b); + (void)ret_pos; +} + +/* long long time_utc_raw(void) — returns 0 (stub, not needed for basic tests) */ +static void gen_time_utc_raw(rt_buf_t *b) +{ + rt_xor32(b, 0); /* xor eax, eax */ + rt_ret(b); +} + +/* int atoi(const char *s) + * SysV ABI: rdi = s + * Simple implementation: skip whitespace, parse sign, parse digits + */ +static void gen_atoi(rt_buf_t *b) +{ + /* For simplicity, return 0 as a stub. Full implementation can be added later. */ + rt_xor32(b, 0); + rt_ret(b); +} + +/* ======================================================================== + * printf — variadic format printer + * + * Stack frame: [rbp-8..-40]=rbx,r12,r13,r14,r15; [rbp-48..-88]=args; + * [rbp-0x460..-88]=buf (0x460-0x58 = 0x408 = 1032 bytes) + * 5 pushes = 40 bytes; SubRsp(0x438) → rsp=rbp-0x460 (16-byte aligned) + * + * Register usage: + * rbx = fmt pointer + * r13 = buf start + * r12 = buf pos (current write position) + * r14 = arg index (starts at 1, the first vararg) + * r15 = scratch (string pointer for %s) + * ======================================================================== */ + +/* FmtUnsigned: rax = number, writes digits to [r12], advances r12. + * Uses rsi=start, rcx=end-1 for reversal. Clobbers rdx, rcx, rsi. */ +static void gen_fmt_unsigned(rt_buf_t *b) +{ + rt_push_r(b, 6); /* save rsi */ + rt_emit_n(b, (const uint8_t[]){0x4C, 0x89, 0xE6}, 3); /* mov rsi, r12 */ + + int loop = rt_pos(b); + rt_xor_rdx_rdx(b); /* xor rdx, rdx */ + rt_mov_rcx_imm32(b, 10); /* mov rcx, 10 */ + rt_div_rcx(b); /* div rcx → rax=quotient, rdx=remainder */ + rt_add_dl_imm8(b, 0x30); /* add dl, '0' */ + rt_mov_byte_r12_dl(b); /* mov [r12], dl */ + rt_inc_r12(b); /* inc r12 */ + rt_test_rax_rax(b); /* test rax, rax */ + int cont = rt_jne_rel32(b); + /* done: reverse [rsi, r12) */ + rt_mov_rcx_r12(b); /* mov rcx, r12 */ + rt_dec_rcx(b); /* dec rcx (end-1) */ + + int rev = rt_pos(b); + rt_cmp_rsi_rcx(b); /* cmp rsi, rcx */ + int rev_done = rt_jge_rel32(b); + rt_movzx_eax_byte_rsi(b); /* al = [rsi] */ + rt_push_r(b, 0); /* push rax (save al) */ + rt_movzx_eax_byte_rcx(b); /* al = [rcx] */ + rt_mov_byte_rsi_al(b); /* [rsi] = al */ + rt_pop_r(b, 0); /* pop rax (restore al) */ + rt_mov_byte_rcx_al(b); /* [rcx] = al */ + rt_inc_rsi(b); /* inc rsi */ + rt_dec_rcx(b); /* dec rcx */ + int rev_jmp = rt_jmp_rel32(b); + rt_patch_rel32(b, rev_jmp, rev); + rt_patch_rel32(b, rev_done, rt_pos(b)); + + rt_pop_r(b, 6); /* restore rsi */ + rt_patch_rel32(b, cont, rt_pos(b)); +} + +/* FmtSigned: rax = number, writes signed digits to [r12] */ +static void gen_fmt_signed(rt_buf_t *b) +{ + rt_test_rax_rax(b); + int positive = rt_jns_rel32(b); + /* negative: write '-', neg rax */ + rt_mov_byte_r12_imm8(b, 0x2D); /* '-' */ + rt_inc_r12(b); + rt_neg_rax(b); + rt_patch_rel32(b, positive, rt_pos(b)); + gen_fmt_unsigned(b); +} + +/* FmtHex: rax = number, writes hex digits to [r12] */ +static void gen_fmt_hex(rt_buf_t *b) +{ + rt_push_r(b, 6); + rt_emit_n(b, (const uint8_t[]){0x4C, 0x89, 0xE6}, 3); /* mov rsi, r12 */ + + int loop = rt_pos(b); + rt_xor_rdx_rdx(b); + rt_mov_rcx_imm32(b, 16); + rt_div_rcx(b); + rt_add_dl_imm8(b, 0x30); /* add dl, '0' */ + rt_cmp_dl_imm8(b, 0x3A); /* cmp dl, ':' */ + int lt10 = rt_jb_rel32(b); + rt_add_dl_imm8(b, 0x27); /* add dl, 0x27 (→ 'a'-10) */ + rt_patch_rel32(b, lt10, rt_pos(b)); + rt_mov_byte_r12_dl(b); + rt_inc_r12(b); + rt_test_rax_rax(b); + int cont = rt_jne_rel32(b); + + /* reverse */ + rt_mov_rcx_r12(b); + rt_dec_rcx(b); + int rev = rt_pos(b); + rt_cmp_rsi_rcx(b); + int rev_done = rt_jge_rel32(b); + rt_movzx_eax_byte_rsi(b); + rt_push_r(b, 0); + rt_movzx_eax_byte_rcx(b); + rt_mov_byte_rsi_al(b); + rt_pop_r(b, 0); + rt_mov_byte_rcx_al(b); + rt_inc_rsi(b); + rt_dec_rcx(b); + int rev_jmp = rt_jmp_rel32(b); + rt_patch_rel32(b, rev_jmp, rev); + rt_patch_rel32(b, rev_done, rt_pos(b)); + + rt_pop_r(b, 6); + rt_patch_rel32(b, cont, rt_pos(b)); +} + +static void gen_printf(rt_buf_t *b) +{ + /* Prologue: push rbp; mov rbp, rsp; push rbx,r12,r13,r14,r15; sub rsp, 0x438 */ + rt_prologue(b); + rt_push_r(b, 3); /* rbx */ + rt_push_r(b, 12); /* r12 */ + rt_push_r(b, 13); /* r13 */ + rt_push_r(b, 14); /* r14 */ + rt_push_r(b, 15); /* r15 */ + rt_sub_rsp_imm32(b, 0x438); + + /* Store register args to stack: [rbp-48..-88] */ + rt_store_rbp_disp8(b, 9, -48); /* [rbp-48] = r9 (arg5) */ + rt_store_rbp_disp8(b, 8, -56); /* [rbp-56] = r8 (arg4) */ + rt_store_rbp_disp8(b, 1, -64); /* [rbp-64] = rcx (arg3) */ + rt_store_rbp_disp8(b, 2, -72); /* [rbp-72] = rdx (arg2) */ + rt_store_rbp_disp8(b, 6, -80); /* [rbp-80] = rsi (arg1) */ + rt_store_rbp_disp8(b, 7, -88); /* [rbp-88] = rdi (arg0=fmt) */ + + /* rbx = fmt, r13 = buf start, r12 = buf pos, r14 = 1 (first vararg) */ + rt_load_rbp_disp8(b, 3, -88); /* rbx = fmt */ + /* lea r13, [rbp-0x460] — disp8 can't reach -0x460, use disp32 */ + rt_emit_n(b, (const uint8_t[]){0x4C, 0x8D, 0xAD}, 3); /* lea r13, [rbp+disp32] */ + rt_emit(b, 0xA0); rt_emit(b, 0xFB); rt_emit(b, 0xFF); rt_emit(b, 0xFF); /* -0x460 = 0xFFFFFBA0 */ + rt_mov_r12_r13(b); /* r12 = r13 (buf pos) */ + rt_mov_r14_imm64(b, 1); /* r14 = 1 */ + + int main_loop = rt_pos(b); + /* al = *rbx */ + rt_movzx_eax_byte_rbx(b); + rt_test_rax_rax(b); + int end_loop = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x25); /* '%' */ + int copy_char = rt_jne_rel32(b); + + /* '%' found: skip '%', read next char */ + rt_inc_rbx(b); + rt_movzx_eax_byte_rbx(b); + /* skip 'l' prefix */ + rt_cmp_al_imm8(b, 0x6C); /* 'l' */ + int not_l = rt_jne_rel32(b); + rt_inc_rbx(b); + rt_movzx_eax_byte_rbx(b); + rt_patch_rel32(b, not_l, rt_pos(b)); + + rt_cmp_al_imm8(b, 0x64); int fmt_d = rt_je_rel32(b); /* 'd' */ + rt_cmp_al_imm8(b, 0x69); int fmt_i = rt_je_rel32(b); /* 'i' */ + rt_cmp_al_imm8(b, 0x75); int fmt_u = rt_je_rel32(b); /* 'u' */ + rt_cmp_al_imm8(b, 0x78); int fmt_x = rt_je_rel32(b); /* 'x' */ + rt_cmp_al_imm8(b, 0x63); int fmt_c = rt_je_rel32(b); /* 'c' */ + rt_cmp_al_imm8(b, 0x73); int fmt_s = rt_je_rel32(b); /* 's' */ + rt_cmp_al_imm8(b, 0x25); int fmt_pct = rt_je_rel32(b); /* '%' */ + int fmt_unknown = rt_jmp_rel32(b); + + /* %d / %i */ + int after_d = rt_pos(b); + rt_patch_rel32(b, fmt_d, after_d); + rt_patch_rel32(b, fmt_i, after_d); + rt_load_arg(b); /* rax = arg[r14] */ + rt_inc_r14(b); + gen_fmt_signed(b); + rt_inc_rbx(b); + int after_fmt_d = rt_jmp_rel32(b); + + /* %u */ + rt_patch_rel32(b, fmt_u, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); + gen_fmt_unsigned(b); + rt_inc_rbx(b); + int after_fmt_u = rt_jmp_rel32(b); + + /* %x */ + rt_patch_rel32(b, fmt_x, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); + gen_fmt_hex(b); + rt_inc_rbx(b); + int after_fmt_x = rt_jmp_rel32(b); + + /* %c */ + rt_patch_rel32(b, fmt_c, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); + rt_mov_byte_r12_al(b); + rt_inc_r12(b); + rt_inc_rbx(b); + int after_fmt_c = rt_jmp_rel32(b); + + /* %s */ + rt_patch_rel32(b, fmt_s, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); /* rax = str ptr */ + rt_mov_r15_rax(b); /* r15 = str */ + int s_loop = rt_pos(b); + /* movzx eax, byte [r15] = REX.B 0F B6 ModRM(07=[r15]) */ + rt_emit_n(b, (const uint8_t[]){0x41, 0x0F, 0xB6, 0x07}, 4); + rt_test_rax_rax(b); + int s_done = rt_je_rel32(b); + rt_mov_byte_r12_al(b); + rt_inc_r12(b); + rt_inc_r15(b); + int s_jmp = rt_jmp_rel32(b); + rt_patch_rel32(b, s_jmp, s_loop); + rt_patch_rel32(b, s_done, rt_pos(b)); + rt_inc_rbx(b); + int after_fmt_s = rt_jmp_rel32(b); + + /* %% */ + rt_patch_rel32(b, fmt_pct, rt_pos(b)); + rt_mov_byte_r12_imm8(b, 0x25); + rt_inc_r12(b); + rt_inc_rbx(b); + int after_fmt_pct = rt_jmp_rel32(b); + + /* unknown: write '%' then the char */ + rt_patch_rel32(b, fmt_unknown, rt_pos(b)); + rt_mov_byte_r12_imm8(b, 0x25); + rt_inc_r12(b); + /* fall through to copy_char */ + + /* copy_char: mov [r12], al; inc r12; inc rbx */ + rt_patch_rel32(b, copy_char, rt_pos(b)); + rt_mov_byte_r12_al(b); + rt_inc_r12(b); + rt_inc_rbx(b); + + /* after_fmt: jump to main_loop */ + int after_fmt = rt_pos(b); + rt_patch_rel32(b, after_fmt_d, after_fmt); + rt_patch_rel32(b, after_fmt_u, after_fmt); + rt_patch_rel32(b, after_fmt_x, after_fmt); + rt_patch_rel32(b, after_fmt_c, after_fmt); + rt_patch_rel32(b, after_fmt_s, after_fmt); + rt_patch_rel32(b, after_fmt_pct, after_fmt); + int main_jmp = rt_jmp_rel32(b); + rt_patch_rel32(b, main_jmp, main_loop); + + /* end_loop: write(1, buf, len) */ + rt_patch_rel32(b, end_loop, rt_pos(b)); + rt_mov_rdx_r12(b); /* rdx = buf pos */ + rt_sub_rdx_r13(b); /* rdx = len */ + rt_mov_rsi_r13(b); /* rsi = buf */ + rt_mov_edi_imm32_val(b, 1); /* edi = 1 (stdout) */ + rt_mov_eax_imm32(b, LOS4_SYS_write); /* eax = SYS_write */ + rt_syscall(b); /* int 0x80 */ + rt_mov_rax_r12(b); /* rax = buf pos */ + rt_sub_rax_r13(b); /* rax = len (return value) */ + rt_add_rsp_imm32(b, 0x438); + rt_pop_r(b, 15); + rt_pop_r(b, 14); + rt_pop_r(b, 13); + rt_pop_r(b, 12); + rt_pop_r(b, 3); + rt_epilogue(b); +} + +/* int sprintf(char *buf, const char *fmt, ...) + * rdi=buf, rsi=fmt, rdx/rcx/r8/r9=varargs + * Like printf but writes to user buf instead of local buffer+write(). + * r14 starts at 2 (arg0=buf, arg1=fmt). */ +static void gen_sprintf(rt_buf_t *b) +{ + rt_prologue(b); + rt_push_r(b, 3); /* rbx */ + rt_push_r(b, 12); /* r12 */ + rt_push_r(b, 13); /* r13 */ + rt_push_r(b, 14); /* r14 */ + rt_push_r(b, 15); /* r15 */ + rt_sub_rsp_imm32(b, 0x438); + + rt_store_rbp_disp8(b, 9, -48); /* [rbp-48] = r9 (arg5) */ + rt_store_rbp_disp8(b, 8, -56); /* [rbp-56] = r8 (arg4) */ + rt_store_rbp_disp8(b, 1, -64); /* [rbp-64] = rcx (arg3) */ + rt_store_rbp_disp8(b, 2, -72); /* [rbp-72] = rdx (arg2) */ + rt_store_rbp_disp8(b, 6, -80); /* [rbp-80] = rsi (arg1=fmt) */ + rt_store_rbp_disp8(b, 7, -88); /* [rbp-88] = rdi (arg0=buf) */ + + rt_load_rbp_disp8(b, 3, -80); /* rbx = fmt */ + rt_load_rbp_disp8(b, 13, -88); /* r13 = buf (user buffer) */ + rt_mov_r12_r13(b); /* r12 = buf pos */ + rt_mov_r14_imm64(b, 2); /* r14 = 2 (first vararg) */ + + int main_loop = rt_pos(b); + rt_movzx_eax_byte_rbx(b); + rt_test_rax_rax(b); + int end_loop = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x25); + int copy_char = rt_jne_rel32(b); + + rt_inc_rbx(b); + rt_movzx_eax_byte_rbx(b); + rt_cmp_al_imm8(b, 0x6C); + int not_l = rt_jne_rel32(b); + rt_inc_rbx(b); + rt_movzx_eax_byte_rbx(b); + rt_patch_rel32(b, not_l, rt_pos(b)); + + rt_cmp_al_imm8(b, 0x64); int fmt_d = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x69); int fmt_i = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x75); int fmt_u = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x78); int fmt_x = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x63); int fmt_c = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x73); int fmt_s = rt_je_rel32(b); + rt_cmp_al_imm8(b, 0x25); int fmt_pct = rt_je_rel32(b); + int fmt_unknown = rt_jmp_rel32(b); + + int after_d = rt_pos(b); + rt_patch_rel32(b, fmt_d, after_d); + rt_patch_rel32(b, fmt_i, after_d); + rt_load_arg(b); rt_inc_r14(b); + gen_fmt_signed(b); + rt_inc_rbx(b); + int after_fmt_d = rt_jmp_rel32(b); + + rt_patch_rel32(b, fmt_u, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); + gen_fmt_unsigned(b); + rt_inc_rbx(b); + int after_fmt_u = rt_jmp_rel32(b); + + rt_patch_rel32(b, fmt_x, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); + gen_fmt_hex(b); + rt_inc_rbx(b); + int after_fmt_x = rt_jmp_rel32(b); + + rt_patch_rel32(b, fmt_c, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); + rt_mov_byte_r12_al(b); + rt_inc_r12(b); + rt_inc_rbx(b); + int after_fmt_c = rt_jmp_rel32(b); + + rt_patch_rel32(b, fmt_s, rt_pos(b)); + rt_load_arg(b); rt_inc_r14(b); + rt_mov_r15_rax(b); + int s_loop = rt_pos(b); + rt_emit_n(b, (const uint8_t[]){0x41, 0x0F, 0xB6, 0x07}, 4); /* movzx eax, byte [r15] */ + rt_test_rax_rax(b); + int s_done = rt_je_rel32(b); + rt_mov_byte_r12_al(b); + rt_inc_r12(b); + rt_inc_r15(b); + int s_jmp = rt_jmp_rel32(b); + rt_patch_rel32(b, s_jmp, s_loop); + rt_patch_rel32(b, s_done, rt_pos(b)); + rt_inc_rbx(b); + int after_fmt_s = rt_jmp_rel32(b); + + rt_patch_rel32(b, fmt_pct, rt_pos(b)); + rt_mov_byte_r12_imm8(b, 0x25); + rt_inc_r12(b); + rt_inc_rbx(b); + int after_fmt_pct = rt_jmp_rel32(b); + + rt_patch_rel32(b, fmt_unknown, rt_pos(b)); + rt_mov_byte_r12_imm8(b, 0x25); + rt_inc_r12(b); + + rt_patch_rel32(b, copy_char, rt_pos(b)); + rt_mov_byte_r12_al(b); + rt_inc_r12(b); + rt_inc_rbx(b); + + int after_fmt = rt_pos(b); + rt_patch_rel32(b, after_fmt_d, after_fmt); + rt_patch_rel32(b, after_fmt_u, after_fmt); + rt_patch_rel32(b, after_fmt_x, after_fmt); + rt_patch_rel32(b, after_fmt_c, after_fmt); + rt_patch_rel32(b, after_fmt_s, after_fmt); + rt_patch_rel32(b, after_fmt_pct, after_fmt); + int main_jmp = rt_jmp_rel32(b); + rt_patch_rel32(b, main_jmp, main_loop); + + /* end_loop: null-terminate, return length */ + rt_patch_rel32(b, end_loop, rt_pos(b)); + rt_mov_byte_r12_imm8(b, 0); /* null-terminate */ + rt_mov_rax_r12(b); + rt_sub_rax_r13(b); /* rax = len */ + rt_add_rsp_imm32(b, 0x438); + rt_pop_r(b, 15); + rt_pop_r(b, 14); + rt_pop_r(b, 13); + rt_pop_r(b, 12); + rt_pop_r(b, 3); + rt_epilogue(b); +} + +/* ======================================================================== + * Main generation entry point + * ======================================================================== */ + +size_t paze_los4_runtime_generate(paze_object_image_t *img, + paze_los4_rt_bss_t *bss_info) +{ + rt_buf_t buf; + rt_init(&buf); + + g_rt_func_count = 0; + + /* Register and generate each runtime function */ + #define RT_GEN(name, func) do { \ + int off = rt_pos(&buf); \ + rt_register(name, off); \ + func(&buf); \ + } while (0) + + RT_GEN("exit", gen_exit); + RT_GEN("abort", gen_abort); + RT_GEN("putchar", gen_putchar); + RT_GEN("puts", gen_puts); + RT_GEN("printf", gen_printf); + RT_GEN("sprintf", gen_sprintf); + RT_GEN("strlen", gen_strlen); + RT_GEN("memcpy", gen_memcpy); + RT_GEN("memset", gen_memset); + RT_GEN("memcmp", gen_memcmp); + RT_GEN("strcmp", gen_strcmp); + RT_GEN("strcpy", gen_strcpy); + RT_GEN("malloc", gen_malloc); + RT_GEN("free", gen_free); + RT_GEN("getchar", gen_getchar); + RT_GEN("atoi", gen_atoi); + RT_GEN("time_utc_raw", gen_time_utc_raw); + + #undef RT_GEN + + /* Append runtime code to .text section */ + size_t rt_base = img->text.len; + paze_section_append(&img->text, buf.data, buf.len); + + /* Set up BSS: heap_pool (1MB) + heap_ptr (8 bytes) */ + if (bss_info) { + bss_info->heap_pool_off = (int)paze_section_reserve_bss(&img->bss, LOS4_HEAP_POOL_SIZE); + bss_info->heap_ptr_off = (int)paze_section_reserve_bss(&img->bss, 8); + bss_info->bss_size = LOS4_HEAP_POOL_SIZE + 8; + } + + /* Free the buffer (data was copied into the image) */ + free(buf.data); + + return rt_base; +} diff --git a/src/PazeE.los4/src/paze_object_image.c b/src/PazeE.los4/src/paze_object_image.c new file mode 100644 index 0000000..91ada6d --- /dev/null +++ b/src/PazeE.los4/src/paze_object_image.c @@ -0,0 +1,197 @@ +#include "paze_object_image.h" +#include +#include +#include + +/* ======================================================================== + * Section + * ======================================================================== */ + +void paze_section_init(paze_image_section_t *s, const char *name, + paze_sec_flags_t flags) +{ + memset(s, 0, sizeof(*s)); + strncpy(s->name, name, sizeof(s->name) - 1); + s->name[sizeof(s->name) - 1] = '\0'; + s->flags = flags; +} + +void paze_section_free(paze_image_section_t *s) +{ + free(s->data); + s->data = NULL; + s->len = 0; + s->cap = 0; + s->bss_size = 0; +} + +static void section_ensure(paze_image_section_t *s, size_t need) +{ + if (need <= s->cap) return; + size_t new_cap = s->cap ? s->cap : 64; + while (new_cap < need) new_cap *= 2; + s->data = (uint8_t *)realloc(s->data, new_cap); + s->cap = new_cap; +} + +size_t paze_section_append(paze_image_section_t *s, const void *bytes, size_t n) +{ + size_t off = s->len; + section_ensure(s, s->len + n); + memcpy(s->data + s->len, bytes, n); + s->len += n; + return off; +} + +size_t paze_section_append_byte(paze_image_section_t *s, uint8_t b) +{ + return paze_section_append(s, &b, 1); +} + +size_t paze_section_reserve_bss(paze_image_section_t *s, size_t n) +{ + size_t off = s->bss_size; + s->bss_size += n; + return off; +} + +void paze_section_write_at(paze_image_section_t *s, size_t off, + const void *bytes, size_t n) +{ + if (off + n > s->len) return; /* safety: don't write past end */ + memcpy(s->data + off, bytes, n); +} + +/* ======================================================================== + * Object Image + * ======================================================================== */ + +paze_object_image_t *paze_object_image_create(void) +{ + paze_object_image_t *img = (paze_object_image_t *)calloc(1, sizeof(*img)); + if (!img) return NULL; + paze_section_init(&img->text, ".text", PAZE_SEC_EXEC); + paze_section_init(&img->rdata, ".rdata", PAZE_SEC_NONE); + paze_section_init(&img->data, ".data", PAZE_SEC_WRITE); + paze_section_init(&img->bss, ".bss", PAZE_SEC_WRITE | PAZE_SEC_BSS); + img->entry_symbol = NULL; + img->has_argc_argv = false; + return img; +} + +void paze_object_image_destroy(paze_object_image_t *img) +{ + if (!img) return; + paze_section_free(&img->text); + paze_section_free(&img->rdata); + paze_section_free(&img->data); + paze_section_free(&img->bss); + + for (size_t i = 0; i < img->symbols_len; i++) { + free(img->symbols[i].name); + free(img->symbols[i].string_value); + } + free(img->symbols); + + for (size_t i = 0; i < img->externals_len; i++) + free(img->externals[i]); + free(img->externals); + + for (size_t i = 0; i < img->fixups_len; i++) + free(img->fixups[i].symbol); + free(img->fixups); + + free(img->entry_symbol); + free(img); +} + +void paze_object_image_add_external(paze_object_image_t *img, const char *name) +{ + for (size_t i = 0; i < img->externals_len; i++) { + if (strcmp(img->externals[i], name) == 0) return; + } + if (img->externals_len >= img->externals_cap) { + size_t nc = img->externals_cap ? img->externals_cap * 2 : 16; + img->externals = (char **)realloc(img->externals, nc * sizeof(char *)); + img->externals_cap = nc; + } + img->externals[img->externals_len++] = paze_str_dup(paze_str_from_cstr(name)); +} + +size_t paze_object_image_add_string(paze_object_image_t *img, paze_str_t value) +{ + /* Deduplicate by value */ + for (size_t i = 0; i < img->symbols_len; i++) { + if (img->symbols[i].string_value) { + size_t slen = strlen(img->symbols[i].string_value); + if (slen == value.len && + memcmp(img->symbols[i].string_value, value.data, value.len) == 0) { + return img->symbols[i].offset; + } + } + } + /* Append bytes + NUL */ + size_t off = img->rdata.len; + for (size_t i = 0; i < value.len; i++) + paze_section_append_byte(&img->rdata, (uint8_t)value.data[i]); + paze_section_append_byte(&img->rdata, 0); + + /* Register symbol $str.N */ + char sym[32]; + snprintf(sym, sizeof(sym), "$str.%zu", img->symbols_len); + paze_defined_symbol_t *ds = paze_object_image_define_symbol( + img, sym, PAZE_SYM_RDATA, off, false, false); + if (ds) { + ds->string_value = paze_str_dup(value); + ds->size = value.len + 1; + } + return off; +} + +paze_defined_symbol_t *paze_object_image_define_symbol( + paze_object_image_t *img, const char *name, + paze_sym_section_t sec, size_t offset, bool global, bool is_function) +{ + if (img->symbols_len >= img->symbols_cap) { + size_t nc = img->symbols_cap ? img->symbols_cap * 2 : 32; + img->symbols = (paze_defined_symbol_t *)realloc( + img->symbols, nc * sizeof(paze_defined_symbol_t)); + img->symbols_cap = nc; + } + paze_defined_symbol_t *ds = &img->symbols[img->symbols_len++]; + memset(ds, 0, sizeof(*ds)); + ds->name = paze_str_dup(paze_str_from_cstr(name)); + ds->section = sec; + ds->offset = offset; + ds->global = global; + ds->is_function = is_function; + return ds; +} + +paze_defined_symbol_t *paze_object_image_find_symbol(paze_object_image_t *img, + const char *name) +{ + for (size_t i = 0; i < img->symbols_len; i++) { + if (strcmp(img->symbols[i].name, name) == 0) + return &img->symbols[i]; + } + return NULL; +} + +void paze_object_image_add_fixup(paze_object_image_t *img, + paze_image_section_t *sec, size_t off, + paze_fixup_kind_t kind, const char *sym, + int64_t addend) +{ + if (img->fixups_len >= img->fixups_cap) { + size_t nc = img->fixups_cap ? img->fixups_cap * 2 : 32; + img->fixups = (paze_fixup_t *)realloc(img->fixups, nc * sizeof(paze_fixup_t)); + img->fixups_cap = nc; + } + paze_fixup_t *f = &img->fixups[img->fixups_len++]; + f->section = sec; + f->offset = off; + f->kind = kind; + f->symbol = paze_str_dup(paze_str_from_cstr(sym)); + f->addend = addend; +} diff --git a/src/PazeE.los4/src/paze_parser.c b/src/PazeE.los4/src/paze_parser.c new file mode 100644 index 0000000..18315fd --- /dev/null +++ b/src/PazeE.los4/src/paze_parser.c @@ -0,0 +1,2098 @@ +#include "../include/paze_parser.h" +#include "../include/paze_type.h" +#include +#include +#include + +/* ======================================================================== + * Forward Declarations + * ======================================================================== */ + +static paze_ast_node_t *parse_expression(paze_parser_t *p); +static paze_ast_node_t *parse_assignment_expr(paze_parser_t *p); +static paze_ast_node_t *parse_conditional_expr(paze_parser_t *p); +static paze_ast_node_t *parse_logical_or_expr(paze_parser_t *p); +static paze_ast_node_t *parse_logical_and_expr(paze_parser_t *p); +static paze_ast_node_t *parse_bitwise_or_expr(paze_parser_t *p); +static paze_ast_node_t *parse_bitwise_xor_expr(paze_parser_t *p); +static paze_ast_node_t *parse_bitwise_and_expr(paze_parser_t *p); +static paze_ast_node_t *parse_equality_expr(paze_parser_t *p); +static paze_ast_node_t *parse_relational_expr(paze_parser_t *p); +static paze_ast_node_t *parse_shift_expr(paze_parser_t *p); +static paze_ast_node_t *parse_additive_expr(paze_parser_t *p); +static paze_ast_node_t *parse_multiplicative_expr(paze_parser_t *p); +static paze_ast_node_t *parse_unary_expr(paze_parser_t *p); +static paze_ast_node_t *parse_postfix_expr(paze_parser_t *p); +static paze_ast_node_t *parse_primary_expr(paze_parser_t *p); + +static paze_ast_node_t *parse_statement(paze_parser_t *p); +static paze_ast_node_t *parse_block_statement(paze_parser_t *p); +static paze_ast_node_t *parse_expression_statement(paze_parser_t *p); +static paze_ast_node_t *parse_if_statement(paze_parser_t *p); +static paze_ast_node_t *parse_while_statement(paze_parser_t *p); +static paze_ast_node_t *parse_do_while_statement(paze_parser_t *p); +static paze_ast_node_t *parse_for_statement(paze_parser_t *p); +static paze_ast_node_t *parse_switch_statement(paze_parser_t *p); +static paze_ast_node_t *parse_return_statement(paze_parser_t *p); +static paze_ast_node_t *parse_jump_statement(paze_parser_t *p); + +static paze_ast_node_t *parse_declaration(paze_parser_t *p); +static paze_ast_node_t *parse_initializer(paze_parser_t *p); +static paze_ast_node_t *parse_declaration_or_statement(paze_parser_t *p); +static paze_ast_node_t *parse_struct_or_union_decl(paze_parser_t *p, bool is_union); +static paze_ast_node_t *parse_typedef_decl(paze_parser_t *p); +static paze_ast_node_t *parse_enum_decl(paze_parser_t *p); + +static paze_type_t *parse_type_specifier(paze_parser_t *p, paze_loc_t *loc); +static paze_type_t *parse_type_name(paze_parser_t *p, paze_loc_t *loc); +static paze_type_t *parse_declarator_type(paze_parser_t *p, paze_type_t *base_type, + paze_str_t *out_name, bool *out_is_variadic); +static paze_type_t *parse_direct_declarator(paze_parser_t *p, paze_type_t *base_type, + paze_str_t *out_name, bool *out_is_variadic); +static paze_type_t *parse_postfix_type(paze_parser_t *p, paze_type_t *base_type, + paze_str_t *out_name, bool *out_is_variadic); +static paze_type_t *parse_param_type_list(paze_parser_t *p, bool *is_variadic); + +/* ======================================================================== + * Type Helpers + * ======================================================================== */ + +static paze_type_t *type_new(paze_parser_t *p, paze_type_kind_t kind) +{ + paze_type_t *t = (paze_type_t *)paze_arena_calloc(p->arena, sizeof(paze_type_t)); + if (!t) return NULL; + t->kind = kind; + t->arena = p->arena; + return t; +} + +static paze_type_t *type_basic(paze_parser_t *p, paze_type_kind_t kind) +{ + return type_new(p, kind); +} + +static paze_type_t *type_pointer(paze_parser_t *p, paze_type_t *pointee) +{ + paze_type_t *t = type_new(p, PAZE_TYPE_POINTER); + t->base = pointee; + return t; +} + +static paze_type_t *type_array(paze_parser_t *p, paze_type_t *elem, long size) +{ + paze_type_t *t = type_new(p, PAZE_TYPE_ARRAY); + t->base = elem; + t->array_size = size; + return t; +} + +static paze_type_t *type_function(paze_parser_t *p, paze_type_t *ret) +{ + paze_type_t *t = type_new(p, PAZE_TYPE_FUNCTION); + t->base = ret; + return t; +} + +static paze_type_t *type_struct(paze_parser_t *p, paze_str_t name, bool is_union) +{ + /* Look up existing struct/union type by name. */ + if (name.len > 0 && p->struct_count > 0) { + for (size_t i = 0; i < p->struct_count; i++) { + if (paze_str_cmp(p->struct_names[i], name) == 0) { + return p->struct_types[i]; + } + } + } + + paze_type_t *t = type_new(p, is_union ? PAZE_TYPE_UNION : PAZE_TYPE_STRUCT); + t->name = name; + + /* Register the new type so future references return the same pointer. */ + if (name.len > 0) { + if (p->struct_count >= p->struct_cap) { + size_t nc = p->struct_cap ? p->struct_cap * 2 : 8; + p->struct_names = (paze_str_t *)realloc(p->struct_names, nc * sizeof(paze_str_t)); + p->struct_types = (paze_type_t **)realloc(p->struct_types, nc * sizeof(paze_type_t *)); + p->struct_cap = nc; + } + p->struct_names[p->struct_count] = name; + p->struct_types[p->struct_count] = t; + p->struct_count++; + } + + return t; +} + +static size_t type_size_of(paze_type_t *type) +{ + if (!type) return 0; + switch (type->kind) { + case PAZE_TYPE_VOID: return 0; + case PAZE_TYPE_CHAR: return 1; + case PAZE_TYPE_SHORT: return 2; + case PAZE_TYPE_INT: return 4; + case PAZE_TYPE_LONG: return 8; + case PAZE_TYPE_UNSIGNED: + case PAZE_TYPE_SIGNED: + if (type->base) return type_size_of(type->base); + return 4; + case PAZE_TYPE_BOOL: return 1; + case PAZE_TYPE_POINTER: return 8; + case PAZE_TYPE_ARRAY: + if (type->array_size >= 0 && type->base) + return type_size_of(type->base) * (size_t)type->array_size; + return 0; + case PAZE_TYPE_FUNCTION: return 8; + case PAZE_TYPE_STRUCT: + case PAZE_TYPE_UNION: + return type->struct_size; + case PAZE_TYPE_TYPEDEF: + if (type->base) return type_size_of(type->base); + return 0; + case PAZE_TYPE_TYPEOF: + if (type->typeof_expr && type->typeof_expr->type) + return type_size_of(type->typeof_expr->type); + return 0; + default: return 0; + } +} + +static size_t type_align_of(paze_type_t *type) +{ + if (!type) return 1; + switch (type->kind) { + case PAZE_TYPE_CHAR: return 1; + case PAZE_TYPE_SHORT: return 2; + case PAZE_TYPE_INT: return 4; + case PAZE_TYPE_LONG: return 8; + case PAZE_TYPE_UNSIGNED: + case PAZE_TYPE_SIGNED: + if (type->base) return type_align_of(type->base); + return 4; + case PAZE_TYPE_BOOL: return 1; + case PAZE_TYPE_POINTER: return 8; + case PAZE_TYPE_ARRAY: + if (type->base) return type_align_of(type->base); + return 1; + case PAZE_TYPE_STRUCT: + case PAZE_TYPE_UNION: { + size_t max_align = 1; + for (int i = 0; i < type->struct_field_count; i++) { + size_t a = type_align_of(type->struct_fields[i].type); + if (a > max_align) max_align = a; + } + return max_align; + } + case PAZE_TYPE_TYPEDEF: + if (type->base) return type_align_of(type->base); + return 1; + default: return 1; + } +} + +static paze_type_t *type_enum(paze_parser_t *p, paze_str_t name) +{ + paze_type_t *t = type_new(p, PAZE_TYPE_ENUM); + t->name = name; + return t; +} + +static paze_type_t *type_typedef(paze_parser_t *p, paze_str_t name, + paze_type_t *underlying) +{ + paze_type_t *t = type_new(p, PAZE_TYPE_TYPEDEF); + t->name = name; + t->base = underlying; + return t; +} + +static paze_type_t *type_typeof(paze_parser_t *p, paze_ast_node_t *expr) +{ + paze_type_t *t = type_new(p, PAZE_TYPE_TYPEOF); + t->typeof_expr = expr; + return t; +} + +/* If `type` is an unsized array (declared as `T x[]`) and `init` provides + * an element count, set array_size accordingly so downstream layout/codegen + * allocate the correct storage. Handles: + * int arr[] = {1,2,3} → array_size = 3 + * char buf[] = "Hello" → array_size = 6 (incl. NUL) + * char buf[] = {"Hello"} → array_size = 6 + * The array type created by parse_postfix_type is freshly allocated per + * declarator, so mutating it in place is safe. */ +static void fixup_array_size_from_init(paze_type_t *type, paze_ast_node_t *init) +{ + if (!type || !init) return; + paze_type_t *rt = type; + while (rt && rt->kind == PAZE_TYPE_TYPEDEF) rt = rt->base; + if (!rt || rt->kind != PAZE_TYPE_ARRAY) return; + if (rt->array_size >= 0) return; /* already sized */ + + if (init->kind == PAZE_NODE_INIT_LIST_EXPR) { + size_t n = init->data.init_list_expr.elements.len; + /* char buf[] = {"Hello"} — single string element sizing a char array. */ + if (n == 1 && rt->base && rt->base->kind == PAZE_TYPE_CHAR) { + paze_ast_node_t *e = init->data.init_list_expr.elements.data[0]; + if (e && e->kind == PAZE_NODE_STRING_LITERAL) { + rt->array_size = (long)e->data.string_literal.value.len + 1; + return; + } + } + rt->array_size = (long)n; + } else if (init->kind == PAZE_NODE_STRING_LITERAL) { + rt->array_size = (long)init->data.string_literal.value.len + 1; + } +} + +/* ======================================================================== + * Typedef Registry + * ======================================================================== */ + +static void register_typedef_name(paze_parser_t *p, paze_str_t name) +{ + if (name.len == 0) return; + for (size_t i = 0; i < p->typedef_count; i++) { + if (p->typedef_names[i].len == name.len && + memcmp(p->typedef_names[i].data, name.data, name.len) == 0) + return; + } + if (p->typedef_count >= p->typedef_cap) { + size_t new_cap = p->typedef_cap == 0 ? 16 : p->typedef_cap * 2; + p->typedef_names = (paze_str_t *)realloc(p->typedef_names, new_cap * sizeof(paze_str_t)); + p->typedef_cap = new_cap; + } + p->typedef_names[p->typedef_count++] = name; +} + +static bool is_typedef_name(paze_parser_t *p, paze_str_t name) +{ + for (size_t i = 0; i < p->typedef_count; i++) { + if (p->typedef_names[i].len == name.len && + memcmp(p->typedef_names[i].data, name.data, name.len) == 0) + return true; + } + return false; +} + +/* ======================================================================== + * Token Helpers + * ======================================================================== */ + +static paze_token_t peek(paze_parser_t *p) +{ + if (p->pos >= p->token_count) { + paze_token_t eof; + memset(&eof, 0, sizeof(eof)); + eof.kind = PAZE_TK_EOF; + return eof; + } + return p->tokens[p->pos]; +} + +static paze_token_t advance(paze_parser_t *p) +{ + paze_token_t tk = peek(p); + if (p->pos < p->token_count) { + p->pos++; + } + return tk; +} + +static paze_token_t consume(paze_parser_t *p, paze_tk_t expected, const char *msg) +{ + paze_token_t tk = peek(p); + if (tk.kind != expected) { + if (p->diag) { + paze_diagnostics_error(p->diag, tk.loc, + "expected '%s', got '%s'", msg ? msg : paze_tk_name(expected), + paze_tk_name(tk.kind)); + } + p->had_error = true; + return tk; + } + advance(p); + return tk; +} + +static bool check(paze_parser_t *p, paze_tk_t kind) +{ + return peek(p).kind == kind; +} + +static bool match(paze_parser_t *p, size_t count, ...) +{ + va_list args; + va_start(args, count); + bool found = false; + for (size_t i = 0; i < count; i++) { + paze_tk_t kind = (paze_tk_t)va_arg(args, int); + if (check(p, kind)) { + found = true; + break; + } + } + va_end(args); + return found; +} + +static bool is_at_end(paze_parser_t *p) +{ + return peek(p).kind == PAZE_TK_EOF; +} + +/* ======================================================================== + * Error Recovery + * ======================================================================== */ + +static void synchronize(paze_parser_t *p) +{ + p->had_error = true; + while (!is_at_end(p)) { + paze_token_t tk = peek(p); + if (tk.kind == PAZE_TK_SEMICOLON) { + advance(p); + return; + } + if (tk.kind == PAZE_TK_RBRACE) { + advance(p); + return; + } + if (tk.kind == PAZE_TK_RPAREN) { + advance(p); + return; + } + advance(p); + } +} + +/* ======================================================================== + * Public API + * ======================================================================== */ + +paze_parser_t *paze_parser_create(const paze_token_t *tokens, + size_t token_count, + paze_diagnostics_t *diag, + paze_arena_t *arena) +{ + paze_parser_t *parser = (paze_parser_t *)calloc(1, sizeof(paze_parser_t)); + if (!parser) return NULL; + + parser->tokens = tokens; + parser->token_count = token_count; + parser->pos = 0; + parser->diag = diag; + parser->had_error = false; + parser->typedef_names = NULL; + parser->typedef_count = 0; + parser->typedef_cap = 0; + + if (arena) { + parser->arena = arena; + parser->arena_owned = false; + } else { + parser->arena = paze_arena_create(); + parser->arena_owned = true; + } + return parser; +} + +void paze_parser_destroy(paze_parser_t *parser) +{ + if (!parser) return; + if (parser->arena_owned && parser->arena) { + paze_arena_destroy(parser->arena); + } + free(parser->typedef_names); + free(parser->struct_names); + free(parser->struct_types); + free(parser); +} + +paze_ast_node_t *paze_parser_parse(paze_parser_t *parser) +{ + paze_ast_node_t *tu = paze_ast_new(parser->arena, + PAZE_NODE_TRANSLATION_UNIT, + PAZE_LOC_EMPTY); + if (!tu) return NULL; + + while (!is_at_end(parser)) { + paze_ast_node_t *decl = parse_declaration(parser); + if (decl) { + paze_ast_node_arr_t_push(&tu->data.translation_unit.decls, decl); + } + if (parser->had_error) { + synchronize(parser); + } + } + return tu; +} + +/* ======================================================================== + * Type Specifier Parsing + * ======================================================================== */ + +static bool is_type_qualifier_token(paze_tk_t kind) +{ + return kind == PAZE_TK_KW_CONST || kind == PAZE_TK_KW_VOLATILE; +} + +static bool is_storage_class_token(paze_tk_t kind) +{ + return kind == PAZE_TK_KW_STATIC || kind == PAZE_TK_KW_EXTERN || + kind == PAZE_TK_KW_AUTO || kind == PAZE_TK_KW_REGISTER; +} + +static paze_type_t *parse_type_specifier(paze_parser_t *p, paze_loc_t *loc) +{ + paze_token_t tk = peek(p); + paze_type_t *type = NULL; + bool got_unsigned = false; + bool got_signed = false; + bool got_long = false; + + if (loc) *loc = tk.loc; + + if (tk.kind == PAZE_TK_KW_STRUCT || tk.kind == PAZE_TK_KW_UNION) { + advance(p); + bool is_union = (tk.kind == PAZE_TK_KW_UNION); + paze_str_t name = PAZE_STR_EMPTY; + + if (check(p, PAZE_TK_IDENTIFIER)) { + tk = advance(p); + name = tk.text; + } + + if (check(p, PAZE_TK_LBRACE)) { + advance(p); + + paze_struct_field_t *tmp_fields = NULL; + int tmp_count = 0; + int tmp_cap = 0; + + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_type_t *field_type = parse_type_specifier(p, loc); + if (!field_type) { + synchronize(p); + free(tmp_fields); + return NULL; + } + + paze_str_t field_name = PAZE_STR_EMPTY; + if (check(p, PAZE_TK_IDENTIFIER)) { + tk = advance(p); + field_name = tk.text; + } + + if (check(p, PAZE_TK_COLON)) { + advance(p); + if (check(p, PAZE_TK_INT_LITERAL)) advance(p); + } + + if (check(p, PAZE_TK_COMMA)) { + advance(p); + while (check(p, PAZE_TK_IDENTIFIER) || check(p, PAZE_TK_COMMA)) { + if (check(p, PAZE_TK_COMMA)) advance(p); + if (check(p, PAZE_TK_IDENTIFIER)) { + tk = advance(p); + field_name = tk.text; + if (tmp_count >= tmp_cap) { + tmp_cap = tmp_cap == 0 ? 4 : tmp_cap * 2; + tmp_fields = (paze_struct_field_t *)realloc(tmp_fields, tmp_cap * sizeof(paze_struct_field_t)); + } + tmp_fields[tmp_count].name = field_name; + tmp_fields[tmp_count].type = field_type; + tmp_count++; + } + if (check(p, PAZE_TK_COLON)) { + advance(p); + if (check(p, PAZE_TK_INT_LITERAL)) advance(p); + } + } + } else { + if (tmp_count >= tmp_cap) { + tmp_cap = tmp_cap == 0 ? 4 : tmp_cap * 2; + tmp_fields = (paze_struct_field_t *)realloc(tmp_fields, tmp_cap * sizeof(paze_struct_field_t)); + } + tmp_fields[tmp_count].name = field_name; + tmp_fields[tmp_count].type = field_type; + tmp_count++; + } + + consume(p, PAZE_TK_SEMICOLON, ";"); + } + + consume(p, PAZE_TK_RBRACE, "}"); + + paze_type_t *type = type_struct(p, name, is_union); + if (type && tmp_count > 0) { + type->struct_fields = (paze_struct_field_t *)paze_arena_calloc(p->arena, sizeof(paze_struct_field_t) * (size_t)tmp_count); + type->struct_field_count = tmp_count; + type->struct_field_cap = tmp_count; + + size_t offset = 0; + size_t max_align = 1; + for (int i = 0; i < tmp_count; i++) { + size_t sz = type_size_of(tmp_fields[i].type); + size_t align = type_align_of(tmp_fields[i].type); + if (align > max_align) max_align = align; + if (!is_union) { + offset = (offset + align - 1) & ~(align - 1); + } + type->struct_fields[i].name = tmp_fields[i].name; + type->struct_fields[i].type = tmp_fields[i].type; + type->struct_fields[i].offset = (int)offset; + type->struct_fields[i].size = sz; + if (!is_union) { + offset += sz; + } + if (sz > type->struct_size) { + type->struct_size = sz; + } + } + if (!is_union) { + offset = (offset + max_align - 1) & ~(max_align - 1); + type->struct_size = offset; + } + } + free(tmp_fields); + return type; + } else { + return type_struct(p, name, is_union); + } + } + + if (tk.kind == PAZE_TK_KW_ENUM) { + advance(p); + paze_str_t name = PAZE_STR_EMPTY; + if (check(p, PAZE_TK_IDENTIFIER)) { + tk = advance(p); + name = tk.text; + } + + if (check(p, PAZE_TK_LBRACE)) { + advance(p); + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + if (check(p, PAZE_TK_IDENTIFIER)) advance(p); + if (check(p, PAZE_TK_ASSIGN)) { + advance(p); + parse_assignment_expr(p); + } + if (check(p, PAZE_TK_COMMA)) advance(p); + } + consume(p, PAZE_TK_RBRACE, "}"); + } + return type_enum(p, name); + } + + if (tk.kind == PAZE_TK_KW_TYPEOF) { + advance(p); + consume(p, PAZE_TK_LPAREN, "("); + if (paze_tk_is_type_keyword(peek(p).kind) || + check(p, PAZE_TK_KW_STRUCT) || check(p, PAZE_TK_KW_UNION) || + check(p, PAZE_TK_KW_ENUM) || check(p, PAZE_TK_KW_TYPEDEF)) { + paze_type_t *t = parse_type_specifier(p, loc); + consume(p, PAZE_TK_RPAREN, ")"); + paze_type_t *result = type_new(p, PAZE_TYPE_TYPEOF); + result->base = t; + return result; + } else { + paze_ast_node_t *expr = parse_expression(p); + consume(p, PAZE_TK_RPAREN, ")"); + return type_typeof(p, expr); + } + } + + if (tk.kind == PAZE_TK_KW_TYPEDEF) { + advance(p); + paze_type_t *underlying = parse_type_specifier(p, loc); + paze_str_t new_name = PAZE_STR_EMPTY; + bool dummy = false; + parse_declarator_type(p, underlying, &new_name, &dummy); + consume(p, PAZE_TK_SEMICOLON, ";"); + return type_typedef(p, new_name, underlying); + } + + while (paze_tk_is_type_keyword(peek(p).kind) || + is_storage_class_token(peek(p).kind) || + is_type_qualifier_token(peek(p).kind)) { + tk = peek(p); + + if (is_storage_class_token(tk.kind)) { + advance(p); + continue; + } + if (is_type_qualifier_token(tk.kind)) { + advance(p); + continue; + } + + switch (tk.kind) { + case PAZE_TK_KW_VOID: + advance(p); + type = type_basic(p, PAZE_TYPE_VOID); + break; + case PAZE_TK_KW_CHAR: + advance(p); + type = type_basic(p, PAZE_TYPE_CHAR); + break; + case PAZE_TK_KW_SHORT: + advance(p); + type = type_basic(p, PAZE_TYPE_SHORT); + break; + case PAZE_TK_KW_INT: + advance(p); + if (!type) type = type_basic(p, PAZE_TYPE_INT); + break; + case PAZE_TK_KW_LONG: + advance(p); + got_long = true; + if (!type) type = type_basic(p, PAZE_TYPE_LONG); + break; + case PAZE_TK_KW_UNSIGNED: + advance(p); + got_unsigned = true; + break; + case PAZE_TK_KW_SIGNED: + advance(p); + got_signed = true; + break; + case PAZE_TK_KW_BOOL: + advance(p); + type = type_basic(p, PAZE_TYPE_BOOL); + break; + default: + goto done_type; + } + } + +done_type: + if (!type && check(p, PAZE_TK_IDENTIFIER)) { + paze_token_t id_tk = peek(p); + if (is_typedef_name(p, id_tk.text)) { + advance(p); + return type_typedef(p, id_tk.text, NULL); + } + } + if (!type && (got_unsigned || got_signed)) { + type = type_basic(p, got_unsigned ? PAZE_TYPE_UNSIGNED : PAZE_TYPE_SIGNED); + } + if (!type && got_long) { + type = type_basic(p, PAZE_TYPE_LONG); + } + if (!type) { + type = type_basic(p, PAZE_TYPE_INT); + } + + if (got_unsigned) { + paze_type_t *ut = type_new(p, PAZE_TYPE_UNSIGNED); + ut->base = type; + type = ut; + } else if (got_signed) { + paze_type_t *st = type_new(p, PAZE_TYPE_SIGNED); + st->base = type; + type = st; + } + + return type; +} + +static paze_type_t *parse_declarator_type(paze_parser_t *p, paze_type_t *base_type, + paze_str_t *out_name, bool *out_is_variadic) +{ + while (check(p, PAZE_TK_STAR)) { + advance(p); + base_type = type_pointer(p, base_type); + } + return parse_direct_declarator(p, base_type, out_name, out_is_variadic); +} + +static paze_type_t *parse_direct_declarator(paze_parser_t *p, paze_type_t *base_type, + paze_str_t *out_name, bool *out_is_variadic) +{ + paze_token_t tk; + + if (check(p, PAZE_TK_LPAREN)) { + size_t save_pos = p->pos; + paze_token_t next = peek(p); + if (next.kind == PAZE_TK_STAR) { + advance(p); + paze_type_t *inner = parse_declarator_type(p, base_type, + out_name, out_is_variadic); + consume(p, PAZE_TK_RPAREN, ")"); + return parse_postfix_type(p, inner, out_name, out_is_variadic); + } else if (next.kind == PAZE_TK_LPAREN) { + advance(p); + paze_type_t *inner = parse_declarator_type(p, base_type, + out_name, out_is_variadic); + consume(p, PAZE_TK_RPAREN, ")"); + return parse_postfix_type(p, inner, out_name, out_is_variadic); + } + p->pos = save_pos; + } + + tk = peek(p); + if (tk.kind == PAZE_TK_IDENTIFIER) { + advance(p); + *out_name = tk.text; + } + + return parse_postfix_type(p, base_type, out_name, out_is_variadic); +} + +static paze_type_t *parse_postfix_type(paze_parser_t *p, paze_type_t *base_type, + paze_str_t *out_name, bool *out_is_variadic) +{ + (void)out_name; + (void)out_is_variadic; + while (true) { + paze_token_t tk = peek(p); + + if (tk.kind == PAZE_TK_LBRACKET) { + advance(p); + long size = -1; + if (check(p, PAZE_TK_INT_LITERAL)) { + tk = advance(p); + size = tk.int_value; + } + consume(p, PAZE_TK_RBRACKET, "]"); + base_type = type_array(p, base_type, size); + continue; + } + + if (tk.kind == PAZE_TK_LPAREN) { + advance(p); + bool is_variadic = false; + parse_param_type_list(p, &is_variadic); + consume(p, PAZE_TK_RPAREN, ")"); + base_type = type_function(p, base_type); + if (out_is_variadic) *out_is_variadic = is_variadic; + continue; + } + + break; + } + return base_type; +} + +static paze_type_t *parse_param_type_list(paze_parser_t *p, bool *is_variadic) +{ + *is_variadic = false; + + if (check(p, PAZE_TK_KW_VOID)) { + advance(p); + if (check(p, PAZE_TK_RPAREN)) { + return NULL; + } + } + + while (!check(p, PAZE_TK_RPAREN) && !is_at_end(p)) { + if (check(p, PAZE_TK_DOT)) { + advance(p); + if (check(p, PAZE_TK_DOT)) { + advance(p); + if (check(p, PAZE_TK_DOT)) { + advance(p); + *is_variadic = true; + break; + } + p->pos -= 2; + } else { + p->pos -= 1; + } + } + + paze_type_t *ptype = parse_type_specifier(p, NULL); + if (!ptype) break; + + paze_str_t pname = PAZE_STR_EMPTY; + bool variadic = false; + parse_declarator_type(p, ptype, &pname, &variadic); + + if (check(p, PAZE_TK_COMMA)) advance(p); + else break; + } + return NULL; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" + +static paze_type_t *parse_type_name(paze_parser_t *p, paze_loc_t *loc) +{ + paze_type_t *base = parse_type_specifier(p, loc); + if (!base) return NULL; + + paze_str_t dummy_name = PAZE_STR_EMPTY; + bool dummy_variadic = false; + return parse_declarator_type(p, base, &dummy_name, &dummy_variadic); +} + +/* ======================================================================== + * Expression Parsing (Precedence Climbing) + * ======================================================================== */ + +static paze_ast_node_t *parse_expression(paze_parser_t *p) +{ + paze_ast_node_t *left = parse_assignment_expr(p); + if (!left) return NULL; + + while (check(p, PAZE_TK_COMMA)) { + paze_token_t tk = advance(p); + paze_ast_node_t *right = parse_assignment_expr(p); + paze_ast_node_t *node = paze_ast_new(p->arena, PAZE_NODE_COMMA_EXPR, tk.loc); + if (node) { + node->data.comma_expr.left = left; + node->data.comma_expr.right = right; + } + left = node; + } + return left; +} + +static paze_ast_node_t *parse_assignment_expr(paze_parser_t *p) +{ + paze_ast_node_t *left = parse_conditional_expr(p); + if (!left) return NULL; + + if (paze_tk_is_assignment(peek(p).kind)) { + paze_token_t tk = advance(p); + paze_ast_node_t *right = parse_assignment_expr(p); + paze_ast_node_t *node = paze_ast_new(p->arena, PAZE_NODE_ASSIGN_EXPR, tk.loc); + if (node) { + node->data.assign_expr.op = tk.kind; + node->data.assign_expr.target = left; + node->data.assign_expr.value = right; + } + return node; + } + return left; +} + +static paze_ast_node_t *parse_conditional_expr(paze_parser_t *p) +{ + paze_ast_node_t *cond = parse_logical_or_expr(p); + if (!cond) return NULL; + + if (check(p, PAZE_TK_QUESTION)) { + paze_token_t qmark = advance(p); + paze_ast_node_t *then_expr = parse_expression(p); + consume(p, PAZE_TK_COLON, ":"); + paze_ast_node_t *else_expr = parse_conditional_expr(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_CONDITIONAL_EXPR, qmark.loc); + if (node) { + node->data.conditional_expr.condition = cond; + node->data.conditional_expr.then_expr = then_expr; + node->data.conditional_expr.else_expr = else_expr; + } + return node; + } + return cond; +} + +#define DEFINE_BINARY_LEFT(fn_name, next_fn, tk1, tk2) \ + static paze_ast_node_t *fn_name(paze_parser_t *p) \ + { \ + paze_ast_node_t *left = next_fn(p); \ + if (!left) return NULL; \ + while (match(p, 2, tk1, tk2)) { \ + paze_token_t tk = advance(p); \ + paze_ast_node_t *right = next_fn(p); \ + paze_ast_node_t *node = paze_ast_new(p->arena, \ + PAZE_NODE_BINARY_EXPR, tk.loc); \ + if (node) { \ + node->data.binary_expr.op = tk.kind; \ + node->data.binary_expr.left = left; \ + node->data.binary_expr.right = right; \ + } \ + left = node; \ + } \ + return left; \ + } + +#define DEFINE_BINARY_SINGLE(fn_name, next_fn, tk1) \ + static paze_ast_node_t *fn_name(paze_parser_t *p) \ + { \ + paze_ast_node_t *left = next_fn(p); \ + if (!left) return NULL; \ + while (check(p, tk1)) { \ + paze_token_t tk = advance(p); \ + paze_ast_node_t *right = next_fn(p); \ + paze_ast_node_t *node = paze_ast_new(p->arena, \ + PAZE_NODE_BINARY_EXPR, tk.loc); \ + if (node) { \ + node->data.binary_expr.op = tk.kind; \ + node->data.binary_expr.left = left; \ + node->data.binary_expr.right = right; \ + } \ + left = node; \ + } \ + return left; \ + } + +DEFINE_BINARY_LEFT(parse_logical_or_expr, parse_logical_and_expr, PAZE_TK_OR_OR, PAZE_TK_OR_OR) +DEFINE_BINARY_LEFT(parse_logical_and_expr, parse_bitwise_or_expr, PAZE_TK_AND_AND, PAZE_TK_AND_AND) +DEFINE_BINARY_SINGLE(parse_bitwise_or_expr, parse_bitwise_xor_expr, PAZE_TK_PIPE) +DEFINE_BINARY_SINGLE(parse_bitwise_xor_expr, parse_bitwise_and_expr, PAZE_TK_CARET) +DEFINE_BINARY_SINGLE(parse_bitwise_and_expr, parse_equality_expr, PAZE_TK_AMP) +DEFINE_BINARY_LEFT(parse_equality_expr, parse_relational_expr, PAZE_TK_EQ, PAZE_TK_NOT_EQ) + +static paze_ast_node_t *parse_relational_expr(paze_parser_t *p) +{ + paze_ast_node_t *left = parse_shift_expr(p); + if (!left) return NULL; + while (match(p, 4, PAZE_TK_LT, PAZE_TK_GT, PAZE_TK_LE, PAZE_TK_GE)) { + paze_token_t tk = advance(p); + paze_ast_node_t *right = parse_shift_expr(p); + paze_ast_node_t *node = paze_ast_new(p->arena, PAZE_NODE_BINARY_EXPR, tk.loc); + if (node) { + node->data.binary_expr.op = tk.kind; + node->data.binary_expr.left = left; + node->data.binary_expr.right = right; + } + left = node; + } + return left; +} + +DEFINE_BINARY_LEFT(parse_shift_expr, parse_additive_expr, PAZE_TK_SHL, PAZE_TK_SHR) +DEFINE_BINARY_LEFT(parse_additive_expr, parse_multiplicative_expr, PAZE_TK_PLUS, PAZE_TK_MINUS) + +static paze_ast_node_t *parse_multiplicative_expr(paze_parser_t *p) +{ + paze_ast_node_t *left = parse_unary_expr(p); + if (!left) return NULL; + while (match(p, 3, PAZE_TK_STAR, PAZE_TK_SLASH, PAZE_TK_PERCENT)) { + paze_token_t tk = advance(p); + paze_ast_node_t *right = parse_unary_expr(p); + paze_ast_node_t *node = paze_ast_new(p->arena, PAZE_NODE_BINARY_EXPR, tk.loc); + if (node) { + node->data.binary_expr.op = tk.kind; + node->data.binary_expr.left = left; + node->data.binary_expr.right = right; + } + left = node; + } + return left; +} + +#undef DEFINE_BINARY_LEFT +#undef DEFINE_BINARY_SINGLE + +static bool is_type_start_ahead(paze_parser_t *p, size_t offset) +{ + size_t idx = p->pos + offset; + if (idx >= p->token_count) return false; + paze_tk_t kind = p->tokens[idx].kind; + return paze_tk_is_type_keyword(kind) || + kind == PAZE_TK_KW_STRUCT || kind == PAZE_TK_KW_UNION || + kind == PAZE_TK_KW_ENUM || kind == PAZE_TK_KW_TYPEDEF; +} + +static paze_ast_node_t *parse_unary_expr(paze_parser_t *p) +{ + paze_token_t tk = peek(p); + + switch (tk.kind) { + case PAZE_TK_PLUS_PLUS: + case PAZE_TK_MINUS_MINUS: + case PAZE_TK_AMP: + case PAZE_TK_STAR: + case PAZE_TK_TILDE: + case PAZE_TK_NOT: + case PAZE_TK_PLUS: + case PAZE_TK_MINUS: { + advance(p); + paze_ast_node_t *operand = parse_unary_expr(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_UNARY_EXPR, tk.loc); + if (node) { + node->data.unary_expr.op = tk.kind; + node->data.unary_expr.operand = operand; + node->data.unary_expr.is_postfix = false; + } + return node; + } + + case PAZE_TK_KW_SIZEOF: { + advance(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_SIZEOF_EXPR, tk.loc); + if (check(p, PAZE_TK_LPAREN) && is_type_start_ahead(p, 1)) { + advance(p); + paze_type_t *t = parse_type_specifier(p, NULL); + consume(p, PAZE_TK_RPAREN, ")"); + node->data.sizeof_expr.is_type = true; + node->data.sizeof_expr.size_type = t; + } else { + paze_ast_node_t *expr = parse_unary_expr(p); + node->data.sizeof_expr.is_type = false; + node->data.sizeof_expr.expr = expr; + } + return node; + } + + default: + break; + } + + return parse_postfix_expr(p); +} + +static paze_ast_node_t *parse_postfix_expr(paze_parser_t *p) +{ + paze_ast_node_t *expr = parse_primary_expr(p); + if (!expr) return NULL; + + while (true) { + paze_token_t tk = peek(p); + + switch (tk.kind) { + case PAZE_TK_LPAREN: { + advance(p); + paze_ast_node_arr_t args; + paze_ast_node_arr_t_init(&args); + + while (!check(p, PAZE_TK_RPAREN) && !is_at_end(p)) { + paze_ast_node_t *arg = parse_assignment_expr(p); + if (arg) paze_ast_node_arr_t_push(&args, arg); + if (check(p, PAZE_TK_COMMA)) advance(p); + else break; + } + consume(p, PAZE_TK_RPAREN, ")"); + + paze_ast_node_t *call = paze_ast_new(p->arena, + PAZE_NODE_CALL_EXPR, tk.loc); + if (call) { + call->data.call_expr.function = expr; + call->data.call_expr.args = args; + } + expr = call; + break; + } + + case PAZE_TK_LBRACKET: { + advance(p); + paze_ast_node_t *index = parse_expression(p); + consume(p, PAZE_TK_RBRACKET, "]"); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_INDEX_EXPR, tk.loc); + if (node) { + node->data.index_expr.array = expr; + node->data.index_expr.index = index; + } + expr = node; + break; + } + + case PAZE_TK_DOT: + case PAZE_TK_ARROW: { + advance(p); + bool is_arrow = (tk.kind == PAZE_TK_ARROW); + paze_token_t member_tk = advance(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_MEMBER_EXPR, tk.loc); + if (node) { + node->data.member_expr.object = expr; + node->data.member_expr.member = member_tk.text; + node->data.member_expr.is_arrow = is_arrow; + } + expr = node; + break; + } + + case PAZE_TK_PLUS_PLUS: + case PAZE_TK_MINUS_MINUS: { + advance(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_UNARY_EXPR, tk.loc); + if (node) { + node->data.unary_expr.op = tk.kind; + node->data.unary_expr.operand = expr; + node->data.unary_expr.is_postfix = true; + } + expr = node; + break; + } + + default: + return expr; + } + } +} + +static paze_ast_node_t *parse_primary_expr(paze_parser_t *p) +{ + paze_token_t tk = peek(p); + + switch (tk.kind) { + case PAZE_TK_INT_LITERAL: { + advance(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_INT_LITERAL, tk.loc); + if (node) node->data.int_literal.value = tk.int_value; + return node; + } + + case PAZE_TK_CHAR_LITERAL: { + advance(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_CHAR_LITERAL, tk.loc); + if (node) node->data.char_literal.value = (char)tk.int_value; + return node; + } + + case PAZE_TK_STRING_LITERAL: { + advance(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_STRING_LITERAL, tk.loc); + if (node) { + node->data.string_literal.value = tk.str_value; + } + while (check(p, PAZE_TK_STRING_LITERAL)) { + paze_token_t next = advance(p); + paze_ast_node_t *s2 = paze_ast_new(p->arena, + PAZE_NODE_STRING_LITERAL, next.loc); + if (s2) s2->data.string_literal.value = next.str_value; + + paze_ast_node_arr_t parts; + paze_ast_node_arr_t_init(&parts); + paze_ast_node_arr_t_push(&parts, node); + paze_ast_node_arr_t_push(&parts, s2); + + paze_ast_node_t *concat = paze_ast_new(p->arena, + PAZE_NODE_STRING_CONCAT_EXPR, tk.loc); + if (concat) { + concat->data.string_concat_expr.parts = parts; + } + node = concat; + } + return node; + } + + case PAZE_TK_IDENTIFIER: { + advance(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_IDENTIFIER_REF, tk.loc); + if (node) node->data.identifier_ref.name = tk.text; + return node; + } + + case PAZE_TK_LPAREN: { + if (is_type_start_ahead(p, 1)) { + paze_token_t lparen = advance(p); + paze_loc_t type_loc = tk.loc; + paze_type_t *t = parse_type_specifier(p, &type_loc); + consume(p, PAZE_TK_RPAREN, ")"); + + if (check(p, PAZE_TK_LBRACE)) { + advance(p); + paze_ast_node_arr_t init_elements; + paze_ast_node_arr_t_init(&init_elements); + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_ast_node_t *elem = parse_assignment_expr(p); + if (elem) paze_ast_node_arr_t_push(&init_elements, elem); + if (check(p, PAZE_TK_COMMA)) advance(p); + else break; + } + consume(p, PAZE_TK_RBRACE, "}"); + paze_ast_node_t *init_list = paze_ast_new(p->arena, + PAZE_NODE_INIT_LIST_EXPR, lparen.loc); + if (init_list) { + init_list->data.init_list_expr.elements = init_elements; + } + paze_ast_node_t *cl = paze_ast_new(p->arena, + PAZE_NODE_COMPOUND_LITERAL_EXPR, lparen.loc); + if (cl) { + cl->data.compound_literal_expr.target_type = t; + cl->data.compound_literal_expr.init = init_list; + } + return cl; + } else { + paze_ast_node_t *expr = parse_postfix_expr(p); + paze_ast_node_t *cast = paze_ast_new(p->arena, + PAZE_NODE_CAST_EXPR, lparen.loc); + if (cast) { + cast->data.cast_expr.target_type = t; + cast->data.cast_expr.expr = expr; + } + return cast; + } + } + + advance(p); + if (check(p, PAZE_TK_LBRACE)) { + paze_token_t lbrace = advance(p); + paze_ast_node_arr_t stmts; + paze_ast_node_arr_t_init(&stmts); + + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_ast_node_t *stmt = parse_declaration_or_statement(p); + if (stmt) paze_ast_node_arr_t_push(&stmts, stmt); + if (p->had_error) synchronize(p); + } + consume(p, PAZE_TK_RBRACE, "}"); + + paze_ast_node_t *block = paze_ast_new(p->arena, + PAZE_NODE_BLOCK_STMT, lbrace.loc); + if (block) block->data.block_stmt.stmts = stmts; + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_STMT_EXPR, tk.loc); + if (node) node->data.stmt_expr.body = block; + return node; + } + + paze_ast_node_t *expr = parse_expression(p); + consume(p, PAZE_TK_RPAREN, ")"); + return expr; + } + + default: + if (p->diag) { + paze_diagnostics_error(p->diag, tk.loc, + "unexpected token '%s'", paze_tk_name(tk.kind)); + } + p->had_error = true; + return NULL; + } +} + +/* ======================================================================== + * Statement Parsing + * ======================================================================== */ + +static paze_ast_node_t *parse_declaration_or_statement(paze_parser_t *p) +{ + paze_tk_t kind = peek(p).kind; + + if (paze_tk_is_type_keyword(kind) || + kind == PAZE_TK_KW_STRUCT || kind == PAZE_TK_KW_UNION || + kind == PAZE_TK_KW_ENUM || kind == PAZE_TK_KW_TYPEDEF) { + paze_ast_node_t *decl = parse_declaration(p); + if (decl && paze_ast_is_declaration(decl->kind)) { + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_DECL_STMT, decl->loc); + if (node) { + node->data.decl_stmt.decl = decl; + } + return node; + } + return decl; + } + + /* Check if this is a typedef name used as a type specifier */ + if (kind == PAZE_TK_IDENTIFIER) { + paze_token_t id_tk = peek(p); + if (is_typedef_name(p, id_tk.text)) { + paze_ast_node_t *decl = parse_declaration(p); + if (decl && paze_ast_is_declaration(decl->kind)) { + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_DECL_STMT, decl->loc); + if (node) { + node->data.decl_stmt.decl = decl; + } + return node; + } + return decl; + } + } + + return parse_statement(p); +} + +static paze_ast_node_t *parse_statement(paze_parser_t *p) +{ + paze_token_t tk = peek(p); + + switch (tk.kind) { + case PAZE_TK_LBRACE: + return parse_block_statement(p); + case PAZE_TK_KW_IF: + return parse_if_statement(p); + case PAZE_TK_KW_WHILE: + return parse_while_statement(p); + case PAZE_TK_KW_DO: + return parse_do_while_statement(p); + case PAZE_TK_KW_FOR: + return parse_for_statement(p); + case PAZE_TK_KW_SWITCH: + return parse_switch_statement(p); + case PAZE_TK_KW_RETURN: + return parse_return_statement(p); + case PAZE_TK_KW_BREAK: + case PAZE_TK_KW_CONTINUE: + case PAZE_TK_KW_GOTO: + return parse_jump_statement(p); + case PAZE_TK_SEMICOLON: { + advance(p); + return paze_ast_new(p->arena, PAZE_NODE_NULL_STMT, tk.loc); + } + case PAZE_TK_IDENTIFIER: { + if (p->pos + 1 < p->token_count && + p->tokens[p->pos + 1].kind == PAZE_TK_COLON) { + advance(p); + advance(p); + paze_ast_node_t *stmt = parse_statement(p); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_LABEL_STMT, tk.loc); + if (node) { + node->data.label_stmt.label = tk.text; + node->data.label_stmt.stmt = stmt; + } + return node; + } + return parse_expression_statement(p); + } + default: + return parse_expression_statement(p); + } +} + +static paze_ast_node_t *parse_block_statement(paze_parser_t *p) +{ + paze_token_t lbrace = consume(p, PAZE_TK_LBRACE, "{"); + paze_ast_node_arr_t stmts; + paze_ast_node_arr_t_init(&stmts); + + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_ast_node_t *stmt = parse_declaration_or_statement(p); + if (stmt) paze_ast_node_arr_t_push(&stmts, stmt); + if (p->had_error) synchronize(p); + } + + consume(p, PAZE_TK_RBRACE, "}"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_BLOCK_STMT, lbrace.loc); + if (node) node->data.block_stmt.stmts = stmts; + return node; +} + +static paze_ast_node_t *parse_expression_statement(paze_parser_t *p) +{ + paze_token_t tk = peek(p); + paze_ast_node_t *expr = parse_expression(p); + if (!expr) return NULL; + consume(p, PAZE_TK_SEMICOLON, ";"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_EXPR_STMT, tk.loc); + if (node) node->data.expr_stmt.expr = expr; + return node; +} + +static paze_ast_node_t *parse_if_statement(paze_parser_t *p) +{ + paze_token_t if_tk = advance(p); + consume(p, PAZE_TK_LPAREN, "("); + paze_ast_node_t *condition = parse_expression(p); + consume(p, PAZE_TK_RPAREN, ")"); + paze_ast_node_t *then_branch = parse_statement(p); + paze_ast_node_t *else_branch = NULL; + if (check(p, PAZE_TK_KW_ELSE)) { + advance(p); + else_branch = parse_statement(p); + } + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_IF_STMT, if_tk.loc); + if (node) { + node->data.if_stmt.condition = condition; + node->data.if_stmt.then_branch = then_branch; + node->data.if_stmt.else_branch = else_branch; + } + return node; +} + +static paze_ast_node_t *parse_while_statement(paze_parser_t *p) +{ + paze_token_t while_tk = advance(p); + consume(p, PAZE_TK_LPAREN, "("); + paze_ast_node_t *condition = parse_expression(p); + consume(p, PAZE_TK_RPAREN, ")"); + paze_ast_node_t *body = parse_statement(p); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_WHILE_STMT, while_tk.loc); + if (node) { + node->data.while_stmt.condition = condition; + node->data.while_stmt.body = body; + } + return node; +} + +static paze_ast_node_t *parse_do_while_statement(paze_parser_t *p) +{ + paze_token_t do_tk = advance(p); + paze_ast_node_t *body = parse_statement(p); + consume(p, PAZE_TK_KW_WHILE, "while"); + consume(p, PAZE_TK_LPAREN, "("); + paze_ast_node_t *condition = parse_expression(p); + consume(p, PAZE_TK_RPAREN, ")"); + consume(p, PAZE_TK_SEMICOLON, ";"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_DO_WHILE_STMT, do_tk.loc); + if (node) { + node->data.do_while_stmt.body = body; + node->data.do_while_stmt.condition = condition; + } + return node; +} + +static paze_ast_node_t *parse_for_statement(paze_parser_t *p) +{ + paze_token_t for_tk = advance(p); + consume(p, PAZE_TK_LPAREN, "("); + + paze_ast_node_t *init = NULL; + paze_ast_node_t *condition = NULL; + paze_ast_node_t *increment = NULL; + + if (check(p, PAZE_TK_SEMICOLON)) { + advance(p); + } else { + paze_tk_t kind = peek(p).kind; + if (paze_tk_is_type_keyword(kind)) { + paze_loc_t loc = PAZE_LOC_EMPTY; + paze_type_t *type = parse_type_specifier(p, &loc); + if (type) { + paze_str_t name = PAZE_STR_EMPTY; + bool is_variadic = false; + paze_type_t *decl_type = parse_declarator_type(p, type, &name, &is_variadic); + + paze_ast_node_t *first_init = NULL; + if (check(p, PAZE_TK_ASSIGN)) { + advance(p); + first_init = parse_initializer(p); + } + + if (check(p, PAZE_TK_COMMA)) { + paze_ast_node_arr_t decls; + paze_ast_node_arr_t_init(&decls); + + paze_ast_node_t *first = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, loc); + if (first) { + first->data.var_decl.name = name; + first->data.var_decl.var_type = decl_type; + first->data.var_decl.init_expr = first_init; + } + paze_ast_node_arr_t_push(&decls, first); + + while (check(p, PAZE_TK_COMMA)) { + advance(p); + paze_str_t n2 = PAZE_STR_EMPTY; + bool dummy = false; + paze_type_t *dtype2 = parse_declarator_type(p, type, &n2, &dummy); + + paze_ast_node_t *init2 = NULL; + if (check(p, PAZE_TK_ASSIGN)) { + advance(p); + init2 = parse_initializer(p); + } + + paze_ast_node_t *var = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, loc); + if (var) { + var->data.var_decl.name = n2; + var->data.var_decl.var_type = dtype2; + var->data.var_decl.init_expr = init2; + } + paze_ast_node_arr_t_push(&decls, var); + } + + if (decls.len == 1) { + init = decls.data[0]; + } else { + init = paze_ast_new(p->arena, PAZE_NODE_DECL_GROUP, loc); + if (init) init->data.decl_group.decls = decls; + } + } else { + if (paze_str_cmp(name, PAZE_STR_EMPTY) == 0) { + init = paze_ast_new(p->arena, PAZE_NODE_NULL_STMT, loc); + } else { + init = paze_ast_new(p->arena, PAZE_NODE_VAR_DECL, loc); + if (init) { + init->data.var_decl.name = name; + init->data.var_decl.var_type = decl_type; + init->data.var_decl.init_expr = first_init; + } + } + } + } + + if (!check(p, PAZE_TK_SEMICOLON)) { + if (p->diag) { + paze_diagnostics_error(p->diag, peek(p).loc, + "expected ';' in for statement"); + } + p->had_error = true; + } else { + advance(p); + } + } else { + init = parse_expression(p); + consume(p, PAZE_TK_SEMICOLON, ";"); + } + } + + if (!check(p, PAZE_TK_SEMICOLON)) { + condition = parse_expression(p); + } + consume(p, PAZE_TK_SEMICOLON, ";"); + + if (!check(p, PAZE_TK_RPAREN)) { + increment = parse_expression(p); + } + consume(p, PAZE_TK_RPAREN, ")"); + + paze_ast_node_t *body = parse_statement(p); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_FOR_STMT, for_tk.loc); + if (node) { + node->data.for_stmt.init = init; + node->data.for_stmt.condition = condition; + node->data.for_stmt.increment = increment; + node->data.for_stmt.body = body; + } + return node; +} + +static paze_ast_node_t *parse_switch_statement(paze_parser_t *p) +{ + paze_token_t switch_tk = advance(p); + consume(p, PAZE_TK_LPAREN, "("); + paze_ast_node_t *expr = parse_expression(p); + consume(p, PAZE_TK_RPAREN, ")"); + consume(p, PAZE_TK_LBRACE, "{"); + + paze_ast_node_arr_t cases; + paze_ast_node_arr_t_init(&cases); + + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_token_t case_tk = advance(p); + bool is_default = false; + paze_ast_node_t *case_value = NULL; + + if (case_tk.kind == PAZE_TK_KW_CASE) { + case_value = parse_expression(p); + consume(p, PAZE_TK_COLON, ":"); + } else if (case_tk.kind == PAZE_TK_KW_DEFAULT) { + consume(p, PAZE_TK_COLON, ":"); + is_default = true; + } else { + if (p->diag) { + paze_diagnostics_error(p->diag, case_tk.loc, + "expected 'case' or 'default' in switch statement"); + } + p->had_error = true; + continue; + } + + paze_ast_node_arr_t case_stmts; + paze_ast_node_arr_t_init(&case_stmts); + + while (!check(p, PAZE_TK_KW_CASE) && !check(p, PAZE_TK_KW_DEFAULT) && + !check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_ast_node_t *stmt = parse_declaration_or_statement(p); + if (stmt) paze_ast_node_arr_t_push(&case_stmts, stmt); + if (p->had_error) synchronize(p); + } + + paze_ast_node_t *case_node = paze_ast_new(p->arena, + PAZE_NODE_CASE_STMT, case_tk.loc); + if (case_node) { + case_node->data.case_stmt.value = case_value; + case_node->data.case_stmt.stmts = case_stmts; + case_node->data.case_stmt.is_default = is_default; + } + paze_ast_node_arr_t_push(&cases, case_node); + } + + consume(p, PAZE_TK_RBRACE, "}"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_SWITCH_STMT, switch_tk.loc); + if (node) { + node->data.switch_stmt.expr = expr; + node->data.switch_stmt.cases = cases; + } + return node; +} + +static paze_ast_node_t *parse_return_statement(paze_parser_t *p) +{ + paze_token_t ret_tk = advance(p); + paze_ast_node_t *value = NULL; + + if (!check(p, PAZE_TK_SEMICOLON)) { + value = parse_expression(p); + } + consume(p, PAZE_TK_SEMICOLON, ";"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_RETURN_STMT, ret_tk.loc); + if (node) node->data.return_stmt.value = value; + return node; +} + +static paze_ast_node_t *parse_jump_statement(paze_parser_t *p) +{ + paze_token_t tk = advance(p); + + switch (tk.kind) { + case PAZE_TK_KW_BREAK: + consume(p, PAZE_TK_SEMICOLON, ";"); + return paze_ast_new(p->arena, PAZE_NODE_BREAK_STMT, tk.loc); + + case PAZE_TK_KW_CONTINUE: + consume(p, PAZE_TK_SEMICOLON, ";"); + return paze_ast_new(p->arena, PAZE_NODE_CONTINUE_STMT, tk.loc); + + case PAZE_TK_KW_GOTO: { + paze_token_t label = advance(p); + consume(p, PAZE_TK_SEMICOLON, ";"); + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_GOTO_STMT, tk.loc); + if (node) node->data.goto_stmt.label = label.text; + return node; + } + + default: + return NULL; + } +} + +/* ======================================================================== + * Declaration Parsing + * ======================================================================== */ + +static paze_ast_node_t *parse_initializer(paze_parser_t *p) +{ + if (check(p, PAZE_TK_LBRACE)) { + advance(p); + paze_ast_node_arr_t elements; + paze_ast_node_arr_t_init(&elements); + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_ast_node_t *elem = parse_assignment_expr(p); + if (elem) paze_ast_node_arr_t_push(&elements, elem); + if (check(p, PAZE_TK_COMMA)) advance(p); + else break; + } + consume(p, PAZE_TK_RBRACE, "}"); + paze_ast_node_t *list = paze_ast_new(p->arena, + PAZE_NODE_INIT_LIST_EXPR, peek(p).loc); + if (list) { + list->data.init_list_expr.elements = elements; + } + return list; + } + return parse_assignment_expr(p); +} + +static paze_ast_node_t *parse_declaration(paze_parser_t *p) +{ + paze_loc_t loc = PAZE_LOC_EMPTY; + paze_type_t *type = parse_type_specifier(p, &loc); + + if (!type) { + if (p->diag) { + paze_diagnostics_error(p->diag, peek(p).loc, + "expected declaration"); + } + p->had_error = true; + return NULL; + } + + if (type->kind == PAZE_TYPE_TYPEDEF) { + if (type->base) { + /* This is a typedef declaration (typedef NAME underlying;) */ + paze_ast_node_t *node = paze_ast_new(p->arena, PAZE_NODE_TYPEDEF_DECL, loc); + if (node) { + node->data.typedef_decl.name = type->name; + node->data.typedef_decl.underlying_type = type->base; + } + register_typedef_name(p, type->name); + return node; + } + /* This is a typedef name used as a type specifier - fall through */ + } + + if ((type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION || + type->kind == PAZE_TYPE_ENUM) && + check(p, PAZE_TK_SEMICOLON)) { + advance(p); + paze_ast_node_t *node = NULL; + if (type->kind == PAZE_TYPE_STRUCT || type->kind == PAZE_TYPE_UNION) { + node = paze_ast_new(p->arena, + type->kind == PAZE_TYPE_STRUCT ? PAZE_NODE_STRUCT_DECL : PAZE_NODE_UNION_DECL, + loc); + if (node) { + node->data.struct_or_union_decl.name = type->name; + node->data.struct_or_union_decl.is_union = (type->kind == PAZE_TYPE_UNION); + node->data.struct_or_union_decl.is_anonymous = (type->name.len == 0); + if (type->struct_field_count > 0) { + node->data.struct_or_union_decl.fields.data = + (struct paze_ast_node_t **)paze_arena_calloc(p->arena, + sizeof(struct paze_ast_node_t *) * (size_t)type->struct_field_count); + node->data.struct_or_union_decl.fields.len = (size_t)type->struct_field_count; + node->data.struct_or_union_decl.fields.cap = (size_t)type->struct_field_count; + for (int fi = 0; fi < type->struct_field_count; fi++) { + paze_ast_node_t *field_node = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, loc); + if (field_node) { + field_node->data.var_decl.name = type->struct_fields[fi].name; + field_node->data.var_decl.var_type = type->struct_fields[fi].type; + } + node->data.struct_or_union_decl.fields.data[fi] = field_node; + } + } + } + } else if (type->kind == PAZE_TYPE_ENUM) { + node = paze_ast_new(p->arena, PAZE_NODE_ENUM_DECL, loc); + if (node) { + node->data.enum_decl.name = type->name; + } + } + if (node) return node; + } + + if (check(p, PAZE_TK_SEMICOLON)) { + advance(p); + return paze_ast_new(p->arena, PAZE_NODE_NULL_STMT, loc); + } + + if (check(p, PAZE_TK_IDENTIFIER)) { + size_t save = p->pos; + paze_token_t name_tk = advance(p); + + if (check(p, PAZE_TK_LPAREN)) { + advance(p); + + paze_ast_node_arr_t params; + paze_ast_node_arr_t_init(¶ms); + bool is_variadic = false; + + if (check(p, PAZE_TK_KW_VOID)) { + size_t vsave = p->pos; + advance(p); + if (check(p, PAZE_TK_RPAREN)) { + /* void means no params */ + } else { + p->pos = vsave; + } + } + + if (!check(p, PAZE_TK_RPAREN)) { + while (!check(p, PAZE_TK_RPAREN) && !is_at_end(p)) { + if (check(p, PAZE_TK_DOT)) { + advance(p); + if (check(p, PAZE_TK_DOT)) { + advance(p); + if (check(p, PAZE_TK_DOT)) { + advance(p); + is_variadic = true; + break; + } + p->pos -= 2; + } else { + p->pos -= 1; + } + } + + paze_type_t *ptype = parse_type_specifier(p, NULL); + if (!ptype) break; + + paze_str_t pname = PAZE_STR_EMPTY; + bool variadic = false; + paze_type_t *pdecl = parse_declarator_type(p, ptype, + &pname, &variadic); + + paze_ast_node_t *param_node = paze_ast_new(p->arena, + PAZE_NODE_PARAM, loc); + if (param_node) { + param_node->data.param.name = pname; + param_node->data.param.param_type = pdecl ? pdecl : ptype; + } + paze_ast_node_arr_t_push(¶ms, param_node); + + if (check(p, PAZE_TK_COMMA)) advance(p); + else break; + } + } + consume(p, PAZE_TK_RPAREN, ")"); + + if (check(p, PAZE_TK_LBRACE)) { + paze_ast_node_t *body = parse_block_statement(p); + paze_ast_node_t *fn = paze_ast_new(p->arena, + PAZE_NODE_FUNCTION_DECL, loc); + if (fn) { + fn->data.function_decl.name = name_tk.text; + fn->data.function_decl.return_type = type; + fn->data.function_decl.params = params; + fn->data.function_decl.body = body; + fn->data.function_decl.is_variadic = is_variadic; + } + return fn; + } + + if (check(p, PAZE_TK_SEMICOLON)) { + advance(p); + paze_ast_node_t *fn = paze_ast_new(p->arena, + PAZE_NODE_FUNCTION_DECL, loc); + if (fn) { + fn->data.function_decl.name = name_tk.text; + fn->data.function_decl.return_type = type; + fn->data.function_decl.params = params; + fn->data.function_decl.is_variadic = is_variadic; + } + return fn; + } + + if (p->diag) { + paze_diagnostics_error(p->diag, peek(p).loc, + "expected '{' or ')' after function declaration"); + } + p->had_error = true; + return NULL; + } + + p->pos = save; + } + + paze_str_t name = PAZE_STR_EMPTY; + bool is_variadic = false; + paze_type_t *decl_type = parse_declarator_type(p, type, &name, &is_variadic); + + paze_ast_node_t *first_init = NULL; + if (check(p, PAZE_TK_ASSIGN)) { + advance(p); + first_init = parse_initializer(p); + fixup_array_size_from_init(decl_type, first_init); + } + + if (check(p, PAZE_TK_COMMA)) { + paze_ast_node_arr_t decls; + paze_ast_node_arr_t_init(&decls); + + paze_ast_node_t *first = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, loc); + if (first) { + first->data.var_decl.name = name; + first->data.var_decl.var_type = decl_type; + first->data.var_decl.init_expr = first_init; + } + paze_ast_node_arr_t_push(&decls, first); + + while (check(p, PAZE_TK_COMMA)) { + advance(p); + paze_str_t n2 = PAZE_STR_EMPTY; + bool dummy = false; + paze_type_t *dtype2 = parse_declarator_type(p, type, &n2, &dummy); + + paze_ast_node_t *init2 = NULL; + if (check(p, PAZE_TK_ASSIGN)) { + advance(p); + init2 = parse_initializer(p); + fixup_array_size_from_init(dtype2, init2); + } + + paze_ast_node_t *var = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, loc); + if (var) { + var->data.var_decl.name = n2; + var->data.var_decl.var_type = dtype2; + var->data.var_decl.init_expr = init2; + } + paze_ast_node_arr_t_push(&decls, var); + } + + consume(p, PAZE_TK_SEMICOLON, ";"); + + if (decls.len == 1) { + return decls.data[0]; + } + + paze_ast_node_t *group = paze_ast_new(p->arena, + PAZE_NODE_DECL_GROUP, loc); + if (group) { + group->data.decl_group.decls = decls; + } + return group; + } + + consume(p, PAZE_TK_SEMICOLON, ";"); + if (paze_str_cmp(name, PAZE_STR_EMPTY) == 0) { + return paze_ast_new(p->arena, PAZE_NODE_NULL_STMT, loc); + } + paze_ast_node_t *var = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, loc); + if (var) { + var->data.var_decl.name = name; + var->data.var_decl.var_type = decl_type; + var->data.var_decl.init_expr = first_init; + } + return var; + + if (p->diag) { + paze_diagnostics_error(p->diag, peek(p).loc, + "unexpected token after declaration"); + } + p->had_error = true; + return NULL; +} + +/* These functions are reserved for future use - currently unused */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" + +static paze_ast_node_t *parse_struct_or_union_decl(paze_parser_t *p, + bool is_union) +{ + paze_token_t kw = advance(p); + paze_str_t name = PAZE_STR_EMPTY; + + if (check(p, PAZE_TK_IDENTIFIER)) { + paze_token_t tk = advance(p); + name = tk.text; + } + + if (check(p, PAZE_TK_LBRACE)) { + advance(p); + paze_ast_node_arr_t fields; + paze_ast_node_arr_t_init(&fields); + + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + paze_type_t *field_type = parse_type_specifier(p, NULL); + if (!field_type) { + synchronize(p); + return NULL; + } + + paze_str_t field_name = PAZE_STR_EMPTY; + if (check(p, PAZE_TK_IDENTIFIER)) { + paze_token_t tk = advance(p); + field_name = tk.text; + } + + if (check(p, PAZE_TK_COLON)) { + advance(p); + if (check(p, PAZE_TK_INT_LITERAL)) advance(p); + } + + if (check(p, PAZE_TK_COMMA)) { + advance(p); + while (check(p, PAZE_TK_IDENTIFIER) || check(p, PAZE_TK_COMMA)) { + if (check(p, PAZE_TK_COMMA)) advance(p); + if (check(p, PAZE_TK_IDENTIFIER)) advance(p); + if (check(p, PAZE_TK_COLON)) { + advance(p); + if (check(p, PAZE_TK_INT_LITERAL)) advance(p); + } + } + } + + paze_ast_node_t *var = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, kw.loc); + if (var) { + var->data.var_decl.name = field_name; + var->data.var_decl.var_type = field_type; + } + paze_ast_node_arr_t_push(&fields, var); + + consume(p, PAZE_TK_SEMICOLON, ";"); + } + + consume(p, PAZE_TK_RBRACE, "}"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + is_union ? PAZE_NODE_UNION_DECL : PAZE_NODE_STRUCT_DECL, kw.loc); + if (node) { + node->data.struct_or_union_decl.name = name; + node->data.struct_or_union_decl.fields = fields; + node->data.struct_or_union_decl.is_anonymous = + (paze_str_cmp(name, PAZE_STR_EMPTY) == 0); + node->data.struct_or_union_decl.is_union = is_union; + } + return node; + } + + paze_ast_node_t *node = paze_ast_new(p->arena, + is_union ? PAZE_NODE_UNION_DECL : PAZE_NODE_STRUCT_DECL, kw.loc); + if (node) { + node->data.struct_or_union_decl.name = name; + node->data.struct_or_union_decl.is_anonymous = + (paze_str_cmp(name, PAZE_STR_EMPTY) == 0); + node->data.struct_or_union_decl.is_union = is_union; + } + return node; +} + +static paze_ast_node_t *parse_typedef_decl(paze_parser_t *p) +{ + paze_token_t kw = advance(p); + paze_type_t *underlying = parse_type_specifier(p, NULL); + paze_str_t new_name = PAZE_STR_EMPTY; + bool dummy = false; + parse_declarator_type(p, underlying, &new_name, &dummy); + consume(p, PAZE_TK_SEMICOLON, ";"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_TYPEDEF_DECL, kw.loc); + if (node) { + node->data.typedef_decl.name = new_name; + node->data.typedef_decl.underlying_type = underlying; + } + return node; +} + +static paze_ast_node_t *parse_enum_decl(paze_parser_t *p) +{ + paze_token_t kw = advance(p); + paze_str_t name = PAZE_STR_EMPTY; + + if (check(p, PAZE_TK_IDENTIFIER)) { + paze_token_t tk = advance(p); + name = tk.text; + } + + paze_ast_node_arr_t constants; + paze_ast_node_arr_t_init(&constants); + + if (check(p, PAZE_TK_LBRACE)) { + advance(p); + while (!check(p, PAZE_TK_RBRACE) && !is_at_end(p)) { + if (check(p, PAZE_TK_IDENTIFIER)) { + paze_token_t tk = advance(p); + paze_ast_node_t *const_node = paze_ast_new(p->arena, + PAZE_NODE_VAR_DECL, tk.loc); + if (const_node) { + const_node->data.var_decl.name = tk.text; + } + paze_ast_node_arr_t_push(&constants, const_node); + } + if (check(p, PAZE_TK_ASSIGN)) { + advance(p); + parse_assignment_expr(p); + } + if (check(p, PAZE_TK_COMMA)) advance(p); + } + consume(p, PAZE_TK_RBRACE, "}"); + } + + consume(p, PAZE_TK_SEMICOLON, ";"); + + paze_ast_node_t *node = paze_ast_new(p->arena, + PAZE_NODE_ENUM_DECL, kw.loc); + if (node) { + node->data.enum_decl.name = name; + node->data.enum_decl.constants = constants; + } + return node; +} + +#pragma GCC diagnostic pop \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_preprocessor.c b/src/PazeE.los4/src/paze_preprocessor.c new file mode 100644 index 0000000..4308886 --- /dev/null +++ b/src/PazeE.los4/src/paze_preprocessor.c @@ -0,0 +1,1136 @@ +#include "paze_preprocessor.h" + +#include +#include +#include + +/* ======================================================================== + * Built-in content + * ======================================================================== */ + +static const char *paze_builtin_paze_h = + "/* Built-in paze.h - automatically included */\n" + "#define PAZE_VERSION_MAJOR 0\n" + "#define PAZE_VERSION_MINOR 1\n" + "#define PAZE_VERSION_PATCH 0\n" + "#define PAZE_VERSION_STRING \"0.1.0\"\n" + "\n" + "typedef signed long long paze_int64_t;\n" + "typedef unsigned long long paze_uint64_t;\n" + "typedef signed int paze_int32_t;\n" + "typedef unsigned int paze_uint32_t;\n" + "typedef signed short paze_int16_t;\n" + "typedef unsigned short paze_uint16_t;\n" + "typedef signed char paze_int8_t;\n" + "typedef unsigned char paze_uint8_t;\n" + "\n" + "typedef paze_uint64_t paze_size_t;\n" + "typedef paze_int64_t paze_ssize_t;\n" + "\n" + "#define PAZE_TRUE 1\n" + "#define PAZE_FALSE 0\n" + "\n" + "#define PAZE_ARR_LEN(arr) (sizeof(arr) / sizeof((arr)[0]))\n" + "#define PAZE_MIN(a, b) ((a) < (b) ? (a) : (b))\n" + "#define PAZE_MAX(a, b) ((a) > (b) ? (a) : (b))\n" + "#define PAZE_ABS(x) ((x) < 0 ? -(x) : (x))\n" + "#define PAZE_CLAMP(x, lo, hi) PAZE_MIN(PAZE_MAX(x, lo), hi)\n" + "\n" + "#define PAZE_CONCAT_(a, b) a##b\n" + "#define PAZE_CONCAT(a, b) PAZE_CONCAT_(a, b)\n" + "#define PAZE_STRINGIFY_(x) #x\n" + "#define PAZE_STRINGIFY(x) PAZE_STRINGIFY_(x)\n" + "\n" + "/* Standard library function declarations */\n" + "int printf(const char *fmt, ...);\n" + "int puts(const char *str);\n" + "int getchar(void);\n" + "void *malloc(paze_size_t size);\n" + "void free(void *ptr);\n" + "void *memcpy(void *dst, const void *src, paze_size_t n);\n" + "void *memset(void *s, int c, paze_size_t n);\n" + "paze_size_t strlen(const char *s);\n" + "char *strcpy(char *dst, const char *src);\n" + "char *strcat(char *dst, const char *src);\n" + "int strcmp(const char *a, const char *b);\n" + "int sprintf(char *buf, const char *fmt, ...);\n" + "\n"; + +/* ======================================================================== + * Internal: Macro Table + * ======================================================================== */ + +static void macro_table_init(paze_macro_table_t *table) +{ + table->data = NULL; + table->len = 0; + table->cap = 0; +} + +static void macro_table_free(paze_macro_table_t *table) +{ + free(table->data); + table->data = NULL; + table->len = 0; + table->cap = 0; +} + +static void macro_table_push(paze_macro_table_t *table, paze_macro_def_t def) +{ + if (table->len >= table->cap) { + size_t new_cap = table->cap == 0 ? 16 : table->cap * 2; + table->data = (paze_macro_def_t *)paze_realloc( + table->data, new_cap * sizeof(paze_macro_def_t)); + table->cap = new_cap; + } + table->data[table->len++] = def; +} + +static paze_macro_def_t *macro_table_find(paze_macro_table_t *table, paze_str_t name) +{ + for (size_t i = 0; i < table->len; i++) { + if (paze_str_cmp(table->data[i].name, name) == 0) { + return &table->data[i]; + } + } + return NULL; +} + +static void macro_table_remove(paze_macro_table_t *table, paze_str_t name) +{ + for (size_t i = 0; i < table->len; i++) { + if (paze_str_cmp(table->data[i].name, name) == 0) { + table->data[i] = table->data[table->len - 1]; + table->len--; + return; + } + } +} + +static void define_macro(paze_preprocessor_t *pp, paze_str_t name, + paze_str_t replacement, bool is_function, + paze_str_t *params, size_t param_count, + bool is_predefined) +{ + /* Remove existing definition if any */ + macro_table_remove(&pp->macros, name); + + /* Copy strings into arena */ + char *name_buf = (char *)paze_arena_alloc(pp->arena, name.len + 1); + memcpy(name_buf, name.data, name.len); + name_buf[name.len] = '\0'; + paze_str_t arena_name = { name_buf, name.len }; + + char *repl_buf = (char *)paze_arena_alloc(pp->arena, replacement.len + 1); + memcpy(repl_buf, replacement.data, replacement.len); + repl_buf[replacement.len] = '\0'; + paze_str_t arena_repl = { repl_buf, replacement.len }; + + paze_str_t *arena_params = NULL; + if (is_function && params && param_count > 0) { + arena_params = (paze_str_t *)paze_arena_alloc( + pp->arena, param_count * sizeof(paze_str_t)); + for (size_t i = 0; i < param_count; i++) { + char *pbuf = (char *)paze_arena_alloc(pp->arena, params[i].len + 1); + memcpy(pbuf, params[i].data, params[i].len); + pbuf[params[i].len] = '\0'; + arena_params[i].data = pbuf; + arena_params[i].len = params[i].len; + } + } + + paze_macro_def_t def; + def.name = arena_name; + def.replacement = arena_repl; + def.is_function = is_function; + def.params = arena_params; + def.param_count = param_count; + def.is_predefined = is_predefined; + + macro_table_push(&pp->macros, def); +} + +static void undef_macro(paze_preprocessor_t *pp, paze_str_t name) +{ + macro_table_remove(&pp->macros, name); +} + +/* ======================================================================== + * Internal: If-Stack + * ======================================================================== */ + +static void if_stack_init(paze_if_stack_t *stack) +{ + stack->data = NULL; + stack->len = 0; + stack->cap = 0; +} + +static void if_stack_free(paze_if_stack_t *stack) +{ + free(stack->data); + stack->data = NULL; + stack->len = 0; + stack->cap = 0; +} + +static void if_stack_push(paze_if_stack_t *stack, bool active) +{ + if (stack->len >= stack->cap) { + size_t new_cap = stack->cap == 0 ? 8 : stack->cap * 2; + stack->data = (paze_if_state_t *)paze_realloc( + stack->data, new_cap * sizeof(paze_if_state_t)); + stack->cap = new_cap; + } + paze_if_state_t state; + state.active = active; + state.was_active = active; + stack->data[stack->len++] = state; +} + +static void if_stack_pop(paze_if_stack_t *stack) +{ + if (stack->len > 0) { + stack->len--; + } +} + +static bool if_stack_is_active(const paze_if_stack_t *stack) +{ + if (stack->len == 0) return true; + return stack->data[stack->len - 1].active; +} + +static void if_stack_set_active(paze_if_stack_t *stack, bool active) +{ + if (stack->len > 0) { + stack->data[stack->len - 1].active = active; + if (active) { + stack->data[stack->len - 1].was_active = true; + } + } +} + +/* ======================================================================== + * Internal: Constant Expression Evaluator (for #if / #elif) + * ======================================================================== */ + +typedef struct { + paze_token_t *tokens; + size_t token_count; + size_t pos; + paze_preprocessor_t *pp; + paze_diagnostics_t *diag; + paze_loc_t loc; +} paze_expr_ctx_t; + +static bool expr_at_end(paze_expr_ctx_t *ctx) +{ + return ctx->pos >= ctx->token_count || + ctx->tokens[ctx->pos].kind == PAZE_TK_EOF; +} + +static paze_token_t expr_peek(paze_expr_ctx_t *ctx) +{ + if (expr_at_end(ctx)) { + paze_token_t tk; + memset(&tk, 0, sizeof(tk)); + tk.kind = PAZE_TK_EOF; + return tk; + } + return ctx->tokens[ctx->pos]; +} + +static paze_token_t expr_next(paze_expr_ctx_t *ctx) +{ + paze_token_t tk = expr_peek(ctx); + if (!expr_at_end(ctx)) { + ctx->pos++; + } + return tk; +} + +static long parse_expr(paze_expr_ctx_t *ctx); + +static long parse_primary(paze_expr_ctx_t *ctx) +{ + paze_token_t tk = expr_peek(ctx); + + if (tk.kind == PAZE_TK_INT_LITERAL) { + expr_next(ctx); + return tk.int_value; + } + + if (tk.kind == PAZE_TK_CHAR_LITERAL) { + expr_next(ctx); + return tk.int_value; + } + + if (tk.kind == PAZE_TK_IDENTIFIER) { + paze_str_t name = tk.text; + + /* defined(NAME) or defined NAME */ + if (name.len == 7 && memcmp(name.data, "defined", 7) == 0) { + expr_next(ctx); + paze_token_t next = expr_peek(ctx); + paze_str_t macro_name; + if (next.kind == PAZE_TK_LPAREN) { + expr_next(ctx); + paze_token_t name_tk = expr_next(ctx); + macro_name = name_tk.text; + expr_next(ctx); /* skip RPAREN */ + } else { + macro_name = next.text; + expr_next(ctx); + } + paze_macro_def_t *def = macro_table_find(&ctx->pp->macros, macro_name); + return def ? 1 : 0; + } + + /* Known macro with integer replacement? */ + paze_macro_def_t *def = macro_table_find(&ctx->pp->macros, name); + if (def && !def->is_function) { + /* Try to parse the replacement as a number */ + if (def->replacement.len > 0 && def->replacement.data[0] >= '0') { + char *end; + long val = strtol(def->replacement.data, &end, 0); + if (end != def->replacement.data) { + expr_next(ctx); + return val; + } + } + return 1; /* defined but not numeric => truthy */ + } + + expr_next(ctx); + return 0; /* undefined identifier => 0 */ + } + + if (tk.kind == PAZE_TK_LPAREN) { + expr_next(ctx); + long val = parse_expr(ctx); + paze_token_t close = expr_next(ctx); + if (close.kind != PAZE_TK_RPAREN) { + paze_diagnostics_error(ctx->diag, ctx->loc, + "expected ')' in constant expression"); + } + return val; + } + + if (tk.kind == PAZE_TK_MINUS) { + expr_next(ctx); + return -parse_primary(ctx); + } + + if (tk.kind == PAZE_TK_PLUS) { + expr_next(ctx); + return parse_primary(ctx); + } + + if (tk.kind == PAZE_TK_NOT) { + expr_next(ctx); + return !parse_primary(ctx); + } + + if (tk.kind == PAZE_TK_TILDE) { + expr_next(ctx); + return ~parse_primary(ctx); + } + + paze_diagnostics_error(ctx->diag, ctx->loc, + "unexpected token in constant expression"); + expr_next(ctx); + return 0; +} + +static long parse_multiplicative(paze_expr_ctx_t *ctx) +{ + long left = parse_primary(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_STAR) { + expr_next(ctx); + left *= parse_primary(ctx); + } else if (op.kind == PAZE_TK_SLASH) { + expr_next(ctx); + long right = parse_primary(ctx); + left = right != 0 ? left / right : 0; + } else if (op.kind == PAZE_TK_PERCENT) { + expr_next(ctx); + long right = parse_primary(ctx); + left = right != 0 ? left % right : 0; + } else { + break; + } + } + return left; +} + +static long parse_additive(paze_expr_ctx_t *ctx) +{ + long left = parse_multiplicative(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_PLUS) { + expr_next(ctx); + left += parse_multiplicative(ctx); + } else if (op.kind == PAZE_TK_MINUS) { + expr_next(ctx); + left -= parse_multiplicative(ctx); + } else { + break; + } + } + return left; +} + +static long parse_shift(paze_expr_ctx_t *ctx) +{ + long left = parse_additive(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_SHL) { + expr_next(ctx); + left <<= parse_additive(ctx); + } else if (op.kind == PAZE_TK_SHR) { + expr_next(ctx); + left >>= parse_additive(ctx); + } else { + break; + } + } + return left; +} + +static long parse_relational(paze_expr_ctx_t *ctx) +{ + long left = parse_shift(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_LT) { + expr_next(ctx); + left = left < parse_shift(ctx) ? 1 : 0; + } else if (op.kind == PAZE_TK_GT) { + expr_next(ctx); + left = left > parse_shift(ctx) ? 1 : 0; + } else if (op.kind == PAZE_TK_LE) { + expr_next(ctx); + left = left <= parse_shift(ctx) ? 1 : 0; + } else if (op.kind == PAZE_TK_GE) { + expr_next(ctx); + left = left >= parse_shift(ctx) ? 1 : 0; + } else { + break; + } + } + return left; +} + +static long parse_equality(paze_expr_ctx_t *ctx) +{ + long left = parse_relational(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_EQ) { + expr_next(ctx); + left = left == parse_relational(ctx) ? 1 : 0; + } else if (op.kind == PAZE_TK_NOT_EQ) { + expr_next(ctx); + left = left != parse_relational(ctx) ? 1 : 0; + } else { + break; + } + } + return left; +} + +static long parse_bitwise_and(paze_expr_ctx_t *ctx) +{ + long left = parse_equality(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_AMP) { + expr_next(ctx); + left &= parse_equality(ctx); + } else { + break; + } + } + return left; +} + +static long parse_bitwise_xor(paze_expr_ctx_t *ctx) +{ + long left = parse_bitwise_and(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_CARET) { + expr_next(ctx); + left ^= parse_bitwise_and(ctx); + } else { + break; + } + } + return left; +} + +static long parse_bitwise_or(paze_expr_ctx_t *ctx) +{ + long left = parse_bitwise_xor(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_PIPE) { + expr_next(ctx); + left |= parse_bitwise_xor(ctx); + } else { + break; + } + } + return left; +} + +static long parse_logical_and(paze_expr_ctx_t *ctx) +{ + long left = parse_bitwise_or(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_AND_AND) { + expr_next(ctx); + long right = parse_bitwise_or(ctx); + left = (left && right) ? 1 : 0; + } else { + break; + } + } + return left; +} + +static long parse_logical_or(paze_expr_ctx_t *ctx) +{ + long left = parse_logical_and(ctx); + while (!expr_at_end(ctx)) { + paze_token_t op = expr_peek(ctx); + if (op.kind == PAZE_TK_OR_OR) { + expr_next(ctx); + long right = parse_logical_and(ctx); + left = (left || right) ? 1 : 0; + } else { + break; + } + } + return left; +} + +static long parse_ternary(paze_expr_ctx_t *ctx) +{ + long cond = parse_logical_or(ctx); + paze_token_t tk = expr_peek(ctx); + if (tk.kind == PAZE_TK_QUESTION) { + expr_next(ctx); + long true_val = parse_expr(ctx); + paze_token_t colon = expr_next(ctx); + if (colon.kind == PAZE_TK_COLON) { + long false_val = parse_ternary(ctx); + return cond ? true_val : false_val; + } + return cond ? true_val : 0; + } + return cond; +} + +static long parse_expr(paze_expr_ctx_t *ctx) +{ + return parse_ternary(ctx); +} + +/* ======================================================================== + * Internal: Macro Expansion + * ======================================================================== */ + +static paze_token_arr_t expand_macros( + paze_preprocessor_t *pp, + paze_token_t *tokens, size_t token_count, + paze_diagnostics_t *diag) +{ + paze_token_arr_t result; + paze_token_arr_t_init(&result); + + for (size_t i = 0; i < token_count; i++) { + paze_token_t tk = tokens[i]; + + if (tk.kind == PAZE_TK_EOF) { + paze_token_arr_t_push(&result, tk); + break; + } + + if (tk.kind == PAZE_TK_IDENTIFIER) { + paze_macro_def_t *def = macro_table_find(&pp->macros, tk.text); + if (def && !def->is_function) { + if (pp->expand_depth >= pp->max_expand) { + paze_diagnostics_error(diag, tk.loc, + "macro expansion depth limit exceeded (recursive macro?)"); + paze_token_arr_t_push(&result, tk); + continue; + } + + pp->expand_depth++; + + paze_lexer_t *repl_lex = paze_lexer_create( + def->replacement.data, + tk.loc.file ? tk.loc.file : ""); + if (repl_lex) { + paze_token_arr_t repl_tokens = paze_lexer_tokenize(repl_lex, diag); + paze_lexer_destroy(repl_lex); + + paze_token_arr_t expanded = expand_macros( + pp, repl_tokens.data, repl_tokens.len, diag); + + for (size_t j = 0; j < expanded.len; j++) { + paze_token_arr_t_push(&result, expanded.data[j]); + } + + paze_token_arr_t_free(&expanded); + paze_token_arr_t_free(&repl_tokens); + } + + pp->expand_depth--; + continue; + } + } + + paze_token_arr_t_push(&result, tk); + } + + return result; +} + +/* ======================================================================== + * Internal: Directive Processing + * ======================================================================== */ + +static bool is_directive_keyword(paze_token_t *tk, const char *kw) +{ + if (tk->kind != PAZE_TK_IDENTIFIER) return false; + size_t len = strlen(kw); + return tk->text.len == len && + memcmp(tk->text.data, kw, len) == 0; +} + +/* Process all preprocessor directives in a token array. + * Returns a new token array with directives removed and macros expanded. */ +static paze_token_arr_t process_directives( + paze_preprocessor_t *pp, + paze_token_t *tokens, size_t token_count, + paze_diagnostics_t *diag) +{ + paze_token_arr_t output; + paze_token_arr_t_init(&output); + + size_t i = 0; + while (i < token_count) { + paze_token_t tk = tokens[i]; + + if (tk.kind == PAZE_TK_EOF) { + paze_token_arr_t_push(&output, tk); + break; + } + + /* Check if this is a preprocessor directive line */ + if (tk.kind == PAZE_TK_HASH && tk.at_line_start && if_stack_is_active(&pp->if_stack)) { + paze_loc_t directive_loc = tk.loc; + + /* Get the directive keyword */ + i++; + if (i >= token_count) break; + paze_token_t directive = tokens[i]; + + if (directive.kind == PAZE_TK_EOF) break; + + /* Handle #define */ + if (is_directive_keyword(&directive, "define")) { + i++; + if (i >= token_count) break; + paze_token_t name_tk = tokens[i]; + if (name_tk.kind != PAZE_TK_IDENTIFIER) { + paze_diagnostics_error(diag, directive_loc, + "expected macro name after '#define'"); + i++; + continue; + } + + paze_str_t macro_name = name_tk.text; + bool is_func = false; + paze_str_t *params = NULL; + size_t param_count = 0; + + i++; + + /* Check for function-like macro: #define NAME(params) */ + if (i < token_count && tokens[i].kind == PAZE_TK_LPAREN) { + is_func = true; + i++; + while (i < token_count && tokens[i].kind != PAZE_TK_RPAREN) { + if (tokens[i].kind == PAZE_TK_IDENTIFIER) { + param_count++; + params = (paze_str_t *)paze_realloc( + params, param_count * sizeof(paze_str_t)); + params[param_count - 1] = tokens[i].text; + } + i++; + } + if (i < token_count && tokens[i].kind == PAZE_TK_RPAREN) { + i++; + } + } + + /* Collect replacement text tokens until end of line */ + size_t repl_start_idx = i; + while (i < token_count && + !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + size_t repl_end_idx = i; + + paze_str_t replacement = { NULL, 0 }; + if (repl_end_idx > repl_start_idx) { + size_t total_len = 0; + for (size_t j = repl_start_idx; j < repl_end_idx; j++) { + if (j > repl_start_idx) total_len++; + total_len += tokens[j].text.len; + } + char *repl_buf = (char *)paze_arena_alloc(pp->arena, total_len + 1); + size_t offset = 0; + for (size_t j = repl_start_idx; j < repl_end_idx; j++) { + if (j > repl_start_idx) repl_buf[offset++] = ' '; + memcpy(repl_buf + offset, tokens[j].text.data, + tokens[j].text.len); + offset += tokens[j].text.len; + } + repl_buf[total_len] = '\0'; + replacement.data = repl_buf; + replacement.len = total_len; + } + + define_macro(pp, macro_name, replacement, is_func, + params, param_count, false); + + free(params); + continue; + } + + /* Handle #include */ + if (is_directive_keyword(&directive, "include")) { + i++; + if (i >= token_count) break; + paze_token_t path_tk = tokens[i]; + + if (path_tk.kind == PAZE_TK_STRING_LITERAL || + path_tk.kind == PAZE_TK_LT) { + paze_str_t path; + if (path_tk.kind == PAZE_TK_STRING_LITERAL) { + path = path_tk.str_value; + } else { + /* Read until > */ + i++; + if (i < token_count && tokens[i].kind != PAZE_TK_GT) { + const char *start = tokens[i].text.data; + size_t len = 0; + while (i < token_count && tokens[i].kind != PAZE_TK_GT) { + len += tokens[i].text.len; + i++; + } + path.data = start; + path.len = len; + } + if (i < token_count) i++; /* skip > */ + } + + /* Check for built-in headers */ + if (path.len == 6 && memcmp(path.data, "paze.h", 6) == 0) { + /* Include built-in paze.h */ + paze_lexer_t *inc_lex = paze_lexer_create( + paze_builtin_paze_h, ""); + if (inc_lex) { + paze_token_arr_t inc_tokens = paze_lexer_tokenize(inc_lex, diag); + paze_lexer_destroy(inc_lex); + + /* Process directives recursively for the included file */ + paze_token_arr_t processed = process_directives( + pp, inc_tokens.data, inc_tokens.len, diag); + + for (size_t j = 0; j < processed.len; j++) { + if (processed.data[j].kind == PAZE_TK_EOF) break; + paze_token_arr_t_push(&output, processed.data[j]); + } + + paze_token_arr_t_free(&processed); + paze_token_arr_t_free(&inc_tokens); + } + /* Skip remaining tokens on this line */ + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } else { + paze_diagnostics_warning(diag, directive_loc, + "include file not found or not supported: '%.*s'", + (int)path.len, path.data); + /* Skip rest of line */ + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } + } else { + paze_diagnostics_error(diag, directive_loc, + "expected filename or '<' after '#include'"); + i++; + continue; + } + } + + /* Handle #ifdef */ + if (is_directive_keyword(&directive, "ifdef")) { + i++; + if (i >= token_count) break; + paze_token_t name_tk = tokens[i]; + if (name_tk.kind != PAZE_TK_IDENTIFIER) { + paze_diagnostics_error(diag, directive_loc, + "expected identifier after '#ifdef'"); + if_stack_push(&pp->if_stack, false); + i++; + continue; + } + paze_macro_def_t *def = macro_table_find(&pp->macros, name_tk.text); + bool active = def != NULL; + /* Check parent state */ + if (!if_stack_is_active(&pp->if_stack)) { + active = false; + } + if_stack_push(&pp->if_stack, active); + i++; + /* Skip rest of line */ + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } + + /* Handle #ifndef */ + if (is_directive_keyword(&directive, "ifndef")) { + i++; + if (i >= token_count) break; + paze_token_t name_tk = tokens[i]; + if (name_tk.kind != PAZE_TK_IDENTIFIER) { + paze_diagnostics_error(diag, directive_loc, + "expected identifier after '#ifndef'"); + if_stack_push(&pp->if_stack, false); + i++; + continue; + } + paze_macro_def_t *def = macro_table_find(&pp->macros, name_tk.text); + bool active = def == NULL; + if (!if_stack_is_active(&pp->if_stack)) { + active = false; + } + if_stack_push(&pp->if_stack, active); + i++; + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } + + /* Handle #if */ + if (is_directive_keyword(&directive, "if")) { + i++; + /* Collect expression tokens until end of line */ + size_t expr_start = i; + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + size_t expr_end = i; + + /* Evaluate the expression */ + bool active = false; + if (expr_end > expr_start) { + paze_expr_ctx_t ctx; + ctx.tokens = tokens + expr_start; + ctx.token_count = expr_end - expr_start; + ctx.pos = 0; + ctx.pp = pp; + ctx.diag = diag; + ctx.loc = directive_loc; + long val = parse_expr(&ctx); + active = val != 0; + } + + /* Check if parent level allows this to be active */ + if (pp->if_stack.len > 0) { + if (!if_stack_is_active(&pp->if_stack)) { + active = false; + } + } + + if_stack_push(&pp->if_stack, active); + continue; + } + + /* Handle #elif */ + if (is_directive_keyword(&directive, "elif")) { + if (pp->if_stack.len == 0) { + paze_diagnostics_error(diag, directive_loc, + "#elif without #if"); + break; + } + i++; + size_t expr_start = i; + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + size_t expr_end = i; + + bool active = false; + if (expr_end > expr_start) { + paze_expr_ctx_t ctx; + ctx.tokens = tokens + expr_start; + ctx.token_count = expr_end - expr_start; + ctx.pos = 0; + ctx.pp = pp; + ctx.diag = diag; + ctx.loc = directive_loc; + long val = parse_expr(&ctx); + active = val != 0; + } + + /* Check parent level (the enclosing #if's state) */ + if (pp->if_stack.len > 1) { + if (!pp->if_stack.data[pp->if_stack.len - 2].active) { + active = false; + } + } + + /* If any branch was already taken, this can't be active */ + if (pp->if_stack.data[pp->if_stack.len - 1].was_active) { + active = false; + } + + if_stack_set_active(&pp->if_stack, active); + continue; + } + + /* Handle #else */ + if (is_directive_keyword(&directive, "else")) { + if (pp->if_stack.len == 0) { + paze_diagnostics_error(diag, directive_loc, + "#else without #if"); + break; + } + bool active = true; + if (pp->if_stack.data[pp->if_stack.len - 1].was_active) { + active = false; + } + if (pp->if_stack.len > 1) { + if (!pp->if_stack.data[pp->if_stack.len - 2].active) { + active = false; + } + } + if_stack_set_active(&pp->if_stack, active); + i++; + continue; + } + + /* Handle #endif */ + if (is_directive_keyword(&directive, "endif")) { + if (pp->if_stack.len == 0) { + paze_diagnostics_error(diag, directive_loc, + "#endif without #if"); + } else { + if_stack_pop(&pp->if_stack); + } + i++; + continue; + } + + /* Handle #undef */ + if (is_directive_keyword(&directive, "undef")) { + i++; + if (i >= token_count) break; + paze_token_t name_tk = tokens[i]; + if (name_tk.kind == PAZE_TK_IDENTIFIER) { + undef_macro(pp, name_tk.text); + } else { + paze_diagnostics_error(diag, directive_loc, + "expected identifier after '#undef'"); + } + i++; + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } + + /* Handle #pragma (no-op) */ + if (is_directive_keyword(&directive, "pragma")) { + i++; + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } + + /* Handle #error */ + if (is_directive_keyword(&directive, "error")) { + i++; + /* Collect message tokens */ + paze_str_t msg = { NULL, 0 }; + if (i < token_count && tokens[i].kind == PAZE_TK_STRING_LITERAL) { + msg = tokens[i].str_value; + i++; + } + paze_diagnostics_error(diag, directive_loc, + "#error directive: %.*s", + (int)msg.len, msg.len > 0 ? msg.data : "(no message)"); + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } + + /* Unknown directive: skip the line */ + paze_diagnostics_warning(diag, directive_loc, + "unknown preprocessor directive"); + i++; + while (i < token_count && !tokens[i].at_line_start && + tokens[i].kind != PAZE_TK_EOF) { + i++; + } + continue; + } + + /* Not a directive - if we're in an inactive #if block, skip */ + if (!if_stack_is_active(&pp->if_stack)) { + i++; + continue; + } + + paze_token_arr_t_push(&output, tk); + i++; + } + + return output; +} + +/* ======================================================================== + * Public API + * ======================================================================== */ + +paze_preprocessor_t *paze_preprocessor_create(void) +{ + paze_preprocessor_t *pp = (paze_preprocessor_t *)paze_calloc( + 1, sizeof(paze_preprocessor_t)); + pp->arena = paze_arena_create(); + macro_table_init(&pp->macros); + if_stack_init(&pp->if_stack); + pp->expand_depth = 0; + pp->max_expand = 100; + return pp; +} + +void paze_preprocessor_destroy(paze_preprocessor_t *pp) +{ + if (!pp) return; + macro_table_free(&pp->macros); + if_stack_free(&pp->if_stack); + paze_arena_destroy(pp->arena); + free(pp); +} + +paze_token_arr_t paze_preprocessor_run( + paze_preprocessor_t *pp, + const char *source, + const char *source_file, + const paze_str_t *platform_defs, + size_t platform_def_count, + paze_diagnostics_t *diag) +{ + /* Add platform definitions */ + if (platform_defs && platform_def_count > 0) { + paze_str_t empty = { "", 0 }; + for (size_t i = 0; i < platform_def_count; i++) { + define_macro(pp, platform_defs[i], empty, false, NULL, 0, true); + } + } + + /* Also define standard platform macros */ + paze_str_t one = { "1", 1 }; + +#if defined(_WIN32) + { + paze_str_t win = { "_WIN32", 6 }; + define_macro(pp, win, one, false, NULL, 0, true); + } +#endif +#if defined(__linux__) + { + paze_str_t linux = { "__linux__", 9 }; + define_macro(pp, linux, one, false, NULL, 0, true); + } +#endif +#if defined(__APPLE__) + { + paze_str_t apple = { "__APPLE__", 9 }; + define_macro(pp, apple, one, false, NULL, 0, true); + } +#endif +#if defined(__x86_64__) + { + paze_str_t arch = { "__x86_64__", 10 }; + define_macro(pp, arch, one, false, NULL, 0, true); + } +#endif +#if defined(__aarch64__) + { + paze_str_t arch = { "__arm64__", 9 }; + define_macro(pp, arch, one, false, NULL, 0, true); + } +#endif + + /* Lex the source */ + paze_lexer_t *lex = paze_lexer_create(source, source_file); + if (!lex) { + paze_token_arr_t empty; + paze_token_arr_t_init(&empty); + return empty; + } + + paze_token_arr_t tokens = paze_lexer_tokenize(lex, diag); + paze_lexer_destroy(lex); + + /* Process directives */ + paze_token_arr_t processed = process_directives( + pp, tokens.data, tokens.len, diag); + + /* Macro expansion */ + paze_token_arr_t expanded = expand_macros( + pp, processed.data, processed.len, diag); + + /* Cleanup intermediate arrays */ + paze_token_arr_t_free(&processed); + paze_token_arr_t_free(&tokens); + + /* Reset expansion depth */ + pp->expand_depth = 0; + + return expanded; +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_sema.c b/src/PazeE.los4/src/paze_sema.c new file mode 100644 index 0000000..f6a13a1 --- /dev/null +++ b/src/PazeE.los4/src/paze_sema.c @@ -0,0 +1,408 @@ +#include "../include/paze_sema.h" +#include "../include/paze_token.h" +#include +#include +#include + +/* ======================================================================== + * Type helpers (mirror of parser's type_size_of / type_align_of, made + * non-static so codegen can query layout without duplicating logic). + * ======================================================================== */ + +paze_type_t *paze_cg_resolve_typedef(paze_type_t *t) +{ + while (t && (t->kind == PAZE_TYPE_TYPEDEF || t->kind == PAZE_TYPE_TYPEOF)) { + if (t->kind == PAZE_TYPE_TYPEDEF) t = t->base; + else if (t->kind == PAZE_TYPE_TYPEOF) { + if (t->typeof_expr) t = t->typeof_expr->type; + else break; + } + } + return t; +} + +size_t paze_cg_sizeof(paze_type_t *t) +{ + if (!t) return 0; + switch (t->kind) { + case PAZE_TYPE_VOID: return 0; + case PAZE_TYPE_CHAR: return 1; + case PAZE_TYPE_SHORT: return 2; + case PAZE_TYPE_INT: return 4; + case PAZE_TYPE_LONG: return 8; + case PAZE_TYPE_UNSIGNED: + case PAZE_TYPE_SIGNED: + if (t->base) return paze_cg_sizeof(t->base); + return 4; + case PAZE_TYPE_BOOL: return 1; + case PAZE_TYPE_POINTER: return 8; + case PAZE_TYPE_ARRAY: + if (t->array_size >= 0 && t->base) + return paze_cg_sizeof(t->base) * (size_t)t->array_size; + return 0; + case PAZE_TYPE_FUNCTION: return 8; + case PAZE_TYPE_STRUCT: + case PAZE_TYPE_UNION: + return t->struct_size; + case PAZE_TYPE_ENUM: return 4; + case PAZE_TYPE_TYPEDEF: + if (t->base) return paze_cg_sizeof(t->base); + return 0; + case PAZE_TYPE_TYPEOF: + if (t->typeof_expr && t->typeof_expr->type) + return paze_cg_sizeof(t->typeof_expr->type); + return 0; + default: return 0; + } +} + +size_t paze_cg_alignof(paze_type_t *t) +{ + if (!t) return 1; + switch (t->kind) { + case PAZE_TYPE_VOID: return 1; + case PAZE_TYPE_CHAR: return 1; + case PAZE_TYPE_SHORT: return 2; + case PAZE_TYPE_INT: return 4; + case PAZE_TYPE_LONG: return 8; + case PAZE_TYPE_UNSIGNED: + case PAZE_TYPE_SIGNED: + if (t->base) return paze_cg_alignof(t->base); + return 4; + case PAZE_TYPE_BOOL: return 1; + case PAZE_TYPE_POINTER: return 8; + case PAZE_TYPE_ARRAY: + if (t->base) return paze_cg_alignof(t->base); + return 1; + case PAZE_TYPE_FUNCTION: return 8; + case PAZE_TYPE_STRUCT: + case PAZE_TYPE_UNION: { + size_t max_align = 1; + for (int i = 0; i < t->struct_field_count; i++) { + size_t a = paze_cg_alignof(t->struct_fields[i].type); + if (a > max_align) max_align = a; + } + return max_align; + } + case PAZE_TYPE_ENUM: return 4; + case PAZE_TYPE_TYPEDEF: + if (t->base) return paze_cg_alignof(t->base); + return 1; + case PAZE_TYPE_TYPEOF: + if (t->typeof_expr && t->typeof_expr->type) + return paze_cg_alignof(t->typeof_expr->type); + return 1; + default: return 1; + } +} + +bool paze_cg_is_unsigned(paze_type_t *t) +{ + if (!t) return false; + if (t->kind == PAZE_TYPE_UNSIGNED) return true; + if (t->kind == PAZE_TYPE_TYPEDEF || t->kind == PAZE_TYPE_SIGNED) + return paze_cg_is_unsigned(t->base); + if (t->kind == PAZE_TYPE_TYPEOF && t->typeof_expr) + return paze_cg_is_unsigned(t->typeof_expr->type); + return t->kind == PAZE_TYPE_POINTER || t->kind == PAZE_TYPE_BOOL; +} + +bool paze_cg_is_integer(paze_type_t *t) +{ + if (!t) return false; + t = paze_cg_resolve_typedef(t); + if (!t) return false; + switch (t->kind) { + case PAZE_TYPE_CHAR: + case PAZE_TYPE_SHORT: + case PAZE_TYPE_INT: + case PAZE_TYPE_LONG: + case PAZE_TYPE_UNSIGNED: + case PAZE_TYPE_SIGNED: + case PAZE_TYPE_BOOL: + case PAZE_TYPE_ENUM: + return true; + default: + return false; + } +} + +bool paze_cg_is_ptr_like(paze_type_t *t) +{ + if (!t) return false; + t = paze_cg_resolve_typedef(t); + if (!t) return false; + return t->kind == PAZE_TYPE_POINTER || t->kind == PAZE_TYPE_ARRAY || + t->kind == PAZE_TYPE_FUNCTION; +} + +paze_type_t *paze_cg_element_type(paze_type_t *t) +{ + if (!t) return NULL; + t = paze_cg_resolve_typedef(t); + if (!t) return NULL; + if (t->kind == PAZE_TYPE_POINTER || t->kind == PAZE_TYPE_ARRAY) + return t->base; + return NULL; +} + +/* Recursive struct field lookup with anonymous member support. + * Accumulates byte offset across nested anonymous structs/unions. */ +static paze_struct_field_t *find_field_rec(paze_type_t *st, const char *name, + int *out_total_offset, int base_off) +{ + if (!st) return NULL; + st = paze_cg_resolve_typedef(st); + if (!st || (st->kind != PAZE_TYPE_STRUCT && st->kind != PAZE_TYPE_UNION)) + return NULL; + /* First: direct named field */ + for (int i = 0; i < st->struct_field_count; i++) { + paze_struct_field_t *f = &st->struct_fields[i]; + if (f->name.len > 0) { + if (strlen(name) == f->name.len && + memcmp(name, f->name.data, f->name.len) == 0) { + if (out_total_offset) *out_total_offset = base_off + f->offset; + return f; + } + } + } + /* Second: recurse into anonymous members */ + for (int i = 0; i < st->struct_field_count; i++) { + paze_struct_field_t *f = &st->struct_fields[i]; + if (f->name.len == 0) { + int saved = out_total_offset ? *out_total_offset : 0; + paze_struct_field_t *r = find_field_rec(f->type, name, + out_total_offset, + base_off + f->offset); + if (r) return r; + if (out_total_offset) *out_total_offset = saved; + } + } + return NULL; +} + +paze_struct_field_t *paze_cg_find_field(paze_type_t *struct_type, const char *name, + int *out_total_offset) +{ + if (out_total_offset) *out_total_offset = 0; + return find_field_rec(struct_type, name, out_total_offset, 0); +} + +/* ======================================================================== + * Symbol Table (scoped) + * ======================================================================== */ + +static paze_cg_scope_t *scope_create(paze_cg_scope_t *parent) +{ + paze_cg_scope_t *s = (paze_cg_scope_t *)calloc(1, sizeof(*s)); + if (!s) return NULL; + s->parent = parent; + return s; +} + +static void scope_destroy(paze_cg_scope_t *s) +{ + if (!s) return; + for (size_t i = 0; i < s->count; i++) { + free(s->items[i]->name); + free(s->items[i]); + } + free(s->items); + free(s); +} + +paze_cg_sym_table_t *paze_cg_sym_table_create(void) +{ + paze_cg_sym_table_t *tbl = (paze_cg_sym_table_t *)calloc(1, sizeof(*tbl)); + if (!tbl) return NULL; + tbl->global_scope = scope_create(NULL); + tbl->stack[0] = tbl->global_scope; + tbl->top = 1; + return tbl; +} + +void paze_cg_sym_table_destroy(paze_cg_sym_table_t *tbl) +{ + if (!tbl) return; + while (tbl->top > 0) { + tbl->top--; + /* Don't destroy global_scope's parent chain (already empty). */ + scope_destroy(tbl->stack[tbl->top]); + } + free(tbl); +} + +void paze_cg_push_scope(paze_cg_sym_table_t *tbl) +{ + if (!tbl) return; + if (tbl->top >= (int)(sizeof(tbl->stack) / sizeof(tbl->stack[0]))) { + /* Too many nested scopes — refuse to push but continue. */ + return; + } + tbl->stack[tbl->top] = scope_create(tbl->stack[tbl->top - 1]); + tbl->top++; +} + +void paze_cg_pop_scope(paze_cg_sym_table_t *tbl) +{ + if (!tbl || tbl->top <= 1) return; + tbl->top--; + scope_destroy(tbl->stack[tbl->top]); + tbl->stack[tbl->top] = NULL; +} + +void paze_cg_scope_add(paze_cg_sym_table_t *tbl, paze_cg_sym_t sym) +{ + if (!tbl || tbl->top <= 0) return; + paze_cg_scope_t *s = tbl->stack[tbl->top - 1]; + paze_cg_sym_t *entry = (paze_cg_sym_t *)calloc(1, sizeof(*entry)); + if (!entry) return; + *entry = sym; + /* Copy name to malloc'd NUL-terminated buffer. */ + if (sym.name) { + size_t n = strlen(sym.name); + entry->name = (char *)malloc(n + 1); + if (entry->name) { + memcpy(entry->name, sym.name, n); + entry->name[n] = '\0'; + } + } else { + entry->name = NULL; + } + if (s->count >= s->cap) { + size_t nc = s->cap ? s->cap * 2 : 8; + s->items = (paze_cg_sym_t **)realloc(s->items, nc * sizeof(*s->items)); + s->cap = nc; + } + s->items[s->count++] = entry; +} + +paze_cg_sym_t *paze_cg_scope_find(paze_cg_sym_table_t *tbl, const char *name) +{ + if (!tbl || !name) return NULL; + for (int i = tbl->top - 1; i >= 0; i--) { + paze_cg_scope_t *s = tbl->stack[i]; + if (!s) continue; + for (size_t j = 0; j < s->count; j++) { + if (s->items[j]->name && strcmp(s->items[j]->name, name) == 0) + return s->items[j]; + } + } + return NULL; +} + +/* ======================================================================== + * Constant Evaluation + * Handles: int/char literals, sizeof, enum consts, unary +,-,~,!, paren, + * binary arithmetic/logical/shift/comparison, conditional, comma. + * ======================================================================== */ + +static bool try_const_expr(paze_ast_node_t *e, int64_t *out_val, + paze_cg_sym_table_t *syms); + +static bool try_const_unary(paze_ast_node_t *e, int64_t *out, + paze_cg_sym_table_t *syms) +{ + if (!try_const_expr(e->data.unary_expr.operand, out, syms)) return false; + int64_t v = *out; + switch (e->data.unary_expr.op) { + case PAZE_TK_PLUS: *out = v; return true; + case PAZE_TK_MINUS: *out = -v; return true; + case PAZE_TK_TILDE: *out = ~v; return true; + case PAZE_TK_NOT: *out = v ? 0 : 1; return true; + default: return false; + } +} + +static bool try_const_binary(paze_ast_node_t *e, int64_t *out, + paze_cg_sym_table_t *syms) +{ + int64_t l, r; + if (!try_const_expr(e->data.binary_expr.left, &l, syms)) return false; + if (!try_const_expr(e->data.binary_expr.right, &r, syms)) return false; + switch (e->data.binary_expr.op) { + case PAZE_TK_PLUS: *out = l + r; return true; + case PAZE_TK_MINUS: *out = l - r; return true; + case PAZE_TK_STAR: *out = l * r; return true; + case PAZE_TK_SLASH: if (r == 0) return false; *out = l / r; return true; + case PAZE_TK_PERCENT: if (r == 0) return false; *out = l % r; return true; + case PAZE_TK_AMP: *out = l & r; return true; + case PAZE_TK_PIPE: *out = l | r; return true; + case PAZE_TK_CARET: *out = l ^ r; return true; + case PAZE_TK_SHL: *out = l << r; return true; + case PAZE_TK_SHR: *out = l >> r; return true; + case PAZE_TK_EQ: *out = l == r; return true; + case PAZE_TK_NOT_EQ: *out = l != r; return true; + case PAZE_TK_LT: *out = l < r; return true; + case PAZE_TK_LE: *out = l <= r; return true; + case PAZE_TK_GT: *out = l > r; return true; + case PAZE_TK_GE: *out = l >= r; return true; + case PAZE_TK_AND_AND: *out = (l && r); return true; + case PAZE_TK_OR_OR: *out = (l || r); return true; + default: return false; + } +} + +static bool try_const_expr(paze_ast_node_t *e, int64_t *out_val, + paze_cg_sym_table_t *syms) +{ + if (!e) return false; + switch (e->kind) { + case PAZE_NODE_INT_LITERAL: + *out_val = (int64_t)e->data.int_literal.value; + return true; + case PAZE_NODE_CHAR_LITERAL: + *out_val = (int64_t)(unsigned char)e->data.char_literal.value; + return true; + case PAZE_NODE_UNARY_EXPR: + return try_const_unary(e, out_val, syms); + case PAZE_NODE_BINARY_EXPR: + return try_const_binary(e, out_val, syms); + case PAZE_NODE_CONDITIONAL_EXPR: { + int64_t c; + if (!try_const_expr(e->data.conditional_expr.condition, &c, syms)) + return false; + if (c) return try_const_expr(e->data.conditional_expr.then_expr, out_val, syms); + else return try_const_expr(e->data.conditional_expr.else_expr, out_val, syms); + } + case PAZE_NODE_COMMA_EXPR: + if (!try_const_expr(e->data.comma_expr.left, out_val, syms)) return false; + return try_const_expr(e->data.comma_expr.right, out_val, syms); + case PAZE_NODE_SIZEOF_EXPR: { + if (e->data.sizeof_expr.is_type) { + *out_val = (int64_t)paze_cg_sizeof(e->data.sizeof_expr.size_type); + } else { + paze_type_t *t = e->data.sizeof_expr.expr ? e->data.sizeof_expr.expr->type : NULL; + *out_val = (int64_t)paze_cg_sizeof(t); + } + return true; + } + case PAZE_NODE_IDENTIFIER_REF: { + /* Enum constants only (variables aren't constants). */ + if (syms && e->data.identifier_ref.name.data) { + char buf[256]; + size_t n = e->data.identifier_ref.name.len; + if (n >= sizeof(buf)) n = sizeof(buf) - 1; + memcpy(buf, e->data.identifier_ref.name.data, n); + buf[n] = '\0'; + paze_cg_sym_t *s = paze_cg_scope_find(syms, buf); + if (s && s->kind == PAZE_CG_SYM_ENUM_CONST) { + *out_val = s->enum_value; + return true; + } + } + return false; + } + case PAZE_NODE_CAST_EXPR: + /* Cast of a constant to integer type is still a constant. */ + return try_const_expr(e->data.cast_expr.expr, out_val, syms); + default: + return false; + } +} + +bool paze_cg_try_const(paze_ast_node_t *e, int64_t *out_val, + paze_cg_sym_table_t *syms) +{ + return try_const_expr(e, out_val, syms); +} diff --git a/src/PazeE.los4/src/paze_token.c b/src/PazeE.los4/src/paze_token.c new file mode 100644 index 0000000..3d46273 --- /dev/null +++ b/src/PazeE.los4/src/paze_token.c @@ -0,0 +1,269 @@ +#include "../include/paze_token.h" + +#include +#include +#include + +/* ======================================================================== + * Keyword Lookup Table + * Sorted by length for better cache locality – the lexer already has + * the full identifier text, so we do a simple linear scan with + * memcmp on equal-length keys. + * ======================================================================== */ + +typedef struct { + const char *keyword; + size_t length; + paze_tk_t kind; +} paze_keyword_entry_t; + +static const paze_keyword_entry_t paze_keywords[] = { + /* 1-char keywords */ + { "do", 2, PAZE_TK_KW_DO }, + { "if", 2, PAZE_TK_KW_IF }, + + /* 3-char keywords */ + { "int", 3, PAZE_TK_KW_INT }, + { "for", 3, PAZE_TK_KW_FOR }, + + /* 4-char keywords */ + { "void", 4, PAZE_TK_KW_VOID }, + { "char", 4, PAZE_TK_KW_CHAR }, + { "long", 4, PAZE_TK_KW_LONG }, + { "auto", 4, PAZE_TK_KW_AUTO }, + { "else", 4, PAZE_TK_KW_ELSE }, + { "enum", 4, PAZE_TK_KW_ENUM }, + { "goto", 4, PAZE_TK_KW_GOTO }, + { "case", 4, PAZE_TK_KW_CASE }, + + /* 5-char keywords */ + { "short", 5, PAZE_TK_KW_SHORT }, + { "const", 5, PAZE_TK_KW_CONST }, + { "break", 5, PAZE_TK_KW_BREAK }, + { "while", 5, PAZE_TK_KW_WHILE }, + { "sizeof", 6, PAZE_TK_KW_SIZEOF }, + + /* 6-char keywords */ + { "signed", 6, PAZE_TK_KW_SIGNED }, + { "static", 6, PAZE_TK_KW_STATIC }, + { "extern", 6, PAZE_TK_KW_EXTERN }, + { "struct", 6, PAZE_TK_KW_STRUCT }, + { "return", 6, PAZE_TK_KW_RETURN }, + { "default", 7, PAZE_TK_KW_DEFAULT }, + + /* 7-char keywords */ + { "unsigned", 8, PAZE_TK_KW_UNSIGNED }, + { "register", 8, PAZE_TK_KW_REGISTER }, + { "volatile", 8, PAZE_TK_KW_VOLATILE }, + { "continue", 8, PAZE_TK_KW_CONTINUE }, + { "switch", 6, PAZE_TK_KW_SWITCH }, + { "typedef", 7, PAZE_TK_KW_TYPEDEF }, + + /* 8-char keywords */ + { "typeof", 6, PAZE_TK_KW_TYPEOF }, + { "_Bool", 5, PAZE_TK_KW_BOOL }, + + /* Preprocessor keywords (contextual) */ + { "__typeof__", 10, PAZE_TK_KW_TYPEOF }, +}; + +static const size_t paze_keyword_count = + sizeof(paze_keywords) / sizeof(paze_keywords[0]); + +paze_tk_t paze_keyword_lookup(paze_str_t ident) +{ + if (ident.len == 0 || ident.len > 16) { + return PAZE_TK_IDENTIFIER; + } + for (size_t i = 0; i < paze_keyword_count; i++) { + if (paze_keywords[i].length == ident.len && + memcmp(paze_keywords[i].keyword, ident.data, ident.len) == 0) { + return paze_keywords[i].kind; + } + } + return PAZE_TK_IDENTIFIER; +} + +/* ======================================================================== + * Helper Predicates + * ======================================================================== */ + +bool paze_tk_is_type_keyword(paze_tk_t kind) +{ + switch (kind) { + case PAZE_TK_KW_VOID: + case PAZE_TK_KW_CHAR: + case PAZE_TK_KW_SHORT: + case PAZE_TK_KW_INT: + case PAZE_TK_KW_LONG: + case PAZE_TK_KW_UNSIGNED: + case PAZE_TK_KW_SIGNED: + case PAZE_TK_KW_CONST: + case PAZE_TK_KW_STRUCT: + case PAZE_TK_KW_UNION: + case PAZE_TK_KW_ENUM: + case PAZE_TK_KW_TYPEDEF: + case PAZE_TK_KW_BOOL: + case PAZE_TK_KW_TYPEOF: + return true; + default: + return false; + } +} + +bool paze_tk_is_assignment(paze_tk_t kind) +{ + switch (kind) { + case PAZE_TK_ASSIGN: + case PAZE_TK_PLUS_ASSIGN: + case PAZE_TK_MINUS_ASSIGN: + case PAZE_TK_STAR_ASSIGN: + case PAZE_TK_SLASH_ASSIGN: + case PAZE_TK_PERCENT_ASSIGN: + case PAZE_TK_SHL_ASSIGN: + case PAZE_TK_SHR_ASSIGN: + case PAZE_TK_AND_ASSIGN: + case PAZE_TK_OR_ASSIGN: + case PAZE_TK_XOR_ASSIGN: + return true; + default: + return false; + } +} + +/* ======================================================================== + * Token Name Table (for diagnostics / debugging) + * ======================================================================== */ + +typedef struct { + paze_tk_t kind; + const char *name; +} paze_tk_name_entry_t; + +static const paze_tk_name_entry_t paze_tk_names[] = { + { PAZE_TK_EOF, "end of file" }, + { PAZE_TK_UNKNOWN, "unknown" }, + { PAZE_TK_INT_LITERAL, "integer literal" }, + { PAZE_TK_CHAR_LITERAL, "char literal" }, + { PAZE_TK_STRING_LITERAL, "string literal" }, + { PAZE_TK_IDENTIFIER, "identifier" }, + + { PAZE_TK_KW_VOID, "void" }, + { PAZE_TK_KW_CHAR, "char" }, + { PAZE_TK_KW_SHORT, "short" }, + { PAZE_TK_KW_INT, "int" }, + { PAZE_TK_KW_LONG, "long" }, + { PAZE_TK_KW_UNSIGNED, "unsigned" }, + { PAZE_TK_KW_SIGNED, "signed" }, + { PAZE_TK_KW_CONST, "const" }, + { PAZE_TK_KW_STATIC, "static" }, + { PAZE_TK_KW_EXTERN, "extern" }, + { PAZE_TK_KW_REGISTER, "register" }, + { PAZE_TK_KW_VOLATILE, "volatile" }, + { PAZE_TK_KW_AUTO, "auto" }, + { PAZE_TK_KW_BOOL, "_Bool" }, + + { PAZE_TK_KW_STRUCT, "struct" }, + { PAZE_TK_KW_UNION, "union" }, + { PAZE_TK_KW_ENUM, "enum" }, + { PAZE_TK_KW_TYPEDEF, "typedef" }, + { PAZE_TK_KW_SIZEOF, "sizeof" }, + { PAZE_TK_KW_TYPEOF, "typeof" }, + { PAZE_TK_KW_RETURN, "return" }, + { PAZE_TK_KW_IF, "if" }, + { PAZE_TK_KW_ELSE, "else" }, + { PAZE_TK_KW_WHILE, "while" }, + { PAZE_TK_KW_DO, "do" }, + { PAZE_TK_KW_FOR, "for" }, + { PAZE_TK_KW_SWITCH, "switch" }, + { PAZE_TK_KW_CASE, "case" }, + { PAZE_TK_KW_DEFAULT, "default" }, + { PAZE_TK_KW_BREAK, "break" }, + { PAZE_TK_KW_CONTINUE, "continue" }, + { PAZE_TK_KW_GOTO, "goto" }, + + { PAZE_TK_PLUS, "+" }, + { PAZE_TK_MINUS, "-" }, + { PAZE_TK_STAR, "*" }, + { PAZE_TK_SLASH, "/" }, + { PAZE_TK_PERCENT, "%" }, + + { PAZE_TK_PLUS_PLUS, "++" }, + { PAZE_TK_MINUS_MINUS, "--" }, + + { PAZE_TK_ASSIGN, "=" }, + { PAZE_TK_PLUS_ASSIGN, "+=" }, + { PAZE_TK_MINUS_ASSIGN, "-=" }, + { PAZE_TK_STAR_ASSIGN, "*=" }, + { PAZE_TK_SLASH_ASSIGN, "/=" }, + { PAZE_TK_PERCENT_ASSIGN, "%=" }, + { PAZE_TK_SHL_ASSIGN, "<<=" }, + { PAZE_TK_SHR_ASSIGN, ">>=" }, + { PAZE_TK_AND_ASSIGN, "&=" }, + { PAZE_TK_OR_ASSIGN, "|=" }, + { PAZE_TK_XOR_ASSIGN, "^=" }, + + { PAZE_TK_EQ, "==" }, + { PAZE_TK_NOT_EQ, "!=" }, + { PAZE_TK_LT, "<" }, + { PAZE_TK_LE, "<=" }, + { PAZE_TK_GT, ">" }, + { PAZE_TK_GE, ">=" }, + + { PAZE_TK_AND_AND, "&&" }, + { PAZE_TK_OR_OR, "||" }, + { PAZE_TK_NOT, "!" }, + + { PAZE_TK_AMP, "&" }, + { PAZE_TK_PIPE, "|" }, + { PAZE_TK_CARET, "^" }, + { PAZE_TK_TILDE, "~" }, + + { PAZE_TK_SHL, "<<" }, + { PAZE_TK_SHR, ">>" }, + + { PAZE_TK_QUESTION, "?" }, + { PAZE_TK_COLON, ":" }, + { PAZE_TK_SEMICOLON, ";" }, + { PAZE_TK_COMMA, "," }, + { PAZE_TK_DOT, "." }, + { PAZE_TK_ARROW, "->" }, + + { PAZE_TK_LPAREN, "(" }, + { PAZE_TK_RPAREN, ")" }, + { PAZE_TK_LBRACKET, "[" }, + { PAZE_TK_RBRACKET, "]" }, + { PAZE_TK_LBRACE, "{" }, + { PAZE_TK_RBRACE, "}" }, + + { PAZE_TK_HASH, "#" }, +}; + +static const size_t paze_tk_name_count = + sizeof(paze_tk_names) / sizeof(paze_tk_names[0]); + +const char *paze_tk_name(paze_tk_t kind) +{ + for (size_t i = 0; i < paze_tk_name_count; i++) { + if (paze_tk_names[i].kind == kind) { + return paze_tk_names[i].name; + } + } + return "?"; +} + +bool paze_tk_is_one_of(paze_tk_t kind, size_t count, ...) +{ + va_list ap; + va_start(ap, count); + bool found = false; + for (size_t i = 0; i < count; i++) { + paze_tk_t k = (paze_tk_t)va_arg(ap, int); + if (k == kind) { + found = true; + break; + } + } + va_end(ap); + return found; +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_types.c b/src/PazeE.los4/src/paze_types.c new file mode 100644 index 0000000..98ac792 --- /dev/null +++ b/src/PazeE.los4/src/paze_types.c @@ -0,0 +1,165 @@ +#include "../include/paze_types.h" + +#include +#include +#include +#include + +/* ======================================================================== + * String Utilities + * ======================================================================== */ + +char *paze_str_dup(paze_str_t s) +{ + if (s.len == 0) { + char *empty = (char *)malloc(1); + if (empty) empty[0] = '\0'; + return empty; + } + char *buf = (char *)malloc(s.len + 1); + if (!buf) return NULL; + memcpy(buf, s.data, s.len); + buf[s.len] = '\0'; + return buf; +} + +int paze_str_cmp(paze_str_t a, paze_str_t b) +{ + if (a.len != b.len) { + return (int)a.len - (int)b.len; + } + if (a.len == 0) return 0; + return memcmp(a.data, b.data, a.len); +} + +/* ======================================================================== + * Arena Allocator + * ======================================================================== */ + +#define PAZE_ARENA_ALIGN 16 + +static size_t align_up(size_t n, size_t align) +{ + return (n + align - 1) & ~(align - 1); +} + +static paze_arena_block_t *arena_block_create(size_t capacity) +{ + paze_arena_block_t *block = (paze_arena_block_t *)malloc( + sizeof(paze_arena_block_t) + capacity); + if (!block) return NULL; + block->next = NULL; + block->used = 0; + block->capacity = capacity; + return block; +} + +paze_arena_t *paze_arena_create(void) +{ + paze_arena_t *arena = (paze_arena_t *)calloc(1, sizeof(paze_arena_t)); + if (!arena) return NULL; + arena->head = arena_block_create(PAZE_ARENA_DEFAULT_SIZE); + if (!arena->head) { + free(arena); + return NULL; + } + return arena; +} + +void paze_arena_destroy(paze_arena_t *arena) +{ + if (!arena) return; + paze_arena_block_t *block = arena->head; + while (block) { + paze_arena_block_t *next = block->next; + free(block); + block = next; + } + free(arena); +} + +void *paze_arena_alloc(paze_arena_t *arena, size_t size) +{ + if (!arena || size == 0) return NULL; + + size_t aligned = align_up(size, PAZE_ARENA_ALIGN); + + paze_arena_block_t *block = arena->head; + if (block && block->capacity - block->used >= aligned) { + void *ptr = block->memory + block->used; + block->used += aligned; + return ptr; + } + + /* Need a new block. If the requested size is larger than the default + * block size, allocate a dedicated block for it. */ + size_t block_capacity = PAZE_ARENA_DEFAULT_SIZE; + if (aligned > block_capacity) { + block_capacity = aligned * 2; + } + + paze_arena_block_t *new_block = arena_block_create(block_capacity); + if (!new_block) return NULL; + + new_block->next = arena->head; + arena->head = new_block; + + void *ptr = new_block->memory; + new_block->used = aligned; + return ptr; +} + +void *paze_arena_calloc(paze_arena_t *arena, size_t size) +{ + void *ptr = paze_arena_alloc(arena, size); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + +char *paze_arena_strdup(paze_arena_t *arena, paze_str_t s) +{ + char *buf = (char *)paze_arena_alloc(arena, s.len + 1); + if (!buf) return NULL; + if (s.len > 0) { + memcpy(buf, s.data, s.len); + } + buf[s.len] = '\0'; + return buf; +} + +/* ======================================================================== + * Memory Allocation Helpers + * ======================================================================== */ + +void *paze_malloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr && size > 0) { + fprintf(stderr, "paze: out of memory (malloc %zu bytes)\n", size); + abort(); + } + return ptr; +} + +void *paze_calloc(size_t count, size_t size) +{ + void *ptr = calloc(count, size); + if (!ptr && count > 0 && size > 0) { + fprintf(stderr, "paze: out of memory (calloc %zu x %zu bytes)\n", + count, size); + abort(); + } + return ptr; +} + +void *paze_realloc(void *ptr, size_t size) +{ + void *p = realloc(ptr, size); + if (!p && size > 0) { + fprintf(stderr, "paze: out of memory (realloc %zu bytes)\n", size); + abort(); + } + return p; +} \ No newline at end of file diff --git a/src/PazeE.los4/src/paze_x64_codegen.c b/src/PazeE.los4/src/paze_x64_codegen.c new file mode 100644 index 0000000..e21ef80 --- /dev/null +++ b/src/PazeE.los4/src/paze_x64_codegen.c @@ -0,0 +1,1295 @@ +#include "../include/paze_x64_codegen.h" +#include "../include/paze_x64_emitter.h" +#include "../include/paze_sema.h" +#include "../include/paze_libc_decls.h" +#include "../include/paze_token.h" +#include +#include +#include + +/* ======================================================================== + * Codegen State + * ======================================================================== */ + +typedef struct { + paze_object_image_t *img; + paze_x64_emitter_t *e; + paze_cg_sym_table_t *syms; + + /* String literal dedup: value -> symbol name */ + struct { char *value; char sym[24]; } *strings; + size_t strings_count, strings_cap; + int string_id; + + /* Static local counter */ + int static_counter; + + /* Current function */ + paze_ast_node_t *cur_func; + int func_end_label; + int frame_size; + + /* Loop / switch context stacks */ + struct { int cont, brk; } loops[64]; + int loop_top; + int switch_end[64]; + int switch_top; +} codegen_t; + +/* Forward declarations */ +static paze_type_t *infer_type(codegen_t *cg, paze_ast_node_t *e); +static void gen_stmt(codegen_t *cg, paze_ast_node_t *stmt); +static void gen_block(codegen_t *cg, paze_ast_node_t *block); +static void gen_value(codegen_t *cg, paze_ast_node_t *e); +static void gen_addr(codegen_t *cg, paze_ast_node_t *e); +static void gen_function(codegen_t *cg, paze_ast_node_t *func); +static void gen_call(codegen_t *cg, paze_ast_node_t *e); + +/* ======================================================================== + * Helpers + * ======================================================================== */ + +static int align_up_i(int v, int a) { + return a <= 1 ? v : (v + a - 1) & ~(a - 1); +} +static int sizeof_type(paze_type_t *t) { return (int)paze_cg_sizeof(t); } +static int alignof_type(paze_type_t *t) { return (int)paze_cg_alignof(t); } +static bool is_unsigned(paze_type_t *t) { return paze_cg_is_unsigned(t); } +static bool is_ptr_like(paze_type_t *t) { return paze_cg_is_ptr_like(t); } +static paze_type_t *resolve_typedef(paze_type_t *t) { return paze_cg_resolve_typedef(t); } +static paze_type_t *element_type(paze_type_t *t) { return paze_cg_element_type(t); } + +static void paze_str_to_buf(paze_str_t s, char *buf, size_t buf_size) { + size_t n = s.len < buf_size - 1 ? s.len : buf_size - 1; + memcpy(buf, s.data, n); + buf[n] = '\0'; +} + +/* Push/pop RAX to stack (for saving temporaries) */ +static void push_rax(codegen_t *cg) { paze_x64_push(cg->e, PAZE_X64_RAX); } +static void pop_rax(codegen_t *cg) { paze_x64_pop(cg->e, PAZE_X64_RAX); } +static void push_rcx(codegen_t *cg) { paze_x64_push(cg->e, PAZE_X64_RCX); } +static void pop_rcx(codegen_t *cg) { paze_x64_pop(cg->e, PAZE_X64_RCX); } + +/* ======================================================================== + * Static type instances + * ======================================================================== */ + +static paze_type_t g_int_type = { PAZE_TYPE_INT, NULL, 0, {0,0}, {0,0}, NULL, false, false, NULL, 0, 0, NULL, 4 }; +static paze_type_t g_long_type = { PAZE_TYPE_LONG, NULL, 0, {0,0}, {0,0}, NULL, false, false, NULL, 0, 0, NULL, 8 }; +static paze_type_t g_char_type = { PAZE_TYPE_CHAR, NULL, 0, {0,0}, {0,0}, NULL, false, false, NULL, 0, 0, NULL, 1 }; + +static paze_type_t *make_pointer(paze_type_t *pointee) { + static paze_type_t ptr_type; + static bool init = false; + if (!init) { ptr_type.kind = PAZE_TYPE_POINTER; ptr_type.base = NULL; init = true; } + ptr_type.base = pointee; + return &ptr_type; +} + +/* ======================================================================== + * String Literal Registration + * ======================================================================== */ + +static const char *get_or_add_string(codegen_t *cg, paze_str_t value) { + for (size_t i = 0; i < cg->strings_count; i++) { + if (strlen(cg->strings[i].value) == value.len && + memcmp(cg->strings[i].value, value.data, value.len) == 0) + return cg->strings[i].sym; + } + int id = cg->string_id++; + char sym[24]; + snprintf(sym, sizeof(sym), "$str.%d", id); + size_t off = paze_object_image_add_string(cg->img, value); + + if (cg->strings_count >= cg->strings_cap) { + cg->strings_cap = cg->strings_cap ? cg->strings_cap * 2 : 16; + cg->strings = realloc(cg->strings, cg->strings_cap * sizeof(*cg->strings)); + } + cg->strings[cg->strings_count].value = paze_str_dup(value); + /* Use the ObjectImage's symbol name */ + paze_defined_symbol_t *ds = NULL; + for (size_t i = cg->img->symbols_len; i > 0; i--) { + if (cg->img->symbols[i-1].string_value) { ds = &cg->img->symbols[i-1]; break; } + } + if (ds) snprintf(cg->strings[cg->strings_count].sym, + sizeof(cg->strings[0].sym), "%s", ds->name); + else snprintf(cg->strings[cg->strings_count].sym, + sizeof(cg->strings[0].sym), "%s", sym); + cg->strings_count++; + (void)off; + return cg->strings[cg->strings_count - 1].sym; +} + +/* ======================================================================== + * Type Inference + * ======================================================================== */ + +static paze_type_t *infer_type(codegen_t *cg, paze_ast_node_t *e) { + if (!e) return &g_int_type; + if (e->type) return e->type; + switch (e->kind) { + case PAZE_NODE_INT_LITERAL: + e->type = &g_int_type; return &g_int_type; + case PAZE_NODE_CHAR_LITERAL: + e->type = &g_int_type; return &g_int_type; + case PAZE_NODE_STRING_LITERAL: + e->type = make_pointer(&g_char_type); return e->type; + case PAZE_NODE_IDENTIFIER_REF: { + char buf[256]; paze_str_to_buf(e->data.identifier_ref.name, buf, sizeof(buf)); + paze_cg_sym_t *s = paze_cg_scope_find(cg->syms, buf); + if (s) { e->type = s->type; return s->type; } + return &g_int_type; + } + case PAZE_NODE_BINARY_EXPR: { + paze_type_t *lt = infer_type(cg, e->data.binary_expr.left); + paze_type_t *rt = infer_type(cg, e->data.binary_expr.right); + paze_type_t *lr = resolve_typedef(lt), *rr = resolve_typedef(rt); + /* Pointer arithmetic */ + if (lr && lr->kind == PAZE_TYPE_POINTER) return lt; + if (rr && rr->kind == PAZE_TYPE_POINTER) return rt; + /* Promote to long if either is long */ + if ((lr && lr->kind == PAZE_TYPE_LONG) || (rr && rr->kind == PAZE_TYPE_LONG)) + { e->type = &g_long_type; return &g_long_type; } + e->type = &g_int_type; return &g_int_type; + } + case PAZE_NODE_UNARY_EXPR: { + if (e->data.unary_expr.op == PAZE_TK_STAR) { + paze_type_t *t = infer_type(cg, e->data.unary_expr.operand); + paze_type_t *rt = resolve_typedef(t); + if (rt && (rt->kind == PAZE_TYPE_POINTER || rt->kind == PAZE_TYPE_ARRAY)) + { e->type = rt->base; return e->type; } + } + if (e->data.unary_expr.op == PAZE_TK_AMP) { + paze_type_t *t = infer_type(cg, e->data.unary_expr.operand); + e->type = make_pointer(t); return e->type; + } + return infer_type(cg, e->data.unary_expr.operand); + } + case PAZE_NODE_CALL_EXPR: { + paze_ast_node_t *fn = e->data.call_expr.function; + if (fn->kind == PAZE_NODE_IDENTIFIER_REF) { + char buf[256]; paze_str_to_buf(fn->data.identifier_ref.name, buf, sizeof(buf)); + paze_cg_sym_t *s = paze_cg_scope_find(cg->syms, buf); + /* Function symbols store their return type as `type`. */ + if (s && s->kind == PAZE_CG_SYM_FUNC && s->type) { + e->type = s->type; return e->type; + } + } + return &g_int_type; + } + case PAZE_NODE_MEMBER_EXPR: { + paze_type_t *bt = infer_type(cg, e->data.member_expr.object); + paze_type_t *rt = resolve_typedef(bt); + if (e->data.member_expr.is_arrow) { + if (rt && (rt->kind == PAZE_TYPE_POINTER)) rt = resolve_typedef(rt->base); + } + if (rt && (rt->kind == PAZE_TYPE_STRUCT || rt->kind == PAZE_TYPE_UNION)) { + char fname[256]; paze_str_to_buf(e->data.member_expr.member, fname, sizeof(fname)); + paze_struct_field_t *f = paze_cg_find_field(rt, fname, NULL); + if (f) { e->type = f->type; return f->type; } + } + return &g_int_type; + } + case PAZE_NODE_INDEX_EXPR: { + paze_type_t *bt = infer_type(cg, e->data.index_expr.array); + paze_type_t *rt = resolve_typedef(bt); + if (rt && (rt->kind == PAZE_TYPE_POINTER || rt->kind == PAZE_TYPE_ARRAY)) { + e->type = rt->base; return e->type; + } + return &g_int_type; + } + case PAZE_NODE_SIZEOF_EXPR: + e->type = &g_long_type; return &g_long_type; + default: + return &g_int_type; + } +} + +/* ======================================================================== + * Load / Store typed + * ======================================================================== */ + +static void fix_rip(codegen_t *cg, paze_x64_mem_t m) { + if (m.rip_relative && m.symbol) { + size_t cur = paze_x64_emitter_position(cg->e); + paze_object_image_add_fixup(cg->img, &cg->img->text, cur - 4, + PAZE_FIXUP_REL32, m.symbol, 0); + } +} + +static void load_typed(codegen_t *cg, paze_x64_mem_t m, paze_type_t *t) { + paze_type_t *rt = resolve_typedef(t); + int sz = sizeof_type(rt); + if (sz == 1) { + if (rt && rt->kind == PAZE_TYPE_CHAR) + paze_x64_movsx8_m(cg->e, PAZE_X64_RAX, m); + else + paze_x64_movzx8_m(cg->e, PAZE_X64_RAX, m); + } else if (sz == 2) { + paze_x64_movsx16_m(cg->e, PAZE_X64_RAX, m); + } else if (sz == 4) { + paze_x64_mov_from_mem(cg->e, PAZE_X64_EAX, m); + } else { + paze_x64_mov64_m(cg->e, PAZE_X64_RAX, m); + } + fix_rip(cg, m); +} + +static void store_typed(codegen_t *cg, paze_x64_mem_t m, paze_type_t *t) { + paze_type_t *rt = resolve_typedef(t); + int sz = sizeof_type(rt); + if (sz == 1) paze_x64_store8(cg->e, m, PAZE_X64_AL); + else if (sz == 2) paze_x64_store16(cg->e, m, PAZE_X64_RAX); + else if (sz == 4) paze_x64_store32(cg->e, m, PAZE_X64_RAX); + else paze_x64_store64(cg->e, m, PAZE_X64_RAX); + fix_rip(cg, m); +} + +/* ======================================================================== + * Address generation (gen_addr: RAX = &expr) + * ======================================================================== */ + +static void gen_addr(codegen_t *cg, paze_ast_node_t *e) { + switch (e->kind) { + case PAZE_NODE_IDENTIFIER_REF: { + char buf[256]; paze_str_to_buf(e->data.identifier_ref.name, buf, sizeof(buf)); + paze_cg_sym_t *s = paze_cg_scope_find(cg->syms, buf); + if (!s) return; + if (s->is_global) { + paze_x64_lea(cg->e, PAZE_X64_RAX, paze_x64_mem_rip(buf)); + fix_rip(cg, paze_x64_mem_rip(buf)); + } else { + paze_x64_lea(cg->e, PAZE_X64_RAX, + paze_x64_mem_base_disp(PAZE_X64_RBP, s->offset)); + } + return; + } + case PAZE_NODE_MEMBER_EXPR: { + if (e->data.member_expr.is_arrow) { + gen_value(cg, e->data.member_expr.object); /* RAX = pointer */ + } else { + gen_addr(cg, e->data.member_expr.object); /* RAX = &struct */ + } + char fname[256]; paze_str_to_buf(e->data.member_expr.member, fname, sizeof(fname)); + paze_type_t *bt = infer_type(cg, e->data.member_expr.object); + paze_type_t *rt = resolve_typedef(bt); + if (e->data.member_expr.is_arrow && rt) + rt = resolve_typedef(rt->base); + int total_off = 0; + if (rt) paze_cg_find_field(rt, fname, &total_off); + if (total_off != 0) paze_x64_add_imm(cg->e, PAZE_X64_RAX, total_off); + return; + } + case PAZE_NODE_INDEX_EXPR: { + /* &arr[i] = &arr + i * elem_size */ + gen_addr(cg, e->data.index_expr.array); /* RAX = &arr */ + push_rax(cg); + gen_value(cg, e->data.index_expr.index); /* RAX = i */ + paze_type_t *bt = infer_type(cg, e->data.index_expr.array); + paze_type_t *et = element_type(bt); + int elem_sz = et ? sizeof_type(et) : 8; + if (elem_sz != 1) { + paze_x64_mov_imm(cg->e, PAZE_X64_RCX, elem_sz); + paze_x64_imul(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); + } + pop_rcx(cg); /* RCX = &arr */ + paze_x64_add(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); + return; + } + case PAZE_NODE_UNARY_EXPR: + if (e->data.unary_expr.op == PAZE_TK_STAR) { + gen_value(cg, e->data.unary_expr.operand); + return; + } + break; + default: + break; + } +} + +/* ======================================================================== + * Value generation (gen_value: RAX = value of expr) + * ======================================================================== */ + +static void gen_value(codegen_t *cg, paze_ast_node_t *e) { + if (!e) return; + switch (e->kind) { + case PAZE_NODE_INT_LITERAL: + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, (int64_t)e->data.int_literal.value); + return; + case PAZE_NODE_CHAR_LITERAL: + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, (int64_t)(unsigned char)e->data.char_literal.value); + return; + case PAZE_NODE_STRING_LITERAL: { + const char *sym = get_or_add_string(cg, e->data.string_literal.value); + paze_x64_lea(cg->e, PAZE_X64_RAX, paze_x64_mem_rip(sym)); + fix_rip(cg, paze_x64_mem_rip(sym)); + return; + } + case PAZE_NODE_IDENTIFIER_REF: { + char buf[256]; paze_str_to_buf(e->data.identifier_ref.name, buf, sizeof(buf)); + paze_cg_sym_t *s = paze_cg_scope_find(cg->syms, buf); + if (!s) { paze_x64_mov_imm(cg->e, PAZE_X64_RAX, 0); return; } + if (s->kind == PAZE_CG_SYM_ENUM_CONST) { + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, s->enum_value); + return; + } + paze_type_t *rt = resolve_typedef(s->type); + /* Arrays and structs decay to address */ + if (rt && (rt->kind == PAZE_TYPE_ARRAY || rt->kind == PAZE_TYPE_STRUCT || + rt->kind == PAZE_TYPE_UNION || rt->kind == PAZE_TYPE_FUNCTION)) { + if (s->is_global) { + paze_x64_lea(cg->e, PAZE_X64_RAX, paze_x64_mem_rip(buf)); + fix_rip(cg, paze_x64_mem_rip(buf)); + } else { + paze_x64_lea(cg->e, PAZE_X64_RAX, + paze_x64_mem_base_disp(PAZE_X64_RBP, s->offset)); + } + return; + } + /* Scalar: load value */ + if (s->is_global) { + load_typed(cg, paze_x64_mem_rip(buf), s->type); + } else { + load_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RBP, s->offset), s->type); + } + return; + } + case PAZE_NODE_UNARY_EXPR: { + paze_tk_t op = e->data.unary_expr.op; + if (op == PAZE_TK_AMP) { gen_addr(cg, e->data.unary_expr.operand); return; } + if (op == PAZE_TK_STAR) { + gen_value(cg, e->data.unary_expr.operand); /* RAX = pointer */ + paze_type_t *t = infer_type(cg, e); + load_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RAX, 0), t); + return; + } + if (op == PAZE_TK_MINUS) { + gen_value(cg, e->data.unary_expr.operand); + paze_x64_neg(cg->e, PAZE_X64_RAX); + return; + } + if (op == PAZE_TK_TILDE) { + gen_value(cg, e->data.unary_expr.operand); + paze_x64_not(cg->e, PAZE_X64_RAX); + return; + } + if (op == PAZE_TK_NOT) { + gen_value(cg, e->data.unary_expr.operand); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_setcc(cg->e, PAZE_COND_E, PAZE_X64_RAX); + paze_x64_movzx8(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + return; + } + if (op == PAZE_TK_PLUS) { gen_value(cg, e->data.unary_expr.operand); return; } + if (op == PAZE_TK_PLUS_PLUS || op == PAZE_TK_MINUS_MINUS) { + paze_ast_node_t *operand = e->data.unary_expr.operand; + paze_type_t *t = infer_type(cg, operand); + int sz = sizeof_type(t); + int delta = (op == PAZE_TK_PLUS_PLUS) ? 1 : -1; + if (is_ptr_like(t)) delta *= sz; + gen_addr(cg, operand); /* RAX = &var */ + push_rax(cg); + paze_x64_mov_rr(cg->e, PAZE_X64_RCX, PAZE_X64_RAX); + load_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RCX, 0), t); + paze_x64_add_imm(cg->e, PAZE_X64_RAX, delta); + store_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RCX, 0), t); + pop_rax(cg); + return; + } + return; + } + case PAZE_NODE_BINARY_EXPR: { + paze_tk_t op = e->data.binary_expr.op; + /* Short-circuit logical operators */ + if (op == PAZE_TK_AND_AND) { + int false_label = paze_x64_new_label(cg->e); + int end_label = paze_x64_new_label(cg->e); + gen_value(cg, e->data.binary_expr.left); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_E, false_label); + gen_value(cg, e->data.binary_expr.right); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_E, false_label); + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, 1); + paze_x64_jmp(cg->e, end_label); + paze_x64_mark_label(cg->e, false_label); + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, 0); + paze_x64_mark_label(cg->e, end_label); + return; + } + if (op == PAZE_TK_OR_OR) { + int true_label = paze_x64_new_label(cg->e); + int end_label = paze_x64_new_label(cg->e); + gen_value(cg, e->data.binary_expr.left); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_NE, true_label); + gen_value(cg, e->data.binary_expr.right); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_NE, true_label); + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, 0); + paze_x64_jmp(cg->e, end_label); + paze_x64_mark_label(cg->e, true_label); + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, 1); + paze_x64_mark_label(cg->e, end_label); + return; + } + + /* Evaluate left → RAX, push; right → RAX, pop left → RCX */ + gen_value(cg, e->data.binary_expr.left); + push_rax(cg); + gen_value(cg, e->data.binary_expr.right); + pop_rcx(cg); /* RCX = left, RAX = right */ + + /* Pointer arithmetic: if one side is pointer, scale the integer */ + paze_type_t *lt = infer_type(cg, e->data.binary_expr.left); + paze_type_t *rt = infer_type(cg, e->data.binary_expr.right); + paze_type_t *lr = resolve_typedef(lt), *rr = resolve_typedef(rt); + bool left_is_ptr = lr && (lr->kind == PAZE_TYPE_POINTER); + bool right_is_ptr = rr && (rr->kind == PAZE_TYPE_POINTER); + int elem_sz = 8; + if (left_is_ptr) { paze_type_t *et = element_type(lt); elem_sz = et ? sizeof_type(et) : 1; } + + if ((op == PAZE_TK_PLUS || op == PAZE_TK_MINUS) && (left_is_ptr || right_is_ptr)) { + if (right_is_ptr) { + /* pointer - pointer = integer (element count) */ + paze_x64_sub(cg->e, PAZE_X64_RCX, PAZE_X64_RAX); /* RCX = ptr_diff */ + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); + if (elem_sz > 1) { + paze_x64_mov_imm(cg->e, PAZE_X64_RCX, elem_sz); + paze_x64_cqo(cg->e); + paze_x64_idiv(cg->e, PAZE_X64_RCX); + } + return; + } + /* pointer + int: scale int by elem_sz */ + if (elem_sz != 1) { + paze_x64_mov_imm(cg->e, PAZE_X64_RDX, elem_sz); + paze_x64_imul(cg->e, PAZE_X64_RAX, PAZE_X64_RDX); + } + if (op == PAZE_TK_PLUS) paze_x64_add(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); + else paze_x64_sub(cg->e, PAZE_X64_RCX, PAZE_X64_RAX), + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); + return; + } + + switch (op) { + case PAZE_TK_PLUS: paze_x64_add(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_MINUS: paze_x64_sub(cg->e, PAZE_X64_RCX, PAZE_X64_RAX); + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_STAR: paze_x64_imul(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_SLASH: + /* idiv: dividend in RDX:RAX, divisor in operand. + * Currently: RCX=left(dividend), RAX=right(divisor). Swap. */ + paze_x64_mov_rr(cg->e, PAZE_X64_RDX, PAZE_X64_RAX); /* RDX = right */ + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); /* RAX = left */ + paze_x64_mov_rr(cg->e, PAZE_X64_RCX, PAZE_X64_RDX); /* RCX = right */ + if (is_unsigned(lt)) { paze_x64_xor(cg->e, PAZE_X64_RDX, PAZE_X64_RDX); paze_x64_div(cg->e, PAZE_X64_RCX); } + else { paze_x64_cqo(cg->e); paze_x64_idiv(cg->e, PAZE_X64_RCX); } + break; + case PAZE_TK_PERCENT: + /* Same as division but return remainder (RDX) */ + paze_x64_mov_rr(cg->e, PAZE_X64_RDX, PAZE_X64_RAX); /* RDX = right */ + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); /* RAX = left */ + paze_x64_mov_rr(cg->e, PAZE_X64_RCX, PAZE_X64_RDX); /* RCX = right */ + if (is_unsigned(lt)) { paze_x64_xor(cg->e, PAZE_X64_RDX, PAZE_X64_RDX); paze_x64_div(cg->e, PAZE_X64_RCX); } + else { paze_x64_cqo(cg->e); paze_x64_idiv(cg->e, PAZE_X64_RCX); } + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RDX); /* remainder */ + break; + case PAZE_TK_AMP: paze_x64_and(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_PIPE: paze_x64_or(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_CARET: paze_x64_xor(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_SHL: + /* Shift: value in RAX, count in CL. + * Currently: RCX=left(value), RAX=right(count). Swap. */ + paze_x64_mov_rr(cg->e, PAZE_X64_RDX, PAZE_X64_RAX); /* RDX = count */ + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); /* RAX = value */ + paze_x64_mov_rr(cg->e, PAZE_X64_RCX, PAZE_X64_RDX); /* RCX = count (CL) */ + paze_x64_shl_cl(cg->e, PAZE_X64_RAX); + break; + case PAZE_TK_SHR: + paze_x64_mov_rr(cg->e, PAZE_X64_RDX, PAZE_X64_RAX); + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); + paze_x64_mov_rr(cg->e, PAZE_X64_RCX, PAZE_X64_RDX); + if (is_unsigned(lt)) paze_x64_shr_cl(cg->e, PAZE_X64_RAX); + else paze_x64_sar_cl(cg->e, PAZE_X64_RAX); + break; + case PAZE_TK_EQ: case PAZE_TK_NOT_EQ: + case PAZE_TK_LT: case PAZE_TK_LE: + case PAZE_TK_GT: case PAZE_TK_GE: { + paze_x64_cmp(cg->e, PAZE_X64_RCX, PAZE_X64_RAX); /* cmp left, right */ + paze_x64_cond_t cc; + switch (op) { + case PAZE_TK_EQ: cc = PAZE_COND_E; break; + case PAZE_TK_NOT_EQ: cc = PAZE_COND_NE; break; + case PAZE_TK_LT: cc = is_unsigned(lt) ? PAZE_COND_B : PAZE_COND_L; break; + case PAZE_TK_LE: cc = is_unsigned(lt) ? PAZE_COND_BE : PAZE_COND_LE; break; + case PAZE_TK_GT: cc = is_unsigned(lt) ? PAZE_COND_A : PAZE_COND_G; break; + case PAZE_TK_GE: cc = is_unsigned(lt) ? PAZE_COND_AE : PAZE_COND_GE; break; + default: cc = PAZE_COND_E; break; + } + paze_x64_setcc(cg->e, cc, PAZE_X64_RAX); + paze_x64_movzx8(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + break; + } + default: break; + } + return; + } + case PAZE_NODE_ASSIGN_EXPR: { + paze_tk_t op = e->data.assign_expr.op; + paze_type_t *t = infer_type(cg, e->data.assign_expr.target); + + /* For compound assignment (+=, etc.), we need to read the current value */ + if (op == PAZE_TK_ASSIGN) { + gen_addr(cg, e->data.assign_expr.target); /* RAX = &target */ + push_rax(cg); + gen_value(cg, e->data.assign_expr.value); /* RAX = value */ + pop_rcx(cg); /* RCX = &target */ + store_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RCX, 0), t); + /* RAX already has the value */ + return; + } + + /* Compound assignment: target op= value + * Stack: push &target, push current_value, gen rhs, pop current → RCX, + * compute result in RAX, pop &target → RCX, store. */ + gen_addr(cg, e->data.assign_expr.target); + push_rax(cg); /* save &target */ + load_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RAX, 0), t); /* RAX = current value */ + push_rax(cg); /* save current value */ + gen_value(cg, e->data.assign_expr.value); /* RAX = rhs */ + pop_rcx(cg); /* RCX = current value (left) */ + /* Now: RCX = left, RAX = right */ + switch (op) { + case PAZE_TK_PLUS_ASSIGN: paze_x64_add(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_MINUS_ASSIGN: paze_x64_sub(cg->e, PAZE_X64_RCX, PAZE_X64_RAX); + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_STAR_ASSIGN: paze_x64_imul(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_AND_ASSIGN: paze_x64_and(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_OR_ASSIGN: paze_x64_or(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + case PAZE_TK_XOR_ASSIGN: paze_x64_xor(cg->e, PAZE_X64_RAX, PAZE_X64_RCX); break; + default: break; + } + /* RAX = result. Pop &target and store. */ + pop_rcx(cg); /* RCX = &target */ + store_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RCX, 0), t); + /* RAX already has the result */ + return; + } + case PAZE_NODE_CALL_EXPR: + gen_call(cg, e); + return; + case PAZE_NODE_INDEX_EXPR: + case PAZE_NODE_MEMBER_EXPR: { + gen_addr(cg, e); + paze_type_t *t = infer_type(cg, e); + paze_type_t *rt = resolve_typedef(t); + if (rt && (rt->kind == PAZE_TYPE_ARRAY || rt->kind == PAZE_TYPE_STRUCT || + rt->kind == PAZE_TYPE_UNION)) + return; /* decays to address */ + load_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RAX, 0), t); + return; + } + case PAZE_NODE_CONDITIONAL_EXPR: { + int else_label = paze_x64_new_label(cg->e); + int end_label = paze_x64_new_label(cg->e); + gen_value(cg, e->data.conditional_expr.condition); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_E, else_label); + gen_value(cg, e->data.conditional_expr.then_expr); + paze_x64_jmp(cg->e, end_label); + paze_x64_mark_label(cg->e, else_label); + gen_value(cg, e->data.conditional_expr.else_expr); + paze_x64_mark_label(cg->e, end_label); + return; + } + case PAZE_NODE_COMMA_EXPR: + gen_value(cg, e->data.comma_expr.left); + gen_value(cg, e->data.comma_expr.right); + return; + case PAZE_NODE_SIZEOF_EXPR: { + if (e->data.sizeof_expr.is_type) { + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, (int64_t)paze_cg_sizeof(e->data.sizeof_expr.size_type)); + } else { + paze_type_t *t = e->data.sizeof_expr.expr ? infer_type(cg, e->data.sizeof_expr.expr) : NULL; + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, (int64_t)paze_cg_sizeof(t)); + } + return; + } + default: + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, 0); + return; + } +} + +/* ======================================================================== + * Function call generation (SysV ABI) + * ======================================================================== */ + +static void gen_call(codegen_t *cg, paze_ast_node_t *e) { + paze_ast_node_arr_t args = e->data.call_expr.args; + int n = (int)args.len; + if (n > 16) n = 16; + + /* SysV ABI: up to 6 integer registers: RDI, RSI, RDX, RCX, R8, R9 */ + int reg_count = n < 6 ? n : 6; + int n_stack = n - reg_count; + + /* Stack: n_stack * 8 bytes, 16-byte aligned. + * SysV has no shadow space. Stack args start at [rsp+0]. */ + int stack_bytes = align_up_i(n_stack * 8, 16); + if (stack_bytes != 0) paze_x64_sub_imm(cg->e, PAZE_X64_RSP, stack_bytes); + + /* Evaluate and store each argument. + * Register args are evaluated to RAX and stored to spill slots on the stack, + * then loaded into arg registers. Stack args go directly to their stack position. */ + /* Spill area: first reg_count*8 bytes of the stack allocation. */ + for (int i = 0; i < n; i++) { + gen_value(cg, args.data[i]); /* RAX = arg value */ + if (i < reg_count) { + /* Spill to [rsp + i*8] */ + paze_x64_store64(cg->e, paze_x64_mem_base_disp(PAZE_X64_RSP, i * 8), PAZE_X64_RAX); + } else { + /* Stack arg at [rsp + reg_count*8 + (i-reg_count)*8] */ + int off = reg_count * 8 + (i - reg_count) * 8; + paze_x64_store64(cg->e, paze_x64_mem_base_disp(PAZE_X64_RSP, off), PAZE_X64_RAX); + } + } + + /* Load register args from spill slots */ + static const paze_x64_reg_t arg_regs[6] = { + {7, 64}, /* RDI */ + {6, 64}, /* RSI */ + {2, 64}, /* RDX */ + {1, 64}, /* RCX */ + {8, 64}, /* R8 */ + {9, 64}, /* R9 */ + }; + for (int i = 0; i < reg_count; i++) { + paze_x64_mov64_m(cg->e, arg_regs[i], paze_x64_mem_base_disp(PAZE_X64_RSP, i * 8)); + } + + /* For variadic functions (printf), AL must be 0 (no vector args) */ + paze_ast_node_t *fn = e->data.call_expr.function; + if (fn->kind == PAZE_NODE_IDENTIFIER_REF) { + char buf[256]; paze_str_to_buf(fn->data.identifier_ref.name, buf, sizeof(buf)); + if (paze_libc_is_variadic(buf)) + paze_x64_mov_imm(cg->e, PAZE_X64_EAX, 0); /* AL = 0 */ + } + + /* Emit call */ + if (fn->kind == PAZE_NODE_IDENTIFIER_REF) { + char buf[256]; paze_str_to_buf(fn->data.identifier_ref.name, buf, sizeof(buf)); + paze_cg_sym_t *s = paze_cg_scope_find(cg->syms, buf); + if (s && s->is_defined && !s->is_extern) { + /* Local function: rel32 call */ + size_t rel32_off = paze_x64_call_rel(cg->e); + paze_object_image_add_fixup(cg->img, &cg->img->text, rel32_off, + PAZE_FIXUP_REL32, buf, 0); + } else { + /* External function: call through a thunk (FF 25 disp32) + * The thunk is an 8-byte slot at the end of .text. + * Actually, for Los4, external functions are resolved to runtime + * functions. We emit a direct call with an EXT_SLOT32 fixup. */ + size_t rel32_off = paze_x64_call_rel(cg->e); + paze_object_image_add_fixup(cg->img, &cg->img->text, rel32_off, + PAZE_FIXUP_EXT_SLOT32, buf, 0); + paze_object_image_add_external(cg->img, buf); + } + } else { + /* Indirect call through register */ + gen_value(cg, fn); /* RAX = function pointer */ + paze_x64_call_reg(cg->e, PAZE_X64_RAX); + } + + /* Clean up stack */ + if (stack_bytes != 0) paze_x64_add_imm(cg->e, PAZE_X64_RSP, stack_bytes); +} + +/* Forward declaration needed for gen_value's CALL_EXPR case */ +static void gen_call(codegen_t *cg, paze_ast_node_t *e); + +/* Forward declaration for recursive aggregate initialization */ +static void gen_aggregate_init_local(codegen_t *cg, paze_ast_node_t *init, + paze_type_t *type, int base_offset); + +/* ======================================================================== + * Aggregate (array/struct) initialization for local variables + * ======================================================================== */ + +/* Zero-fill `size` bytes at [rbp + base_offset] using a compact byte loop. + * C semantics: unspecified elements/fields of an initializer are zero. + * Clobbers RAX, RCX, RDX. */ +static void gen_zero_local(codegen_t *cg, int base_offset, int size) { + if (size <= 0) return; + paze_x64_lea(cg->e, PAZE_X64_RCX, + paze_x64_mem_base_disp(PAZE_X64_RBP, base_offset)); /* rcx = ptr */ + paze_x64_mov_imm(cg->e, PAZE_X64_RDX, size); /* rdx = count */ + paze_x64_xor(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); /* al = 0 */ + int loop = paze_x64_new_label(cg->e); + int done = paze_x64_new_label(cg->e); + paze_x64_mark_label(cg->e, loop); + paze_x64_test(cg->e, PAZE_X64_RDX, PAZE_X64_RDX); + paze_x64_jcc(cg->e, PAZE_COND_E, done); + paze_x64_store8(cg->e, paze_x64_mem_base_disp(PAZE_X64_RCX, 0), PAZE_X64_AL); + paze_x64_add_imm(cg->e, PAZE_X64_RCX, 1); + paze_x64_sub_imm(cg->e, PAZE_X64_RDX, 1); + paze_x64_jmp(cg->e, loop); + paze_x64_mark_label(cg->e, done); +} + +/* Generate code to initialize a local aggregate (array or struct) at + * [rbp + base_offset] from `init` (an INIT_LIST_EXPR, or a STRING_LITERAL + * for char arrays). The whole storage is zeroed first. */ +static void gen_aggregate_init_local(codegen_t *cg, paze_ast_node_t *init, + paze_type_t *type, int base_offset) { + paze_type_t *rt = resolve_typedef(type); + int total = rt ? (int)paze_cg_sizeof(rt) : 0; + gen_zero_local(cg, base_offset, total); + if (!init || !rt) return; + + /* char arr[] = "string" (or char[N] = "string") */ + if (init->kind == PAZE_NODE_STRING_LITERAL && rt->kind == PAZE_TYPE_ARRAY) { + paze_type_t *et = element_type(rt); + if (et && et->kind == PAZE_TYPE_CHAR) { + paze_str_t s = init->data.string_literal.value; + for (size_t i = 0; i < s.len; i++) { + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, (unsigned char)s.data[i]); + store_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RBP, base_offset + (int)i), et); + } + } + return; + } + + if (init->kind != PAZE_NODE_INIT_LIST_EXPR) return; + paze_ast_node_arr_t elems = init->data.init_list_expr.elements; + + if (rt->kind == PAZE_TYPE_ARRAY) { + paze_type_t *et = element_type(rt); + int elem_sz = et ? sizeof_type(et) : 8; + for (size_t i = 0; i < elems.len; i++) { + int elem_off = base_offset + (int)i * elem_sz; + paze_ast_node_t *e = elems.data[i]; + if (e->kind == PAZE_NODE_INIT_LIST_EXPR) { + gen_aggregate_init_local(cg, e, et, elem_off); + } else if (e->kind == PAZE_NODE_STRING_LITERAL && et && + et->kind == PAZE_TYPE_CHAR) { + /* e.g. char[][N] = {"ab", "cd"} */ + paze_str_t s = e->data.string_literal.value; + for (size_t k = 0; k < s.len; k++) { + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, (unsigned char)s.data[k]); + store_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RBP, elem_off + (int)k), et); + } + } else { + gen_value(cg, e); + store_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RBP, elem_off), et); + } + } + } else if (rt->kind == PAZE_TYPE_STRUCT || rt->kind == PAZE_TYPE_UNION) { + for (int i = 0; i < rt->struct_field_count && i < (int)elems.len; i++) { + paze_struct_field_t *f = &rt->struct_fields[i]; + int foff = base_offset + f->offset; + paze_ast_node_t *e = elems.data[i]; + if (e->kind == PAZE_NODE_INIT_LIST_EXPR) { + gen_aggregate_init_local(cg, e, f->type, foff); + } else { + gen_value(cg, e); + store_typed(cg, paze_x64_mem_base_disp(PAZE_X64_RBP, foff), f->type); + } + } + } +} + +/* ======================================================================== + * Statement generation + * ======================================================================== */ + +static void gen_local_var(codegen_t *cg, paze_ast_node_t *decl) { + if (!decl || decl->kind != PAZE_NODE_VAR_DECL) return; + if (decl->data.var_decl.is_static || decl->data.var_decl.is_extern) return; + + char name_buf[256]; paze_str_to_buf(decl->data.var_decl.name, name_buf, sizeof(name_buf)); + paze_cg_sym_t sym; + memset(&sym, 0, sizeof(sym)); + sym.name = name_buf; + sym.type = decl->data.var_decl.var_type; + sym.kind = PAZE_CG_SYM_VAR; + sym.is_global = false; + sym.offset = decl->data.var_decl.frame_offset; + paze_cg_scope_add(cg->syms, sym); + + paze_ast_node_t *init = decl->data.var_decl.init_expr; + if (!init) return; + + paze_type_t *type = decl->data.var_decl.var_type; + paze_type_t *rt = resolve_typedef(type); + + /* Aggregate types (array/struct) use element-wise initialization */ + if (init->kind == PAZE_NODE_INIT_LIST_EXPR || + (init->kind == PAZE_NODE_STRING_LITERAL && rt && + rt->kind == PAZE_TYPE_ARRAY)) { + gen_aggregate_init_local(cg, init, type, sym.offset); + return; + } + + paze_x64_mem_t slot = paze_x64_mem_base_disp(PAZE_X64_RBP, sym.offset); + gen_value(cg, init); + store_typed(cg, slot, type); +} + +static void gen_stmt(codegen_t *cg, paze_ast_node_t *stmt) { + if (!stmt) return; + switch (stmt->kind) { + case PAZE_NODE_BLOCK_STMT: + gen_block(cg, stmt); + return; + case PAZE_NODE_DECL_STMT: + if (stmt->data.decl_stmt.decl) + gen_local_var(cg, stmt->data.decl_stmt.decl); + return; + case PAZE_NODE_EXPR_STMT: + gen_value(cg, stmt->data.expr_stmt.expr); + return; + case PAZE_NODE_IF_STMT: { + int else_label = paze_x64_new_label(cg->e); + int end_label = paze_x64_new_label(cg->e); + gen_value(cg, stmt->data.if_stmt.condition); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_E, else_label); + gen_stmt(cg, stmt->data.if_stmt.then_branch); + paze_x64_jmp(cg->e, end_label); + paze_x64_mark_label(cg->e, else_label); + if (stmt->data.if_stmt.else_branch) gen_stmt(cg, stmt->data.if_stmt.else_branch); + paze_x64_mark_label(cg->e, end_label); + return; + } + case PAZE_NODE_WHILE_STMT: { + int loop_label = paze_x64_new_label(cg->e); + int end_label = paze_x64_new_label(cg->e); + if (cg->loop_top < 64) { + cg->loops[cg->loop_top].cont = loop_label; + cg->loops[cg->loop_top].brk = end_label; + cg->loop_top++; + } + paze_x64_mark_label(cg->e, loop_label); + gen_value(cg, stmt->data.while_stmt.condition); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_E, end_label); + gen_stmt(cg, stmt->data.while_stmt.body); + paze_x64_jmp(cg->e, loop_label); + paze_x64_mark_label(cg->e, end_label); + if (cg->loop_top > 0) cg->loop_top--; + return; + } + case PAZE_NODE_DO_WHILE_STMT: { + int loop_label = paze_x64_new_label(cg->e); + int cont_label = paze_x64_new_label(cg->e); + int end_label = paze_x64_new_label(cg->e); + if (cg->loop_top < 64) { + cg->loops[cg->loop_top].cont = cont_label; + cg->loops[cg->loop_top].brk = end_label; + cg->loop_top++; + } + paze_x64_mark_label(cg->e, loop_label); + gen_stmt(cg, stmt->data.do_while_stmt.body); + paze_x64_mark_label(cg->e, cont_label); + gen_value(cg, stmt->data.do_while_stmt.condition); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_NE, loop_label); + paze_x64_mark_label(cg->e, end_label); + if (cg->loop_top > 0) cg->loop_top--; + return; + } + case PAZE_NODE_FOR_STMT: { + int loop_label = paze_x64_new_label(cg->e); + int cont_label = paze_x64_new_label(cg->e); + int end_label = paze_x64_new_label(cg->e); + if (cg->loop_top < 64) { + cg->loops[cg->loop_top].cont = cont_label; + cg->loops[cg->loop_top].brk = end_label; + cg->loop_top++; + } + /* for-init */ + if (stmt->data.for_stmt.init) gen_stmt(cg, stmt->data.for_stmt.init); + paze_x64_mark_label(cg->e, loop_label); + if (stmt->data.for_stmt.condition) { + gen_value(cg, stmt->data.for_stmt.condition); + paze_x64_test(cg->e, PAZE_X64_RAX, PAZE_X64_RAX); + paze_x64_jcc(cg->e, PAZE_COND_E, end_label); + } + gen_stmt(cg, stmt->data.for_stmt.body); + paze_x64_mark_label(cg->e, cont_label); + if (stmt->data.for_stmt.increment) gen_value(cg, stmt->data.for_stmt.increment); + paze_x64_jmp(cg->e, loop_label); + paze_x64_mark_label(cg->e, end_label); + if (cg->loop_top > 0) cg->loop_top--; + return; + } + case PAZE_NODE_RETURN_STMT: + if (stmt->data.return_stmt.value) + gen_value(cg, stmt->data.return_stmt.value); + paze_x64_jmp(cg->e, cg->func_end_label); + return; + case PAZE_NODE_BREAK_STMT: + if (cg->loop_top > 0) paze_x64_jmp(cg->e, cg->loops[cg->loop_top-1].brk); + return; + case PAZE_NODE_CONTINUE_STMT: + if (cg->loop_top > 0) paze_x64_jmp(cg->e, cg->loops[cg->loop_top-1].cont); + return; + case PAZE_NODE_NULL_STMT: + return; + default: + return; + } +} + +static void gen_block(codegen_t *cg, paze_ast_node_t *block) { + paze_cg_push_scope(cg->syms); + for (size_t i = 0; i < block->data.block_stmt.stmts.len; i++) { + gen_stmt(cg, block->data.block_stmt.stmts.data[i]); + } + paze_cg_pop_scope(cg->syms); +} + +/* ======================================================================== + * Function generation + * ======================================================================== */ + +static void gen_function(codegen_t *cg, paze_ast_node_t *func) { + /* Register function symbol */ + char fname[256]; paze_str_to_buf(func->data.function_decl.name, fname, sizeof(fname)); + paze_cg_sym_t fsym; + memset(&fsym, 0, sizeof(fsym)); + fsym.name = fname; + fsym.type = func->data.function_decl.return_type; + fsym.kind = PAZE_CG_SYM_FUNC; + fsym.is_global = true; + fsym.is_defined = true; + paze_cg_scope_add(cg->syms, fsym); + + /* Define symbol in image */ + size_t func_offset = cg->img->text.len; + paze_object_image_define_symbol(cg->img, fname, PAZE_SYM_TEXT, func_offset, true, true); + + cg->cur_func = func; + cg->func_end_label = paze_x64_new_label(cg->e); + cg->loop_top = 0; + cg->switch_top = 0; + + /* Layout local variables and parameters */ + paze_cg_push_scope(cg->syms); + int offset = 0; + + /* Parameters: SysV ABI registers RDI, RSI, RDX, RCX, R8, R9 */ + static const paze_x64_reg_t arg_regs[6] = { + {7, 64}, {6, 64}, {2, 64}, {1, 64}, {8, 64}, {9, 64} + }; + int param_offsets[64]; /* frame offset per param (param nodes have no slot) */ + int reg_idx = 0; + int stack_idx = 0; + for (size_t i = 0; i < func->data.function_decl.params.len; i++) { + paze_ast_node_t *p = func->data.function_decl.params.data[i]; + paze_type_t *pt = p->data.param.param_type; + paze_type_t *rt = resolve_typedef(pt); + if (rt && rt->kind == PAZE_TYPE_ARRAY) pt = make_pointer(rt->base); + int sz = sizeof_type(pt); + int al = alignof_type(pt); + offset = align_up_i(offset + sz, al > 8 ? 8 : al); + int param_off = -offset; + if (i < 64) param_offsets[i] = param_off; + + paze_cg_sym_t psym; + memset(&psym, 0, sizeof(psym)); + char pname[256]; paze_str_to_buf(p->data.param.name, pname, sizeof(pname)); + psym.name = pname; + psym.type = pt; + psym.kind = PAZE_CG_SYM_VAR; + psym.is_global = false; + psym.offset = param_off; + paze_cg_scope_add(cg->syms, psym); + + (void)reg_idx; (void)stack_idx; (void)arg_regs; /* used in prologue below */ + } + + /* Layout local variables in the body */ + /* Walk the body and lay out all local declarations */ + if (func->data.function_decl.body) { + paze_ast_node_arr_t stmts = func->data.function_decl.body->data.block_stmt.stmts; + for (size_t i = 0; i < stmts.len; i++) { + paze_ast_node_t *s = stmts.data[i]; + if (s->kind == PAZE_NODE_DECL_STMT && s->data.decl_stmt.decl) { + paze_ast_node_t *decl = s->data.decl_stmt.decl; + if (decl->kind == PAZE_NODE_VAR_DECL && !decl->data.var_decl.is_static) { + paze_type_t *t = decl->data.var_decl.var_type; + int sz = sizeof_type(t); + int al = alignof_type(t); + offset = align_up_i(offset + sz, al > 8 ? 8 : al); + decl->data.var_decl.frame_offset = -offset; + } + } + } + } + + /* Frame size: align to 16, no shadow space (SysV) */ + cg->frame_size = align_up_i(offset, 16); + /* SysV ABI: must keep RSP 16-byte aligned. push rbp already misaligns by 8, + * so sub rsp by frame_size (which is 16-aligned) keeps alignment. */ + + /* Prologue */ + paze_x64_push(cg->e, PAZE_X64_RBP); + paze_x64_mov_rr(cg->e, PAZE_X64_RBP, PAZE_X64_RSP); + if (cg->frame_size > 0) + paze_x64_sub_imm(cg->e, PAZE_X64_RSP, cg->frame_size); + + /* Store parameters from registers to local slots */ + reg_idx = 0; + stack_idx = 0; + for (size_t i = 0; i < func->data.function_decl.params.len; i++) { + paze_ast_node_t *p = func->data.function_decl.params.data[i]; + paze_type_t *pt = p->data.param.param_type; + paze_type_t *rt = resolve_typedef(pt); + if (rt && rt->kind == PAZE_TYPE_ARRAY) pt = make_pointer(rt->base); + int param_off = (i < 64) ? param_offsets[i] : 0; + paze_x64_mem_t slot = paze_x64_mem_base_disp(PAZE_X64_RBP, param_off); + + if (reg_idx < 6) { + paze_x64_mov_rr(cg->e, PAZE_X64_RAX, arg_regs[reg_idx]); + store_typed(cg, slot, pt); + reg_idx++; + } else { + /* Stack param: at [rbp + 16 + stack_idx*8] (return addr + saved rbp) */ + int stack_off = 16 + stack_idx * 8; + paze_x64_mov64_m(cg->e, PAZE_X64_RAX, + paze_x64_mem_base_disp(PAZE_X64_RBP, stack_off)); + store_typed(cg, slot, pt); + stack_idx++; + } + } + + /* Generate body */ + if (func->data.function_decl.body) + gen_block(cg, func->data.function_decl.body); + + /* Epilogue (default return 0) */ + paze_x64_mark_label(cg->e, cg->func_end_label); + paze_x64_mov_imm(cg->e, PAZE_X64_RAX, 0); /* default return value */ + paze_x64_leave(cg->e); + paze_x64_ret(cg->e); + + paze_cg_pop_scope(cg->syms); +} + +/* ======================================================================== + * Global variable generation + * ======================================================================== */ + +/* Forward declaration for recursive static aggregate initialization. + * Writes initializer bytes for `type` into `buf` (size `buf_size`). + * `data_base_off` is the absolute .data offset of this object, used for + * emitting ABS64 pointer fixups. The buffer is pre-zeroed by the caller. */ +static void write_aggregate_static(codegen_t *cg, paze_ast_node_t *init, + paze_type_t *type, uint8_t *buf, + size_t buf_size, size_t data_base_off); + +/* Write a scalar initializer into `buf` for type `t`. + * `data_off` is the absolute .data offset of this slot (for pointer fixups). */ +static void write_scalar_static(codegen_t *cg, paze_ast_node_t *e, paze_type_t *t, + uint8_t *buf, size_t data_off) { + paze_type_t *rt = resolve_typedef(t); + int sz = rt ? (int)paze_cg_sizeof(rt) : 8; + if (e->kind == PAZE_NODE_STRING_LITERAL && rt && + rt->kind == PAZE_TYPE_POINTER) { + /* char *p = "..." : 8-byte pointer + ABS64 fixup to string symbol */ + const char *ssym = get_or_add_string(cg, e->data.string_literal.value); + memset(buf, 0, 8); + paze_object_image_add_fixup(cg->img, &cg->img->data, data_off, + PAZE_FIXUP_ABS64, ssym, 0); + return; + } + int64_t val = 0; + paze_cg_try_const(e, &val, cg->syms); + for (int i = 0; i < sz; i++) + buf[i] = (uint8_t)(val >> (i * 8)); +} + +static void write_aggregate_static(codegen_t *cg, paze_ast_node_t *init, + paze_type_t *type, uint8_t *buf, + size_t buf_size, size_t data_base_off) { + paze_type_t *rt = resolve_typedef(type); + if (!rt) return; + + /* char arr[] = "string" (or char[N] = "string") */ + if (init->kind == PAZE_NODE_STRING_LITERAL && rt->kind == PAZE_TYPE_ARRAY) { + paze_type_t *et = element_type(rt); + if (et && et->kind == PAZE_TYPE_CHAR) { + paze_str_t s = init->data.string_literal.value; + int elem_sz = sizeof_type(et); + for (size_t i = 0; i < s.len && i * (size_t)elem_sz < buf_size; i++) + buf[i * (size_t)elem_sz] = (uint8_t)s.data[i]; + } + return; + } + + if (init->kind != PAZE_NODE_INIT_LIST_EXPR) return; + paze_ast_node_arr_t elems = init->data.init_list_expr.elements; + + if (rt->kind == PAZE_TYPE_ARRAY) { + paze_type_t *et = element_type(rt); + int elem_sz = et ? sizeof_type(et) : 8; + for (size_t i = 0; i < elems.len; i++) { + size_t elem_off = i * (size_t)elem_sz; + if (elem_off >= buf_size) break; + paze_ast_node_t *e = elems.data[i]; + if (e->kind == PAZE_NODE_INIT_LIST_EXPR) { + write_aggregate_static(cg, e, et, buf + elem_off, + buf_size - elem_off, data_base_off + elem_off); + } else { + write_scalar_static(cg, e, et, buf + elem_off, + data_base_off + elem_off); + } + } + } else if (rt->kind == PAZE_TYPE_STRUCT || rt->kind == PAZE_TYPE_UNION) { + for (int i = 0; i < rt->struct_field_count && i < (int)elems.len; i++) { + paze_struct_field_t *f = &rt->struct_fields[i]; + size_t foff = (size_t)f->offset; + if (foff >= buf_size) break; + paze_ast_node_t *e = elems.data[i]; + if (e->kind == PAZE_NODE_INIT_LIST_EXPR) { + write_aggregate_static(cg, e, f->type, buf + foff, + buf_size - foff, data_base_off + foff); + } else { + write_scalar_static(cg, e, f->type, buf + foff, + data_base_off + foff); + } + } + } +} + +static void gen_global_var(codegen_t *cg, paze_ast_node_t *decl) { + if (decl->kind != PAZE_NODE_VAR_DECL) return; + char name[256]; paze_str_to_buf(decl->data.var_decl.name, name, sizeof(name)); + paze_type_t *type = decl->data.var_decl.var_type; + int sz = sizeof_type(type); + + /* Register symbol */ + paze_cg_sym_t sym; + memset(&sym, 0, sizeof(sym)); + sym.name = name; + sym.type = type; + sym.kind = PAZE_CG_SYM_VAR; + sym.is_global = true; + sym.is_extern = decl->data.var_decl.is_extern; + sym.is_defined = !decl->data.var_decl.is_extern; + paze_cg_scope_add(cg->syms, sym); + + if (decl->data.var_decl.is_extern) return; + + /* Allocate in .data (with initializer) or .bss (without) */ + paze_ast_node_t *init = decl->data.var_decl.init_expr; + paze_type_t *rt = resolve_typedef(type); + bool is_aggregate = rt && (rt->kind == PAZE_TYPE_ARRAY || + rt->kind == PAZE_TYPE_STRUCT || + rt->kind == PAZE_TYPE_UNION); + + if (init && init->kind == PAZE_NODE_INT_LITERAL && !is_aggregate) { + size_t off = cg->img->data.len; + int64_t val = (int64_t)init->data.int_literal.value; + for (int i = 0; i < sz; i++) { + uint8_t b = (uint8_t)(val >> (i * 8)); + paze_section_append(&cg->img->data, &b, 1); + } + /* Align to 8 */ + while (cg->img->data.len % 8 != 0) { + uint8_t z = 0; + paze_section_append(&cg->img->data, &z, 1); + } + paze_object_image_define_symbol(cg->img, name, PAZE_SYM_DATA, off, true, false); + } else if (init && init->kind == PAZE_NODE_STRING_LITERAL && !is_aggregate) { + /* char *s = "..." — pointer to string in .rdata */ + const char *ssym = get_or_add_string(cg, init->data.string_literal.value); + size_t off = cg->img->data.len; + uint64_t zero = 0; + paze_section_append(&cg->img->data, &zero, 8); + paze_object_image_define_symbol(cg->img, name, PAZE_SYM_DATA, off, true, false); + paze_object_image_add_fixup(cg->img, &cg->img->data, off, + PAZE_FIXUP_ABS64, ssym, 0); + } else if (init && (init->kind == PAZE_NODE_INIT_LIST_EXPR || + (init->kind == PAZE_NODE_STRING_LITERAL && is_aggregate))) { + /* Aggregate initializer (array/struct): build bytes in .data */ + size_t off = cg->img->data.len; + size_t bufsz = sz > 0 ? (size_t)sz : 1; + uint8_t *buf = (uint8_t *)calloc(bufsz, 1); + if (buf) { + write_aggregate_static(cg, init, type, buf, bufsz, off); + paze_section_append(&cg->img->data, buf, bufsz); + free(buf); + } + while (cg->img->data.len % 8 != 0) { + uint8_t z = 0; + paze_section_append(&cg->img->data, &z, 1); + } + paze_object_image_define_symbol(cg->img, name, PAZE_SYM_DATA, off, true, false); + } else { + /* No initializer → BSS */ + size_t off = paze_section_reserve_bss(&cg->img->bss, sz); + paze_object_image_define_symbol(cg->img, name, PAZE_SYM_BSS, off, true, false); + } +} + +/* ======================================================================== + * Main entry point + * ======================================================================== */ + +bool paze_x64_codegen_generate(paze_ast_node_t *tu, paze_object_image_t *img) { + if (!tu || tu->kind != PAZE_NODE_TRANSLATION_UNIT) return false; + + codegen_t cg; + memset(&cg, 0, sizeof(cg)); + cg.img = img; + cg.e = paze_x64_emitter_create(&img->text); + cg.syms = paze_cg_sym_table_create(); + cg.static_counter = 0; + cg.loop_top = 0; + cg.switch_top = 0; + + /* First pass: register all global declarations (functions + variables) */ + paze_ast_node_arr_t decls = tu->data.translation_unit.decls; + for (size_t i = 0; i < decls.len; i++) { + paze_ast_node_t *d = decls.data[i]; + if (d->kind == PAZE_NODE_FUNCTION_DECL) { + char fname[256]; paze_str_to_buf(d->data.function_decl.name, fname, sizeof(fname)); + paze_cg_sym_t fsym; + memset(&fsym, 0, sizeof(fsym)); + fsym.name = fname; + fsym.type = d->data.function_decl.return_type; + fsym.kind = PAZE_CG_SYM_FUNC; + fsym.is_global = true; + fsym.is_extern = d->data.function_decl.is_extern; + fsym.is_defined = d->data.function_decl.body != NULL; + paze_cg_scope_add(cg.syms, fsym); + } + } + + /* Second pass: generate code for all declarations */ + for (size_t i = 0; i < decls.len; i++) { + paze_ast_node_t *d = decls.data[i]; + if (d->kind == PAZE_NODE_FUNCTION_DECL) { + if (d->data.function_decl.body) + gen_function(&cg, d); + } else if (d->kind == PAZE_NODE_VAR_DECL) { + gen_global_var(&cg, d); + } else if (d->kind == PAZE_NODE_DECL_GROUP) { + for (size_t j = 0; j < d->data.decl_group.decls.len; j++) + gen_global_var(&cg, d->data.decl_group.decls.data[j]); + } + } + + paze_x64_emitter_finish(cg.e); + paze_x64_emitter_destroy(cg.e); + paze_cg_sym_table_destroy(cg.syms); + free(cg.strings); + + return true; +} diff --git a/src/PazeE.los4/src/paze_x64_emitter.c b/src/PazeE.los4/src/paze_x64_emitter.c new file mode 100644 index 0000000..79ba179 --- /dev/null +++ b/src/PazeE.los4/src/paze_x64_emitter.c @@ -0,0 +1,524 @@ +#include "paze_x64_emitter.h" +#include +#include + +/* ======================================================================== + * Register Constants + * ======================================================================== */ + +const paze_x64_reg_t PAZE_X64_RAX = { 0, 64 }; +const paze_x64_reg_t PAZE_X64_RCX = { 1, 64 }; +const paze_x64_reg_t PAZE_X64_RDX = { 2, 64 }; +const paze_x64_reg_t PAZE_X64_RBX = { 3, 64 }; +const paze_x64_reg_t PAZE_X64_RSP = { 4, 64 }; +const paze_x64_reg_t PAZE_X64_RBP = { 5, 64 }; +const paze_x64_reg_t PAZE_X64_RSI = { 6, 64 }; +const paze_x64_reg_t PAZE_X64_RDI = { 7, 64 }; +const paze_x64_reg_t PAZE_X64_R8 = { 8, 64 }; +const paze_x64_reg_t PAZE_X64_R9 = { 9, 64 }; +const paze_x64_reg_t PAZE_X64_R10 = { 10, 64 }; +const paze_x64_reg_t PAZE_X64_R11 = { 11, 64 }; +const paze_x64_reg_t PAZE_X64_R12 = { 12, 64 }; +const paze_x64_reg_t PAZE_X64_R13 = { 13, 64 }; +const paze_x64_reg_t PAZE_X64_R14 = { 14, 64 }; +const paze_x64_reg_t PAZE_X64_R15 = { 15, 64 }; +const paze_x64_reg_t PAZE_X64_EAX = { 0, 32 }; +const paze_x64_reg_t PAZE_X64_AL = { 0, 8 }; +const paze_x64_reg_t PAZE_X64_CL = { 1, 8 }; + +/* ======================================================================== + * Memory Operand Constructors + * ======================================================================== */ + +paze_x64_mem_t paze_x64_mem_rip(const char *sym) +{ + paze_x64_mem_t m; + memset(&m, 0, sizeof(m)); + m.rip_relative = true; + m.symbol = sym; + return m; +} + +paze_x64_mem_t paze_x64_mem_base_disp(paze_x64_reg_t b, int64_t d) +{ + paze_x64_mem_t m; + memset(&m, 0, sizeof(m)); + m.has_base = true; + m.base = b; + m.disp = d; + return m; +} + +paze_x64_mem_t paze_x64_mem_base_index_disp(paze_x64_reg_t b, paze_x64_reg_t i, + uint8_t s, int64_t d) +{ + paze_x64_mem_t m; + memset(&m, 0, sizeof(m)); + m.has_base = true; + m.base = b; + m.has_index = true; + m.index = i; + m.scale = s; + m.disp = d; + return m; +} + +/* ======================================================================== + * Emitter + * ======================================================================== */ + +typedef struct { size_t off; int label; } paze_x64_patch_t; + +struct paze_x64_emitter_t { + paze_image_section_t *target; + paze_x64_patch_t *patches; + size_t patches_len, patches_cap; + size_t *labels; + size_t labels_len, labels_cap; + int label_counter; +}; + +paze_x64_emitter_t *paze_x64_emitter_create(paze_image_section_t *target) +{ + paze_x64_emitter_t *e = (paze_x64_emitter_t *)calloc(1, sizeof(*e)); + if (!e) return NULL; + e->target = target; + return e; +} + +void paze_x64_emitter_destroy(paze_x64_emitter_t *e) +{ + if (!e) return; + free(e->patches); + free(e->labels); + free(e); +} + +size_t paze_x64_emitter_position(const paze_x64_emitter_t *e) +{ + return e->target->len; +} + +/* ---- Basic writes ---- */ + +void paze_x64_emit8(paze_x64_emitter_t *e, uint8_t b) +{ + paze_section_append_byte(e->target, b); +} + +static void emit16(paze_x64_emitter_t *e, uint16_t v) +{ + paze_x64_emit8(e, (uint8_t)v); + paze_x64_emit8(e, (uint8_t)(v >> 8)); +} + +void paze_x64_emit32(paze_x64_emitter_t *e, uint32_t v) +{ + paze_x64_emit8(e, (uint8_t)v); + paze_x64_emit8(e, (uint8_t)(v >> 8)); + paze_x64_emit8(e, (uint8_t)(v >> 16)); + paze_x64_emit8(e, (uint8_t)(v >> 24)); +} + +void paze_x64_emit64(paze_x64_emitter_t *e, uint64_t v) +{ + for (int i = 0; i < 8; i++) + paze_x64_emit8(e, (uint8_t)(v >> (i * 8))); +} + +/* ---- Labels ---- */ + +int paze_x64_new_label(paze_x64_emitter_t *e) +{ + if (e->label_counter >= (int)e->labels_cap) { + size_t nc = e->labels_cap ? e->labels_cap * 2 : 32; + e->labels = (size_t *)realloc(e->labels, nc * sizeof(size_t)); + for (size_t i = e->labels_cap; i < nc; i++) + e->labels[i] = (size_t)-1; + e->labels_cap = nc; + } + int label = e->label_counter++; + e->labels[label] = (size_t)-1; + return label; +} + +void paze_x64_mark_label(paze_x64_emitter_t *e, int label) +{ + if (label >= 0 && label < e->label_counter) + e->labels[label] = e->target->len; +} + +void paze_x64_emitter_finish(paze_x64_emitter_t *e) +{ + for (size_t i = 0; i < e->patches_len; i++) { + paze_x64_patch_t *p = &e->patches[i]; + size_t target = (p->label >= 0 && p->label < e->label_counter && + e->labels[p->label] != (size_t)-1) + ? e->labels[p->label] : 0; + int32_t rel = (int32_t)((int64_t)target - (int64_t)(p->off + 4)); + paze_section_write_at(e->target, p->off, &rel, 4); + } +} + +static void add_patch(paze_x64_emitter_t *e, size_t off, int label) +{ + if (e->patches_len >= e->patches_cap) { + size_t nc = e->patches_cap ? e->patches_cap * 2 : 32; + e->patches = (paze_x64_patch_t *)realloc(e->patches, nc * sizeof(paze_x64_patch_t)); + e->patches_cap = nc; + } + e->patches[e->patches_len].off = off; + e->patches[e->patches_len].label = label; + e->patches_len++; +} + +/* ---- REX / ModRM / SIB ---- */ + +static void rex(paze_x64_emitter_t *e, uint8_t w, uint8_t r, uint8_t x, uint8_t b) +{ + if (w != 0 || r != 0 || x != 0 || b != 0) + paze_x64_emit8(e, (uint8_t)(0x40 | (w << 3) | (r << 2) | (x << 1) | b)); +} + +static void modrm(paze_x64_emitter_t *e, uint8_t mod, uint8_t reg, uint8_t rm) +{ + paze_x64_emit8(e, (uint8_t)((mod << 6) | ((reg & 7) << 3) | (rm & 7))); +} + +static void sib(paze_x64_emitter_t *e, uint8_t ss, uint8_t index, uint8_t bse) +{ + paze_x64_emit8(e, (uint8_t)((ss << 6) | ((index & 7) << 3) | (bse & 7))); +} + +static bool in_disp8(int64_t d) { return d >= -128 && d <= 127; } +static bool in_imm8(int64_t v) { return v >= -128 && v <= 127; } + +static void encode_modrm_for(paze_x64_emitter_t *e, uint8_t regField, paze_x64_mem_t m) +{ + int bse = m.has_base ? (int)m.base.index : -1; + int idx = m.has_index ? (int)m.index.index : -1; + + if (m.rip_relative) { + modrm(e, 0b00, regField, 5); + paze_x64_emit32(e, (uint32_t)m.disp); + return; + } + + bool need_sib = (bse == 4) || (idx >= 0); + if (bse < 0 && idx < 0) { + /* Absolute [disp32] */ + modrm(e, 0b00, regField, 4); + sib(e, 0, 4, 5); + paze_x64_emit32(e, (uint32_t)m.disp); + return; + } + + uint8_t mod; + if (bse == 5) mod = in_disp8(m.disp) ? 0b01 : 0b10; /* rbp/r13 can't use mod=00 */ + else if (m.disp == 0) mod = 0b00; + else if (in_disp8(m.disp)) mod = 0b01; + else mod = 0b10; + + int rm = need_sib ? 4 : bse; + modrm(e, mod, regField, (uint8_t)rm); + if (need_sib) { + uint8_t ss = (m.scale == 2) ? 1 : (m.scale == 4) ? 2 : (m.scale == 8) ? 3 : 0; + int sib_base = (bse < 0) ? 5 : bse; + int sib_index = (idx < 0) ? 4 : idx; + sib(e, ss, (uint8_t)sib_index, (uint8_t)sib_base); + } + if (mod == 0b01) paze_x64_emit8(e, (uint8_t)m.disp); + else if (mod == 0b10) paze_x64_emit32(e, (uint32_t)m.disp); +} + +static void rex_for(paze_x64_emitter_t *e, uint8_t w, uint8_t regField, paze_x64_mem_t m) +{ + int bse = m.has_base ? (int)m.base.index : -1; + int idx = m.has_index ? (int)m.index.index : -1; + rex(e, w, + (uint8_t)(regField >> 3), + idx >= 0 ? (uint8_t)(idx >> 3) : 0, + bse >= 0 ? (uint8_t)(bse >> 3) : 0); +} + +/* ---- MOV ---- */ + +void paze_x64_mov_rr(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src) +{ + uint8_t w = (dst.size == 64) ? 1 : 0; + rex(e, w, (uint8_t)(src.index >> 3), 0, (uint8_t)(dst.index >> 3)); + paze_x64_emit8(e, 0x89); + modrm(e, 0b11, src.index, dst.index); +} + +void paze_x64_mov_from_mem(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m) +{ + rex_for(e, dst.size == 64 ? 1 : 0, dst.index, m); + paze_x64_emit8(e, 0x8B); + encode_modrm_for(e, dst.index, m); +} + +void paze_x64_mov_to_mem(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src) +{ + rex_for(e, src.size == 64 ? 1 : 0, src.index, m); + paze_x64_emit8(e, 0x89); + encode_modrm_for(e, src.index, m); +} + +void paze_x64_lea(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m) +{ + rex_for(e, 1, dst.index, m); + paze_x64_emit8(e, 0x8D); + encode_modrm_for(e, dst.index, m); +} + +void paze_x64_mov_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm) +{ + bool w = (dst.size == 64); + if (w && imm >= INT32_MIN && imm <= INT32_MAX) { + rex(e, 1, 0, 0, (uint8_t)(dst.index >> 3)); + paze_x64_emit8(e, 0xC7); + modrm(e, 0b11, 0, dst.index); + paze_x64_emit32(e, (uint32_t)imm); + } else if (!w) { + rex(e, 0, 0, 0, (uint8_t)(dst.index >> 3)); + paze_x64_emit8(e, (uint8_t)(0xB8 + (dst.index & 7))); + paze_x64_emit32(e, (uint32_t)imm); + } else { + rex(e, 1, 0, 0, (uint8_t)(dst.index >> 3)); + paze_x64_emit8(e, (uint8_t)(0xB8 + (dst.index & 7))); + paze_x64_emit64(e, (uint64_t)imm); + } +} + +/* ---- Arithmetic / Logic helpers ---- */ + +static void emit_rr(paze_x64_emitter_t *e, uint8_t opcode, paze_x64_reg_t dst, + paze_x64_reg_t src, bool op64) +{ + rex(e, op64 ? 1 : 0, (uint8_t)(src.index >> 3), 0, (uint8_t)(dst.index >> 3)); + paze_x64_emit8(e, opcode); + modrm(e, 0b11, src.index, dst.index); +} + +void paze_x64_add(paze_x64_emitter_t *e, paze_x64_reg_t d, paze_x64_reg_t s) { emit_rr(e, 0x01, d, s, d.size==64); } +void paze_x64_sub(paze_x64_emitter_t *e, paze_x64_reg_t d, paze_x64_reg_t s) { emit_rr(e, 0x29, d, s, d.size==64); } +void paze_x64_and(paze_x64_emitter_t *e, paze_x64_reg_t d, paze_x64_reg_t s) { emit_rr(e, 0x21, d, s, d.size==64); } +void paze_x64_or (paze_x64_emitter_t *e, paze_x64_reg_t d, paze_x64_reg_t s) { emit_rr(e, 0x09, d, s, d.size==64); } +void paze_x64_xor(paze_x64_emitter_t *e, paze_x64_reg_t d, paze_x64_reg_t s) { emit_rr(e, 0x31, d, s, d.size==64); } +void paze_x64_cmp(paze_x64_emitter_t *e, paze_x64_reg_t d, paze_x64_reg_t s) { emit_rr(e, 0x39, d, s, d.size==64); } +void paze_x64_test(paze_x64_emitter_t *e, paze_x64_reg_t a, paze_x64_reg_t b) { emit_rr(e, 0x85, a, b, a.size==64); } + +static void emit_alu_imm(paze_x64_emitter_t *e, uint8_t sub, paze_x64_reg_t dst, int64_t imm) +{ + bool op64 = (dst.size == 64); + rex(e, op64 ? 1 : 0, 0, 0, (uint8_t)(dst.index >> 3)); + if (in_imm8(imm)) { + paze_x64_emit8(e, 0x83); + modrm(e, 0b11, sub, dst.index); + paze_x64_emit8(e, (uint8_t)imm); + } else { + paze_x64_emit8(e, 0x81); + modrm(e, 0b11, sub, dst.index); + paze_x64_emit32(e, (uint32_t)imm); + } +} + +void paze_x64_add_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm) { emit_alu_imm(e, 0, dst, imm); } +void paze_x64_sub_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm) { emit_alu_imm(e, 5, dst, imm); } +void paze_x64_cmp_imm(paze_x64_emitter_t *e, paze_x64_reg_t dst, int64_t imm) { emit_alu_imm(e, 7, dst, imm); } + +void paze_x64_imul(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src) +{ + rex(e, 1, (uint8_t)(dst.index >> 3), 0, (uint8_t)(src.index >> 3)); + paze_x64_emit8(e, 0x0F); + paze_x64_emit8(e, 0xAF); + modrm(e, 0b11, dst.index, src.index); +} + +void paze_x64_idiv(paze_x64_emitter_t *e, paze_x64_reg_t src) +{ + rex(e, 1, 0, 0, (uint8_t)(src.index >> 3)); + paze_x64_emit8(e, 0xF7); + modrm(e, 0b11, 7, src.index); +} + +void paze_x64_div(paze_x64_emitter_t *e, paze_x64_reg_t src) +{ + rex(e, 1, 0, 0, (uint8_t)(src.index >> 3)); + paze_x64_emit8(e, 0xF7); + modrm(e, 0b11, 6, src.index); +} + +void paze_x64_cqo(paze_x64_emitter_t *e) +{ + paze_x64_emit8(e, 0x48); + paze_x64_emit8(e, 0x99); +} + +void paze_x64_neg(paze_x64_emitter_t *e, paze_x64_reg_t r) +{ + rex(e, r.size == 64 ? 1 : 0, 0, 0, (uint8_t)(r.index >> 3)); + paze_x64_emit8(e, 0xF7); + modrm(e, 0b11, 3, r.index); +} + +void paze_x64_not(paze_x64_emitter_t *e, paze_x64_reg_t r) +{ + rex(e, r.size == 64 ? 1 : 0, 0, 0, (uint8_t)(r.index >> 3)); + paze_x64_emit8(e, 0xF7); + modrm(e, 0b11, 2, r.index); +} + +/* ---- Shifts ---- */ + +static void shift(paze_x64_emitter_t *e, uint8_t sub, paze_x64_reg_t r) +{ + rex(e, r.size == 64 ? 1 : 0, 0, 0, (uint8_t)(r.index >> 3)); + paze_x64_emit8(e, 0xD3); + modrm(e, 0b11, sub, r.index); +} + +void paze_x64_shl_cl(paze_x64_emitter_t *e, paze_x64_reg_t r) { shift(e, 4, r); } +void paze_x64_shr_cl(paze_x64_emitter_t *e, paze_x64_reg_t r) { shift(e, 5, r); } +void paze_x64_sar_cl(paze_x64_emitter_t *e, paze_x64_reg_t r) { shift(e, 7, r); } + +/* ---- Extend load / store ---- */ + +void paze_x64_movzx8(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src) +{ + rex(e, 1, (uint8_t)(dst.index >> 3), 0, (uint8_t)(src.index >> 3)); + paze_x64_emit8(e, 0x0F); paze_x64_emit8(e, 0xB6); + modrm(e, 0b11, dst.index, src.index); +} + +void paze_x64_movsx8(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src) +{ + rex(e, 1, (uint8_t)(dst.index >> 3), 0, (uint8_t)(src.index >> 3)); + paze_x64_emit8(e, 0x0F); paze_x64_emit8(e, 0xBE); + modrm(e, 0b11, dst.index, src.index); +} + +void paze_x64_movsx32(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_reg_t src) +{ + rex(e, 1, (uint8_t)(dst.index >> 3), 0, (uint8_t)(src.index >> 3)); + paze_x64_emit8(e, 0x63); + modrm(e, 0b11, dst.index, src.index); +} + +static void load_ext(paze_x64_emitter_t *e, uint8_t op2, paze_x64_reg_t dst, paze_x64_mem_t m) +{ + rex_for(e, 1, dst.index, m); + paze_x64_emit8(e, 0x0F); paze_x64_emit8(e, op2); + encode_modrm_for(e, dst.index, m); +} + +void paze_x64_movzx8_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m) { load_ext(e, 0xB6, dst, m); } +void paze_x64_movsx8_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m) { load_ext(e, 0xBE, dst, m); } +void paze_x64_movzx16_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m) { load_ext(e, 0xB7, dst, m); } +void paze_x64_movsx16_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m) { load_ext(e, 0xBF, dst, m); } + +void paze_x64_mov64_m(paze_x64_emitter_t *e, paze_x64_reg_t dst, paze_x64_mem_t m) +{ + rex_for(e, 1, dst.index, m); + paze_x64_emit8(e, 0x8B); + encode_modrm_for(e, dst.index, m); +} + +static void emit_mr(paze_x64_emitter_t *e, uint8_t opcode, paze_x64_reg_t src, + paze_x64_mem_t m, bool op64) +{ + rex_for(e, op64 ? 1 : 0, src.index, m); + paze_x64_emit8(e, opcode); + encode_modrm_for(e, src.index, m); +} + +void paze_x64_store8(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src) +{ + bool need_rex = src.index >= 8 || + (m.has_base && m.base.index >= 8) || + (m.has_index && m.index.index >= 8); + if (need_rex) + rex(e, 0, + (uint8_t)(src.index >> 3), + m.has_index ? (uint8_t)(m.index.index >> 3) : 0, + m.has_base ? (uint8_t)(m.base.index >> 3) : 0); + paze_x64_emit8(e, 0x88); + encode_modrm_for(e, src.index, m); +} + +void paze_x64_store16(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src) +{ + paze_x64_emit8(e, 0x66); + emit_mr(e, 0x89, src, m, false); +} + +void paze_x64_store32(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src) +{ + emit_mr(e, 0x89, src, m, false); +} + +void paze_x64_store64(paze_x64_emitter_t *e, paze_x64_mem_t m, paze_x64_reg_t src) +{ + emit_mr(e, 0x89, src, m, true); +} + +/* ---- Stack ---- */ + +void paze_x64_push(paze_x64_emitter_t *e, paze_x64_reg_t r) +{ + if (r.index >= 8) rex(e, 0, 0, 0, 1); + paze_x64_emit8(e, (uint8_t)(0x50 + (r.index & 7))); +} + +void paze_x64_pop(paze_x64_emitter_t *e, paze_x64_reg_t r) +{ + if (r.index >= 8) rex(e, 0, 0, 0, 1); + paze_x64_emit8(e, (uint8_t)(0x58 + (r.index & 7))); +} + +/* ---- Control flow ---- */ + +void paze_x64_ret(paze_x64_emitter_t *e) { paze_x64_emit8(e, 0xC3); } +void paze_x64_leave(paze_x64_emitter_t *e) { paze_x64_emit8(e, 0xC9); } + +void paze_x64_jmp(paze_x64_emitter_t *e, int label) +{ + paze_x64_emit8(e, 0xE9); + add_patch(e, e->target->len, label); + paze_x64_emit32(e, 0); +} + +void paze_x64_jcc(paze_x64_emitter_t *e, paze_x64_cond_t cc, int label) +{ + paze_x64_emit8(e, 0x0F); + paze_x64_emit8(e, (uint8_t)(0x80 | (int)cc)); + add_patch(e, e->target->len, label); + paze_x64_emit32(e, 0); +} + +void paze_x64_setcc(paze_x64_emitter_t *e, paze_x64_cond_t cc, paze_x64_reg_t r) +{ + if (r.index >= 8 || r.index == 4 || r.index == 5 || r.index == 6 || r.index == 7) + rex(e, 0, 0, 0, (uint8_t)(r.index >> 3)); + paze_x64_emit8(e, 0x0F); + paze_x64_emit8(e, (uint8_t)(0x90 | (int)cc)); + modrm(e, 0b11, 0, r.index); +} + +/* ---- Call ---- */ + +size_t paze_x64_call_rel(paze_x64_emitter_t *e) +{ + paze_x64_emit8(e, 0xE8); + size_t off = e->target->len; + paze_x64_emit32(e, 0); + return off; +} + +void paze_x64_call_reg(paze_x64_emitter_t *e, paze_x64_reg_t r) +{ + if (r.index >= 8) rex(e, 0, 0, 0, 1); + paze_x64_emit8(e, 0xFF); + modrm(e, 0b11, 2, r.index); +} diff --git a/test_comprehensive.pe b/test_comprehensive.pe new file mode 100644 index 0000000..9a52eda --- /dev/null +++ b/test_comprehensive.pe @@ -0,0 +1,63 @@ +#include + +int factorial(int n) { + if (n <= 1) return 1; + return n * factorial(n - 1); +} + +int fibonacci(int n) { + if (n <= 1) return n; + int a = 0, b = 1; + for (int i = 2; i <= n; i++) { + int temp = a + b; + a = b; + b = temp; + } + return b; +} + +int main(void) { + printf("=== PazeE Comprehensive Test ===\n"); + + // Test typedef + paze_uint64_t big_num = 123456789ULL; + printf("big_num = %llu\n", big_num); + + // Test arithmetic + int x = 10, y = 3; + printf("x + y = %d\n", x + y); + printf("x - y = %d\n", x - y); + printf("x * y = %d\n", x * y); + printf("x / y = %d\n", x / y); + printf("x %% y = %d\n", x % y); + + // Test loops + printf("Counting: "); + for (int i = 0; i < 5; i++) { + printf("%d ", i); + } + printf("\n"); + + // Test while loop + printf("While: "); + int i = 0; + while (i < 3) { + printf("%d ", i); + i++; + } + printf("\n"); + + // Test functions + printf("factorial(5) = %d\n", factorial(5)); + printf("fibonacci(10) = %d\n", fibonacci(10)); + + // Test do-while + int n = 0; + do { + printf("do-while: %d\n", n); + n++; + } while (n < 2); + + printf("=== All tests passed! ===\n"); + return 0; +} \ No newline at end of file diff --git a/test_simple.pe b/test_simple.pe new file mode 100644 index 0000000..e18a2e6 --- /dev/null +++ b/test_simple.pe @@ -0,0 +1,4 @@ +int main(void) { + int x = 42; + return 0; +} \ No newline at end of file diff --git a/test_typedef.pe b/test_typedef.pe new file mode 100644 index 0000000..afcee5e --- /dev/null +++ b/test_typedef.pe @@ -0,0 +1,8 @@ +#include + +int main(void) { + paze_uint64_t x = 42; + paze_size_t y = x; + printf("%llu\n", y); + return 0; +} \ No newline at end of file diff --git a/test_typedef2.pe b/test_typedef2.pe new file mode 100644 index 0000000..f0d48bc --- /dev/null +++ b/test_typedef2.pe @@ -0,0 +1,7 @@ +#include + +int main(void) { + paze_uint64_t x = 42; + printf("%llu\n", x); + return 0; +} \ No newline at end of file diff --git a/tests/debug_fib.pe b/tests/debug_fib.pe new file mode 100644 index 0000000..85267a6 --- /dev/null +++ b/tests/debug_fib.pe @@ -0,0 +1,10 @@ +int fib(int n) { + if (n < 2) return n; + return fib(n - 1) + fib(n - 2); +} + +int main() { + int r = fib(5); + printf("fib(5) = %d\n", r); + return 0; +} \ No newline at end of file diff --git a/tests/debug_fordec.pe b/tests/debug_fordec.pe new file mode 100644 index 0000000..27405ff --- /dev/null +++ b/tests/debug_fordec.pe @@ -0,0 +1,8 @@ +int main() { + int sum = 0; + for (int i = 0; i < 3; i++) { + sum += i; + } + printf("sum = %d\n", sum); + return 0; +} \ No newline at end of file diff --git a/tests/debug_func.pe b/tests/debug_func.pe new file mode 100644 index 0000000..5c53f80 --- /dev/null +++ b/tests/debug_func.pe @@ -0,0 +1,11 @@ +int add(int a, int b) { return a + b; } + +int main(void) { + int sum = 0; + int i; + for (i = 0; i < 5; i++) { + sum += add(i, i); + } + printf("sum = %d\n", sum); + return 0; +} \ No newline at end of file diff --git a/tests/debug_loop.pe b/tests/debug_loop.pe new file mode 100644 index 0000000..7a9b60b --- /dev/null +++ b/tests/debug_loop.pe @@ -0,0 +1,9 @@ +int main(void) { + int i; + int sum = 0; + for (i = 0; i < 5; i++) { + sum += i; + } + printf("sum = %d\n", sum); + return 0; +} \ No newline at end of file diff --git a/tests/debug_loop2.pe b/tests/debug_loop2.pe new file mode 100644 index 0000000..6235283 --- /dev/null +++ b/tests/debug_loop2.pe @@ -0,0 +1,10 @@ +int main(void) { + int i; + int sum = 0; + for (i = 0; i < 5; i++) { + printf("i=%d sum=%d\n", i, sum); + sum += i; + } + printf("final sum = %d\n", sum); + return 0; +} \ No newline at end of file diff --git a/tests/debug_simple.pe b/tests/debug_simple.pe new file mode 100644 index 0000000..73c0248 --- /dev/null +++ b/tests/debug_simple.pe @@ -0,0 +1,5 @@ +int main(void) { + int i = 42; + printf("i = %d\n", i); + return 0; +} \ No newline at end of file diff --git a/tests/debug_struct.pe b/tests/debug_struct.pe new file mode 100644 index 0000000..82cd092 --- /dev/null +++ b/tests/debug_struct.pe @@ -0,0 +1,12 @@ +struct Point { + int x; + int y; +}; + +int main() { + struct Point p; + p.x = 3; + p.y = 4; + printf("p = (%d, %d)\n", p.x, p.y); + return 0; +} \ No newline at end of file diff --git a/tests/debug_test.pe b/tests/debug_test.pe new file mode 100644 index 0000000..7a9b60b --- /dev/null +++ b/tests/debug_test.pe @@ -0,0 +1,9 @@ +int main(void) { + int i; + int sum = 0; + for (i = 0; i < 5; i++) { + sum += i; + } + printf("sum = %d\n", sum); + return 0; +} \ No newline at end of file diff --git a/tests/debug_while.pe b/tests/debug_while.pe new file mode 100644 index 0000000..b06d455 --- /dev/null +++ b/tests/debug_while.pe @@ -0,0 +1,10 @@ +int main(void) { + int i = 0; + int sum = 0; + while (i < 5) { + sum += i; + i++; + } + printf("sum = %d\n", sum); + return 0; +} \ No newline at end of file diff --git a/tests/localinit.pe b/tests/localinit.pe new file mode 100644 index 0000000..5ce3f0c --- /dev/null +++ b/tests/localinit.pe @@ -0,0 +1,10 @@ +// localinit.pe — 验证局部数组初始化列表 +int main(void) { + int arr[] = {1, 2, 3, 4, 5}; + int s = 0; + for (int i = 0; i < 5; i++) { + s += arr[i]; + } + printf("sum = %d\n", s); // 1+2+3+4+5 = 15 + return 0; +} diff --git a/tests/min_pure.pe b/tests/min_pure.pe new file mode 100644 index 0000000..9b6bdc2 --- /dev/null +++ b/tests/min_pure.pe @@ -0,0 +1,3 @@ +int main(void) { + return 0; +}