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;
+}