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.
这个提交包含在:
2026-08-10 21:32:39 +08:00
父节点 2ea642875f
当前提交 7b0a4c535b
修改 52 个文件,包含 15692 行新增48 行删除
文件差异内容过多而无法显示 加载差异
+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}");
}
}
}
+48
查看文件
@@ -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
}
+349
查看文件
@@ -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 */
+70
查看文件
@@ -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 */
+100
查看文件
@@ -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 */
+58
查看文件
@@ -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 */
+43
查看文件
@@ -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 */
+27
查看文件
@@ -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 */
+141
查看文件
@@ -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 */
+57
查看文件
@@ -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 */
+91
查看文件
@@ -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 */
+179
查看文件
@@ -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 */
+62
查看文件
@@ -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 */
+182
查看文件
@@ -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 */
+31
查看文件
@@ -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 */
+145
查看文件
@@ -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 */
+259
查看文件
@@ -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;
}
+142
查看文件
@@ -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";
}
}
+140
查看文件
@@ -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)");
}
}
}
+305
查看文件
@@ -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;
}
文件差异内容过多而无法显示 加载差异
+655
查看文件
@@ -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;
}
+82
查看文件
@@ -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;
}
+70
查看文件
@@ -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;
}
文件差异内容过多而无法显示 加载差异
+197
查看文件
@@ -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;
}
文件差异内容过多而无法显示 加载差异
文件差异内容过多而无法显示 加载差异
+408
查看文件
@@ -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);
}
+269
查看文件
@@ -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;
}
+165
查看文件
@@ -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;
}
文件差异内容过多而无法显示 加载差异
+524
查看文件
@@ -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);
}
+63
查看文件
@@ -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;
}
+4
查看文件
@@ -0,0 +1,4 @@
int main(void) {
int x = 42;
return 0;
}
+8
查看文件
@@ -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;
}
+7
查看文件
@@ -0,0 +1,7 @@
#include <paze.h>
int main(void) {
paze_uint64_t x = 42;
printf("%llu\n", x);
return 0;
}
+10
查看文件
@@ -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;
}
+8
查看文件
@@ -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;
}
+11
查看文件
@@ -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;
}
+9
查看文件
@@ -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;
}
+10
查看文件
@@ -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;
}
+5
查看文件
@@ -0,0 +1,5 @@
int main(void) {
int i = 42;
printf("i = %d\n", i);
return 0;
}
+12
查看文件
@@ -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;
}
+9
查看文件
@@ -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;
}
+10
查看文件
@@ -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;
}
+10
查看文件
@@ -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;
}
+3
查看文件
@@ -0,0 +1,3 @@
int main(void) {
return 0;
}