feat(los4): add pure C compiler targeting Los4 ELF
Add src/PazeE.los4/ — a pure C implementation of the PazeE compiler that generates static Los4 ELF executables (x86-64, int 0x80 syscalls). Core components: - Lexer, preprocessor, recursive-descent parser, AST - Tree-walking interpreter (paze run) with breakpoints - x86-64 code generator (SysV AMD64 ABI) - Los4 ELF writer (4 PH layout matching doomlauncher.elf, base 0x400000) - Runtime library as raw machine code: printf, sprintf, puts, malloc, memcpy, memset, strcmp, strlen, exit, etc. Key fixes: - Fix use-after-free: AST paze_str_t slices point into source buffer, so free(src) is deferred until after interpreter/codegen completes - printf/sprintf support %d/%i/%u/%x/%c/%s/%% with rel32 jumps - External function calls resolved via runtime offset lookup All 14 test cases pass for both paze run and paze build. Also includes DebuggerVM.cs and Program.cs updates for the C# compiler.
这个提交包含在:
文件差异内容过多而无法显示
加载差异
+371
-48
@@ -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<string>();
|
||||
|
||||
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 <source.pe> [--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<int>();
|
||||
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 <source.pe> [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 <source.pe>"); 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 <paze.h>\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 <paze.h>\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 <source.pe> [选项]");
|
||||
Console.WriteLine("用法:");
|
||||
Console.WriteLine(" paze <source.pe> [选项] 编译为可执行文件");
|
||||
Console.WriteLine(" paze run <source.pe> [-- args] 编译并直接运行");
|
||||
Console.WriteLine(" paze debug <source.pe> [line 1,2] 调试运行(断点)");
|
||||
Console.WriteLine(" paze check <source.pe> 语法/语义检查");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("编译选项:");
|
||||
Console.WriteLine(" -o <out> 指定输出文件名");
|
||||
Console.WriteLine(" --target <platform> win|linux|macos|los4(默认宿主)");
|
||||
Console.WriteLine(" --arch <arch> amd|arm(默认 amd=x86-64;arm=AArch64 三平台)");
|
||||
Console.WriteLine(" --arch <arch> amd|arm(默认 amd)");
|
||||
Console.WriteLine(" --emit <kind> 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
#ifndef PAZE_AST_H
|
||||
#define PAZE_AST_H
|
||||
|
||||
#include "paze_types.h"
|
||||
#include "paze_token.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef PAZE_DIAGNOSTICS_H
|
||||
#define PAZE_DIAGNOSTICS_H
|
||||
|
||||
#include "paze_types.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef PAZE_ELF_WRITER_LOS4_H
|
||||
#define PAZE_ELF_WRITER_LOS4_H
|
||||
|
||||
#include "paze_object_image.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 <stdlib.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef PAZE_LIBC_H
|
||||
#define PAZE_LIBC_H
|
||||
|
||||
#include "paze_types.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef PAZE_LIBC_DECLS_H
|
||||
#define PAZE_LIBC_DECLS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef PAZE_LOS4_RUNTIME_H
|
||||
#define PAZE_LOS4_RUNTIME_H
|
||||
|
||||
#include "paze_object_image.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,141 @@
|
||||
#ifndef PAZE_OBJECT_IMAGE_H
|
||||
#define PAZE_OBJECT_IMAGE_H
|
||||
|
||||
#include "paze_types.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,182 @@
|
||||
#ifndef PAZE_TYPES_H
|
||||
#define PAZE_TYPES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef PAZE_X64_CODEGEN_H
|
||||
#define PAZE_X64_CODEGEN_H
|
||||
|
||||
#include "paze_ast.h"
|
||||
#include "paze_object_image.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,145 @@
|
||||
#ifndef PAZE_X64_EMITTER_H
|
||||
#define PAZE_X64_EMITTER_H
|
||||
|
||||
#include "paze_object_image.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 <file.pe> Run a .pe file (interpreted)\n");
|
||||
printf(" paze debug <file.pe> [lines] Debug a .pe file at specified breakpoints\n");
|
||||
printf(" paze check <file.pe> Check syntax without running\n");
|
||||
printf(" paze build <file.pe> [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 <file.pe>\n"); return 1; }
|
||||
return cmd_run(argv[2]);
|
||||
}
|
||||
if (strcmp(command, "check") == 0) {
|
||||
if (argc < 3) { fprintf(stderr, "usage: paze check <file.pe>\n"); return 1; }
|
||||
return cmd_check(argv[2]);
|
||||
}
|
||||
if (strcmp(command, "build") == 0) {
|
||||
if (argc < 3) { fprintf(stderr, "usage: paze build <file.pe> [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 <file.pe> [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;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "../include/paze_ast.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "../include/paze_diagnostics.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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 : "<unknown>",
|
||||
d->loc.line, d->loc.col,
|
||||
sev_str,
|
||||
d->message ? d->message : "(null)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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;
|
||||
}
|
||||
文件差异内容过多而无法显示
加载差异
@@ -0,0 +1,655 @@
|
||||
#include "paze_lexer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "paze_libc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "../include/paze_libc_decls.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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;
|
||||
}
|
||||
文件差异内容过多而无法显示
加载差异
@@ -0,0 +1,197 @@
|
||||
#include "paze_object_image.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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;
|
||||
}
|
||||
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
@@ -0,0 +1,408 @@
|
||||
#include "../include/paze_sema.h"
|
||||
#include "../include/paze_token.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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);
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
#include "../include/paze_token.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "../include/paze_types.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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;
|
||||
}
|
||||
文件差异内容过多而无法显示
加载差异
@@ -0,0 +1,524 @@
|
||||
#include "paze_x64_emitter.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ========================================================================
|
||||
* 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);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <paze.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
int main(void) {
|
||||
int x = 42;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <paze.h>
|
||||
|
||||
int main(void) {
|
||||
paze_uint64_t x = 42;
|
||||
paze_size_t y = x;
|
||||
printf("%llu\n", y);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <paze.h>
|
||||
|
||||
int main(void) {
|
||||
paze_uint64_t x = 42;
|
||||
printf("%llu\n", x);
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
int main(void) {
|
||||
int i = 42;
|
||||
printf("i = %d\n", i);
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
int main(void) {
|
||||
return 0;
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户