From e22811f2f52f7670efaff27c59f8acb17ab20f41 Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Thu, 20 Nov 2025 10:44:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(build):=20=E5=BC=95=E5=85=A5=E6=96=B0?= =?UTF-8?q?=E7=9A=84=20Python=20=E6=9E=84=E5=BB=BA=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E5=B9=B6=E7=A7=BB=E9=99=A4=E6=97=A7=20Makefile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增基于 Python 的构建脚本 `cbuild.py`,支持包管理、依赖解析和模块化编译。 同时添加 `.gitignore` 忽略 `build` 目录,并在 `justfile` 中更新构建命令。 移除了原有的 `lib/Makefile` 和主目录下的相关 make 规则,统一使用新构建系统。 --- .gitignore | 3 + Makefile | 9 - README.md | 15 + cbuild.py | 512 +++++ justfile | 11 + lib/Makefile | 48 - lib/acutest.h | 1994 ----------------- lib/core.c | 10 - lib/core.h | 28 - lib/rt/log/color.h | 59 - lib/rt/log/log.c | 72 - lib/rt/log/log.h | 161 -- lib/rt/rt.c | 10 - lib/rt/rt.h | 18 - lib/rt/rt_alloc.c | 135 -- lib/rt/rt_alloc.h | 132 -- lib/rt/rt_string.c | 49 - lib/rt/rt_string.h | 67 - lib/rt/std/rt_api_def.h | 183 -- lib/rt/std/rt_std.c | 32 - lib/rt/std/rt_std.h | 6 - lib/rt/std/rt_type.h | 120 - lib/test/test.py | 0 lib/test/test_alloc..c | 0 lib/test/test_log.c | 15 - lib/utils/ds/hashtable.c | 140 -- lib/utils/ds/hashtable.h | 124 - lib/utils/ds/kllist.h | 158 -- lib/utils/ds/vector-gdb.py | 202 -- lib/utils/ds/vector.h | 116 - lib/utils/gdb.py | 0 lib/utils/strpool/strpool.c | 32 - lib/utils/strpool/strpool.h | 54 - lib/utils/tokbuf/tokbuf.c | 19 - lib/utils/tokbuf/tokbuf.h | 75 - lib/utils/utils.h | 7 - libs/README.md | 9 + .../frontend => libs}/lexer/README.md | 0 libs/lexer/cbuild.toml | 6 + libs/lexer/include/lexer.h | 62 + libs/lexer/include/lexer_log.h | 48 + libs/lexer/include/lexer_stream.h | 37 + libs/lexer/include/lexer_token.h | 137 ++ libs/lexer/src/lexer.c | 637 ++++++ libs/lexer/src/lexer_log.c | 7 + libs/lexer/src/mem_stream.c | 101 + libs/lexer/src/token.c | 30 + libs/lexer/tests/test_number.c | 4 + libs/lexer/tests/test_run.c | 83 + src/Makefile | 30 - src/assembler/assembler.c | 144 -- src/assembler/assembler.h | 18 - src/assembler/riscv32/Makefile | 17 - src/assembler/riscv32/riscv32_asm.c | 29 - src/assembler/riscv32/riscv32_asm.h | 18 - src/assembler/riscv32/symtab_asm.c | 48 - src/assembler/riscv32/symtab_asm.h | 26 - src/assembler/riscv32/test.asm.s | 62 - src/assembler/syscall.h | 0 src/ccompiler/Makefile | 57 - src/ccompiler/backend/Makefile | 30 - src/ccompiler/backend/backend.c | 23 - src/ccompiler/backend/backend.h | 30 - src/ccompiler/backend/riscv32/README.md | 41 - src/ccompiler/backend/riscv32/riscv32.c | 302 --- src/ccompiler/backend/riscv32/riscv32.h | 9 - src/ccompiler/ccompiler.c | 11 - src/ccompiler/ccompiler.h | 21 - src/ccompiler/frontend/Makefile | 43 - src/ccompiler/frontend/frontend.c | 23 - src/ccompiler/frontend/frontend.h | 9 - src/ccompiler/frontend/lexer/lexer.c | 525 ----- src/ccompiler/frontend/lexer/lexer.h | 76 - src/ccompiler/frontend/lexer/lexer_log.h | 46 - src/ccompiler/frontend/lexer/token.c | 86 - src/ccompiler/frontend/lexer/token.h | 142 -- src/ccompiler/frontend/parser/ast-gdb.py | 0 src/ccompiler/frontend/parser/ast.c | 172 -- src/ccompiler/frontend/parser/ast.h | 190 -- src/ccompiler/frontend/parser/ast/README.md | 18 - src/ccompiler/frontend/parser/ast/block.c | 49 - src/ccompiler/frontend/parser/ast/decl.c | 98 - src/ccompiler/frontend/parser/ast/expr.c | 425 ---- src/ccompiler/frontend/parser/ast/func.c | 176 -- src/ccompiler/frontend/parser/ast/program.c | 34 - src/ccompiler/frontend/parser/ast/stmt.c | 246 -- src/ccompiler/frontend/parser/ast/term.c | 51 - src/ccompiler/frontend/parser/ast/type.c | 136 -- src/ccompiler/frontend/parser/parser.c | 20 - src/ccompiler/frontend/parser/parser.h | 23 - src/ccompiler/frontend/parser/symtab/symtab.c | 62 - src/ccompiler/frontend/parser/symtab/symtab.h | 39 - src/ccompiler/frontend/parser/type.h | 35 - src/ccompiler/middleend/Makefile | 33 - src/ccompiler/middleend/ir/ir.c | 0 src/ccompiler/middleend/ir/ir.h | 152 -- src/ccompiler/middleend/ir/ir_ast.c | 446 ---- src/ccompiler/middleend/ir/ir_ast.h | 8 - src/ccompiler/middleend/ir/ir_dump.c | 76 - src/ccompiler/middleend/ir/ir_lib.c | 118 - src/ccompiler/middleend/ir/ir_lib.h | 9 - src/ccompiler/middleend/ir/ir_type.c | 12 - src/ccompiler/middleend/ir/ir_type.h | 8 - src/ccompiler/middleend/middleend.c | 5 - src/ccompiler/middleend/middleend.h | 13 - src/ccompiler/middleend/reg_alloc.c | 0 src/ccompiler/middleend/reg_alloc.h | 8 - src/linker/Makefile | 17 - src/linker/header.h | 10 - src/linker/riscv32_crt.c | 25 - src/linker/riscv32_linker.c | 67 - src/mcode/Makefile | 17 - src/mcode/reloc_symtab.c | 48 - src/mcode/reloc_symtab.h | 27 - src/mcode/riscv32/riscv32_def.h | 164 -- src/mcode/riscv32/riscv32_instr.h | 939 -------- src/mcode/riscv32/riscv32_mcode.c | 108 - src/mcode/riscv32/riscv32_mcode.h | 23 - src/pprocesser/main.c | 0 src/smcc.c | 50 - tests/cbuild/cbuild.toml | 2 + tests/cbuild/include/add.h | 6 + tests/cbuild/src/add.c | 3 + tests/cbuild/src/main.c | 8 + tests/cbuild/tests/test_main.c | 10 + tests/simple/01_return.c | 3 + tests/simple/02_decl_expr.c | 8 + tests/simple/03_decl_init.c | 6 + tests/simple/04_if.c | 10 + tests/simple/05_else.c | 10 + tests/simple/06_fcall.c | 9 + tests/simple/07_while.c | 5 + tests/simple/08_do_while.c | 12 + tests/simple/09_for.c | 7 + tests/simple/10_main.c | 7 + tests/simple/11_recursive.c | 18 + tests/simple/Makefile | 30 + tests/simple/hard_01.c | 6 + tests/simple/test.py | 86 + wc.py | 48 + 140 files changed, 1996 insertions(+), 10098 deletions(-) create mode 100644 README.md create mode 100644 cbuild.py create mode 100644 justfile delete mode 100644 lib/Makefile delete mode 100644 lib/acutest.h delete mode 100644 lib/core.c delete mode 100644 lib/core.h delete mode 100644 lib/rt/log/color.h delete mode 100644 lib/rt/log/log.c delete mode 100644 lib/rt/log/log.h delete mode 100644 lib/rt/rt.c delete mode 100644 lib/rt/rt.h delete mode 100644 lib/rt/rt_alloc.c delete mode 100644 lib/rt/rt_alloc.h delete mode 100644 lib/rt/rt_string.c delete mode 100644 lib/rt/rt_string.h delete mode 100644 lib/rt/std/rt_api_def.h delete mode 100644 lib/rt/std/rt_std.c delete mode 100644 lib/rt/std/rt_std.h delete mode 100644 lib/rt/std/rt_type.h delete mode 100644 lib/test/test.py delete mode 100644 lib/test/test_alloc..c delete mode 100644 lib/test/test_log.c delete mode 100644 lib/utils/ds/hashtable.c delete mode 100644 lib/utils/ds/hashtable.h delete mode 100644 lib/utils/ds/kllist.h delete mode 100644 lib/utils/ds/vector-gdb.py delete mode 100644 lib/utils/ds/vector.h delete mode 100644 lib/utils/gdb.py delete mode 100644 lib/utils/strpool/strpool.c delete mode 100644 lib/utils/strpool/strpool.h delete mode 100644 lib/utils/tokbuf/tokbuf.c delete mode 100644 lib/utils/tokbuf/tokbuf.h delete mode 100644 lib/utils/utils.h create mode 100644 libs/README.md rename {src/ccompiler/frontend => libs}/lexer/README.md (100%) create mode 100644 libs/lexer/cbuild.toml create mode 100644 libs/lexer/include/lexer.h create mode 100644 libs/lexer/include/lexer_log.h create mode 100644 libs/lexer/include/lexer_stream.h create mode 100644 libs/lexer/include/lexer_token.h create mode 100644 libs/lexer/src/lexer.c create mode 100644 libs/lexer/src/lexer_log.c create mode 100644 libs/lexer/src/mem_stream.c create mode 100644 libs/lexer/src/token.c create mode 100644 libs/lexer/tests/test_number.c create mode 100644 libs/lexer/tests/test_run.c delete mode 100644 src/Makefile delete mode 100644 src/assembler/assembler.c delete mode 100644 src/assembler/assembler.h delete mode 100644 src/assembler/riscv32/Makefile delete mode 100644 src/assembler/riscv32/riscv32_asm.c delete mode 100644 src/assembler/riscv32/riscv32_asm.h delete mode 100644 src/assembler/riscv32/symtab_asm.c delete mode 100644 src/assembler/riscv32/symtab_asm.h delete mode 100644 src/assembler/riscv32/test.asm.s delete mode 100644 src/assembler/syscall.h delete mode 100644 src/ccompiler/Makefile delete mode 100644 src/ccompiler/backend/Makefile delete mode 100644 src/ccompiler/backend/backend.c delete mode 100644 src/ccompiler/backend/backend.h delete mode 100644 src/ccompiler/backend/riscv32/README.md delete mode 100644 src/ccompiler/backend/riscv32/riscv32.c delete mode 100644 src/ccompiler/backend/riscv32/riscv32.h delete mode 100644 src/ccompiler/ccompiler.c delete mode 100644 src/ccompiler/ccompiler.h delete mode 100644 src/ccompiler/frontend/Makefile delete mode 100644 src/ccompiler/frontend/frontend.c delete mode 100644 src/ccompiler/frontend/frontend.h delete mode 100644 src/ccompiler/frontend/lexer/lexer.c delete mode 100644 src/ccompiler/frontend/lexer/lexer.h delete mode 100644 src/ccompiler/frontend/lexer/lexer_log.h delete mode 100644 src/ccompiler/frontend/lexer/token.c delete mode 100644 src/ccompiler/frontend/lexer/token.h delete mode 100644 src/ccompiler/frontend/parser/ast-gdb.py delete mode 100644 src/ccompiler/frontend/parser/ast.c delete mode 100644 src/ccompiler/frontend/parser/ast.h delete mode 100644 src/ccompiler/frontend/parser/ast/README.md delete mode 100644 src/ccompiler/frontend/parser/ast/block.c delete mode 100644 src/ccompiler/frontend/parser/ast/decl.c delete mode 100644 src/ccompiler/frontend/parser/ast/expr.c delete mode 100644 src/ccompiler/frontend/parser/ast/func.c delete mode 100644 src/ccompiler/frontend/parser/ast/program.c delete mode 100644 src/ccompiler/frontend/parser/ast/stmt.c delete mode 100644 src/ccompiler/frontend/parser/ast/term.c delete mode 100644 src/ccompiler/frontend/parser/ast/type.c delete mode 100644 src/ccompiler/frontend/parser/parser.c delete mode 100644 src/ccompiler/frontend/parser/parser.h delete mode 100644 src/ccompiler/frontend/parser/symtab/symtab.c delete mode 100644 src/ccompiler/frontend/parser/symtab/symtab.h delete mode 100644 src/ccompiler/frontend/parser/type.h delete mode 100644 src/ccompiler/middleend/Makefile delete mode 100644 src/ccompiler/middleend/ir/ir.c delete mode 100644 src/ccompiler/middleend/ir/ir.h delete mode 100644 src/ccompiler/middleend/ir/ir_ast.c delete mode 100644 src/ccompiler/middleend/ir/ir_ast.h delete mode 100644 src/ccompiler/middleend/ir/ir_dump.c delete mode 100644 src/ccompiler/middleend/ir/ir_lib.c delete mode 100644 src/ccompiler/middleend/ir/ir_lib.h delete mode 100644 src/ccompiler/middleend/ir/ir_type.c delete mode 100644 src/ccompiler/middleend/ir/ir_type.h delete mode 100644 src/ccompiler/middleend/middleend.c delete mode 100644 src/ccompiler/middleend/middleend.h delete mode 100644 src/ccompiler/middleend/reg_alloc.c delete mode 100644 src/ccompiler/middleend/reg_alloc.h delete mode 100644 src/linker/Makefile delete mode 100644 src/linker/header.h delete mode 100644 src/linker/riscv32_crt.c delete mode 100644 src/linker/riscv32_linker.c delete mode 100644 src/mcode/Makefile delete mode 100644 src/mcode/reloc_symtab.c delete mode 100644 src/mcode/reloc_symtab.h delete mode 100644 src/mcode/riscv32/riscv32_def.h delete mode 100644 src/mcode/riscv32/riscv32_instr.h delete mode 100644 src/mcode/riscv32/riscv32_mcode.c delete mode 100644 src/mcode/riscv32/riscv32_mcode.h delete mode 100644 src/pprocesser/main.c delete mode 100644 src/smcc.c create mode 100644 tests/cbuild/cbuild.toml create mode 100644 tests/cbuild/include/add.h create mode 100644 tests/cbuild/src/add.c create mode 100644 tests/cbuild/src/main.c create mode 100644 tests/cbuild/tests/test_main.c create mode 100644 tests/simple/01_return.c create mode 100644 tests/simple/02_decl_expr.c create mode 100644 tests/simple/03_decl_init.c create mode 100644 tests/simple/04_if.c create mode 100644 tests/simple/05_else.c create mode 100644 tests/simple/06_fcall.c create mode 100644 tests/simple/07_while.c create mode 100644 tests/simple/08_do_while.c create mode 100644 tests/simple/09_for.c create mode 100644 tests/simple/10_main.c create mode 100644 tests/simple/11_recursive.c create mode 100644 tests/simple/Makefile create mode 100644 tests/simple/hard_01.c create mode 100644 tests/simple/test.py create mode 100644 wc.py diff --git a/.gitignore b/.gitignore index cb19b79..cf61256 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ note.md # python .venv + +# cbuilder +build diff --git a/Makefile b/Makefile index e3a8597..ebda706 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,3 @@ build-docs: docs: build-docs python -m http.server -d docs/html - -smcc-build: - make -C src - -smcc-clean: - make -C src clean - -smcc-test: smcc-build - make -C tests/simple diff --git a/README.md b/README.md new file mode 100644 index 0000000..2627de5 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Simple Models C Compiler + +> Smaller Compiler(SMCC) + +This is a simple C compiler that generates executable code from a simple c99 sub programming language. The language supports basic operations such as arithmetic, logical, conditional statements and if else while for switch case statements and function calls and system calls. + +## Features + +- 隔离标准库 + +- 轻量化 + +- 模块化 + +- 自举构建 diff --git a/cbuild.py b/cbuild.py new file mode 100644 index 0000000..3502d15 --- /dev/null +++ b/cbuild.py @@ -0,0 +1,512 @@ +# build.py +from abc import ABC, abstractmethod +import tomllib +import pprint +import subprocess +from pathlib import Path +from dataclasses import dataclass, field +from enum import Enum, auto +import logging + +import argparse +import sys +from typing import Self + +@dataclass +class Dependency: + """依赖配置""" + name: str + path: str + version: str = "0.0.0" + optional: bool = False + +@dataclass +class Feature: + """特性配置""" + name: str + description: str = "" + dependencies: list[str] = field(default_factory=list) + +class PackageConfig: + """包配置类, 用于解析和管理cbuild.toml配置""" + CONFIG_FILE = "cbuild.toml" + + def __init__(self, config_path: Path): + package_file = config_path / self.CONFIG_FILE + if not package_file.exists(): + raise ValueError(f"Package path {package_file} does not exist") + with open(package_file, "rb") as file: + raw_config = tomllib.load(file) + self.config: dict = raw_config.get("package", {}) + self.path: Path = config_path + + def __str__(self) -> str: + return pprint.pformat(self.config) + + @property + def name(self) -> str: + """获取包的名称 + + Returns: + str: 包的名称,如果未定义则返回空字符串 + """ + return self.config.get("name", "") + + @property + def version(self) -> str: + """获取包的版本 + + Returns: + str: 包的版本号,如果未定义则返回空字符串 + """ + return self.config.get("version", "") + + @property + def default_features(self) -> list[str]: + """获取默认启用的特性列表 + + Returns: + list[str]: 默认特性的名称列表 + """ + return self.config.get("default_features", []) + + @property + def features(self) -> list[Feature]: + """获取所有可用特性及其依赖 + + Returns: + list[Feature]: 特性对象列表 + """ + features_data = self.config.get("features", {}) + features = [] + for feature in features_data: + if isinstance(feature, str): + feature = Feature(name=feature) + elif isinstance(feature, dict): + name = feature.get("name", None) + if name is None: + continue + feature = Feature( + name=name, + description=feature.get("description", ""), + dependencies=feature.get("dependencies", []) + ) + features.append(feature) + return features + + @property + def dependencies(self) -> list[Dependency]: + """获取包的依赖列表 + + Returns: + list[Dependency]: 依赖对象列表 + """ + deps_data = self.config.get("dependencies", []) + dependencies = [] + for dep_dict in deps_data: + if isinstance(dep_dict, dict): + dependency = Dependency( + name=dep_dict.get("name", ""), + path=dep_dict.get("path", ""), + version=dep_dict.get("version", "0.0.0"), + optional=dep_dict.get("optional", False) + ) + dependencies.append(dependency) + return dependencies + + @property + def authors(self) -> list[str]: + """获取作者列表 + + Returns: + list[str]: 作者列表 + """ + return self.config.get("authors", []) + + @property + def description(self) -> str: + """获取包的描述 + + Returns: + str: 包的描述文本 + """ + return self.config.get("description", "") + +@dataclass(frozen=True) +class BuildPath: + """path""" + root_path: Path + tests_path: Path + output_path: Path + object_path: Path + src_path: Path + default_bin_path: Path + default_lib_path: Path + + @classmethod + def from_root_path(cls, root_path: Path) -> Self: + """_summary_ + + Args: + output_path (Path): _description_ + + Returns: + Self: _description_ + """ + src_path = root_path / "src" + output_path = root_path / "build" + return cls( + root_path = root_path, + tests_path = root_path / "tests", + output_path = output_path , + object_path = output_path / "obj", + src_path = src_path, + default_bin_path = src_path / "main.c", + default_lib_path = src_path / "lib.c", + ) + +class TargetType(Enum): + """目标文件类型枚举""" + MAIN_EXECUTABLE = auto() + EXECUTABLE = auto() + TEST_EXECUTABLE = auto() + STATIC_LIBRARY = auto() + SHARED_LIBRARY = auto() + +@dataclass +class Target: + """目标文件信息""" + name: str + type: TargetType + source: Path + object: Path + output: Path + +class CBuildContext: + """构建上下文,管理所有包的信息""" + def __init__(self, package: PackageConfig, build_path: BuildPath | None = None): + self.package = package + self.path = BuildPath.from_root_path(package.path) \ + if build_path is None else build_path + + @property + def sources_path(self) -> list[Path]: + """获取所有包的源文件路径""" + return list(self.path.src_path.glob("**/*.c")) + + @property + def tests_path(self) -> list[Path]: + """获取所有测试源文件路径 eg. `tests/test_*.c`""" + test_sources = [] + test_path = self.path.root_path / "tests" + if test_path.exists(): + for file in test_path.glob("test_*.c"): + test_sources.append(file) + return test_sources + + @property + def includes(self) -> list[Path]: + """获取包的包含路径""" + includes = [self.path.root_path / "include"] + # check folders available + deps = self.get_dependencies() + for dep in deps: + includes.extend(dep.includes) + return [inc for inc in includes if inc.exists()] + + + def get_targets(self) -> list[Target]: + """获取所有构建目标""" + targets = [] + # TODO + ext = ".exe" + + # 添加主可执行文件目标 + if self.path.default_bin_path.exists(): + targets.append(Target( + name=self.package.name, + type=TargetType.MAIN_EXECUTABLE, + source=self.path.default_bin_path, + object=self.get_object_path(self.path.default_bin_path), + output=self.path.output_path / f"{self.package.name}{ext}", + )) + + # 添加静态库目标 + if self.path.default_lib_path.exists(): + targets.append(Target( + name=f"lib{self.package.name}", + type=TargetType.STATIC_LIBRARY, + source=self.path.default_lib_path, + object=self.get_object_path(self.path.default_lib_path), + output=self.path.output_path / f"lib{self.package.name}.a" + )) + + # 添加测试目标 + if self.path.tests_path.exists(): + for test_source in self.tests_path: + targets.append(Target( + name=test_source.stem, + type=TargetType.TEST_EXECUTABLE, + source=test_source, + object=self.get_object_path(test_source), + output=self.path.output_path / f"{test_source.stem}{ext}" + )) + + return targets + + def get_build_components(self) -> list[tuple[Path, Path]]: + """获取所有需要编译的源文件及目标文件(排除主文件即main.c, lib.c, bin/*.c)""" + objs = [] + for path in self.sources_path: + if path == self.path.default_bin_path: + continue + if path == self.path.default_lib_path: + continue + objs.append((path, self.get_object_path(path))) + # logging.debug("[+] build_components: %s", objs) + deps = self.get_dependencies() + for dep in deps: + objs.extend(dep.get_build_components()) + return objs + + def get_object_path(self, source_path: Path) -> Path: + """将源文件路径映射到对象文件路径 + + Args: + source_path (Path): 源文件路径(.c文件) + + Returns: + Path: 对应的对象文件路径(.o文件) + """ + # 确保输出目录存在 + objects_dir = self.path.object_path + objects_dir.mkdir(parents=True, exist_ok=True) + + # 生成相对于src目录的路径结构 + try: + relative_path = source_path.relative_to(self.path.src_path) + object_path = objects_dir / relative_path.with_suffix('.o') + # 确保对象文件的目录存在 + object_path.parent.mkdir(parents=True, exist_ok=True) + return object_path + except ValueError: + # 如果源文件不在src目录下,使用文件名作为对象文件名 + return objects_dir / source_path.with_suffix('.o').name + + def get_dependencies(self) -> list[Self]: + """_summary_ + + Returns: + list[CBuildContext]: _description_ + """ + deps = [] + for dep in self.package.dependencies: + ctx = CBuildContext(PackageConfig(Path(self.package.path / dep.path))) + deps.append(ctx) + deps.extend(ctx.get_dependencies()) + return deps + +@dataclass +class CacheEntry: + """缓存条目""" + source_hash: str + object_hash: str + compile_time: float + includes_hash: str + +class BuildCache: + """构建缓存管理器""" + pass + +class Compiler(ABC): + """编译器抽象类""" + @abstractmethod + def compile(self, sources: Path, output: Path, includes: list[Path], flags: list[str]): + """编译源文件""" + + @abstractmethod + def link(self, objects: list[Path], libraries: list[str], flags: list[str], output: Path): + """链接对象文件""" + + def compile_all(self, sources: list[tuple[Path, Path]], includes: list[Path], flags: list[str]): + """编译所有源文件""" + for source, output in sources: + self.compile(source, output, includes, flags) + + + def cmd(self, cmd: str | list[str]): + """执行命令并处理错误输出""" + try: + logging.debug("command: `%s`", ' '.join(cmd) if isinstance(cmd, list) else cmd) + subprocess.run(cmd, check=True) + except subprocess.CalledProcessError as e: + # 输出详细错误信息帮助调试 + cmd_str = ' '.join(e.cmd) if isinstance(e.cmd, list) else e.cmd + logging.error("command running error: [%d]`%s`", e.returncode, cmd_str) + raise + +class ClangCompiler(Compiler): + """Clang编译器""" + def compile(self, sources: Path, output: Path, includes: list[Path], flags: list[str]): + """编译源文件""" + cmd = ["clang"] + cmd.extend(flags) + cmd.extend(["-c", str(sources), "-o", str(output)]) + cmd.extend(f"-I{inc}" for inc in includes) + self.cmd(cmd) + + def link(self, objects: list[Path], libraries: list[str], flags: list[str], output: Path): + """链接对象文件""" + cmd = ["clang"] + cmd.extend(flags) + cmd.extend(["-o", str(output)]) + cmd.extend(str(obj) for obj in objects) + cmd.extend(lib for lib in libraries) + self.cmd(cmd) + +class GccCompiler(Compiler): + """Gcc编译器""" + def compile(self, sources: Path, output: Path, includes: list[Path], flags: list[str]): + """编译源文件""" + cmd = ["gcc"] + cmd.extend(flags) + cmd.extend(["-c", str(sources), "-o", str(output)]) + cmd.extend(f"-I{inc}" for inc in includes) + self.cmd(cmd) + + def link(self, objects: list[Path], libraries: list[str], flags: list[str], output: Path): + """链接对象文件""" + cmd = ["gcc"] + objs = set(str(i.absolute()) for i in objects) + cmd.extend(flags) + cmd.extend(["-o", str(output)]) + cmd.extend(objs) + cmd.extend(lib for lib in libraries) + self.cmd(cmd) + +class CPackageBuilder: + """包构建器""" + # TODO + EXT = ".exe" + + def __init__(self, package_path: Path, compiler: Compiler): + self.package = PackageConfig(package_path) + self.context = CBuildContext(self.package, None) + self.compiler: Compiler = compiler + self.global_flags = ["-g"] + + def _build_ctx(self) -> list[Path]: + """构建上下文""" + # 确保输出目录存在 + self.context.path.output_path.mkdir(parents=True, exist_ok=True) + self.context.path.object_path.mkdir(parents=True, exist_ok=True) + # TODO use cache and add dependency include and flags + deps = self.context.get_dependencies() + path_map = self.context.get_build_components() + self.compiler.compile_all(path_map, self.context.includes, self.global_flags) + return [pair[1] for pair in path_map] + + def build(self): + """构建包""" + object_files = self._build_ctx() + targets = self.context.get_targets() + for target in targets: + match target.type: + case TargetType.MAIN_EXECUTABLE: + self.compiler.compile(target.source, target.object, self.context.includes, + self.global_flags) + object_files.append(target.object) + self.compiler.link(object_files, [], self.global_flags, target.output) + object_files.remove(target.object) + case TargetType.TEST_EXECUTABLE: + self.compiler.compile(target.source, target.object, self.context.includes, + self.global_flags) + object_files.append(target.object) + self.compiler.link(object_files, [], self.global_flags, target.output) + object_files.remove(target.object) + logging.info("Building is Ok...") + + def run(self): + """运行项目""" + targets = [target for target in self.context.get_targets() \ + if target.type == TargetType.MAIN_EXECUTABLE] + if len(targets) != 1: + logging.error("not have target to run") + subprocess.run(targets[0].output, check=False) + + def tests(self): + """运行测试""" + targets = [target for target in self.context.get_targets() \ + if target.type == TargetType.TEST_EXECUTABLE] + passed = 0 + failed = 0 + for target in targets: + name = target.name + print(f"运行测试: {name}") + logging.debug("test run %s", target.output) + try: + result = subprocess.run(target.output, + check=True, + # capture_output=True, + # text=True, + timeout=30) + if result.returncode == 0: + print(f" ✓ 测试 {name} 通过") + passed += 1 + else: + print(f" ✗ 测试 {name} 失败") + if result.stdout: + print(f" 输出: {result.stdout}") + if result.stderr: + print(f" 错误: {result.stderr}") + failed += 1 + except subprocess.TimeoutExpired: + print(f" ✗ 测试 {name} 超时") + failed += 1 + except subprocess.SubprocessError as e: + print(f" ✗ 测试 {name} 运行异常: {e}") + failed += 1 + print(f"\n测试结果: {passed} 通过, {failed} 失败") + +def main(): + """main""" + parser = argparse.ArgumentParser(description="Simple C Package Manager") + parser.add_argument("command", choices=["build", "run", "test"], + help="Command to execute") + parser.add_argument("--verbose", "-v", action="store_true", + help="enable the logging to debug (defalut: false)") + parser.add_argument("--path", "-p", default=".", + help="Path to the package (default: current directory)") + + args = parser.parse_args() + + package_path = Path(args.path) + if args.verbose: + logging.getLogger().setLevel(logging.NOTSET) + compiler = GccCompiler() + + try: + if args.command == "build": + builder = CPackageBuilder(package_path, compiler) + builder.build() + print("build is Ok...") + elif args.command == "run": + builder = CPackageBuilder(package_path, compiler) + bin_path = builder.context.path.default_bin_path + if not bin_path.exists(): + print(f"{bin_path} not exist") + return + builder.build() + builder.run() + elif args.command == "test": + builder = CPackageBuilder(package_path, compiler) + builder.build() + builder.tests() + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + # builder = CPackageBuilder(Path("./runtime/libcore/"), ClangCompiler()) + # builder.build() + main() diff --git a/justfile b/justfile new file mode 100644 index 0000000..7723bb7 --- /dev/null +++ b/justfile @@ -0,0 +1,11 @@ +list: + just --list + +build-lexer: + python build.py build -p libs/lexer + +build-docs: + doxygen Doxyfile + +docs: build-docs + python -m http.server -d docs/html diff --git a/lib/Makefile b/lib/Makefile deleted file mode 100644 index 1b6cc55..0000000 --- a/lib/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# 编译器设置 -CC = gcc -AR = ar -CFLAGS = -g -Wall -I.. - -# 源文件路径 -RT_DIR = ./rt -LOG_DIR = ./rt/log - -# basic rt lib -SRCS = \ - $(RT_DIR)/std/rt_std.c \ - ./core.c \ - $(RT_DIR)/rt.c \ - $(RT_DIR)/rt_alloc.c \ - $(RT_DIR)/rt_string.c \ - $(LOG_DIR)/log.c - -# utils lib -UTILS_DIR = ./utils -DS_DIR = $(UTILS_DIR)/ds -STRPOOL_DIR = $(UTILS_DIR)/strpool -SYMTAB_DIR = $(UTILS_DIR)/symtab -TOKBUF_DIR = $(UTILS_DIR)/tokbuf -SRCS += \ - $(DS_DIR)/hashtable.c \ - $(STRPOOL_DIR)/strpool.c \ - # $(SYMTAB_DIR)/symtab.c \ - # $(TOKBUF_DIR)/tokbuf.c - -# 生成目标文件列表 -OBJS = $(SRCS:.c=.o) - -# 最终目标 -TARGET = libcore.a - -all: $(TARGET) - -$(TARGET): $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f $(OBJS) $(TARGET) - -.PHONY: all clean diff --git a/lib/acutest.h b/lib/acutest.h deleted file mode 100644 index 5f9cb19..0000000 --- a/lib/acutest.h +++ /dev/null @@ -1,1994 +0,0 @@ -/* - * Acutest -- Another C/C++ Unit Test facility - * - * - * Copyright 2013-2023 Martin Mitáš - * Copyright 2019 Garrett D'Amore - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef ACUTEST_H -#define ACUTEST_H - - -/* Try to auto-detect whether we need to disable C++ exception handling. - * If the detection fails, you may always define TEST_NO_EXCEPTIONS before - * including "acutest.h" manually. */ -#ifdef __cplusplus - #if (__cplusplus >= 199711L && !defined __cpp_exceptions) || \ - ((defined(__GNUC__) || defined(__clang__)) && !defined __EXCEPTIONS) - #ifndef TEST_NO_EXCEPTIONS - #define TEST_NO_EXCEPTIONS - #endif - #endif -#endif - - -/************************ - *** Public interface *** - ************************/ - -/* By default, "acutest.h" provides the main program entry point (function - * main()). However, if the test suite is composed of multiple source files - * which include "acutest.h", then this causes a problem of multiple main() - * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all - * compilation units but one. - */ - -/* Macro to specify list of unit tests in the suite. - * The unit test implementation MUST provide list of unit tests it implements - * with this macro: - * - * TEST_LIST = { - * { "test1_name", test1_func_ptr }, - * { "test2_name", test2_func_ptr }, - * ... - * { NULL, NULL } // zeroed record marking the end of the list - * }; - * - * The list specifies names of each test (must be unique) and pointer to - * a function implementing it. The function does not take any arguments - * and has no return values, i.e. every test function has to be compatible - * with this prototype: - * - * void test_func(void); - * - * Note the list has to be ended with a zeroed record. - */ -#define TEST_LIST const struct acutest_test_ acutest_list_[] - - -/* Macros for testing whether an unit test succeeds or fails. These macros - * can be used arbitrarily in functions implementing the unit tests. - * - * If any condition fails throughout execution of a test, the test fails. - * - * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows - * also to specify an error message to print out if the condition fails. - * (It expects printf-like format string and its parameters). The macros - * return non-zero (condition passes) or 0 (condition fails). - * - * That can be useful when more conditions should be checked only if some - * preceding condition passes, as illustrated in this code snippet: - * - * SomeStruct* ptr = allocate_some_struct(); - * if(TEST_CHECK(ptr != NULL)) { - * TEST_CHECK(ptr->member1 < 100); - * TEST_CHECK(ptr->member2 > 200); - * } - */ -#define TEST_CHECK_(cond,...) \ - acutest_check_(!!(cond), __FILE__, __LINE__, __VA_ARGS__) -#define TEST_CHECK(cond) \ - acutest_check_(!!(cond), __FILE__, __LINE__, "%s", #cond) - - -/* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the - * condition fails, the currently executed unit test is immediately aborted. - * - * That is done either by calling abort() if the unit test is executed as a - * child process; or via longjmp() if the unit test is executed within the - * main Acutest process. - * - * As a side effect of such abortion, your unit tests may cause memory leaks, - * unflushed file descriptors, and other phenomena caused by the abortion. - * - * Therefore you should not use these as a general replacement for TEST_CHECK. - * Use it with some caution, especially if your test causes some other side - * effects to the outside world (e.g. communicating with some server, inserting - * into a database etc.). - */ -#define TEST_ASSERT_(cond,...) \ - do { \ - if(!acutest_check_(!!(cond), __FILE__, __LINE__, __VA_ARGS__)) \ - acutest_abort_(); \ - } while(0) -#define TEST_ASSERT(cond) \ - do { \ - if(!acutest_check_(!!(cond), __FILE__, __LINE__, "%s", #cond)) \ - acutest_abort_(); \ - } while(0) - - -#ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS -/* Macros to verify that the code (the 1st argument) throws exception of given - * type (the 2nd argument). (Note these macros are only available in C++.) - * - * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like - * message. - * - * For example: - * - * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType); - * - * If the function_that_throw() throws ExpectedExceptionType, the check passes. - * If the function throws anything incompatible with ExpectedExceptionType - * (or if it does not thrown an exception at all), the check fails. - */ -#define TEST_EXCEPTION(code, exctype) \ - do { \ - bool exc_ok_ = false; \ - const char *msg_ = NULL; \ - try { \ - code; \ - msg_ = "No exception thrown."; \ - } catch(exctype const&) { \ - exc_ok_= true; \ - } catch(...) { \ - msg_ = "Unexpected exception thrown."; \ - } \ - acutest_check_(exc_ok_, __FILE__, __LINE__, #code " throws " #exctype);\ - if(msg_ != NULL) \ - acutest_message_("%s", msg_); \ - } while(0) -#define TEST_EXCEPTION_(code, exctype, ...) \ - do { \ - bool exc_ok_ = false; \ - const char *msg_ = NULL; \ - try { \ - code; \ - msg_ = "No exception thrown."; \ - } catch(exctype const&) { \ - exc_ok_= true; \ - } catch(...) { \ - msg_ = "Unexpected exception thrown."; \ - } \ - acutest_check_(exc_ok_, __FILE__, __LINE__, __VA_ARGS__); \ - if(msg_ != NULL) \ - acutest_message_("%s", msg_); \ - } while(0) -#endif /* #ifndef TEST_NO_EXCEPTIONS */ -#endif /* #ifdef __cplusplus */ - - -/* Sometimes it is useful to split execution of more complex unit tests to some - * smaller parts and associate those parts with some names. - * - * This is especially handy if the given unit test is implemented as a loop - * over some vector of multiple testing inputs. Using these macros allow to use - * sort of subtitle for each iteration of the loop (e.g. outputting the input - * itself or a name associated to it), so that if any TEST_CHECK condition - * fails in the loop, it can be easily seen which iteration triggers the - * failure, without the need to manually output the iteration-specific data in - * every single TEST_CHECK inside the loop body. - * - * TEST_CASE allows to specify only single string as the name of the case, - * TEST_CASE_ provides all the power of printf-like string formatting. - * - * Note that the test cases cannot be nested. Starting a new test case ends - * implicitly the previous one. To end the test case explicitly (e.g. to end - * the last test case after exiting the loop), you may use TEST_CASE(NULL). - */ -#define TEST_CASE_(...) acutest_case_(__VA_ARGS__) -#define TEST_CASE(name) acutest_case_("%s", name) - - -/* Maximal output per TEST_CASE call. Longer messages are cut. - * You may define another limit prior including "acutest.h" - */ -#ifndef TEST_CASE_MAXSIZE - #define TEST_CASE_MAXSIZE 64 -#endif - - -/* printf-like macro for outputting an extra information about a failure. - * - * Intended use is to output some computed output versus the expected value, - * e.g. like this: - * - * if(!TEST_CHECK(produced == expected)) { - * TEST_MSG("Expected: %d", expected); - * TEST_MSG("Produced: %d", produced); - * } - * - * Note the message is only written down if the most recent use of any checking - * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed. - * This means the above is equivalent to just this: - * - * TEST_CHECK(produced == expected); - * TEST_MSG("Expected: %d", expected); - * TEST_MSG("Produced: %d", produced); - * - * The macro can deal with multi-line output fairly well. It also automatically - * adds a final new-line if there is none present. - */ -#define TEST_MSG(...) acutest_message_(__VA_ARGS__) - - -/* Maximal output per TEST_MSG call. Longer messages are cut. - * You may define another limit prior including "acutest.h" - */ -#ifndef TEST_MSG_MAXSIZE - #define TEST_MSG_MAXSIZE 1024 -#endif - - -/* Macro for dumping a block of memory. - * - * Its intended use is very similar to what TEST_MSG is for, but instead of - * generating any printf-like message, this is for dumping raw block of a - * memory in a hexadecimal form: - * - * TEST_CHECK(size_produced == size_expected && - * memcmp(addr_produced, addr_expected, size_produced) == 0); - * TEST_DUMP("Expected:", addr_expected, size_expected); - * TEST_DUMP("Produced:", addr_produced, size_produced); - */ -#define TEST_DUMP(title, addr, size) acutest_dump_(title, addr, size) - -/* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut. - * You may define another limit prior including "acutest.h" - */ -#ifndef TEST_DUMP_MAXSIZE - #define TEST_DUMP_MAXSIZE 1024 -#endif - - -/* Macros for marking the test as SKIPPED. - * Note it can only be used at the beginning of a test, before any other - * checking. - * - * Once used, the best practice is to return from the test routine as soon - * as possible. - */ -#define TEST_SKIP(...) acutest_skip_(__FILE__, __LINE__, __VA_ARGS__) - - -/* Common test initialisation/clean-up - * - * In some test suites, it may be needed to perform some sort of the same - * initialization and/or clean-up in all the tests. - * - * Such test suites may use macros TEST_INIT and/or TEST_FINI prior including - * this header. The expansion of the macro is then used as a body of helper - * function called just before executing every single (TEST_INIT) or just after - * it ends (TEST_FINI). - * - * Examples of various ways how to use the macro TEST_INIT: - * - * #define TEST_INIT my_init_func(); - * #define TEST_INIT my_init_func() // Works even without the semicolon - * #define TEST_INIT setlocale(LC_ALL, NULL); - * #define TEST_INIT { setlocale(LC_ALL, NULL); my_init_func(); } - * - * TEST_FINI is to be used in the same way. - */ - - -/********************** - *** Implementation *** - **********************/ - -/* The unit test files should not rely on anything below. */ - -#include - -/* Enable the use of the non-standard keyword __attribute__ to silence warnings under some compilers */ -#if defined(__GNUC__) || defined(__clang__) - #define ACUTEST_ATTRIBUTE_(attr) __attribute__((attr)) -#else - #define ACUTEST_ATTRIBUTE_(attr) -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -enum acutest_state_ { - ACUTEST_STATE_INITIAL = -4, - ACUTEST_STATE_SELECTED = -3, - ACUTEST_STATE_NEEDTORUN = -2, - - /* By the end all tests should be in one of the following: */ - ACUTEST_STATE_EXCLUDED = -1, - ACUTEST_STATE_SUCCESS = 0, - ACUTEST_STATE_FAILED = 1, - ACUTEST_STATE_SKIPPED = 2 -}; - -int acutest_check_(int cond, const char* file, int line, const char* fmt, ...); -void acutest_case_(const char* fmt, ...); -void acutest_message_(const char* fmt, ...); -void acutest_dump_(const char* title, const void* addr, size_t size); -void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn); -#ifdef __cplusplus - } /* extern "C" */ -#endif - -#ifndef TEST_NO_MAIN - -#include -#include -#include -#include -#include - -#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) - #define ACUTEST_UNIX_ 1 - #include - #include - #include - #include - #include - #include - #include - - #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC - #define ACUTEST_HAS_POSIX_TIMER_ 1 - #endif -#endif - -#if defined(_gnu_linux_) || defined(__linux__) - #define ACUTEST_LINUX_ 1 - #include - #include -#endif - -#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) - #define ACUTEST_WIN_ 1 - #include - #include -#endif - -#if defined(__APPLE__) - #define ACUTEST_MACOS_ - #include - #include - #include - #include - #include -#endif - -#ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS - #include -#endif -#endif - -#ifdef __has_include - #if __has_include() - #include - #endif -#endif - -/* Note our global private identifiers end with '_' to mitigate risk of clash - * with the unit tests implementation. */ - -#ifdef __cplusplus - extern "C" { -#endif - -#ifdef _MSC_VER - /* In the multi-platform code like ours, we cannot use the non-standard - * "safe" functions from Microsoft C lib like e.g. sprintf_s() instead of - * standard sprintf(). Hence, lets disable the warning C4996. */ - #pragma warning(push) - #pragma warning(disable: 4996) -#endif - - -struct acutest_test_ { - const char* name; - void (*func)(void); -}; - -struct acutest_test_data_ { - enum acutest_state_ state; - double duration; -}; - - -extern const struct acutest_test_ acutest_list_[]; - - -static char* acutest_argv0_ = NULL; -static int acutest_list_size_ = 0; -static struct acutest_test_data_* acutest_test_data_ = NULL; -static int acutest_no_exec_ = -1; -static int acutest_no_summary_ = 0; -static int acutest_tap_ = 0; -static int acutest_exclude_mode_ = 0; -static int acutest_worker_ = 0; -static int acutest_worker_index_ = 0; -static int acutest_cond_failed_ = 0; -static FILE *acutest_xml_output_ = NULL; - -static const struct acutest_test_* acutest_current_test_ = NULL; -static int acutest_current_index_ = 0; -static char acutest_case_name_[TEST_CASE_MAXSIZE] = ""; -static int acutest_test_check_count_ = 0; -static int acutest_test_skip_count_ = 0; -static char acutest_test_skip_reason_[256] = ""; -static int acutest_test_already_logged_ = 0; -static int acutest_case_already_logged_ = 0; -static int acutest_verbose_level_ = 2; -static int acutest_test_failures_ = 0; -static int acutest_colorize_ = 0; -static int acutest_timer_ = 0; - -static int acutest_abort_has_jmp_buf_ = 0; -static jmp_buf acutest_abort_jmp_buf_; - -static int -acutest_count_(enum acutest_state_ state) -{ - int i, n; - - for(i = 0, n = 0; i < acutest_list_size_; i++) { - if(acutest_test_data_[i].state == state) - n++; - } - - return n; -} - -static void -acutest_cleanup_(void) -{ - free((void*) acutest_test_data_); -} - -static void ACUTEST_ATTRIBUTE_(noreturn) -acutest_exit_(int exit_code) -{ - acutest_cleanup_(); - exit(exit_code); -} - - -#if defined ACUTEST_WIN_ - typedef LARGE_INTEGER acutest_timer_type_; - static LARGE_INTEGER acutest_timer_freq_; - static acutest_timer_type_ acutest_timer_start_; - static acutest_timer_type_ acutest_timer_end_; - - static void - acutest_timer_init_(void) - { - QueryPerformanceFrequency(´st_timer_freq_); - } - - static void - acutest_timer_get_time_(LARGE_INTEGER* ts) - { - QueryPerformanceCounter(ts); - } - - static double - acutest_timer_diff_(LARGE_INTEGER start, LARGE_INTEGER end) - { - double duration = (double)(end.QuadPart - start.QuadPart); - duration /= (double)acutest_timer_freq_.QuadPart; - return duration; - } - - static void - acutest_timer_print_diff_(void) - { - printf("%.6lf secs", acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); - } -#elif defined ACUTEST_HAS_POSIX_TIMER_ - static clockid_t acutest_timer_id_; - typedef struct timespec acutest_timer_type_; - static acutest_timer_type_ acutest_timer_start_; - static acutest_timer_type_ acutest_timer_end_; - - static void - acutest_timer_init_(void) - { - if(acutest_timer_ == 1) - acutest_timer_id_ = CLOCK_MONOTONIC; - else if(acutest_timer_ == 2) - acutest_timer_id_ = CLOCK_PROCESS_CPUTIME_ID; - } - - static void - acutest_timer_get_time_(struct timespec* ts) - { - clock_gettime(acutest_timer_id_, ts); - } - - static double - acutest_timer_diff_(struct timespec start, struct timespec end) - { - return (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1e9; - } - - static void - acutest_timer_print_diff_(void) - { - printf("%.6lf secs", - acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); - } -#else - typedef int acutest_timer_type_; - static acutest_timer_type_ acutest_timer_start_; - static acutest_timer_type_ acutest_timer_end_; - - void - acutest_timer_init_(void) - {} - - static void - acutest_timer_get_time_(int* ts) - { - (void) ts; - } - - static double - acutest_timer_diff_(int start, int end) - { - (void) start; - (void) end; - return 0.0; - } - - static void - acutest_timer_print_diff_(void) - {} -#endif - -#define ACUTEST_COLOR_DEFAULT_ 0 -#define ACUTEST_COLOR_RED_ 1 -#define ACUTEST_COLOR_GREEN_ 2 -#define ACUTEST_COLOR_YELLOW_ 3 -#define ACUTEST_COLOR_DEFAULT_INTENSIVE_ 10 -#define ACUTEST_COLOR_RED_INTENSIVE_ 11 -#define ACUTEST_COLOR_GREEN_INTENSIVE_ 12 -#define ACUTEST_COLOR_YELLOW_INTENSIVE_ 13 - -static int ACUTEST_ATTRIBUTE_(format (printf, 2, 3)) -acutest_colored_printf_(int color, const char* fmt, ...) -{ - va_list args; - char buffer[256]; - int n; - - va_start(args, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, args); - va_end(args); - buffer[sizeof(buffer)-1] = '\0'; - - if(!acutest_colorize_) { - return printf("%s", buffer); - } - -#if defined ACUTEST_UNIX_ - { - const char* col_str; - switch(color) { - case ACUTEST_COLOR_RED_: col_str = "\033[0;31m"; break; - case ACUTEST_COLOR_GREEN_: col_str = "\033[0;32m"; break; - case ACUTEST_COLOR_YELLOW_: col_str = "\033[0;33m"; break; - case ACUTEST_COLOR_RED_INTENSIVE_: col_str = "\033[1;31m"; break; - case ACUTEST_COLOR_GREEN_INTENSIVE_: col_str = "\033[1;32m"; break; - case ACUTEST_COLOR_YELLOW_INTENSIVE_: col_str = "\033[1;33m"; break; - case ACUTEST_COLOR_DEFAULT_INTENSIVE_: col_str = "\033[1m"; break; - default: col_str = "\033[0m"; break; - } - printf("%s", col_str); - n = printf("%s", buffer); - printf("\033[0m"); - return n; - } -#elif defined ACUTEST_WIN_ - { - HANDLE h; - CONSOLE_SCREEN_BUFFER_INFO info; - WORD attr; - - h = GetStdHandle(STD_OUTPUT_HANDLE); - GetConsoleScreenBufferInfo(h, &info); - - switch(color) { - case ACUTEST_COLOR_RED_: attr = FOREGROUND_RED; break; - case ACUTEST_COLOR_GREEN_: attr = FOREGROUND_GREEN; break; - case ACUTEST_COLOR_YELLOW_: attr = FOREGROUND_RED | FOREGROUND_GREEN; break; - case ACUTEST_COLOR_RED_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; - case ACUTEST_COLOR_GREEN_INTENSIVE_: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; - case ACUTEST_COLOR_DEFAULT_INTENSIVE_: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; - case ACUTEST_COLOR_YELLOW_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; - default: attr = 0; break; - } - if(attr != 0) - SetConsoleTextAttribute(h, attr); - n = printf("%s", buffer); - SetConsoleTextAttribute(h, info.wAttributes); - return n; - } -#else - n = printf("%s", buffer); - return n; -#endif -} - -static const char* -acutest_basename_(const char* path) -{ - const char* name; - - name = strrchr(path, '/'); - if(name != NULL) - name++; - else - name = path; - -#ifdef ACUTEST_WIN_ - { - const char* alt_name; - - alt_name = strrchr(path, '\\'); - if(alt_name != NULL) - alt_name++; - else - alt_name = path; - - if(alt_name > name) - name = alt_name; - } -#endif - - return name; -} - -static void -acutest_begin_test_line_(const struct acutest_test_* test) -{ - if(!acutest_tap_) { - if(acutest_verbose_level_ >= 3) { - acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s:\n", test->name); - acutest_test_already_logged_++; - } else if(acutest_verbose_level_ >= 1) { - int n; - char spaces[48]; - - n = acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s... ", test->name); - memset(spaces, ' ', sizeof(spaces)); - if(n < (int) sizeof(spaces)) - printf("%.*s", (int) sizeof(spaces) - n, spaces); - } else { - acutest_test_already_logged_ = 1; - } - } -} - -static void -acutest_finish_test_line_(enum acutest_state_ state) -{ - if(acutest_tap_) { - printf("%s %d - %s%s\n", - (state == ACUTEST_STATE_SUCCESS || state == ACUTEST_STATE_SKIPPED) ? "ok" : "not ok", - acutest_current_index_ + 1, - acutest_current_test_->name, - (state == ACUTEST_STATE_SKIPPED) ? " # SKIP" : ""); - - if(state == ACUTEST_STATE_SUCCESS && acutest_timer_) { - printf("# Duration: "); - acutest_timer_print_diff_(); - printf("\n"); - } - } else { - int color; - const char* str; - - switch(state) { - case ACUTEST_STATE_SUCCESS: color = ACUTEST_COLOR_GREEN_INTENSIVE_; str = "OK"; break; - case ACUTEST_STATE_SKIPPED: color = ACUTEST_COLOR_YELLOW_INTENSIVE_; str = "SKIPPED"; break; - case ACUTEST_STATE_FAILED: /* Fall through. */ - default: color = ACUTEST_COLOR_RED_INTENSIVE_; str = "FAILED"; break; - } - - printf("[ "); - acutest_colored_printf_(color, "%s", str); - printf(" ]"); - - if(state == ACUTEST_STATE_SUCCESS && acutest_timer_) { - printf(" "); - acutest_timer_print_diff_(); - } - - printf("\n"); - } -} - -static void -acutest_line_indent_(int level) -{ - static const char spaces[] = " "; - int n = level * 2; - - if(acutest_tap_ && n > 0) { - n--; - printf("#"); - } - - while(n > 16) { - printf("%s", spaces); - n -= 16; - } - printf("%.*s", n, spaces); -} - -void ACUTEST_ATTRIBUTE_(format (printf, 3, 4)) -acutest_skip_(const char* file, int line, const char* fmt, ...) -{ - va_list args; - size_t reason_len; - - va_start(args, fmt); - vsnprintf(acutest_test_skip_reason_, sizeof(acutest_test_skip_reason_), fmt, args); - va_end(args); - acutest_test_skip_reason_[sizeof(acutest_test_skip_reason_)-1] = '\0'; - - /* Remove final dot, if provided; that collides with our other logic. */ - reason_len = strlen(acutest_test_skip_reason_); - if(acutest_test_skip_reason_[reason_len-1] == '.') - acutest_test_skip_reason_[reason_len-1] = '\0'; - - if(acutest_test_check_count_ > 0) { - acutest_check_(0, file, line, "Cannot skip, already performed some checks"); - return; - } - - if(acutest_verbose_level_ >= 2) { - const char *result_str = "skipped"; - int result_color = ACUTEST_COLOR_YELLOW_; - - if(!acutest_test_already_logged_ && acutest_current_test_ != NULL) - acutest_finish_test_line_(ACUTEST_STATE_SKIPPED); - acutest_test_already_logged_++; - - acutest_line_indent_(1); - - if(file != NULL) { - file = acutest_basename_(file); - printf("%s:%d: ", file, line); - } - - printf("%s... ", acutest_test_skip_reason_); - acutest_colored_printf_(result_color, "%s", result_str); - printf("\n"); - acutest_test_already_logged_++; - } - - acutest_test_skip_count_++; -} - -int ACUTEST_ATTRIBUTE_(format (printf, 4, 5)) -acutest_check_(int cond, const char* file, int line, const char* fmt, ...) -{ - const char *result_str; - int result_color; - int verbose_level; - - if(acutest_test_skip_count_) { - /* We've skipped the test. We shouldn't be here: The test implementation - * should have already return before. So lets suppress the following - * output. */ - cond = 1; - goto skip_check; - } - - if(cond) { - result_str = "ok"; - result_color = ACUTEST_COLOR_GREEN_; - verbose_level = 3; - } else { - if(!acutest_test_already_logged_ && acutest_current_test_ != NULL) - acutest_finish_test_line_(ACUTEST_STATE_FAILED); - - acutest_test_failures_++; - acutest_test_already_logged_++; - - result_str = "failed"; - result_color = ACUTEST_COLOR_RED_; - verbose_level = 2; - } - - if(acutest_verbose_level_ >= verbose_level) { - va_list args; - - if(!acutest_case_already_logged_ && acutest_case_name_[0]) { - acutest_line_indent_(1); - acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_); - acutest_test_already_logged_++; - acutest_case_already_logged_++; - } - - acutest_line_indent_(acutest_case_name_[0] ? 2 : 1); - if(file != NULL) { - file = acutest_basename_(file); - printf("%s:%d: ", file, line); - } - - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - - printf("... "); - acutest_colored_printf_(result_color, "%s", result_str); - printf("\n"); - acutest_test_already_logged_++; - } - - acutest_test_check_count_++; - -skip_check: - acutest_cond_failed_ = (cond == 0); - return !acutest_cond_failed_; -} - -void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) -acutest_case_(const char* fmt, ...) -{ - va_list args; - - if(acutest_verbose_level_ < 2) - return; - - if(acutest_case_name_[0]) { - acutest_case_already_logged_ = 0; - acutest_case_name_[0] = '\0'; - } - - if(fmt == NULL) - return; - - va_start(args, fmt); - vsnprintf(acutest_case_name_, sizeof(acutest_case_name_) - 1, fmt, args); - va_end(args); - acutest_case_name_[sizeof(acutest_case_name_) - 1] = '\0'; - - if(acutest_verbose_level_ >= 3) { - acutest_line_indent_(1); - acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_); - acutest_test_already_logged_++; - acutest_case_already_logged_++; - } -} - -void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) -acutest_message_(const char* fmt, ...) -{ - char buffer[TEST_MSG_MAXSIZE]; - char* line_beg; - char* line_end; - va_list args; - - if(acutest_verbose_level_ < 2) - return; - - /* We allow extra message only when something is already wrong in the - * current test. */ - if(acutest_current_test_ == NULL || !acutest_cond_failed_) - return; - - va_start(args, fmt); - vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); - va_end(args); - buffer[TEST_MSG_MAXSIZE-1] = '\0'; - - line_beg = buffer; - while(1) { - line_end = strchr(line_beg, '\n'); - if(line_end == NULL) - break; - acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); - printf("%.*s\n", (int)(line_end - line_beg), line_beg); - line_beg = line_end + 1; - } - if(line_beg[0] != '\0') { - acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); - printf("%s\n", line_beg); - } -} - -void -acutest_dump_(const char* title, const void* addr, size_t size) -{ - static const size_t BYTES_PER_LINE = 16; - size_t line_beg; - size_t truncate = 0; - - if(acutest_verbose_level_ < 2) - return; - - /* We allow extra message only when something is already wrong in the - * current test. */ - if(acutest_current_test_ == NULL || !acutest_cond_failed_) - return; - - if(size > TEST_DUMP_MAXSIZE) { - truncate = size - TEST_DUMP_MAXSIZE; - size = TEST_DUMP_MAXSIZE; - } - - acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); - printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title); - - for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) { - size_t line_end = line_beg + BYTES_PER_LINE; - size_t off; - - acutest_line_indent_(acutest_case_name_[0] ? 4 : 3); - printf("%08lx: ", (unsigned long)line_beg); - for(off = line_beg; off < line_end; off++) { - if(off < size) - printf(" %02x", ((const unsigned char*)addr)[off]); - else - printf(" "); - } - - printf(" "); - for(off = line_beg; off < line_end; off++) { - unsigned char byte = ((const unsigned char*)addr)[off]; - if(off < size) - printf("%c", (iscntrl(byte) ? '.' : byte)); - else - break; - } - - printf("\n"); - } - - if(truncate > 0) { - acutest_line_indent_(acutest_case_name_[0] ? 4 : 3); - printf(" ... (and more %u bytes)\n", (unsigned) truncate); - } -} - -/* This is called just before each test */ -static void -acutest_init_(const char *test_name) -{ -#ifdef TEST_INIT - TEST_INIT - ; /* Allow for a single unterminated function call */ -#endif - - /* Suppress any warnings about unused variable. */ - (void) test_name; -} - -/* This is called after each test */ -static void -acutest_fini_(const char *test_name) -{ -#ifdef TEST_FINI - TEST_FINI - ; /* Allow for a single unterminated function call */ -#endif - - /* Suppress any warnings about unused variable. */ - (void) test_name; -} - -void -acutest_abort_(void) -{ - if(acutest_abort_has_jmp_buf_) { - longjmp(acutest_abort_jmp_buf_, 1); - } else { - if(acutest_current_test_ != NULL) - acutest_fini_(acutest_current_test_->name); - fflush(stdout); - fflush(stderr); - acutest_exit_(ACUTEST_STATE_FAILED); - } -} - -static void -acutest_list_names_(void) -{ - const struct acutest_test_* test; - - printf("Unit tests:\n"); - for(test = ´st_list_[0]; test->func != NULL; test++) - printf(" %s\n", test->name); -} - -static int -acutest_name_contains_word_(const char* name, const char* pattern) -{ - static const char word_delim[] = " \t-_/.,:;"; - const char* substr; - size_t pattern_len; - - pattern_len = strlen(pattern); - - substr = strstr(name, pattern); - while(substr != NULL) { - int starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); - int ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); - - if(starts_on_word_boundary && ends_on_word_boundary) - return 1; - - substr = strstr(substr+1, pattern); - } - - return 0; -} - -static int -acutest_select_(const char* pattern) -{ - int i; - int n = 0; - - /* Try exact match. */ - for(i = 0; i < acutest_list_size_; i++) { - if(strcmp(acutest_list_[i].name, pattern) == 0) { - acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; - n++; - break; - } - } - if(n > 0) - return n; - - /* Try word match. */ - for(i = 0; i < acutest_list_size_; i++) { - if(acutest_name_contains_word_(acutest_list_[i].name, pattern)) { - acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; - n++; - } - } - if(n > 0) - return n; - - /* Try relaxed match. */ - for(i = 0; i < acutest_list_size_; i++) { - if(strstr(acutest_list_[i].name, pattern) != NULL) { - acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; - n++; - } - } - - return n; -} - - -/* Called if anything goes bad in Acutest, or if the unit test ends in other - * way then by normal returning from its function (e.g. exception or some - * abnormal child process termination). */ -static void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) -acutest_error_(const char* fmt, ...) -{ - if(acutest_verbose_level_ == 0) - return; - - if(acutest_verbose_level_ >= 2) { - va_list args; - - acutest_line_indent_(1); - if(acutest_verbose_level_ >= 3) - acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "ERROR: "); - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - printf("\n"); - } - - if(acutest_verbose_level_ >= 3) { - printf("\n"); - } -} - -/* Call directly the given test unit function. */ -static enum acutest_state_ -acutest_do_run_(const struct acutest_test_* test, int index) -{ - enum acutest_state_ state = ACUTEST_STATE_FAILED; - - acutest_current_test_ = test; - acutest_current_index_ = index; - acutest_test_failures_ = 0; - acutest_test_already_logged_ = 0; - acutest_test_check_count_ = 0; - acutest_test_skip_count_ = 0; - acutest_cond_failed_ = 0; - -#ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS - try { -#endif -#endif - acutest_init_(test->name); - acutest_begin_test_line_(test); - - /* This is good to do in case the test unit crashes. */ - fflush(stdout); - fflush(stderr); - - if(!acutest_worker_) { - acutest_abort_has_jmp_buf_ = 1; - if(setjmp(acutest_abort_jmp_buf_) != 0) - goto aborted; - } - - acutest_timer_get_time_(´st_timer_start_); - test->func(); - -aborted: - acutest_abort_has_jmp_buf_ = 0; - acutest_timer_get_time_(´st_timer_end_); - - if(acutest_test_failures_ > 0) - state = ACUTEST_STATE_FAILED; - else if(acutest_test_skip_count_ > 0) - state = ACUTEST_STATE_SKIPPED; - else - state = ACUTEST_STATE_SUCCESS; - - if(!acutest_test_already_logged_) - acutest_finish_test_line_(state); - - if(acutest_verbose_level_ >= 3) { - acutest_line_indent_(1); - switch(state) { - case ACUTEST_STATE_SUCCESS: - acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS: "); - printf("All conditions have passed.\n"); - - if(acutest_timer_) { - acutest_line_indent_(1); - printf("Duration: "); - acutest_timer_print_diff_(); - printf("\n"); - } - break; - - case ACUTEST_STATE_SKIPPED: - acutest_colored_printf_(ACUTEST_COLOR_YELLOW_INTENSIVE_, "SKIPPED: "); - printf("%s.\n", acutest_test_skip_reason_); - break; - - default: - acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); - printf("%d condition%s %s failed.\n", - acutest_test_failures_, - (acutest_test_failures_ == 1) ? "" : "s", - (acutest_test_failures_ == 1) ? "has" : "have"); - break; - } - printf("\n"); - } - -#ifdef __cplusplus -#ifndef TEST_NO_EXCEPTIONS - } catch(std::exception& e) { - const char* what = e.what(); - acutest_check_(0, NULL, 0, "Threw std::exception"); - if(what != NULL) - acutest_message_("std::exception::what(): %s", what); - - if(acutest_verbose_level_ >= 3) { - acutest_line_indent_(1); - acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); - printf("C++ exception.\n\n"); - } - } catch(...) { - acutest_check_(0, NULL, 0, "Threw an exception"); - - if(acutest_verbose_level_ >= 3) { - acutest_line_indent_(1); - acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); - printf("C++ exception.\n\n"); - } - } -#endif -#endif - - acutest_fini_(test->name); - acutest_case_(NULL); - acutest_current_test_ = NULL; - - return state; -} - -/* Trigger the unit test. If possible (and not suppressed) it starts a child - * process who calls acutest_do_run_(), otherwise it calls acutest_do_run_() - * directly. */ -static void -acutest_run_(const struct acutest_test_* test, int index, int master_index) -{ - enum acutest_state_ state = ACUTEST_STATE_FAILED; - acutest_timer_type_ start, end; - - acutest_current_test_ = test; - acutest_test_already_logged_ = 0; - acutest_timer_get_time_(&start); - - if(!acutest_no_exec_) { - -#if defined(ACUTEST_UNIX_) - - pid_t pid; - int exit_code; - - /* Make sure the child starts with empty I/O buffers. */ - fflush(stdout); - fflush(stderr); - - pid = fork(); - if(pid == (pid_t)-1) { - acutest_error_("Cannot fork. %s [%d]", strerror(errno), errno); - } else if(pid == 0) { - /* Child: Do the test. */ - acutest_worker_ = 1; - state = acutest_do_run_(test, index); - acutest_exit_((int) state); - } else { - /* Parent: Wait until child terminates and analyze its exit code. */ - waitpid(pid, &exit_code, 0); - if(WIFEXITED(exit_code)) { - state = (enum acutest_state_) WEXITSTATUS(exit_code); - } else if(WIFSIGNALED(exit_code)) { - char tmp[32]; - const char* signame; - switch(WTERMSIG(exit_code)) { - case SIGINT: signame = "SIGINT"; break; - case SIGHUP: signame = "SIGHUP"; break; - case SIGQUIT: signame = "SIGQUIT"; break; - case SIGABRT: signame = "SIGABRT"; break; - case SIGKILL: signame = "SIGKILL"; break; - case SIGSEGV: signame = "SIGSEGV"; break; - case SIGILL: signame = "SIGILL"; break; - case SIGTERM: signame = "SIGTERM"; break; - default: snprintf(tmp, sizeof(tmp), "signal %d", WTERMSIG(exit_code)); signame = tmp; break; - } - acutest_error_("Test interrupted by %s.", signame); - } else { - acutest_error_("Test ended in an unexpected way [%d].", exit_code); - } - } - -#elif defined(ACUTEST_WIN_) - - char buffer[512] = {0}; - STARTUPINFOA startupInfo; - PROCESS_INFORMATION processInfo; - DWORD exitCode; - - /* Windows has no fork(). So we propagate all info into the child - * through a command line arguments. */ - snprintf(buffer, sizeof(buffer), - "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", - acutest_argv0_, index, acutest_timer_ ? "--time" : "", - acutest_tap_ ? "--tap" : "", acutest_verbose_level_, - acutest_colorize_ ? "always" : "never", - test->name); - memset(&startupInfo, 0, sizeof(startupInfo)); - startupInfo.cb = sizeof(STARTUPINFO); - if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { - WaitForSingleObject(processInfo.hProcess, INFINITE); - GetExitCodeProcess(processInfo.hProcess, &exitCode); - CloseHandle(processInfo.hThread); - CloseHandle(processInfo.hProcess); - switch(exitCode) { - case 0: state = ACUTEST_STATE_SUCCESS; break; - case 1: state = ACUTEST_STATE_FAILED; break; - case 2: state = ACUTEST_STATE_SKIPPED; break; - case 3: acutest_error_("Aborted."); break; - case 0xC0000005: acutest_error_("Access violation."); break; - default: acutest_error_("Test ended in an unexpected way [%lu].", exitCode); break; - } - } else { - acutest_error_("Cannot create unit test subprocess [%ld].", GetLastError()); - } - -#else - - /* A platform where we don't know how to run child process. */ - state = acutest_do_run_(test, index); - -#endif - - } else { - /* Child processes suppressed through --no-exec. */ - state = acutest_do_run_(test, index); - } - acutest_timer_get_time_(&end); - - acutest_current_test_ = NULL; - - acutest_test_data_[master_index].state = state; - acutest_test_data_[master_index].duration = acutest_timer_diff_(start, end); -} - -#if defined(ACUTEST_WIN_) -/* Callback for SEH events. */ -static LONG CALLBACK -acutest_seh_exception_filter_(EXCEPTION_POINTERS *ptrs) -{ - acutest_check_(0, NULL, 0, "Unhandled SEH exception"); - acutest_message_("Exception code: 0x%08lx", ptrs->ExceptionRecord->ExceptionCode); - acutest_message_("Exception address: 0x%p", ptrs->ExceptionRecord->ExceptionAddress); - - fflush(stdout); - fflush(stderr); - - return EXCEPTION_EXECUTE_HANDLER; -} -#endif - - -#define ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ 0x0001 -#define ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ 0x0002 - -#define ACUTEST_CMDLINE_OPTID_NONE_ 0 -#define ACUTEST_CMDLINE_OPTID_UNKNOWN_ (-0x7fffffff + 0) -#define ACUTEST_CMDLINE_OPTID_MISSINGARG_ (-0x7fffffff + 1) -#define ACUTEST_CMDLINE_OPTID_BOGUSARG_ (-0x7fffffff + 2) - -typedef struct acutest_test_CMDLINE_OPTION_ { - char shortname; - const char* longname; - int id; - unsigned flags; -} ACUTEST_CMDLINE_OPTION_; - -static int -acutest_cmdline_handle_short_opt_group_(const ACUTEST_CMDLINE_OPTION_* options, - const char* arggroup, - int (*callback)(int /*optval*/, const char* /*arg*/)) -{ - const ACUTEST_CMDLINE_OPTION_* opt; - int i; - int ret = 0; - - for(i = 0; arggroup[i] != '\0'; i++) { - for(opt = options; opt->id != 0; opt++) { - if(arggroup[i] == opt->shortname) - break; - } - - if(opt->id != 0 && !(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) { - ret = callback(opt->id, NULL); - } else { - /* Unknown option. */ - char badoptname[3]; - badoptname[0] = '-'; - badoptname[1] = arggroup[i]; - badoptname[2] = '\0'; - ret = callback((opt->id != 0 ? ACUTEST_CMDLINE_OPTID_MISSINGARG_ : ACUTEST_CMDLINE_OPTID_UNKNOWN_), - badoptname); - } - - if(ret != 0) - break; - } - - return ret; -} - -#define ACUTEST_CMDLINE_AUXBUF_SIZE_ 32 - -static int -acutest_cmdline_read_(const ACUTEST_CMDLINE_OPTION_* options, int argc, char** argv, - int (*callback)(int /*optval*/, const char* /*arg*/)) -{ - - const ACUTEST_CMDLINE_OPTION_* opt; - char auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_+1]; - int after_doubledash = 0; - int i = 1; - int ret = 0; - - auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_] = '\0'; - - while(i < argc) { - if(after_doubledash || strcmp(argv[i], "-") == 0) { - /* Non-option argument. */ - ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); - } else if(strcmp(argv[i], "--") == 0) { - /* End of options. All the remaining members are non-option arguments. */ - after_doubledash = 1; - } else if(argv[i][0] != '-') { - /* Non-option argument. */ - ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); - } else { - for(opt = options; opt->id != 0; opt++) { - if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { - size_t len = strlen(opt->longname); - if(strncmp(argv[i]+2, opt->longname, len) == 0) { - /* Regular long option. */ - if(argv[i][2+len] == '\0') { - /* with no argument provided. */ - if(!(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) - ret = callback(opt->id, NULL); - else - ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]); - break; - } else if(argv[i][2+len] == '=') { - /* with an argument provided. */ - if(opt->flags & (ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ | ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) { - ret = callback(opt->id, argv[i]+2+len+1); - } else { - snprintf(auxbuf, sizeof(auxbuf), "--%s", opt->longname); - ret = callback(ACUTEST_CMDLINE_OPTID_BOGUSARG_, auxbuf); - } - break; - } else { - continue; - } - } - } else if(opt->shortname != '\0' && argv[i][0] == '-') { - if(argv[i][1] == opt->shortname) { - /* Regular short option. */ - if(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_) { - if(argv[i][2] != '\0') - ret = callback(opt->id, argv[i]+2); - else if(i+1 < argc) - ret = callback(opt->id, argv[++i]); - else - ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]); - break; - } else { - ret = callback(opt->id, NULL); - - /* There might be more (argument-less) short options - * grouped together. */ - if(ret == 0 && argv[i][2] != '\0') - ret = acutest_cmdline_handle_short_opt_group_(options, argv[i]+2, callback); - break; - } - } - } - } - - if(opt->id == 0) { /* still not handled? */ - if(argv[i][0] != '-') { - /* Non-option argument. */ - ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); - } else { - /* Unknown option. */ - char* badoptname = argv[i]; - - if(strncmp(badoptname, "--", 2) == 0) { - /* Strip any argument from the long option. */ - char* assignment = strchr(badoptname, '='); - if(assignment != NULL) { - size_t len = (size_t)(assignment - badoptname); - if(len > ACUTEST_CMDLINE_AUXBUF_SIZE_) - len = ACUTEST_CMDLINE_AUXBUF_SIZE_; - strncpy(auxbuf, badoptname, len); - auxbuf[len] = '\0'; - badoptname = auxbuf; - } - } - - ret = callback(ACUTEST_CMDLINE_OPTID_UNKNOWN_, badoptname); - } - } - } - - if(ret != 0) - return ret; - i++; - } - - return ret; -} - -static void -acutest_help_(void) -{ - printf("Usage: %s [options] [test...]\n", acutest_argv0_); - printf("\n"); - printf("Run the specified unit tests; or if the option '--exclude' is used, run all\n"); - printf("tests in the suite but those listed. By default, if no tests are specified\n"); - printf("on the command line, all unit tests in the suite are run.\n"); - printf("\n"); - printf("Options:\n"); - printf(" -X, --exclude Execute all unit tests but the listed ones\n"); - printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); - printf(" (WHEN is one of 'auto', 'always', 'never')\n"); - printf(" -E, --no-exec Same as --exec=never\n"); -#if defined ACUTEST_WIN_ - printf(" -t, --time Measure test duration\n"); -#elif defined ACUTEST_HAS_POSIX_TIMER_ - printf(" -t, --time Measure test duration (real time)\n"); - printf(" --time=TIMER Measure test duration, using given timer\n"); - printf(" (TIMER is one of 'real', 'cpu')\n"); -#endif - printf(" --no-summary Suppress printing of test results summary\n"); - printf(" --tap Produce TAP-compliant output\n"); - printf(" (See https://testanything.org/)\n"); - printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n"); - printf(" -l, --list List unit tests in the suite and exit\n"); - printf(" -v, --verbose Make output more verbose\n"); - printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); - printf(" 0 ... Be silent\n"); - printf(" 1 ... Output one line per test (and summary)\n"); - printf(" 2 ... As 1 and failed conditions (this is default)\n"); - printf(" 3 ... As 1 and all conditions (and extended summary)\n"); - printf(" -q, --quiet Same as --verbose=0\n"); - printf(" --color[=WHEN] Enable colorized output\n"); - printf(" (WHEN is one of 'auto', 'always', 'never')\n"); - printf(" --no-color Same as --color=never\n"); - printf(" -h, --help Display this help and exit\n"); - - if(acutest_list_size_ < 16) { - printf("\n"); - acutest_list_names_(); - } -} - -static const ACUTEST_CMDLINE_OPTION_ acutest_cmdline_options_[] = { - { 'X', "exclude", 'X', 0 }, - { 's', "skip", 'X', 0 }, /* kept for compatibility, use --exclude instead */ - { 0, "exec", 'e', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 'E', "no-exec", 'E', 0 }, -#if defined ACUTEST_WIN_ - { 't', "time", 't', 0 }, - { 0, "timer", 't', 0 }, /* kept for compatibility */ -#elif defined ACUTEST_HAS_POSIX_TIMER_ - { 't', "time", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 0, "timer", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, /* kept for compatibility */ -#endif - { 0, "no-summary", 'S', 0 }, - { 0, "tap", 'T', 0 }, - { 'l', "list", 'l', 0 }, - { 'v', "verbose", 'v', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 'q', "quiet", 'q', 0 }, - { 0, "color", 'c', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, - { 0, "no-color", 'C', 0 }, - { 'h', "help", 'h', 0 }, - { 0, "worker", 'w', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, /* internal */ - { 'x', "xml-output", 'x', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, - { 0, NULL, 0, 0 } -}; - -static int -acutest_cmdline_callback_(int id, const char* arg) -{ - switch(id) { - case 'X': - acutest_exclude_mode_ = 1; - break; - - case 'e': - if(arg == NULL || strcmp(arg, "always") == 0) { - acutest_no_exec_ = 0; - } else if(strcmp(arg, "never") == 0) { - acutest_no_exec_ = 1; - } else if(strcmp(arg, "auto") == 0) { - /*noop*/ - } else { - fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", acutest_argv0_, arg); - fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); - acutest_exit_(2); - } - break; - - case 'E': - acutest_no_exec_ = 1; - break; - - case 't': -#if defined ACUTEST_WIN_ || defined ACUTEST_HAS_POSIX_TIMER_ - if(arg == NULL || strcmp(arg, "real") == 0) { - acutest_timer_ = 1; - #ifndef ACUTEST_WIN_ - } else if(strcmp(arg, "cpu") == 0) { - acutest_timer_ = 2; - #endif - } else { - fprintf(stderr, "%s: Unrecognized argument '%s' for option --time.\n", acutest_argv0_, arg); - fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); - acutest_exit_(2); - } -#endif - break; - - case 'S': - acutest_no_summary_ = 1; - break; - - case 'T': - acutest_tap_ = 1; - break; - - case 'l': - acutest_list_names_(); - acutest_exit_(0); - break; - - case 'v': - acutest_verbose_level_ = (arg != NULL ? atoi(arg) : acutest_verbose_level_+1); - break; - - case 'q': - acutest_verbose_level_ = 0; - break; - - case 'c': - if(arg == NULL || strcmp(arg, "always") == 0) { - acutest_colorize_ = 1; - } else if(strcmp(arg, "never") == 0) { - acutest_colorize_ = 0; - } else if(strcmp(arg, "auto") == 0) { - /*noop*/ - } else { - fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", acutest_argv0_, arg); - fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); - acutest_exit_(2); - } - break; - - case 'C': - acutest_colorize_ = 0; - break; - - case 'h': - acutest_help_(); - acutest_exit_(0); - break; - - case 'w': - acutest_worker_ = 1; - acutest_worker_index_ = atoi(arg); - break; - case 'x': - acutest_xml_output_ = fopen(arg, "w"); - if (!acutest_xml_output_) { - fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno)); - acutest_exit_(2); - } - break; - - case 0: - if(acutest_select_(arg) == 0) { - fprintf(stderr, "%s: Unrecognized unit test '%s'\n", acutest_argv0_, arg); - fprintf(stderr, "Try '%s --list' for list of unit tests.\n", acutest_argv0_); - acutest_exit_(2); - } - break; - - case ACUTEST_CMDLINE_OPTID_UNKNOWN_: - fprintf(stderr, "Unrecognized command line option '%s'.\n", arg); - fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); - acutest_exit_(2); - break; - - case ACUTEST_CMDLINE_OPTID_MISSINGARG_: - fprintf(stderr, "The command line option '%s' requires an argument.\n", arg); - fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); - acutest_exit_(2); - break; - - case ACUTEST_CMDLINE_OPTID_BOGUSARG_: - fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg); - fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); - acutest_exit_(2); - break; - } - - return 0; -} - -static int -acutest_under_debugger_(void) -{ -#ifdef ACUTEST_LINUX_ - /* Scan /proc/self/status for line "TracerPid: [PID]". If such line exists - * and the PID is non-zero, we're being debugged. */ - { - static const int OVERLAP = 32; - int fd; - char buf[512]; - size_t n_read; - pid_t tracer_pid = 0; - - /* Little trick so that we can treat the 1st line the same as any other - * and detect line start easily. */ - buf[0] = '\n'; - n_read = 1; - - fd = open("/proc/self/status", O_RDONLY); - if(fd != -1) { - while(1) { - static const char pattern[] = "\nTracerPid:"; - const char* field; - - while(n_read < sizeof(buf) - 1) { - ssize_t n; - - n = read(fd, buf + n_read, sizeof(buf) - 1 - n_read); - if(n <= 0) - break; - n_read += (size_t)n; - } - buf[n_read] = '\0'; - - field = strstr(buf, pattern); - if(field != NULL && field < buf + sizeof(buf) - OVERLAP) { - tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); - break; - } - - if(n_read == sizeof(buf) - 1) { - /* Move the tail with the potentially incomplete line we're - * be looking for to the beginning of the buffer. - * (The OVERLAP must be large enough so the searched line - * can fit in completely.) */ - memmove(buf, buf + sizeof(buf) - 1 - OVERLAP, OVERLAP); - n_read = OVERLAP; - } else { - break; - } - } - - close(fd); - - if(tracer_pid != 0) - return 1; - } - } -#endif - -#ifdef ACUTEST_MACOS_ - /* See https://developer.apple.com/library/archive/qa/qa1361/_index.html */ - { - int mib[4]; - struct kinfo_proc info; - size_t size; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - - size = sizeof(info); - info.kp_proc.p_flag = 0; - sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); - - if(info.kp_proc.p_flag & P_TRACED) - return 1; - } -#endif - -#ifdef ACUTEST_WIN_ - if(IsDebuggerPresent()) - return 1; -#endif - -#ifdef RUNNING_ON_VALGRIND - /* We treat Valgrind as a debugger of sorts. - * (Macro RUNNING_ON_VALGRIND is provided by , if available.) */ - if(RUNNING_ON_VALGRIND) - return 1; -#endif - - return 0; -} - -int -main(int argc, char** argv) -{ - int i, index; - int exit_code = 1; - - acutest_argv0_ = argv[0]; - -#if defined ACUTEST_UNIX_ - acutest_colorize_ = isatty(STDOUT_FILENO); -#elif defined ACUTEST_WIN_ - #if defined _BORLANDC_ - acutest_colorize_ = isatty(_fileno(stdout)); - #else - acutest_colorize_ = _isatty(_fileno(stdout)); - #endif -#else - acutest_colorize_ = 0; -#endif - - /* Count all test units */ - acutest_list_size_ = 0; - for(i = 0; acutest_list_[i].func != NULL; i++) - acutest_list_size_++; - - acutest_test_data_ = (struct acutest_test_data_*)calloc(acutest_list_size_, sizeof(struct acutest_test_data_)); - if(acutest_test_data_ == NULL) { - fprintf(stderr, "Out of memory.\n"); - acutest_exit_(2); - } - - /* Parse options */ - acutest_cmdline_read_(acutest_cmdline_options_, argc, argv, acutest_cmdline_callback_); - - /* Initialize the proper timer. */ - acutest_timer_init_(); - -#if defined(ACUTEST_WIN_) - SetUnhandledExceptionFilter(acutest_seh_exception_filter_); -#ifdef _MSC_VER - _set_abort_behavior(0, _WRITE_ABORT_MSG); -#endif -#endif - - /* Determine what to run. */ - if(acutest_count_(ACUTEST_STATE_SELECTED) > 0) { - enum acutest_state_ if_selected; - enum acutest_state_ if_unselected; - - if(!acutest_exclude_mode_) { - if_selected = ACUTEST_STATE_NEEDTORUN; - if_unselected = ACUTEST_STATE_EXCLUDED; - } else { - if_selected = ACUTEST_STATE_EXCLUDED; - if_unselected = ACUTEST_STATE_NEEDTORUN; - } - - for(i = 0; acutest_list_[i].func != NULL; i++) { - if(acutest_test_data_[i].state == ACUTEST_STATE_SELECTED) - acutest_test_data_[i].state = if_selected; - else - acutest_test_data_[i].state = if_unselected; - } - } else { - /* By default, we want to run all tests. */ - for(i = 0; acutest_list_[i].func != NULL; i++) - acutest_test_data_[i].state = ACUTEST_STATE_NEEDTORUN; - } - - /* By default, we want to suppress running tests as child processes if we - * run just one test, or if we're under debugger: Debugging tests is then - * so much easier. */ - if(acutest_no_exec_ < 0) { - if(acutest_count_(ACUTEST_STATE_NEEDTORUN) <= 1 || acutest_under_debugger_()) - acutest_no_exec_ = 1; - else - acutest_no_exec_ = 0; - } - - if(acutest_tap_) { - /* TAP requires we know test result ("ok", "not ok") before we output - * anything about the test, and this gets problematic for larger verbose - * levels. */ - if(acutest_verbose_level_ > 2) - acutest_verbose_level_ = 2; - - /* TAP harness should provide some summary. */ - acutest_no_summary_ = 1; - - if(!acutest_worker_) - printf("1..%d\n", acutest_count_(ACUTEST_STATE_NEEDTORUN)); - } - - index = acutest_worker_index_; - for(i = 0; acutest_list_[i].func != NULL; i++) { - if(acutest_test_data_[i].state == ACUTEST_STATE_NEEDTORUN) - acutest_run_(´st_list_[i], index++, i); - } - - /* Write a summary */ - if(!acutest_no_summary_ && acutest_verbose_level_ >= 1) { - int n_run, n_success, n_failed ; - - n_run = acutest_list_size_ - acutest_count_(ACUTEST_STATE_EXCLUDED); - n_success = acutest_count_(ACUTEST_STATE_SUCCESS); - n_failed = acutest_count_(ACUTEST_STATE_FAILED); - - if(acutest_verbose_level_ >= 3) { - acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Summary:\n"); - - printf(" Count of run unit tests: %4d\n", n_run); - printf(" Count of successful unit tests: %4d\n", n_success); - printf(" Count of failed unit tests: %4d\n", n_failed); - } - - if(n_failed == 0) { - acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS:"); - printf(" No unit tests have failed.\n"); - } else { - acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED:"); - printf(" %d of %d unit tests %s failed.\n", - n_failed, n_run, (n_failed == 1) ? "has" : "have"); - } - - if(acutest_verbose_level_ >= 3) - printf("\n"); - } - - if (acutest_xml_output_) { - const char* suite_name = acutest_basename_(argv[0]); - fprintf(acutest_xml_output_, "\n"); - fprintf(acutest_xml_output_, "\n", - suite_name, - (int)acutest_list_size_, - acutest_count_(ACUTEST_STATE_FAILED), - acutest_count_(ACUTEST_STATE_SKIPPED) + acutest_count_(ACUTEST_STATE_EXCLUDED)); - for(i = 0; acutest_list_[i].func != NULL; i++) { - struct acutest_test_data_ *details = ´st_test_data_[i]; - const char* str_state; - fprintf(acutest_xml_output_, " \n", acutest_list_[i].name, details->duration); - - switch(details->state) { - case ACUTEST_STATE_SUCCESS: str_state = NULL; break; - case ACUTEST_STATE_EXCLUDED: /* Fall through. */ - case ACUTEST_STATE_SKIPPED: str_state = ""; break; - case ACUTEST_STATE_FAILED: /* Fall through. */ - default: str_state = ""; break; - } - - if(str_state != NULL) - fprintf(acutest_xml_output_, " %s\n", str_state); - fprintf(acutest_xml_output_, " \n"); - } - fprintf(acutest_xml_output_, "\n"); - fclose(acutest_xml_output_); - } - - if(acutest_worker_ && acutest_count_(ACUTEST_STATE_EXCLUDED)+1 == acutest_list_size_) { - /* If we are the child process, we need to propagate the test state - * without any moderation. */ - for(i = 0; acutest_list_[i].func != NULL; i++) { - if(acutest_test_data_[i].state != ACUTEST_STATE_EXCLUDED) { - exit_code = (int) acutest_test_data_[i].state; - break; - } - } - } else { - if(acutest_count_(ACUTEST_STATE_FAILED) > 0) - exit_code = 1; - else - exit_code = 0; - } - - acutest_cleanup_(); - return exit_code; -} - - -#endif /* #ifndef TEST_NO_MAIN */ - -#ifdef _MSC_VER - #pragma warning(pop) -#endif - -#ifdef __cplusplus - } /* extern "C" */ -#endif - -#endif /* #ifndef ACUTEST_H */ diff --git a/lib/core.c b/lib/core.c deleted file mode 100644 index 72593e8..0000000 --- a/lib/core.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "core.h" - -void init_lib_core() { - static int init = 0; - if (init) return; - init_rt(); - - init += 1; - Assert(init == 1); -} diff --git a/lib/core.h b/lib/core.h deleted file mode 100644 index 5e3a8eb..0000000 --- a/lib/core.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file core.h - * @brief 核心库初始化接口 - * - * 定义SMCC库的核心初始化函数和基础服务配置 - */ - -#ifndef __SMCC_LIB_CORE_H__ -#define __SMCC_LIB_CORE_H__ - -#include "rt/rt.h" - -/** - * @brief 初始化核心库组件 - * - * 此函数必须在调用任何其他库函数之前调用,可能负责: - * - TODO 初始化内存管理系统 - * - TODO 注册基础信号处理器 - * - TODO 配置默认日志系统 - * - 设置运行时环境 - * - * @note 必须作为程序启动后第一个调用的库函数 - * @warning 重复调用不会导致未定义行为,你必须为此至少调用一次否则会导致未定义行为 - * @see rt.h 中相关的运行时环境配置 - */ -void init_lib_core(); - -#endif // __SMCC_LIB_CORE_H__ diff --git a/lib/rt/log/color.h b/lib/rt/log/color.h deleted file mode 100644 index 71f65f4..0000000 --- a/lib/rt/log/color.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file color.h - * @brief ANSI终端颜色控制码定义 - * - * 提供跨平台的终端文本颜色和样式控制支持 - */ - -#ifndef __SMCC_TERMINAL_COLOR_H__ -#define __SMCC_TERMINAL_COLOR_H__ - -/// @name 前景色控制码 -/// @{ -#define ANSI_FG_BLACK "\33[30m" ///< 黑色前景 -#define ANSI_FG_RED "\33[31m" ///< 红色前景 -#define ANSI_FG_GREEN "\33[32m" ///< 绿色前景 -#define ANSI_FG_YELLOW "\33[33m" ///< 黄色前景 -#define ANSI_FG_BLUE "\33[34m" ///< 蓝色前景 -#define ANSI_FG_MAGENTA "\33[35m" ///< 品红色前景 -#define ANSI_FG_CYAN "\33[36m" ///< 青色前景 -#define ANSI_FG_WHITE "\33[37m" ///< 白色前景 -/// @} - -/// @name 背景色控制码 -/// @{ -#define ANSI_BG_BLACK "\33[40m" ///< 黑色背景 -#define ANSI_BG_RED "\33[41m" ///< 红色背景 -#define ANSI_BG_GREEN "\33[42m" ///< 绿色背景 -#define ANSI_BG_YELLOW "\33[43m" ///< 黄色背景 -#define ANSI_BG_BLUE "\33[44m" ///< 蓝色背景 -#define ANSI_BG_MAGENTA "\33[45m" ///< 品红色背景(注:原始代码此处应为45m) -#define ANSI_BG_CYAN "\33[46m" ///< 青色背景 -#define ANSI_BG_WHITE "\33[47m" ///< 白色背景 -/// @} - -/// @name 文字样式控制码 -/// @{ -#define ANSI_UNDERLINED "\33[4m" ///< 下划线样式 -#define ANSI_BOLD "\33[1m" ///< 粗体样式 -#define ANSI_NONE "\33[0m" ///< 重置所有样式 -/// @} - -/** - * @def ANSI_FMT - * @brief 安全文本格式化宏 - * @param str 目标字符串 - * @param fmt ANSI格式序列(可组合多个样式) - * - * @note 当定义ANSI_FMT_DISABLE时自动禁用颜色输出 - * @code - * printf(ANSI_FMT("Warning!", ANSI_FG_YELLOW ANSI_BOLD)); - * @endcode - */ -#ifndef ANSI_FMT_DISABLE -#define ANSI_FMT(str, fmt) fmt str ANSI_NONE ///< 启用样式包裹 -#else -#define ANSI_FMT(str, fmt) str ///< 禁用样式输出 -#endif - -#endif // __SMCC_TERMINAL_COLOR_H__ diff --git a/lib/rt/log/log.c b/lib/rt/log/log.c deleted file mode 100644 index 62551b8..0000000 --- a/lib/rt/log/log.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "log.h" - -typedef void (*log_handler)( - log_level_t level, - const char* module, - const char* file, - int line, - const char* message -); - -static void default_handler(log_level_t level, const char* module, const char* file, int line, const char* message) { - if (!rt_stderr) return; - const char* level_str; - switch (level) { - case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break; - case LOG_LEVEL_INFO: level_str = "INFO "; break; - case LOG_LEVEL_WARN: level_str = "WARN "; break; - case LOG_LEVEL_ERROR: level_str = "ERROR"; break; - case LOG_LEVEL_FATAL: level_str = "FATAL"; break; - case LOG_LEVEL_TRACE: level_str = "TRACE"; break; - default: level_str = "NOTSET"; break; - } - -/// @note: 定义 __LOG_NO_COLOR__ 会取消颜色输出 -#ifndef __LOG_NO_COLOR__ - const char* color_code = ANSI_NONE; - switch (level) { - case LOG_LEVEL_DEBUG: color_code = ANSI_FG_CYAN; break; - case LOG_LEVEL_INFO: color_code = ANSI_FG_GREEN; break; - case LOG_LEVEL_TRACE: color_code = ANSI_FG_BLUE; break; - case LOG_LEVEL_WARN: color_code = ANSI_FG_YELLOW; break; - case LOG_LEVEL_ERROR: color_code = ANSI_FG_RED; break; - case LOG_LEVEL_FATAL: color_code = ANSI_FG_RED ANSI_UNDERLINED; break; // 增强对比度 - default: color_code = ANSI_NONE; - } - - rt.fprintf(rt_stderr, ANSI_BOLD "%s[%s] - %s - %s:%d | %s" ANSI_NONE "\n", color_code, - level_str, module, file, line, message); -#else - rt.fprintf(rt_stderr, "[%s] %s:%d | %s: %s\n", - level_str, file, line, module, message); -#endif - if (level & LOG_LEVEL_FATAL) { - rt.exit(-LOG_LEVEL_FATAL); - } -} - -static logger_t root_logger = { - .name = "root", - .level = LOG_LEVEL_ALL, - .handler = default_handler, -}; - -void init_logger(logger_t* logger, const char* name) { - logger->name = name; - logger->handler = default_handler; - log_set_level(logger, LOG_LEVEL_ALL); -} - -logger_t* log_get(const char* name) { - return &root_logger; -} - -void log_set_level(logger_t* logger, log_level_t level) { - if (logger) logger->level = level; - else root_logger.level = level; -} - -void log_set_handler(logger_t* logger, log_handler handler) { - if (logger) logger->handler = handler; - else root_logger.handler = handler; -} diff --git a/lib/rt/log/log.h b/lib/rt/log/log.h deleted file mode 100644 index 037dfe0..0000000 --- a/lib/rt/log/log.h +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @file log.h - * @brief 日志系统核心模块(支持多级日志、断言和异常处理) - */ - -#ifndef __SMCC_LOG_H__ -#define __SMCC_LOG_H__ - -#include "../std/rt_api_def.h" -#include "color.h" - -/** - * @brief 日志级别枚举 - * - * 定义日志系统的输出级别和组合标志位 - */ -typedef enum log_level { - LOG_LEVEL_NOTSET = 0, ///< 未设置级别(继承默认配置) - LOG_LEVEL_DEBUG = 1 << 0, ///< 调试信息(开发阶段详细信息) - LOG_LEVEL_INFO = 1 << 1, ///< 常规信息(系统运行状态) - LOG_LEVEL_WARN = 1 << 2, ///< 警告信息(潜在问题提示) - LOG_LEVEL_ERROR = 1 << 3, ///< 错误信息(可恢复的错误) - LOG_LEVEL_FATAL = 1 << 4, ///< 致命错误(导致程序终止的严重错误) - LOG_LEVEL_TRACE = 1 << 5, ///< 追踪(性能追踪或者栈帧追踪) - LOG_LEVEL_ALL = 0xFF, ///< 全级别标志(组合所有日志级别) -} log_level_t; - -/** - * @brief 日志处理回调函数类型 - * @param level 日志级别 - * @param module 模块名称(可为NULL) - * @param file 源文件名 - * @param line 代码行号 - * @param message 格式化后的日志消息 - * @todo 待实现模块名称,输入的模块名称,都将被忽略 - */ -typedef void (*log_handler)( - log_level_t level, - const char* module, - const char* file, - int line, - const char* message -); - -#ifndef LOGGER_MAX_BUF_SIZE -#define LOGGER_MAX_BUF_SIZE 512 ///< 单条日志最大缓冲区尺寸 -#endif - -/** - * @brief 日志器实例结构体 - * - * 每个日志器实例维护独立的配置和缓冲区 - */ -typedef struct logger { - const char* name; ///< 日志器名称(用于模块区分) - log_level_t level; ///< 当前设置的日志级别 - log_handler handler; ///< 日志处理回调函数 - char buf[LOGGER_MAX_BUF_SIZE]; ///< 格式化缓冲区 -} logger_t; - -/** - * @brief 初始化日志实例 其余参数设置为默认值 - * @param[in] logger 日志器实例指针 - * @param[in] name 日志器名称(NULL表示获取默认日志器名称) - */ -void init_logger(logger_t* logger, const char* name); - -// TODO log_set(); 暂未实现 日志注册 - -/** - * @brief 获取或创建日志器实例 - * @param[in] name 日志器名称(NULL表示获取默认日志器) - * @return 日志器实例指针 - * @warning 若没有找到相应日志器则会返回根日志器 - */ -logger_t* log_get(const char* name); - -/** - * @brief 设置日志级别 - * @param[in] logger 目标日志器实例 - * @param[in] level 要设置的日志级别(可组合多个级别) - */ -void log_set_level(logger_t* logger, log_level_t level); - -/** - * @brief 设置自定义日志处理器 - * @param[in] logger 目标日志器实例 - * @param[in] handler 自定义处理函数(NULL恢复默认处理) - */ -void log_set_handler(logger_t* logger, log_handler handler); - -#ifndef LOG_MAX_MAROC_BUF_SIZE -#define LOG_MAX_MAROC_BUF_SIZE LOGGER_MAX_BUF_SIZE ///< 宏展开缓冲区尺寸 -#endif - -/** - * @def _LOG - * @brief 内部日志宏(供其他日志宏调用) - * @param _module_ 模块实例(NULL表示使用默认日志器) - * @param _level_ 日志级别 - * @param _msg_ 格式字符串 - * @param ... 可变参数列表 - */ -#define _LOG(_module_, _level_, _msg_, ...) \ - do { \ - logger_t* _logger; \ - if (!_module_) { \ - _logger = log_get(NULL); \ - } \ - else _logger = _module_; \ - if (_logger && _logger->handler && (_logger->level & (_level_))) { \ - rt.snprintf(_logger->buf, sizeof(_logger->buf), (_msg_), ##__VA_ARGS__); \ - _logger->handler((_level_), _logger->name, __FILE__, __LINE__, _logger->buf); \ - } \ - } while(0) - -/// @name 模块日志宏 -/// @{ -#define MLOG_NOTSET(module, ...) _LOG(module, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志 -#define MLOG_DEBUG( module, ...) _LOG(module, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别) -#define MLOG_INFO( module, ...) _LOG(module, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志) -#define MLOG_WARN( module, ...) _LOG(module, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题) -#define MLOG_ERROR( module, ...) _LOG(module, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误) -#define MLOG_FATAL( module, ...) _LOG(module, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前) -#define MLOG_TRACE( module, ...) _LOG(module, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪) -/// @} - -/// @name 快捷日志宏 -/// @{ -#define LOG_NOTSET(...) _LOG(NULL, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志 -#define LOG_DEBUG(...) _LOG(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别) -#define LOG_INFO(...) _LOG(NULL, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志) -#define LOG_WARN(...) _LOG(NULL, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题) -#define LOG_ERROR(...) _LOG(NULL, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误) -#define LOG_FATAL(...) _LOG(NULL, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前) -#define LOG_TRACE(...) _LOG(NULL, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪) -/// @} - -/** - * @def _Assert - * @brief 断言检查内部宏 - * @param cond 检查条件表达式 - * @param ... 错误信息参数(格式字符串+参数) - */ -#define _Assert(cond, ...) \ - do { \ - if (!(cond)) { \ - LOG_FATAL(__VA_ARGS__); \ - } \ - } while (0) - -/// @name 断言工具宏 -/// @{ -#define AssertFmt(cond, format, ...) _Assert(cond, "Assertion Failure: " format, ## __VA_ARGS__) ///< 带格式的断言检查 -#define PanicFmt(format, ...) _Assert(0, "Panic: " format, ## __VA_ARGS__) ///< 立即触发致命错误 -#define Assert(cond) AssertFmt(cond, "cond is `" SMCC_STR(cond) "`") ///< 基础断言检查 -#define Panic(...) PanicFmt(__VA_ARGS__) ///< 触发致命错误(带自定义消息) -#define TODO() PanicFmt("TODO please implement me") ///< 标记未实现代码(触发致命错误) -/// @} - -#endif // __SMCC_LOG_H__ diff --git a/lib/rt/rt.c b/lib/rt/rt.c deleted file mode 100644 index 3d20f45..0000000 --- a/lib/rt/rt.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "rt.h" - -void init_rt() { -// TODO Choice OS -#ifndef __SMCC_NO_OS_STD__ - #include "std/rt_std.h" - init_rt_std(); -#endif - return; -} diff --git a/lib/rt/rt.h b/lib/rt/rt.h deleted file mode 100644 index 8c2d0ee..0000000 --- a/lib/rt/rt.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __SMCC_RT_H__ -#define __SMCC_RT_H__ - -#include "std/rt_api_def.h" -#include "std/rt_type.h" -#include "log/log.h" -#include "rt_alloc.h" -#include "rt_string.h" - -void init_rt(); - -// define -#define _SMCC_STR(str) #str -#define SMCC_STR(str) _SMCC_STR(str) - -#define SMCC_ARRLEN(arr) (sizeof(arr) / sizeof(arr[0])) - -#endif // __SMCC_RT_H__ diff --git a/lib/rt/rt_alloc.c b/lib/rt/rt_alloc.c deleted file mode 100644 index 5a3f863..0000000 --- a/lib/rt/rt_alloc.c +++ /dev/null @@ -1,135 +0,0 @@ -#include "rt_alloc.h" - -#define ALLOCATOR_PAGE_SIZE (4096) - -/* Simple / Static Allocator */ - -void* salloc_alloc(int size) { - // TODO do some trace - return rt._malloc(size); -} - -void* salloc_realloc(void* ptr, int size) { - return rt._realloc(ptr, size); -} - -void salloc_free(void* ptr) { - // TODO do some trace - rt._free(ptr); -} - -/* Fixed Allocator */ - -#define PAGE_ALIGN(size) (((size) + ALLOCATOR_PAGE_SIZE -1) & ~(ALLOCATOR_PAGE_SIZE-1)) - -void falloc_init(fixed_alloc_t* fa, int fixed_size, int init_blocks) { - fa->free_list = NULL; - fa->page_list = NULL; - - // 确保块大小至少能存放指针(用于空闲链表) - const int min_size = sizeof(void*); - fa->block_size = (fixed_size < min_size) ? min_size : - (fixed_size + 15) & ~15; // 16字节对齐 - - // 计算每页块数(优化缓存利用率) - const int page_size = ALLOCATOR_PAGE_SIZE - sizeof(void*); - fa->blocks_per_page = page_size / fa->block_size; - - // TODO copy paste 需要使用函数抽象 申请过程 - // 预分配初始页 - void* page = salloc_alloc(PAGE_ALIGN(fa->block_size * init_blocks)); - unsigned char* p = (unsigned char*)page; - for (int i = 0; i < init_blocks; ++i) { - void** block = (void**)p; - *block = fa->free_list; - fa->free_list = block; - p += fa->block_size; - } - - *(void**)page = fa->page_list; - fa->page_list = page; -} - -void* falloc_alloc(fixed_alloc_t* fa) { - if (!fa->free_list) { - // 分配新页(带页头保存链表指针) - void* page = salloc_alloc(ALLOCATOR_PAGE_SIZE); - unsigned char* p = (unsigned char*)page + sizeof(void*); - - // 链接新页块到空闲链表 - for (int i = 0; i < fa->blocks_per_page; ++i) { - void** block = (void**)p; - *block = fa->free_list; - fa->free_list = block; - p += fa->block_size; - } - - *(void**)page = fa->page_list; - fa->page_list = page; - } - - void* block = fa->free_list; - fa->free_list = *(void**)block; - return (void*)((unsigned char*)block + sizeof(void*)); // 跳过链表指针 -} - -void falloc_free(fixed_alloc_t* fa, void* ptr) { - if (!fa || !ptr) return; - - void** block = (void**)((u8_t*)ptr - sizeof(void*)); - *block = fa->free_list; - fa->free_list = block; -} - -void falloc_destroy(fixed_alloc_t* fa) { - if (!fa) return; - - // 逆向释放所有内存页(保持地址连续性) - void* current_page = fa->page_list; - while (current_page) { - void* next_page = *(void**)current_page; // 页头保存了链表指针 - salloc_free(current_page); - current_page = next_page; - } - - // 防御性清零(防止悬垂指针) - fa->free_list = NULL; - fa->blocks_per_page = 0; - fa->block_size = 0; - fa->page_list = NULL; -} - -/* Long Allocator */ - -void lalloc_init(long_alloc_t* la) { - la->current = NULL; - la->block_size = ALLOCATOR_PAGE_SIZE; // 初始块大小 -} - -void* lalloc_alloc(long_alloc_t* la, int size) { - size = (size + 15) & ~15; // 16字节对齐 - - if (!la->current || (la->current->used + size) > la->block_size) { - int new_size = la->block_size; - if (new_size < size + sizeof(long_block_t)) - new_size = size + sizeof(long_block_t); - - long_block_t* new_block = (long_block_t*)salloc_alloc(new_size); - new_block->next = la->current; - new_block->used = sizeof(long_block_t); - la->current = new_block; - la->block_size = new_size; - } - - void* ptr = (unsigned char*)la->current + la->current->used; - la->current->used += size; - return ptr; -} - -void lalloc_destroy(long_alloc_t* la) { - while (la->current) { - long_block_t* prev = la->current->next; - salloc_free(la->current); - la->current = prev; - } -} diff --git a/lib/rt/rt_alloc.h b/lib/rt/rt_alloc.h deleted file mode 100644 index 73b4d0a..0000000 --- a/lib/rt/rt_alloc.h +++ /dev/null @@ -1,132 +0,0 @@ -/** - * @file rt_alloc.h - * @brief 内存分配器接口 - * - * 提供三种内存分配器实现:简单分配器、固定大小分配器和长块分配器 - */ - -#ifndef __SMCC_RT_ALLOC_H__ -#define __SMCC_RT_ALLOC_H__ - -#include "std/rt_api_def.h" - -/** @defgroup simple_allocator 简单分配器 */ - -/** - * @brief 分配指定大小的内存块 - * @param size 请求分配的内存大小(字节) - * @return 成功返回内存指针,失败返回NULL - */ -void* salloc_alloc(int size); - -/** - * @brief 重新分配内存块 - * @param ptr 原内存指针 - * @param size 新内存大小(字节) - * @return 成功返回新内存指针,失败返回NULL - */ -void* salloc_realloc(void* ptr, int size); - -/** - * @brief 释放内存块 - * @param ptr 要释放的内存指针 - */ -void salloc_free(void* ptr); - -/** @defgroup fixed_allocator 固定大小分配器 */ - -/** - * @struct fixed_alloc_t - * @brief 固定大小内存分配器上下文 - */ -typedef struct fixed_alloc { - /** @brief 内存页链表头指针 */ - void* page_list; - /** @brief 空闲块链表头指针 */ - void* free_list; - /** @brief 每个内存块的固定大小 */ - int block_size; - /** @brief 每页包含的块数量 */ - int blocks_per_page; -} fixed_alloc_t; - -/** - * @brief 初始化固定大小分配器 - * @param fa 分配器上下文指针 - * @param fixed_size 每个内存块的固定大小 - * @param init_size 初始预分配块数量 - */ -void falloc_init(fixed_alloc_t* fa, int fixed_size, int init_size); - -/** - * @brief 分配固定大小内存块 - * @param fa 分配器上下文指针 - * @return 成功返回内存指针,失败返回NULL - */ -void* falloc_alloc(fixed_alloc_t* fa); - -/** - * @brief 释放内存块 - * @param fa 分配器上下文指针 - * @param ptr 要释放的内存指针 - */ -void falloc_free(fixed_alloc_t* fa, void* ptr); - -/** - * @brief 销毁分配器并释放所有内存页 - * @param fa 分配器上下文指针 - */ -void falloc_destroy(fixed_alloc_t* fa); - -/** @defgroup long_allocator 长块分配器 */ - -/** - * @struct long_block_t - * @brief 长块内存块头结构 - */ -typedef struct long_block { - /** @brief 指向下一个内存块的指针 */ - struct long_block* next; - /** @brief 当前块使用状态标志 */ - int used; -} long_block_t; - -/** - * @struct long_alloc_t - * @brief 长块分配器上下文 - */ -typedef struct long_alloc { - /** @brief 当前内存块指针 */ - long_block_t* current; - /** @brief 内存块的标准大小 */ - int block_size; -} long_alloc_t; - -/** - * @brief 初始化长块分配器 - * @param la 分配器上下文指针 - */ -void lalloc_init(long_alloc_t* la); - -/** - * @brief 分配指定大小的内存块 - * @param la 分配器上下文指针 - * @param size 请求分配的内存大小 - * @return 成功返回内存指针,失败返回NULL - */ -void* lalloc_alloc(long_alloc_t* la, int size); - -/** - * @brief 标记释放内存块(实际在块耗尽时统一释放) - * @param la 分配器上下文指针 - * @param ptr 要释放的内存指针 - */ -void lalloc_free(long_alloc_t* la, void* ptr); - -/** - * @brief 销毁分配器并释放所有内存块 - * @param la 分配器上下文指针 - */ -void lalloc_destroy(long_alloc_t* la); - -#endif // __SMCC_RT_ALLOC_H__ diff --git a/lib/rt/rt_string.c b/lib/rt/rt_string.c deleted file mode 100644 index c0d3943..0000000 --- a/lib/rt/rt_string.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "rt_string.h" - -int rt_memcmp(const void* s1, const void* s2, rt_size_t n) { - const unsigned char *p1 = s1, *p2 = s2; - for (rt_size_t i = 0; i < n; ++i) { - if (p1[i] != p2[i]) - return p1[i] - p2[i]; - } - return 0; -} - -int rt_strcmp(const char* s1, const char* s2) { - while (*s1 && *s2 && (*s1 == *s2)) { - s1++; - s2++; - } - return *(const unsigned char*)s1 - *(const unsigned char*)s2; -} - -void* rt_memcpy(void* restrict dest, const void* restrict src, rt_size_t n) { - u8_t* d = dest; - const u8_t* s = src; - for (rt_size_t i = 0; i < n; ++i) - d[i] = s[i]; - return dest; -} - -void* rt_memset(void* dest, int val, rt_size_t n) { - u8_t* p = dest; - for (rt_size_t i = 0; i < n; ++i) - p[i] = (u8_t)val; - return dest; -} - -rt_size_t rt_strlen(const char* s) { - const char* p = s; - while (*p) p++; - return p - s; -} - -/* strhash - 字符串哈希(用于符号表) */ -u32_t rt_strhash(const char* s) { - u32_t hash = 2166136261u; // FNV-1a偏移基础值 - while (*s) { - hash ^= *s++; - hash *= 16777619u; - } - return hash; -} diff --git a/lib/rt/rt_string.h b/lib/rt/rt_string.h deleted file mode 100644 index def2519..0000000 --- a/lib/rt/rt_string.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file rt_string.h - * @brief 运行时字符串与内存操作 - * - * 提供基本的内存操作和字符串处理函数实现 - */ - -#ifndef __SMCC_RT_STRING_H__ -#define __SMCC_RT_STRING_H__ - -#include "std/rt_api_def.h" - -/** @defgroup memory_operations 内存操作 */ - -/** - * @brief 内存区域比较 - * @param s1 第一个内存区域指针 - * @param s2 第二个内存区域指针 - * @param n 比较的字节数 - * @return 差异值(<0: s10: s1>s2) - */ -int rt_memcmp(const void* s1, const void* s2, rt_size_t n); - -/** - * @brief 安全内存拷贝(要求内存区域不重叠) - * @param dest 目标内存地址(restrict修饰) - * @param src 源内存地址(restrict修饰) - * @param n 拷贝的字节数 - * @return 目标内存地址 - */ -void* rt_memcpy(void* restrict dest, const void* restrict src, rt_size_t n); - -/** - * @brief 内存区域填充 - * @param s 目标内存地址 - * @param c 填充字节值(转换为unsigned char) - * @param n 填充的字节数 - * @return 原始内存地址 - */ -void* rt_memset(void* dest, int val, rt_size_t n); - -/** @defgroup string_operations 字符串操作 */ - -/** - * @brief 字符串比较 - * @param s1 第一个字符串指针 - * @param s2 第二个字符串指针 - * @return 差异值(<0: s10: s1>s2) - */ -int rt_strcmp(const char* s1, const char* s2); - -/** - * @brief 计算字符串长度 - * @param s 字符串指针 - * @return 字符串长度(不含终止符) - */ -rt_size_t rt_strlen(const char* s); - -/** - * @brief 计算字符串哈希值 - * @param s 输入字符串 - * @return 32位无符号哈希值 - * @note 使用FNV-1a哈希算法实现 - */ -u32_t rt_strhash(const char* s); - -#endif // __SMCC_RT_STRING_H__ diff --git a/lib/rt/std/rt_api_def.h b/lib/rt/std/rt_api_def.h deleted file mode 100644 index b97d344..0000000 --- a/lib/rt/std/rt_api_def.h +++ /dev/null @@ -1,183 +0,0 @@ -/** - * @file rt_api_def.h - * @brief SMCC运行时库接口定义 - * - * 定义运行时基础API函数指针类型及运行时接口结构体 - */ - -#ifndef __SMCC_RT_API_DEF_H__ -#define __SMCC_RT_API_DEF_H__ - -#include "rt_type.h" - -#ifndef __RT_SIZE_TYPE__ -#define __RT_SIZE_TYPE__ -/** - * @typedef rt_size_t - * @brief 表示内存大小的类型定义 - */ -typedef usz_t rt_size_t; -#endif - -/** - * @typedef rt_malloc - * @brief 内存分配函数指针类型 - * @param size 需要分配的内存大小 - * @return 分配的内存指针,失败返回NULL - */ -typedef void* (*rt_malloc)(rt_size_t size); - -/** - * @typedef rt_free - * @brief 内存释放函数指针类型 - * @param ptr 需要释放的内存指针 - */ -typedef void (*rt_free)(void* ptr); - -/** - * @typedef rt_exit - * @brief 程序退出函数指针类型 - * @param code 退出状态码 - */ -typedef void (*rt_exit)(int code); - -/** @defgroup file_io 文件I/O相关类型 */ - -#ifndef __RT_FILE_TYPE__ -#define __RT_FILE_TYPE__ -/** - * @typedef rt_file_t - * @brief 文件句柄类型定义 - */ -typedef void* rt_file_t; -#endif - -/** @brief 标准输入文件句柄 */ -extern rt_file_t rt_stdin; -/** @brief 标准输出文件句柄 */ -extern rt_file_t rt_stdout; -/** @brief 标准错误文件句柄 */ -extern rt_file_t rt_stderr; - -/** - * @typedef rt_fopen_t - * @brief 文件打开函数指针类型 - * @param file_name 文件名 - * @param mode 打开模式(同fopen) - * @return 文件句柄,失败返回NULL - */ -typedef rt_file_t (*rt_fopen_t)(const char* file_name, const char* mode); - -/** - * @typedef rt_fflush_t - * @brief 文件缓冲刷新函数指针类型 - * @param file 文件句柄指针 - * @return 成功返回0,失败返回非0值 - */ -typedef int (*rt_fflush_t)(rt_file_t* file); - -/** - * @typedef rt_fclose_t - * @brief 文件关闭函数指针类型 - * @param file 文件句柄 - * @return 成功返回0,失败返回EOF - */ -typedef int (*rt_fclose_t)(rt_file_t file); - -/** - * @typedef rt_fread_t - * @brief 文件读取函数指针类型 - * @param dst_buf 目标缓冲区 - * @param elem_size 单个元素大小 - * @param count 元素数量 - * @param file 文件句柄 - * @return 实际读取的元素数量 - */ -typedef int (*rt_fread_t)(void * dst_buf, rt_size_t elem_size, rt_size_t count, rt_file_t file); - -/** - * @typedef rt_fwrite_t - * @brief 文件写入函数指针类型 - * @param buf 源缓冲区 - * @param size 单个元素大小 - * @param count 元素数量 - * @param file 文件句柄 - * @return 实际写入的元素数量 - */ -typedef int (*rt_fwrite_t)(const void * buf, rt_size_t size, rt_size_t count, rt_file_t file); - -/** @defgroup utility 实用工具函数 */ - -/** - * @typedef rt_fprintf_t - * @brief 格式化输出函数指针类型 - * @param file 文件句柄 - * @param format 格式化字符串 - * @param ... 可变参数 - * @return 输出的字符数 - */ -typedef int (*rt_fprintf_t)(void * file, const char *format, ...); - -/** - * @typedef rt_snprintf_t - * @brief 安全格式化字符串函数指针类型 - * @param stream 目标缓冲区 - * @param n 缓冲区大小 - * @param format 格式化字符串 - * @param ... 可变参数 - * @return 写入的字符数(不含终止符) - */ -typedef int (*rt_snprintf_t)(char * stream, rt_size_t n, const char * format, ...); - -/** - * @typedef rt_realloc_t - * @brief 内存重分配函数指针类型 - * @param memory 原内存指针 - * @param new_size 新内存大小 - * @return 新内存指针,失败返回NULL - */ -typedef void* (*rt_realloc_t)(void *memory, rt_size_t new_size); - -/** - * @struct smcc_rt_t - * @brief 运行时接口集合 - * - * 包含内存管理、文件操作等核心运行时函数的指针集合 - */ -typedef struct smcc_rt { - /** @brief 内存分配函数指针 */ - rt_malloc _malloc; - /** @brief 内存释放函数指针 */ - rt_free _free; - /** @brief 程序退出函数指针 */ - rt_exit exit; - - /** @brief 文件打开函数指针 */ - rt_fopen_t fopen; - /** @brief 文件缓冲刷新函数指针 */ - rt_fflush_t fflush; - /** @brief 文件关闭函数指针 */ - rt_fclose_t fclose; - /** @brief 文件读取函数指针 */ - rt_fread_t fread; - /** @brief 文件写入函数指针 */ - rt_fwrite_t fwrite; - - /** @name 可选工具函数 */ - ///@{ - /** @brief 格式化输出函数指针(可选) */ - rt_fprintf_t fprintf; - /** @brief 安全格式化字符串函数指针(可选) */ - rt_snprintf_t snprintf; - /** @brief 内存重分配函数指针(可选) */ - rt_realloc_t _realloc; - ///@} -} smcc_rt_t; - -/** @brief 全局运行时接口实例 */ -extern const smcc_rt_t rt; - -/** @brief 空指针定义 */ -#define NULL ((void *)0) - -#endif // __SMCC_RT_API_DEF_H__ diff --git a/lib/rt/std/rt_std.c b/lib/rt/std/rt_std.c deleted file mode 100644 index 6e6edfb..0000000 --- a/lib/rt/std/rt_std.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include -#include - -#include "rt_api_def.h" - -const smcc_rt_t rt = { - ._malloc = (rt_malloc)malloc, - ._free = (rt_free)free, - .exit = (rt_exit)exit, - - .fopen = (rt_fopen_t)fopen, - .fflush = (rt_fflush_t)fflush, - .fclose = (rt_fclose_t)fclose, - .fread = (rt_fread_t)fread, - .fwrite = (rt_fwrite_t)fwrite, - - ._realloc = (rt_realloc_t)realloc, - .fprintf = (rt_fprintf_t)fprintf, - .snprintf = (rt_snprintf_t)snprintf, -}; - -rt_file_t rt_stdin; -rt_file_t rt_stdout; -rt_file_t rt_stderr; - -void init_rt_std() { - rt_stdin = stdin; - rt_stdout = stdout; - rt_stderr = stderr; -} diff --git a/lib/rt/std/rt_std.h b/lib/rt/std/rt_std.h deleted file mode 100644 index f18d786..0000000 --- a/lib/rt/std/rt_std.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __SMCC_RT_STD_H__ -#define __SMCC_RT_STD_H__ - -void init_rt_std(); - -#endif // __SMCC_RT_STD_H__ diff --git a/lib/rt/std/rt_type.h b/lib/rt/std/rt_type.h deleted file mode 100644 index d10e5ea..0000000 --- a/lib/rt/std/rt_type.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * @file rt_type.h - * @brief 基础类型定义 - * - * 定义跨平台基础数据类型别名,基于C99标准头文件 - */ - -#ifndef __SMCC_RT_TYPE_H__ -#define __SMCC_RT_TYPE_H__ - -#include -#include - -/** @defgroup integer_types 整数类型 */ - -/** - * @typedef i8_t - * @brief 8位有符号整数 - */ -typedef int8_t i8_t; - -/** - * @typedef i16_t - * @brief 16位有符号整数 - */ -typedef int16_t i16_t; - -/** - * @typedef i32_t - * @brief 32位有符号整数 - */ -typedef int32_t i32_t; - -/** - * @typedef i64_t - * @brief 64位有符号整数 - */ -typedef int64_t i64_t; - -/** - * @typedef u8_t - * @brief 8位无符号整数 - */ -typedef uint8_t u8_t; - -/** - * @typedef u16_t - * @brief 16位无符号整数 - */ -typedef uint16_t u16_t; - -/** - * @typedef u32_t - * @brief 32位无符号整数 - */ -typedef uint32_t u32_t; - -/** - * @typedef u64_t - * @brief 64位无符号整数 - */ -typedef uint64_t u64_t; - -/** @defgroup floating_point 浮点类型 */ - -/** - * @typedef f32_t - * @brief 32位单精度浮点数 - */ -typedef float f32_t; - -/** - * @typedef f64_t - * @brief 64位双精度浮点数 - */ -typedef double f64_t; - -/** @defgroup pointer_types 指针类型 */ - -/** - * @typedef iptr_t - * @brief 带符号指针类型(intptr_t别名) - */ -typedef intptr_t iptr_t; - -/** - * @typedef uptr_t - * @brief 无符号指针类型(uintptr_t别名) - */ -typedef uintptr_t uptr_t; - -/** @defgroup size_types 大小类型 */ - -/** - * @typedef usz_t - * @brief 无符号大小类型(size_t别名) - */ -typedef size_t usz_t; - -// /** -// * @typedef isz_t -// * @brief 带符号大小类型(ssize_t别名) -// */ -// typedef ssize_t isz_t; - -// /** @defgroup word_types 字类型 */ -// -// /** -// * @typedef uw_t -// * @brief 无符号机器字类型(32位) -// */ -// typedef u32_t uw_t; -// -// /** -// * @typedef iw_t -// * @brief 带符号机器字类型(32位) -// */ -// typedef i32_t iw_t; - -#endif // __SMCC_RT_TYPE_H__ diff --git a/lib/test/test.py b/lib/test/test.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/test/test_alloc..c b/lib/test/test_alloc..c deleted file mode 100644 index e69de29..0000000 diff --git a/lib/test/test_log.c b/lib/test/test_log.c deleted file mode 100644 index 293de70..0000000 --- a/lib/test/test_log.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -int main(void) { - // make -C .. - // gcc -g -Wall -I../.. test_log.c -L.. -lcore -o test_log - // ./test_log - init_lib_core(); - - LOG_NOTSET("Log notset"); - LOG_DEBUG("Log debug"); - LOG_INFO("Log info"); - LOG_WARN("Log warn"); - LOG_ERROR("Log error"); - LOG_FATAL("Log fatal"); -} diff --git a/lib/utils/ds/hashtable.c b/lib/utils/ds/hashtable.c deleted file mode 100644 index f12ba11..0000000 --- a/lib/utils/ds/hashtable.c +++ /dev/null @@ -1,140 +0,0 @@ -#include "hashtable.h" - -#define INIT_HASH_TABLE_SIZE (32) - -void init_hashtable(hash_table_t* ht) { - vector_init(ht->entries); - ht->count = 0; - ht->tombstone_count = 0; - // Assert(ht->key_cmp != NULL && ht->hash_func != NULL); -} - -static int next_power_of_two(int n) { - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - return n + 1; -} - -static hash_entry_t* find_entry(hash_table_t* ht, const void* key, u32_t hash) { - if (ht->entries.cap == 0) return NULL; - - u32_t index = hash & (ht->entries.cap - 1); // 容量是2的幂 - u32_t probe = 0; - - hash_entry_t* tombstone = NULL; - - while (1) { - hash_entry_t* entry = &vector_at(ht->entries, index); - if (entry->state == ENTRY_EMPTY) { - return tombstone ? tombstone : entry; - } - - if (entry->state == ENTRY_TOMBSTONE) { - if (!tombstone) tombstone = entry; - } else if (entry->hash == hash && ht->key_cmp(entry->key, key) == 0) { - return entry; - } - - // Liner finding - index = (index + 1) & (ht->entries.cap - 1); - probe++; - if (probe >= ht->entries.cap) break; - } - LOG_ERROR("hashset_find: hash table is full"); - return NULL; -} - -static void adjust_capacity(hash_table_t* ht, int new_cap) { - new_cap = next_power_of_two(new_cap); - Assert(new_cap >= ht->entries.cap); - - VECTOR_HEADER(old_entries, hash_entry_t); - old_entries.data = ht->entries.data; - old_entries.cap = ht->entries.cap; - - // Not used size but for gdb python extention debug - ht->entries.size = new_cap; - ht->entries.cap = new_cap; - ht->entries.data = salloc_realloc(NULL, new_cap * sizeof(hash_entry_t)); - rt_memset(ht->entries.data, 0, new_cap * sizeof(hash_entry_t)); - - // rehash the all of the old data - for (rt_size_t i = 0; i < old_entries.cap; i++) { - hash_entry_t* entry = &vector_at(old_entries, i); - if (entry->state == ENTRY_ACTIVE) { - hash_entry_t* dest = find_entry(ht, entry->key, entry->hash); - *dest = *entry; - } - } - - vector_free(old_entries); - ht->tombstone_count = 0; -} - -void* hashtable_set(hash_table_t* ht, const void* key, void* value) { - if (ht->count + ht->tombstone_count >= ht->entries.cap * 0.75) { - int new_cap = ht->entries.cap < INIT_HASH_TABLE_SIZE ? INIT_HASH_TABLE_SIZE : ht->entries.cap * 2; - adjust_capacity(ht, new_cap); - } - - u32_t hash = ht->hash_func(key); - hash_entry_t* entry = find_entry(ht, key, hash); - - void* old_value = NULL; - if (entry->state == ENTRY_ACTIVE) { - old_value = entry->value; - } else { - if (entry->state == ENTRY_TOMBSTONE) ht->tombstone_count--; - ht->count++; - } - - entry->key = key; - entry->value = value; - entry->hash = hash; - entry->state = ENTRY_ACTIVE; - return old_value; -} - -void* hashtable_get(hash_table_t* ht, const void* key) { - if (ht->entries.cap == 0) return NULL; - - u32_t hash = ht->hash_func(key); - hash_entry_t* entry = find_entry(ht, key, hash); - return (entry && entry->state == ENTRY_ACTIVE) ? entry->value : NULL; -} - -void* hashtable_del(hash_table_t* ht, const void* key) { - if (ht->entries.cap == 0) return NULL; - - u32_t hash = ht->hash_func(key); - hash_entry_t* entry = find_entry(ht, key, hash); - - if (entry == NULL || entry->state != ENTRY_ACTIVE) return NULL; - - void* value = entry->value; - entry->state = ENTRY_TOMBSTONE; - ht->count--; - ht->tombstone_count++; - return value; -} - -void hashtable_destory(hash_table_t* ht) { - vector_free(ht->entries); - ht->count = 0; - ht->tombstone_count = 0; -} - -void hashtable_foreach(hash_table_t* ht, hash_table_iter_func iter_func, void* context) { - for (rt_size_t i = 0; i < ht->entries.cap; i++) { - hash_entry_t* entry = &vector_at(ht->entries, i); - if (entry->state == ENTRY_ACTIVE) { - if (!iter_func(entry->key, entry->value, context)) { - break; // enable callback function terminal the iter - } - } - } -} diff --git a/lib/utils/ds/hashtable.h b/lib/utils/ds/hashtable.h deleted file mode 100644 index 33fbfdd..0000000 --- a/lib/utils/ds/hashtable.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @file hashtable.h - * @brief 开放寻址法哈希表实现 - * - * 提供基于向量容器的哈希表实现,支持动态扩容和墓碑机制 - */ - -#ifndef __SMCC_HASHTABLE_H__ -#define __SMCC_HASHTABLE_H__ - -#include -#include -#include "vector.h" - -/** - * @enum ht_entry_state_t - * @brief 哈希表条目状态标识 - */ -typedef enum hash_table_entry_state { - ENTRY_EMPTY, /**< 空槽位(从未使用过) */ - ENTRY_ACTIVE, /**< 有效条目(包含键值对) */ - ENTRY_TOMBSTONE /**< 墓碑标记(已删除条目) */ -} ht_entry_state_t; - -/** - * @struct hash_entry_t - * @brief 哈希表条目结构 - * - * @note key/value内存由调用者管理,哈希表不负责其生命周期 - */ -typedef struct hash_entry { - const void* key; /**< 键指针(不可变) */ - void* value; /**< 值指针 */ - u32_t hash; /**< 预计算的哈希值(避免重复计算) */ - ht_entry_state_t state; /**< 当前条目状态 */ -} hash_entry_t; - -/** - * @struct hash_table_t - * @brief 哈希表主体结构 - * - * 使用开放寻址法实现,采用墓碑标记处理删除操作 - */ -typedef struct hash_table { - VECTOR_HEADER(entries, hash_entry_t); /**< 条目存储容器 */ - u32_t count; /**< 有效条目数量(不含墓碑) */ - u32_t tombstone_count; /**< 墓碑条目数量 */ - /** - * @brief 哈希函数指针 - * @param key 键指针 - * @return 32位无符号哈希值 - */ - u32_t (*hash_func)(const void* key); - /** - * @brief 键比较函数指针 - * @param key1 第一个键指针 - * @param key2 第二个键指针 - * @return 相同返回0,不同返回非0 - */ - int(*key_cmp)(const void* key1, const void* key2); -} hash_table_t; - -/** - * @brief 初始化哈希表结构 - * @param ht 哈希表实例指针 - * - * @warning 必须设置hash_func和key_cmp后才能使用 - */ -void init_hashtable(hash_table_t* ht); - -/** - * @brief 插入/更新键值对 - * @param ht 哈希表实例指针 - * @param key 键指针 - * @param value 值指针 - * @return 被替换的旧值指针(无替换返回NULL) - */ -void* hashtable_set(hash_table_t* ht, const void* key, void* value); - -/** - * @brief 查找键对应值 - * @param ht 哈希表实例指针 - * @param key 查找键指针 - * @return 找到返回值指针,未找到返回NULL - */ -void* hashtable_get(hash_table_t* ht, const void* key); - -/** - * @brief 删除键值对 - * @param ht 哈希表实例指针 - * @param key 要删除的键指针 - * @return 被删除的值指针(不存在返回NULL) - * - * @note 实际采用墓碑标记方式删除 - */ -void* hashtable_del(hash_table_t* ht, const void* key); - -/** - * @brief 销毁哈希表 - * @param ht 哈希表实例指针 - * - * @note 仅释放哈希表内部内存,不会释放key/value内存 - */ -void hashtable_destory(hash_table_t* ht); - -/** - * @typedef hash_table_iter_func - * @brief 哈希表迭代回调函数类型 - * @param key 当前键指针 - * @param value 当前值指针 - * @param context 用户上下文指针 - * @return 返回非0停止迭代 - */ -typedef int (*hash_table_iter_func)(const void* key, void* value, void* context); - -/** - * @brief 遍历哈希表所有有效条目 - * @param ht 哈希表实例指针 - * @param iter_func 迭代回调函数 - * @param context 用户上下文指针 - */ -void hashtable_foreach(hash_table_t* ht, hash_table_iter_func iter_func, void* context); - -#endif // __SMCC_HASHTABLE_H__ diff --git a/lib/utils/ds/kllist.h b/lib/utils/ds/kllist.h deleted file mode 100644 index d6c9d59..0000000 --- a/lib/utils/ds/kllist.h +++ /dev/null @@ -1,158 +0,0 @@ -/** - * kllist.h is a list implement by linux kernel list - * @link https://njusecourse.feishu.cn/wiki/I8vkw2zkwiEInUkujTJc7zzOnwf - * @link https://kernelnewlbies.org/FAQ/LinkedLists - * @link https://lwn.net/Articles/887097/ - * @link https://liuluheng.github.io/wiki/public_html/Embedded-System/kernel/list-and-hlist.html - */ - -#ifndef __KLLIST_H__ -#define __KLLIST_H__ - -#ifndef NULL -#define NULL (0) -#define __NULL_KLIST_DEFINED__ -#endif - -#ifndef container_of -// Magic: https://radek.io/posts/magical-container_of-macro/ -// StackOverflow: https://stackoverflow.com/q/15832301/1833118 -#ifdef __GNUC__ -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) -#else -#define container_of(ptr, type, member) ({ \ - const void *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) -#endif -#endif - -/** - * used by list - */ -struct list_head { - struct list_head *next, *prev; -}; - -/** - * list init - * @example - * 1. struct list_head your_list = LIST_HEAD_INIT(your_list); - * 2. struct list_head your_list; INIT_LIST_HEAD(&your_list); - * 3. LIST_HEAD(your_list); => struct your_list = { &(your_list), &(your_list) }; - */ - -#define LIST_HEAD_INIT(name) { &(name), &(name) } -static inline void INIT_LIST_HEAD(struct list_head *list) { - list->next = list; - list->prev = list; -} -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -/** - * list add - */ - -static inline void __list_add(struct list_head *newl, - struct list_head *prev, - struct list_head *next) { - next->prev = newl; - newl->next = next; - newl->prev = prev; - prev->next = newl; -} - -static inline void list_add(struct list_head *newl, struct list_head *head) { - __list_add(newl, head, head->next); -} - -static inline void list_add_tail(struct list_head *newl, struct list_head *head) { - __list_add(newl, head->prev, head); -} - -/** - * list delete - */ - -static inline void __list_del(struct list_head * prev, struct list_head * next) { - next->prev = prev; - prev->next = next; -} - -static inline void list_del(struct list_head *entry) { - __list_del(entry->prev, entry->next); - entry->next = NULL; - entry->prev = NULL; -} - -/** - * list_is_first -- tests whether @list is the first entry in list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_first(const struct list_head *list, const struct list_head *head) { - return list->prev == head; -} - -/** - * list_is_last - tests whether @list is the last entry in list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_last(const struct list_head *list, const struct list_head *head) { - return list->next == head; -} - -/** - * list_is_head - tests whether @list is the list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_head(const struct list_head *list, const struct list_head *head) { - return list == head; -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(const struct list_head *head) { - return head->next == head; -} - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next) - -/** - * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - */ -#define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev) - -/** - * list sort - * by linux kernel 6.3.1 /lib/list_sort.c - * it remain use sigle linked list to merge sort - * @link https://www.geeksforgeeks.org/merge-sort-for-linked-list/ - */ - -#ifdef HAVE_KLIST_SORT -typedef int (*list_cmp_func_t)(void *, - const struct list_head *, const struct list_head *); -static void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp); -#endif - -#if defined(__NULL_KLIST_DEFINED__) && !defined(__NULL_KLIST_DEFINED_NOMOVE__) -#undef NULL -#endif - -#endif \ No newline at end of file diff --git a/lib/utils/ds/vector-gdb.py b/lib/utils/ds/vector-gdb.py deleted file mode 100644 index d370750..0000000 --- a/lib/utils/ds/vector-gdb.py +++ /dev/null @@ -1,202 +0,0 @@ -# # vector_gdb.py -# import gdb -# import re - -# class VectorPrinter: -# """解析宏定义的 vector 结构体""" - -# def __init__(self, val): -# self.val = val - -# def check_vector_type(self): -# """验证是否为合法 vector 结构体""" -# try: -# # 检查是否包含 size/cap/data 字段 -# return all(self.val.type.has_key(field) -# for field in ['size', 'cap', 'data']) -# except gdb.error: -# return False - -# def get_array_view(self): -# """将 data 字段转换为数组视图""" -# if not self.check_vector_type(): -# return None - -# cap = int(self.val['cap']) -# data_ptr = self.val['data'] - -# if cap == 0 or data_ptr == 0: -# return [] - -# # 构造数组类型 (例如 int[cap]) -# element_type = data_ptr.type.target() -# array_type = element_type.array(cap - 1) # C 数组声明语法 - -# return data_ptr.cast(array_type.pointer()).dereference() - -# def to_string(self): -# if not self.check_vector_type(): -# return "Not a vector type" - -# size = self.val['size'] -# cap = self.val['cap'] -# data = self.get_array_view() - -# return (f"vector(size={size}, cap={cap}, data={data})") - -# class VectorInfoCommand(gdb.Command): -# """自定义命令:显示 vector 详细信息""" - -# def __init__(self): -# super(VectorInfoCommand, self).__init__("vector_info", -# gdb.COMMAND_USER) - -# def invoke(self, arg, from_tty): -# val = gdb.parse_and_eval(arg) -# printer = VectorPrinter(val) - -# if not printer.check_vector_type(): -# print(f"'{arg}' is not a vector structure") -# return - -# size = int(val['size']) -# cap = int(val['cap']) -# data = printer.get_array_view() - -# # 输出格式化信息 -# print(f"Vector {arg}:") -# print(f"├─ Size: {size}") -# print(f"├─ Capacity: {cap}") -# print("└─ Data elements [0..{}]:".format(min(size, cap)-1)) - -# for i in range(min(size, cap)): -# try: -# print(f" [{i}]: {data[i]}") -# except gdb.MemoryError: -# print(f" [{i}]: ") - -# def register_printers(): -# """注册自动类型识别""" -# def vector_matcher(val): -# return VectorPrinter(val).check_vector_type() - -# # 使用 lambda 包装以动态创建 printer -# gdb.pretty_printers.append(lambda val: -# VectorPrinter(val) if vector_matcher(val) else None) - -# # 注册命令和打印机 -# VectorInfoCommand() -# register_printers() - -# vector_gdb.py -import gdb # type: ignore -from gdb.printing import PrettyPrinter # type: ignore - -class VectorPrinter: - """兼容新旧注册方式的最终方案""" - - def __init__(self, val: gdb.Value): - self.val:gdb.Value = val - - def check_type(self) -> bool: - """类型检查(兼容匿名结构体)""" - try: - if self.val.type.code != gdb.TYPE_CODE_STRUCT: - return False - fields = self.val.type.fields() - if not fields: - return False - exp = ['size', 'cap', 'data'] - for t in fields: - if t.name in exp: - exp.remove(t.name) - else: - return False - return True - except gdb.error: - return False - - def to_string(self): - if not self.check_type(): - return "Not a vector" - - return "vector({} size={}, cap={})".format( - self.val.address, - self.val['size'], - self.val['cap'], - ) - - def display_hint(self): - return 'array' - - def children(self): - """生成数组元素(关键改进点)""" - if not self.check_type(): - return [] - - size = int(self.val['size']) - cap = int(self.val['cap']) - data_ptr = self.val['data'] - - if cap == 0 or data_ptr == 0: - return [] - - # 使用 GDB 内置数组转换 - array = data_ptr.dereference() - array = array.cast(data_ptr.type.target().array(cap - 1)) - - for i in range(size): - # state = "" if i < size else "" - try: - value = array[i] - yield (f"[{i}] {value.type} {value.address}", value) - except gdb.MemoryError: - yield (f"[{i}]", "") - -# 注册方式一:传统append方法(您之前有效的方式)self -def append_printer(): - gdb.pretty_printers.append( - lambda val: VectorPrinter(val) if VectorPrinter(val).check_type() else None - ) - -# 注册方式二:新版注册方法(备用方案) -def register_new_printer(): - class VectorPrinterLocator(PrettyPrinter): - def __init__(self): - super().__init__("vector_printer") - - def __call__(self, val): - ret = VectorPrinter(val).check_type() - print(f"ret {ret}, type {val.type}, {[(i.name, i.type) for i in val.type.fields()]}") - return None - - gdb.printing.register_pretty_printer( - gdb.current_objfile(), - VectorPrinterLocator() - ) - -# 双重注册保证兼容性 -append_printer() # 保留您原来有效的方式 -# register_new_printer() # 添加新版注册 - -class VectorInfoCommand(gdb.Command): - """保持原有命令不变""" - def __init__(self): - super().__init__("vector_info", gdb.COMMAND_USER) - - def invoke(self, arg, from_tty): - val = gdb.parse_and_eval(arg) - printer = VectorPrinter(val) - - if not printer.check_type(): - print("Invalid vector") - return - - print("=== Vector Details ===") - print("Size:", val['size']) - print("Capacity:", val['cap']) - print("Elements:") - for name, value in printer.children(): - print(f" {name}: {value}") - -VectorInfoCommand() diff --git a/lib/utils/ds/vector.h b/lib/utils/ds/vector.h deleted file mode 100644 index de25f0f..0000000 --- a/lib/utils/ds/vector.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @file vector.h - * @brief 动态数组(Vector)实现 - * - * 提供类型安全的动态数组容器实现,支持自动扩容和基本操作 - */ - -#ifndef __SMCC_DS_VECTOR_H__ -#define __SMCC_DS_VECTOR_H__ - -#include - -/** @defgroup vector_struct 数据结构定义 */ - -/** - * @def VECTOR_HEADER(name, type) - * @brief 声明向量结构体 - * @param name 结构体变量名 - * @param type 存储的数据类型 - * - * 生成包含size/cap/data三个字段的结构体定义: - * - size: 当前元素数量 - * - cap: 数组容量 - * - data: 存储数组指针 - */ -#define VECTOR_HEADER(name, type) \ - struct { \ - rt_size_t size; /**< 当前元素数量 */ \ - rt_size_t cap; /**< 数组容量 */ \ - type *data; /**< 数据存储指针 */ \ - } name - -/** @defgroup vector_operations 向量操作宏 */ - -/** - * @def vector_init(vec) - * @brief 初始化向量结构体 - * @param vec 要初始化的向量结构体变量 - * - * @note 此宏不会分配内存,仅做零初始化 - */ -#define vector_init(vec) \ - do { \ - (vec).size = 0, \ - (vec).cap = 0, \ - (vec).data = 0; \ - } while(0) - -/** - * @def vector_push(vec, value) - * @brief 添加元素到向量末尾 - * @param vec 目标向量结构体 - * @param value 要添加的值(需匹配存储类型) - * - * @note 当容量不足时自动扩容为2倍(初始容量为8) - * @warning 内存分配失败时会触发LOG_FATAL - */ -#define vector_push(vec, value) \ - do { \ - if (vec.size >= vec.cap) { \ - int cap = vec.cap ? vec.cap * 2 : 8; \ - void* data = salloc_realloc(vec.data, cap * sizeof(*vec.data)); \ - if (!data) { \ - LOG_FATAL("vector_push: rt_realloc failed\n"); \ - } \ - (vec).cap = cap; \ - (vec).data = data; \ - } \ - (vec).data[(vec).size++] = value; \ - } while(0) - -/** - * @def vector_pop(vec) - * @brief 弹出最后一个元素 - * @param vec 目标向量结构体 - * @return 最后元素的引用 - * @warning 需确保size > 0时使用 - */ -#define vector_pop(vec) \ - ((vec).data[--(vec).size]) - -/** - * @def vector_at(vec, idx) - * @brief 获取指定索引元素 - * @param vec 目标向量结构体 - * @param idx 元素索引(0 <= idx < size) - * @return 对应元素的引用 - */ -#define vector_at(vec, idx) \ - (((vec).data)[idx]) - -/** - * @def vector_idx(vec, ptr) - * @brief 获取元素指针对应的索引 - * @param vec 目标向量结构体 - * @param ptr 元素指针(需在data数组范围内) - * @return 元素索引值 - */ -#define vector_idx(vec, ptr) \ - ((ptr) - (vec).data) - -/** - * @def vector_free(vec) - * @brief 释放向量内存 - * @param vec 目标向量结构体 - * - * @note 释放后需重新初始化才能再次使用 - */ -#define vector_free(vec) \ - do { \ - salloc_free((vec).data); \ - (vec).data = NULL; \ - (vec).size = (vec).cap = 0; \ - } while(0) - -#endif // __SMCC_DS_VECTOR_H__ diff --git a/lib/utils/gdb.py b/lib/utils/gdb.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/utils/strpool/strpool.c b/lib/utils/strpool/strpool.c deleted file mode 100644 index 98512cb..0000000 --- a/lib/utils/strpool/strpool.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "strpool.h" - -void init_strpool(strpool_t* pool) { - lalloc_init(&pool->stralloc); - - pool->ht.hash_func = (u32_t(*)(const void*))rt_strhash; - pool->ht.key_cmp = (int(*)(const void*, const void*))rt_strcmp; - init_hashtable(&pool->ht); -} - -const char* strpool_intern(strpool_t* pool, const char* str) { - void* existing = hashtable_get(&pool->ht, str); - if (existing) { - return existing; - } - - rt_size_t len = rt_strlen(str) + 1; - char* new_str = lalloc_alloc(&pool->stralloc, len); - if (!new_str) { - LOG_ERROR("strpool: Failed to allocate memory for string"); - return NULL; - } - rt_memcpy(new_str, str, len); - - hashtable_set(&pool->ht, new_str, new_str); - return new_str; -} - -void strpool_destroy(strpool_t* pool) { - hashtable_destory(&pool->ht); - lalloc_destroy(&pool->stralloc); -} diff --git a/lib/utils/strpool/strpool.h b/lib/utils/strpool/strpool.h deleted file mode 100644 index cbe0dda..0000000 --- a/lib/utils/strpool/strpool.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file strpool.h - * @brief 字符串池实现 - * - * 提供字符串驻留(String Interning)功能,保证相同字符串的唯一性存储 - */ - -#ifndef __SMCC_STRPOOL_H__ -#define __SMCC_STRPOOL_H__ - -#include -#include -#include - -/** - * @struct strpool_t - * @brief 字符串池上下文 - * - * 组合哈希表和专用内存分配器实现的高效字符串存储池 - */ -typedef struct strpool { - hash_table_t ht; /**< 哈希表用于快速查找已存储字符串 */ - long_alloc_t stralloc; /**< 长块分配器优化小字符串内存管理 */ -} strpool_t; - -/** - * @brief 初始化字符串池 - * @param pool 字符串池实例指针 - * - * @warning 使用前需确保 hashtable 的 hash_func 和 key_cmp 已正确设置 - */ -void init_strpool(strpool_t* pool); - -/** - * @brief 驻留字符串到池中 - * @param pool 字符串池实例指针 - * @param str 要驻留的 C 字符串 - * @return 池中唯一字符串的持久指针 - * - * @note 返回值生命周期与字符串池一致 - * @note 重复插入相同字符串会返回已有指针 - */ -const char* strpool_intern(strpool_t* pool, const char* str); - -/** - * @brief 销毁字符串池 - * @param pool 字符串池实例指针 - * - * @warning 销毁后已获取的字符串指针将失效 - * @note 会自动释放所有驻留字符串内存 - */ -void strpool_destroy(strpool_t* pool); - -#endif // __SMCC_STRPOOL_H__ diff --git a/lib/utils/tokbuf/tokbuf.c b/lib/utils/tokbuf/tokbuf.c deleted file mode 100644 index 66fc37e..0000000 --- a/lib/utils/tokbuf/tokbuf.c +++ /dev/null @@ -1,19 +0,0 @@ -// #include -// #include "token.h" - -// #define ROUND_IDX(idx) ((idx) % tokbuf->cap) -// #define POW2(x) (1 << (x)) - -// void init_toks(tok_stream_t* tokbuf, int cap, -// tok_stream_close_func close, tok_stream_get_func gettok, void* stream) -// { -// tokbuf->cap_mask = POW2(cap) - 1; -// // tokbuf->buf = - -// } -// int toks_next( tok_stream_t* toks, tok_t* out); -// int toks_peek( tok_stream_t* toks, tok_t* out, int lookahead); -// const tok_t* toks_peek_ref(tok_stream_t* toks, int lookahead); -// int toks_reset(tok_stream_t* toks); -// int toks_seek( tok_stream_t* toks, int pos); -// int toks_close(tok_stream_t* toks); diff --git a/lib/utils/tokbuf/tokbuf.h b/lib/utils/tokbuf/tokbuf.h deleted file mode 100644 index af055d1..0000000 --- a/lib/utils/tokbuf/tokbuf.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef __SMCC_TOKBUF_H__ -#define __SMCC_TOKBUF_H__ - -#include - -typedef struct loc { - const char *fname; - int line; - int col; - int len; -} loc_t; - -typedef enum tok_basic_type { - TK_BASIC_INVALID, // 错误占位 - TK_BASIC_KEYWORD, // 关键字 - TK_BASIC_OPERATOR, // 操作符 - TK_BASIC_IDENTIFIER, // 标识符 - TK_BASIC_LITERAL, // 字面量 - - TK_BASIC_WHITESPACE, // 空白 - TK_BASIC_COMMENT, // 注释 - TK_BASIC_EOF // 结束标记 -} tok_basic_type_t; - -typedef union ctype { - u8_t u8; - u16_t u16; - u32_t u32; - u64_t u64; - i8_t i8; - i16_t i16; - i32_t i32; - i64_t i64; - f32_t f32; - f64_t f64; - iptr_t iptr; - uptr_t uptr; - void* ptr; - char ch; - int i; - - // MUST BE strpool ptr - const char* str; -} ctype_t; - -typedef struct tok { - tok_basic_type_t type; - int sub_type; - loc_t loc; - ctype_t val; -} tok_t; - -// typedef void(*tok_stream_close_func)(void* stream); -// typedef void(*tok_stream_get_func)(void* stream, tok_t* token); -// typedef struct tok_stream { -// int cur; -// int end; -// int cap_mask; -// tok_t* buf; - -// void* stream; -// tok_stream_close_func close; -// tok_stream_get_func gettok; -// } tok_stream_t; - -// void init_toks(tok_stream_t* tokbuf, int cap, -// tok_stream_close_func close, tok_stream_get_func gettok, void* stream); -// int toks_next( tok_stream_t* toks, tok_t* out); -// int toks_peek( tok_stream_t* toks, tok_t* out, int lookahead); -// const tok_t* toks_peek_unsafe(tok_stream_t* toks, int lookahead); -// int toks_reset(tok_stream_t* toks); -// int toks_seek( tok_stream_t* toks, int pos); -// int toks_close(tok_stream_t* toks); - -#endif // __SMCC_TOKEN_H__ diff --git a/lib/utils/utils.h b/lib/utils/utils.h deleted file mode 100644 index 96a3393..0000000 --- a/lib/utils/utils.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __SMCC_LIB_UTILS_H__ -#define __SMCC_LIB_UTILS_H__ - -#include "strpool/strpool.h" -#include "tokbuf/tokbuf.h" - -#endif diff --git a/libs/README.md b/libs/README.md new file mode 100644 index 0000000..9b4b272 --- /dev/null +++ b/libs/README.md @@ -0,0 +1,9 @@ + +lexer 词法分析 +parse 语法分析 +ast 抽象语法树 +sema 语义分析 +ir 中间代码标识 +opt 优化器 +codegen 代码生成 +target 目标平台支持 diff --git a/src/ccompiler/frontend/lexer/README.md b/libs/lexer/README.md similarity index 100% rename from src/ccompiler/frontend/lexer/README.md rename to libs/lexer/README.md diff --git a/libs/lexer/cbuild.toml b/libs/lexer/cbuild.toml new file mode 100644 index 0000000..04391f6 --- /dev/null +++ b/libs/lexer/cbuild.toml @@ -0,0 +1,6 @@ +[package] +name = "smcc_lex" + +dependencies = [ + { name = "libcore", path = "../../runtime/libcore" }, +] diff --git a/libs/lexer/include/lexer.h b/libs/lexer/include/lexer.h new file mode 100644 index 0000000..76da65f --- /dev/null +++ b/libs/lexer/include/lexer.h @@ -0,0 +1,62 @@ +/** + * @file lexer.h + * @brief C语言词法分析器核心数据结构与接口 + */ + +#ifndef __SMCC_CC_LEXER_H__ +#define __SMCC_CC_LEXER_H__ + +#include +#include "lexer_stream.h" +#include "lexer_token.h" + +typedef struct lexer_loc { + const char *name; + usize name_len; + usize line; + usize column; + usize offset; +} lexer_loc_t; + +typedef struct lexer_token { + token_type_t type; + core_cvalue_t value; + lexer_loc_t loc; +} lexer_tok_t; + +/** + * @brief 词法分析器核心结构体 + * + * 封装词法分析所需的状态信息和缓冲区管理 + */ +typedef struct cc_lexer { + lexer_stream_t* stream; + lexer_loc_t pos; +} smcc_lexer_t; + +/** + * @brief 初始化词法分析器 + * @param[out] lexer 要初始化的词法分析器实例 + * @param[in] stream 输入流对象指针 + */ +void lexer_init(smcc_lexer_t* lexer, lexer_stream_t* stream); + +/** + * @brief 获取原始token + * @param[in] lexer 词法分析器实例 + * @param[out] token 输出token存储位置 + * + * 此函数会返回所有类型的token,包括空白符等无效token + */ +void lexer_get_token(smcc_lexer_t* lexer, lexer_tok_t* token); + +/** + * @brief 获取有效token + * @param[in] lexer 词法分析器实例 + * @param[out] token 输出token存储位置 + * + * 此函数会自动跳过空白符等无效token,返回对语法分析有意义的token + */ +void lexer_get_valid_token(smcc_lexer_t* lexer, lexer_tok_t* token); + +#endif diff --git a/libs/lexer/include/lexer_log.h b/libs/lexer/include/lexer_log.h new file mode 100644 index 0000000..493dbec --- /dev/null +++ b/libs/lexer/include/lexer_log.h @@ -0,0 +1,48 @@ +#ifndef __SMCC_LEXER_LOG_H__ +#define __SMCC_LEXER_LOG_H__ + +#include + +#ifndef LEX_LOG_LEVEL +#define LEX_LOG_LEVEL 4 +#endif + +#if LEX_LOG_LEVEL <= 1 +#define LEX_NOTSET( fmt, ...) MLOG_NOTSET(&__smcc_lexer_log, fmt, ##__VA_ARGS__) +#else +#define LEX_NOTSET( fmt, ...) +#endif + +#if LEX_LOG_LEVEL <= 2 +#define LEX_DEBUG( fmt, ...) MLOG_DEBUG(&__smcc_lexer_log, fmt, ##__VA_ARGS__) +#else +#define LEX_DEBUG( fmt, ...) +#endif + +#if LEX_LOG_LEVEL <= 3 +#define LEX_INFO( fmt, ...) MLOG_INFO(&__smcc_lexer_log, fmt, ##__VA_ARGS__) +#else +#define LEX_INFO( fmt, ...) +#endif + +#if LEX_LOG_LEVEL <= 4 +#define LEX_WARN( fmt, ...) MLOG_WARN(&__smcc_lexer_log, fmt, ##__VA_ARGS__) +#else +#define LEX_WARN( fmt, ...) +#endif + +#if LEX_LOG_LEVEL <= 5 +#define LEX_ERROR( fmt, ...) MLOG_ERROR(&__smcc_lexer_log, fmt, ##__VA_ARGS__) +#else +#define LEX_ERROR( fmt, ...) +#endif + +#if LEX_LOG_LEVEL <= 6 +#define LEX_FATAL( fmt, ...) MLOG_FATAL(&__smcc_lexer_log, fmt, ##__VA_ARGS__) +#else +#define LEX_FATAL( fmt, ...) +#endif + +extern logger_t __smcc_lexer_log; + +#endif // __SMCC_LEXER_LOG_H__ diff --git a/libs/lexer/include/lexer_stream.h b/libs/lexer/include/lexer_stream.h new file mode 100644 index 0000000..968e9b8 --- /dev/null +++ b/libs/lexer/include/lexer_stream.h @@ -0,0 +1,37 @@ +#include + +typedef struct lexer_stream lexer_stream_t; + +#define lexer_stream_eof (-1) + +struct lexer_stream { + const char* name; + usize name_len; + + /// @brief 读取指定数量的字符到缓冲区 + usize (*read_buf)(lexer_stream_t* stream, char* buffer, usize count); + + /// @brief 获取下一个字符 + int (*peek_char)(lexer_stream_t* stream); + + /// @brief 重置字符流位置 + void (*reset_char) (lexer_stream_t* stream); + + /// @brief 读取并消费下一个字符(移动流位置) + int (*next_char)(lexer_stream_t* stream); + + /// @brief 释放资源 + void (*free_stream) (lexer_stream_t* steam); +}; + +#ifndef __SMCC_LEXER_NO_MEM_STREAM__ +typedef struct lexer_mem_stream { + lexer_stream_t stream; + const char* data; + usize data_length; + usize curr_pos; + usize peek_pos; + cbool owned; +} lexer_mem_stream_t; +lexer_stream_t* lexer_mem_stream_init(lexer_mem_stream_t* stream, const char* data, usize length, cbool need_copy); +#endif diff --git a/libs/lexer/include/lexer_token.h b/libs/lexer/include/lexer_token.h new file mode 100644 index 0000000..59ccc55 --- /dev/null +++ b/libs/lexer/include/lexer_token.h @@ -0,0 +1,137 @@ +#ifndef __SMCC_CC_TOKEN_H__ +#define __SMCC_CC_TOKEN_H__ + +#include + +typedef enum ckeyword { + CSTD_C89, + CSTD_C99, + CEXT_ASM, +} ckeyword_t; + +// Using Binary Search To Fast Find Keyword +#define KEYWORD_TABLE \ + X(asm , TK_BASIC_KEYWORD , TOKEN_ASM , CEXT_ASM) \ + X(break , TK_BASIC_KEYWORD , TOKEN_BREAK , CSTD_C89) \ + X(case , TK_BASIC_KEYWORD , TOKEN_CASE , CSTD_C89) \ + X(char , TK_BASIC_KEYWORD , TOKEN_CHAR , CSTD_C89) \ + X(const , TK_BASIC_KEYWORD , TOKEN_CONST , CSTD_C89) \ + X(continue , TK_BASIC_KEYWORD , TOKEN_CONTINUE , CSTD_C89) \ + X(default , TK_BASIC_KEYWORD , TOKEN_DEFAULT , CSTD_C89) \ + X(do , TK_BASIC_KEYWORD , TOKEN_DO , CSTD_C89) \ + X(double , TK_BASIC_KEYWORD , TOKEN_DOUBLE , CSTD_C89) \ + X(else , TK_BASIC_KEYWORD , TOKEN_ELSE , CSTD_C89) \ + X(enum , TK_BASIC_KEYWORD , TOKEN_ENUM , CSTD_C89) \ + X(extern , TK_BASIC_KEYWORD , TOKEN_EXTERN , CSTD_C89) \ + X(float , TK_BASIC_KEYWORD , TOKEN_FLOAT , CSTD_C89) \ + X(for , TK_BASIC_KEYWORD , TOKEN_FOR , CSTD_C89) \ + X(goto , TK_BASIC_KEYWORD , TOKEN_GOTO , CSTD_C89) \ + X(if , TK_BASIC_KEYWORD , TOKEN_IF , CSTD_C89) \ + X(inline , TK_BASIC_KEYWORD , TOKEN_INLINE , CSTD_C99) \ + X(int , TK_BASIC_KEYWORD , TOKEN_INT , CSTD_C89) \ + X(long , TK_BASIC_KEYWORD , TOKEN_LONG , CSTD_C89) \ + X(register , TK_BASIC_KEYWORD , TOKEN_REGISTER , CSTD_C89) \ + X(restrict , TK_BASIC_KEYWORD , TOKEN_RESTRICT , CSTD_C99) \ + X(return , TK_BASIC_KEYWORD , TOKEN_RETURN , CSTD_C89) \ + X(short , TK_BASIC_KEYWORD , TOKEN_SHORT , CSTD_C89) \ + X(signed , TK_BASIC_KEYWORD , TOKEN_SIGNED , CSTD_C89) \ + X(sizeof , TK_BASIC_KEYWORD , TOKEN_SIZEOF , CSTD_C89) \ + X(static , TK_BASIC_KEYWORD , TOKEN_STATIC , CSTD_C89) \ + X(struct , TK_BASIC_KEYWORD , TOKEN_STRUCT , CSTD_C89) \ + X(switch , TK_BASIC_KEYWORD , TOKEN_SWITCH , CSTD_C89) \ + X(typedef , TK_BASIC_KEYWORD , TOKEN_TYPEDEF , CSTD_C89) \ + X(union , TK_BASIC_KEYWORD , TOKEN_UNION , CSTD_C89) \ + X(unsigned , TK_BASIC_KEYWORD , TOKEN_UNSIGNED , CSTD_C89) \ + X(void , TK_BASIC_KEYWORD , TOKEN_VOID , CSTD_C89) \ + X(volatile , TK_BASIC_KEYWORD , TOKEN_VOLATILE , CSTD_C89) \ + X(while , TK_BASIC_KEYWORD , TOKEN_WHILE , CSTD_C89) \ + // KEYWORD_TABLE + +#define TOKEN_TABLE \ + X(unknown , TK_BASIC_INVALID, TOKEN_UNKNOWN ) \ + X(EOF , TK_BASIC_EOF, TOKEN_EOF ) \ + X(blank , TK_BASIC_EMPTYSPACE, TOKEN_BLANK ) \ + X("==" , TK_BASIC_OPERATOR, TOKEN_EQ ) \ + X("=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN ) \ + X("++" , TK_BASIC_OPERATOR, TOKEN_ADD_ADD ) \ + X("+=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_ADD ) \ + X("+" , TK_BASIC_OPERATOR, TOKEN_ADD ) \ + X("--" , TK_BASIC_OPERATOR, TOKEN_SUB_SUB ) \ + X("-=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_SUB ) \ + X("->" , TK_BASIC_OPERATOR, TOKEN_DEREF ) \ + X("-" , TK_BASIC_OPERATOR, TOKEN_SUB ) \ + X("*=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_MUL ) \ + X("*" , TK_BASIC_OPERATOR, TOKEN_MUL ) \ + X("/=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_DIV ) \ + X("/" , TK_BASIC_OPERATOR, TOKEN_DIV ) \ + X("//" , TK_BASIC_COMMENT , TOKEN_LINE_COMMENT ) \ + X("/* */" , TK_BASIC_COMMENT , TOKEN_BLOCK_COMMENT ) \ + X("%=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_MOD ) \ + X("%" , TK_BASIC_OPERATOR, TOKEN_MOD ) \ + X("&&" , TK_BASIC_OPERATOR, TOKEN_AND_AND ) \ + X("&=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_AND ) \ + X("&" , TK_BASIC_OPERATOR, TOKEN_AND ) \ + X("||" , TK_BASIC_OPERATOR, TOKEN_OR_OR ) \ + X("|=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_OR ) \ + X("|" , TK_BASIC_OPERATOR, TOKEN_OR ) \ + X("^=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_XOR ) \ + X("^" , TK_BASIC_OPERATOR, TOKEN_XOR ) \ + X("<<=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_L_SH ) \ + X("<<" , TK_BASIC_OPERATOR, TOKEN_L_SH ) \ + X("<=" , TK_BASIC_OPERATOR, TOKEN_LE ) \ + X("<" , TK_BASIC_OPERATOR, TOKEN_LT ) \ + X(">>=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_R_SH ) \ + X(">>" , TK_BASIC_OPERATOR, TOKEN_R_SH ) \ + X(">=" , TK_BASIC_OPERATOR, TOKEN_GE ) \ + X(">" , TK_BASIC_OPERATOR, TOKEN_GT ) \ + X("!" , TK_BASIC_OPERATOR, TOKEN_NOT ) \ + X("!=" , TK_BASIC_OPERATOR, TOKEN_NEQ ) \ + X("~" , TK_BASIC_OPERATOR, TOKEN_BIT_NOT ) \ + X("[" , TK_BASIC_OPERATOR, TOKEN_L_BRACKET ) \ + X("]" , TK_BASIC_OPERATOR, TOKEN_R_BRACKET ) \ + X("(" , TK_BASIC_OPERATOR, TOKEN_L_PAREN ) \ + X(")" , TK_BASIC_OPERATOR, TOKEN_R_PAREN ) \ + X("{" , TK_BASIC_OPERATOR, TOKEN_L_BRACE ) \ + X("}" , TK_BASIC_OPERATOR, TOKEN_R_BRACE ) \ + X(";" , TK_BASIC_OPERATOR, TOKEN_SEMICOLON ) \ + X("," , TK_BASIC_OPERATOR, TOKEN_COMMA ) \ + X(":" , TK_BASIC_OPERATOR, TOKEN_COLON ) \ + X("." , TK_BASIC_OPERATOR, TOKEN_DOT ) \ + X("..." , TK_BASIC_OPERATOR, TOKEN_ELLIPSIS ) \ + X("?" , TK_BASIC_OPERATOR, TOKEN_COND ) \ + X(ident , TK_BASIC_IDENTIFIER, TOKEN_IDENT ) \ + X(int_literal , TK_BASIC_LITERAL, TOKEN_INT_LITERAL ) \ + X(float_literal , TK_BASIC_LITERAL, TOKEN_FLOAT_LITERAL ) \ + X(char_literal , TK_BASIC_LITERAL, TOKEN_CHAR_LITERAL ) \ + X(string_literal , TK_BASIC_LITERAL, TOKEN_STRING_LITERAL ) \ + // END + +// 定义TokenType枚举 +typedef enum cc_tktype { + // 处理普通token + #define X(str, subtype, tok) tok, + TOKEN_TABLE + #undef X + + // 处理关键字(保持原有格式) + #define X(name, subtype, tok, std) tok, + KEYWORD_TABLE + #undef X +} token_type_t; + +typedef enum token_subtype { + TK_BASIC_INVALID, // 错误占位 + TK_BASIC_KEYWORD, // 关键字 + TK_BASIC_OPERATOR, // 操作符 + TK_BASIC_IDENTIFIER, // 标识符 + TK_BASIC_LITERAL, // 字面量 + + TK_BASIC_EMPTYSPACE, // 空白 + TK_BASIC_COMMENT, // 注释 + TK_BASIC_EOF // 结束标记 +} token_subtype_t; + +token_subtype_t get_tok_subtype(token_type_t type); +const char* get_tok_name(token_type_t type); + +#endif diff --git a/libs/lexer/src/lexer.c b/libs/lexer/src/lexer.c new file mode 100644 index 0000000..5d34861 --- /dev/null +++ b/libs/lexer/src/lexer.c @@ -0,0 +1,637 @@ +/** + * 仿照LCCompiler的词法分析部分 + * + * 如下为LCC的README in 2025.2 +This hierarchy is the distribution for lcc version 4.2. + +lcc version 3.x is described in the book "A Retargetable C Compiler: +Design and Implementation" (Addison-Wesley, 1995, ISBN 0-8053-1670-1). +There are significant differences between 3.x and 4.x, most notably in +the intermediate code. For details, see +https://drh.github.io/lcc/documents/interface4.pdf. + +VERSION 4.2 IS INCOMPATIBLE WITH EARLIER VERSIONS OF LCC. DO NOT +UNLOAD THIS DISTRIBUTION ON TOP OF A 3.X DISTRIBUTION. + +LCC is a C89 ("ANSI C") compiler designed to be highly retargetable. + +LOG describes the changes since the last release. + +CPYRIGHT describes the conditions under you can use, copy, modify, and +distribute lcc or works derived from lcc. + +doc/install.html is an HTML file that gives a complete description of +the distribution and installation instructions. + +Chris Fraser / cwf@aya.yale.edu +David Hanson / drh@drhanson.net + */ +#include +#include + +static const struct { + const char* name; + ckeyword_t std_type; + token_type_t tok; +} keywords[] = { + #define X(name, subtype, tok, std_type,...) { #name, std_type, tok }, + KEYWORD_TABLE + #undef X +}; + +// by using binary search to find the keyword +static inline int keyword_cmp(const char* name, int len) { + int low = 0; + int high = sizeof(keywords) / sizeof(keywords[0]) - 1; + while (low <= high) { + int mid = (low + high) / 2; + const char *key = keywords[mid].name; + int cmp = 0; + + // 自定义字符串比较逻辑 + for (int i = 0; i < len; i++) { + if (name[i] != key[i]) { + cmp = (unsigned char)name[i] - (unsigned char)key[i]; + break; + } + if (name[i] == '\0') break; // 遇到终止符提前结束 + } + + if (cmp == 0) { + // 完全匹配检查(长度相同) + if (key[len] == '\0') return mid; + cmp = -1; // 当前关键词比输入长 + } + + if (cmp < 0) { + high = mid - 1; + } else { + low = mid + 1; + } + } + return -1; // Not a keyword. +} + +void lexer_init(smcc_lexer_t* lexer, lexer_stream_t* stream) { + lexer->stream = stream; + lexer->pos = (lexer_loc_t) { + .name = stream->name, + .name_len = stream->name_len, + .line = 1, + .column = 1, + .offset = 0, + }; +} + +#define stream_reset_char(stream) ((stream)->reset_char(stream)) +#define stream_next_char(stream) ((stream)->next_char(stream)) +#define stream_peek_char(stream) ((stream)->peek_char(stream)) +#define lexer_next_pos(lexer) ((lexer)->pos.column ++, (lexer)->pos.offset ++) +#define lexer_next_line(lexer) ((lexer)->pos.line ++, (lexer)->pos.column = 1) +#define set_err_token(token) ((token)->type = TOKEN_UNKNOWN) + +static void skip_newline(smcc_lexer_t* lexer, lexer_tok_t* token) { + lexer_stream_t* stream = lexer->stream; + token->type = TOKEN_LINE_COMMENT; + + // 循环直到遇到换行符或文件结束 + while (1) { + int ch = stream_next_char(stream); + + if (ch == lexer_stream_eof) { + // 到达文件末尾,直接返回 + return; + } + + // 更新位置信息 + lexer_next_pos(lexer); + if (ch == '\n') { + // 遇到换行符,增加行号并重置列号 + lexer_next_line(lexer); + return; + } + } +} + +static void skip_block_comment(smcc_lexer_t* lexer, lexer_tok_t* token) { + lexer_stream_t* stream = lexer->stream; + token->type = TOKEN_BLOCK_COMMENT; + int ch; + + stream_reset_char(stream); + ch = stream_next_char(stream); + lexer_next_pos(lexer); + // FIXME Assertion + Assert (ch == '/'); + ch = stream_next_char(stream); + lexer_next_pos(lexer); + Assert (ch == '*'); + // 我们已经识别了 "/*",现在需要找到 "*/" + while (1) { + ch = stream_next_char(stream); + lexer_next_pos(lexer); + + if (ch == lexer_stream_eof) { + // 未闭合的块注释 + LEX_WARN("Unterminated block comment"); + return; + } + + // LEX_ERROR("%c", ch); + + // 更新位置信息 + if (ch == '\n') { + lexer_next_line(lexer); + } else if (ch == '*') { + // 查看下一个字符是否是 '/' + int next_ch = stream_peek_char(stream); + + if (next_ch == '/') { + // 消费 '/' 字符 + stream_next_char(stream); + + // 更新位置信息 + lexer_next_pos(lexer); + + // 成功找到注释结束标记 + return; + } + } + } +} + +// TODO escape character not enough +static inline int got_slash(int peek) { + switch (peek) { + case '\\': return '\\'; + case '\'': return '\''; + case '\"': return '\"'; + case '\?': return '\?'; + case '0': return '\0'; + + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + default: break; + } + return -1; +} + +static void parse_char(smcc_lexer_t* lexer, lexer_tok_t* token) { + token->loc = lexer->pos; + token->type = TOKEN_CHAR_LITERAL; + lexer_stream_t *stream = lexer->stream; + stream_reset_char(stream); + int ch = stream_peek_char(stream); + + if (ch == lexer_stream_eof) { + LEX_WARN("Unexpected EOF at begin"); + goto ERR; + } else if (ch != '\'') { + LEX_WARN("Unexpected character '%c' at begin", ch); + goto ERR; + } + stream_next_char(stream); + lexer_next_pos(lexer); + + ch = stream_next_char(stream); + lexer_next_pos(lexer); + + if (ch == lexer_stream_eof) { + LEX_WARN("Unexpected EOF at middle"); + goto ERR; + } else if (ch == '\\') { + ch = stream_next_char(stream); + lexer_next_pos(lexer); + if ((ch = got_slash(ch)) == -1) { + LEX_ERROR("Invalid escape character"); + // TODO 特殊情况处理 + goto ERR; + } + token->value.ch = ch; + } else { + token->value.ch = ch; + } + if ((ch = stream_next_char(stream)) != '\'') { + LEX_ERROR("Unclosed character literal '%c' at end, expect `'`", ch); + lexer_next_pos(lexer); + goto ERR; + } + + return; +ERR: + set_err_token(token); +} + +static void parse_string(smcc_lexer_t* lexer, lexer_tok_t* token) { + token->loc = lexer->pos; + token->type = TOKEN_STRING_LITERAL; + lexer_stream_t *stream = lexer->stream; + stream_reset_char(stream); + int ch = stream_peek_char(stream); + + if (ch == lexer_stream_eof) { + LEX_WARN("Unexpected EOF at begin"); + goto ERR; + } else if (ch != '"') { + LEX_WARN("Unexpected character '%c' at begin", ch); + goto ERR; + } + stream_next_char(stream); + lexer_next_pos(lexer); + + int base = 0; + cstring_t str = cstring_new(); + while (1) { + ch = stream_peek_char(stream); + + if (ch == lexer_stream_eof) { + LEX_ERROR("Unexpected EOF at string literal"); + break; + } else if (ch == '\n') { + LEX_ERROR("Unexpected newline at string literal"); + break; + } else if (ch == '\\') { + // TODO bad practice and maybe bugs here + stream_next_char(stream); + ch = stream_next_char(stream); + int val = got_slash(ch); + if (val == -1) { + LEX_ERROR("Invalid escape character it is \\%c [%d]", ch, ch); + } else { + cstring_push(&str, val); + continue; + } + } else if (ch == '"') { + stream_next_char(stream); + lexer_next_pos(lexer); + break; + } + + stream_next_char(stream); + lexer_next_pos(lexer); + cstring_push(&str, ch); + } + + token->value.cstr.data = (char*)cstring_as_cstr(&str); + token->value.cstr.len = cstring_len(&str); + return; +ERR: + set_err_token(token); +} + +static void parse_number(smcc_lexer_t* lexer, lexer_tok_t* token) { + token->loc = lexer->pos; + lexer_stream_t *stream = lexer->stream; + stream_reset_char(stream); + int ch = stream_peek_char(stream); + int base = 0; + if (ch == lexer_stream_eof) { + LEX_WARN("Unexpected EOF at begin"); + goto ERR; + } else if (ch == '0') { + ch = stream_peek_char(stream); + if (ch == 'x' || ch == 'X') { + base = 16; + stream_next_char(stream); + lexer_next_pos(lexer); + stream_next_char(stream); + lexer_next_pos(lexer); + } else if (ch == 'b' || ch == 'B') { + // FIXME C23 external integer base + base = 2; + stream_next_char(stream); + lexer_next_pos(lexer); + stream_next_char(stream); + lexer_next_pos(lexer); + } else if (ch >= '0' && ch <= '7') { + base = 8; + stream_next_char(stream); + lexer_next_pos(lexer); + } else { + base = 10; + } + } else { + base = 10; + } + + // 解析整数部分 + stream_reset_char(stream); + int tmp = 0; + token->value.n = 0; + while (1) { + ch = stream_peek_char(stream); + + if (ch == lexer_stream_eof) { + break; + } else if (ch >= 'a' && ch <= 'z') { + tmp = ch - 'a' + 10; + } else if (ch >= 'A' && ch <= 'Z') { + tmp = ch - 'A' + 10; + } else if (ch >= '0' && ch <= '9') { + tmp = ch - '0'; + } else { + break; + } + + if (tmp >= base) { + LOG_ERROR("Invalid digit"); + break; + } + + stream_next_char(stream); + lexer_next_pos(lexer); + token->value.n = token->value.n * base + tmp; + // TODO number overflow + } + + token->type = TOKEN_INT_LITERAL; + return; +ERR: + set_err_token(token); +} + +static void parse_line(smcc_lexer_t* lexer, lexer_tok_t* token) { + token->loc = lexer->pos; + lexer_stream_t *stream = lexer->stream; + stream_reset_char(stream); + int ch = stream_peek_char(stream); + + if (ch == lexer_stream_eof) { + LEX_WARN("Unexpected EOF at begin"); + goto ERR; + } else if (ch != '#') { + LEX_WARN("Unexpected character '%c' at begin", ch); + goto ERR; + } + + const char line[] = "line"; + + for (int i = 0; i < sizeof(line); i++) { + ch = stream_next_char(stream); + lexer_next_pos(lexer); + if (ch != line[i]) { + LEX_WARN("Maroc does not support in lexer rather in preprocessor, it will be ignored"); + skip_newline(lexer, token); + goto SKIP_LINE; + } + } + + parse_number(lexer, token); + if (token->type != TOKEN_INT_LITERAL) { + LEX_ERROR("Invalid line number"); + goto SKIP_LINE; + } + + if (stream_next_char(stream) != ' ') { + skip_newline(lexer, token); + token->loc.line = token->value.n; + } + + if (stream_peek_char(stream) != '"') { + LEX_ERROR("Invalid `#` line"); + goto SKIP_LINE; + } + parse_string(lexer, token); + if (token->type != TOKEN_STRING_LITERAL) { + LEX_ERROR("Invalid filename"); + goto SKIP_LINE; + } + + skip_newline(lexer, token); + token->loc.line = token->value.n; + // FIXME memory leak + token->loc.name = cstring_as_cstr((const cstring_t *)&token->value.cstr); + token->loc.name_len = cstring_len((const cstring_t *)&token->value.cstr); + + return; +SKIP_LINE: + skip_newline(lexer, token); +ERR: + set_err_token(token); +} + +// /zh/c/language/operator_arithmetic.html +void lexer_get_token(smcc_lexer_t* lexer, lexer_tok_t* token) { + token->loc = lexer->pos; + token->type = TOKEN_UNKNOWN; + lexer_stream_t *stream = lexer->stream; + + stream_reset_char(stream); + token_type_t type = TOKEN_UNKNOWN; + int ch = stream_peek_char(stream); + + // once step + switch (ch) { + case '=': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_EQ; goto double_char; + default: stream_reset_char(stream), type = TOKEN_ASSIGN; break; + } break; + case '+': + switch (stream_peek_char(stream)) { + case '+': type = TOKEN_ADD_ADD; goto double_char; + case '=': type = TOKEN_ASSIGN_ADD; goto double_char; + default: stream_reset_char(stream), type = TOKEN_ADD; break; + } break; + case '-': + switch (stream_peek_char(stream)) { + case '-': type = TOKEN_SUB_SUB; goto double_char; + case '=': type = TOKEN_ASSIGN_SUB; goto double_char; + case '>': type = TOKEN_DEREF; goto double_char; + default: stream_reset_char(stream), type = TOKEN_SUB; break; + } break; + case '*': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_ASSIGN_MUL; goto double_char; + default: stream_reset_char(stream), type = TOKEN_MUL; break; + } break; + case '/': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_ASSIGN_DIV; goto double_char; + case '/': skip_newline(lexer, token); goto END; + case '*': skip_block_comment(lexer, token); goto END; + default: stream_reset_char(stream), type = TOKEN_DIV; break; + } break; + case '%': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_ASSIGN_MOD; goto double_char; + default: stream_reset_char(stream), type = TOKEN_MOD; break; + } break; + case '&': + switch (stream_peek_char(stream)) { + case '&': type = TOKEN_AND_AND; goto double_char; + case '=': type = TOKEN_ASSIGN_AND; goto double_char; + default: stream_reset_char(stream), type = TOKEN_AND; break; + } break; + case '|': + switch (stream_peek_char(stream)) { + case '|': type = TOKEN_OR_OR; goto double_char; + case '=': type = TOKEN_ASSIGN_OR; goto double_char; + default: stream_reset_char(stream), type = TOKEN_OR; break; + } break; + case '^': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_ASSIGN_XOR; goto double_char; + default: stream_reset_char(stream), type = TOKEN_XOR; break; + } break; + case '<': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_LE; goto double_char; + case '<': { + if (stream_peek_char(stream) == '=') { + type = TOKEN_ASSIGN_L_SH; + goto triple_char; + } else { + type = TOKEN_L_SH; + goto double_char; + } + break; + } + default: stream_reset_char(stream), type = TOKEN_LT; break; + } break; + case '>': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_GE; goto double_char; + case '>': { + if (stream_peek_char(stream) == '=') { + type = TOKEN_ASSIGN_R_SH; + goto triple_char; + } else { + type = TOKEN_R_SH; + goto double_char; + } + break; + } + default: stream_reset_char(stream), type = TOKEN_GT; break; + } break; + case '~': + type = TOKEN_BIT_NOT; break; + case '!': + switch (stream_peek_char(stream)) { + case '=': type = TOKEN_NEQ; goto double_char; + default: stream_reset_char(stream), type = TOKEN_NOT; break; + } break; + case '[': + type = TOKEN_L_BRACKET; break; + case ']': + type = TOKEN_R_BRACKET; break; + case '(': + type = TOKEN_L_PAREN; break; + case ')': + type = TOKEN_R_PAREN; break; + case '{': + type = TOKEN_L_BRACE; break; + case '}': + type = TOKEN_R_BRACE; break; + case ';': + type = TOKEN_SEMICOLON; break; + case ',': + type = TOKEN_COMMA; break; + case ':': + type = TOKEN_COLON; break; + case '.': + if (stream_peek_char(stream) == '.' && stream_peek_char(stream) == '.') { + type = TOKEN_ELLIPSIS; + goto triple_char; + } + type = TOKEN_DOT; break; + case '?': + type = TOKEN_COND; break; + case '\v': case '\r': case '\f': + case ' ': case '\t': + type = TOKEN_BLANK; break; + case '\n': + // you need to flush a newline or blank + stream_next_char(stream); + lexer_next_line(lexer); + // FIXME some error + token->type = TOKEN_BLANK; + goto END; + case '#': + parse_line(lexer, token); + token->type = TOKEN_BLANK; + goto END; + case '\0': + case lexer_stream_eof: + // EOF + type = TOKEN_EOF; + break; + case '\'': + parse_char(lexer, token); + goto END; + case '"': + parse_string(lexer, token); + goto END; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + parse_number(lexer, token); + goto END; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': + case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': + case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':case 'Y': case 'Z': + case '_': + // TOKEN_IDENT + // TODO + // if ((ch == 'L' && ch == '\'') || (ch == 'L' && ch == '"')) { + // LEX_ERROR("unsupport wide-character char literal by `L` format"); + // } + cstring_t str = cstring_new(); + while (1) { + ch = stream_peek_char(stream); + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + (ch == '_') || (ch >= '0' && ch <= '9')) { + stream_next_char(stream); + lexer_next_pos(lexer); + cstring_push(&str, ch); + continue; + } + break; + } + + int res = keyword_cmp((const char*)str.data, str.len); + if (res == -1) { + token->value.cstr.data = (char*)cstring_as_cstr(&str); + token->value.cstr.len = cstring_len(&str); + type = TOKEN_IDENT; break; + } else { + type = keywords[res].tok; break; + } + default: + LEX_ERROR("unsupport char in sourse code `%c`", ch); + break; + } + goto once_char; +triple_char: + stream_next_char(stream); + lexer_next_pos(lexer); +double_char: + stream_next_char(stream); + lexer_next_pos(lexer); +once_char: + stream_next_char(stream); + lexer_next_pos(lexer); + token->type = type; +END: + LEX_DEBUG("get token `%s` in %s:%d:%d", get_tok_name(token->type), + token->loc.name, token->loc.line, token->loc.column); +} + +// lexer_get_token maybe got invalid (with parser) +void lexer_get_valid_token(smcc_lexer_t* lexer, lexer_tok_t* token) { + token_subtype_t type; + do { + lexer_get_token(lexer, token); + type = get_tok_subtype(token->type); + AssertFmt(type != TK_BASIC_INVALID, "Invalid token: `%s` at %s:%d:%d", + get_tok_name(token->type), token->loc.name, token->loc.line, token->loc.column); + } while (type == TK_BASIC_EMPTYSPACE || type == TK_BASIC_COMMENT); +} diff --git a/libs/lexer/src/lexer_log.c b/libs/lexer/src/lexer_log.c new file mode 100644 index 0000000..d39a8f3 --- /dev/null +++ b/libs/lexer/src/lexer_log.c @@ -0,0 +1,7 @@ +#include + +logger_t __smcc_lexer_log = { + .name = "lexer", + .level = LOG_LEVEL_ALL, + .handler = log_default_handler, +}; diff --git a/libs/lexer/src/mem_stream.c b/libs/lexer/src/mem_stream.c new file mode 100644 index 0000000..a7f1b42 --- /dev/null +++ b/libs/lexer/src/mem_stream.c @@ -0,0 +1,101 @@ +#include +#include +#include + +// 内存流的具体实现结构 + +static usize read_buf(lexer_stream_t* _stream, char* buffer, usize count) { + Assert(buffer != null && buffer != null); + lexer_mem_stream_t* stream = (lexer_mem_stream_t*)_stream; + + usize remaining = stream->data_length - stream->curr_pos; + usize to_read = (remaining < count) ? remaining : count; + + if (to_read > 0) { + smcc_memcpy(buffer, stream->data + stream->curr_pos, to_read); + stream->curr_pos += to_read; + } else { + LEX_WARN("Reading past end of stream [maybe count is too large or negative?]"); + } + + return to_read; +} + +static int peek_char(lexer_stream_t* _stream) { + Assert(_stream != null); + lexer_mem_stream_t* stream = (lexer_mem_stream_t*)_stream; + + // 如果已经到达末尾,返回EOF + if (stream->peek_pos >= stream->data_length) { + return lexer_stream_eof; // EOF + } + + return (int)(unsigned char)stream->data[stream->peek_pos++]; +} + +static int next_char(lexer_stream_t* _stream) { + Assert(_stream != NULL); + lexer_mem_stream_t* stream = (lexer_mem_stream_t*)_stream; + + // 如果已经到达末尾,返回EOF + if (stream->curr_pos >= stream->data_length) { + return lexer_stream_eof; // EOF + } + + unsigned char ch = stream->data[stream->curr_pos++]; + if (stream->peek_pos < stream->curr_pos) { + stream->peek_pos = stream->curr_pos; + } + return (int)ch; +} + +static void reset_char(lexer_stream_t* _stream) { + Assert(_stream != NULL); + lexer_mem_stream_t* stream = (lexer_mem_stream_t*)_stream; + + stream->peek_pos = stream->curr_pos; +} + +static void free_stream(lexer_stream_t* _stream) { + Assert(_stream != null); + lexer_mem_stream_t* stream = (lexer_mem_stream_t*)_stream; + if (stream->owned) { + smcc_free((void*)stream->data); + } +} + +lexer_stream_t* lexer_mem_stream_init(lexer_mem_stream_t* stream, const char* data, usize length, cbool need_copy) { + if (stream == null || data == NULL || length == 0) { + LEX_ERROR("param error"); + return null; + } + + stream->owned = need_copy; + if (need_copy) { + char* buf = (char*)smcc_malloc(length); + if (buf == null) { + LEX_ERROR("malloc error"); + return null; + } + + smcc_memcpy(buf, data, length); + stream->data = buf; + } else { + stream->data = data; + } + stream->data_length = length; + stream->curr_pos = 0; + stream->peek_pos = 0; + + static const char name[] = "mem_stream"; + stream->stream.name = name; + stream->stream.name_len = sizeof(name) - 1; + + stream->stream.read_buf = read_buf; + stream->stream.peek_char = peek_char; + stream->stream.next_char = next_char; + stream->stream.reset_char = reset_char; + stream->stream.free_stream = free_stream; + + return (void*)stream; +} diff --git a/libs/lexer/src/token.c b/libs/lexer/src/token.c new file mode 100644 index 0000000..1eba3d1 --- /dev/null +++ b/libs/lexer/src/token.c @@ -0,0 +1,30 @@ +#include + +// 生成字符串映射(根据需求选择#str或#name) +static const char* token_strings[] = { + #define X(str, subtype, tok) [tok] = #str, + TOKEN_TABLE + #undef X + + #define X(str, subtype, tok, std) [tok] = #str, + KEYWORD_TABLE + #undef X +}; + +static token_subtype_t token_subtypes[] = { + #define X(str, subtype, tok) [tok] = subtype, + TOKEN_TABLE + #undef X + + #define X(str, subtype, tok, std) [tok] = subtype, + KEYWORD_TABLE + #undef X +}; + +token_subtype_t get_tok_subtype(token_type_t type) { + return token_subtypes[type]; +} + +const char* get_tok_name(token_type_t type) { + return token_strings[type]; +} diff --git a/libs/lexer/tests/test_number.c b/libs/lexer/tests/test_number.c new file mode 100644 index 0000000..815d771 --- /dev/null +++ b/libs/lexer/tests/test_number.c @@ -0,0 +1,4 @@ + +int main() { + +} \ No newline at end of file diff --git a/libs/lexer/tests/test_run.c b/libs/lexer/tests/test_run.c new file mode 100644 index 0000000..afe8505 --- /dev/null +++ b/libs/lexer/tests/test_run.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +/// gcc -g ../lexer.c ../token.c test_lexer.c -o test_lexer +/* +tok_tConstant { + int have; + union { + char ch; + int i; + float f; + double d; + long long ll; + char* str; + }; +}; +*/ + +int g_num; +int g_num_arr[3]; +int main(int argc, char* argv[]) { + // int num = 0; + if (argc == 3 && strcmp(argv[2], "-nodebug") == 0) { + log_set_level(NULL, LOG_LEVEL_ALL); + } + + const char* file_name = __FILE__; + if (argc == 2) { + file_name = argv[1]; + } + FILE* fp = fopen(file_name, "rb"); + if (fp == NULL) { + perror("open file failed"); + return 1; + } + printf("open file success\n"); + + if (fseek(fp, 0, SEEK_END) != 0) { + perror("fseek failed"); + return 1; + } + usize fsize = ftell(fp); + LOG_INFO("file size: %zu", fsize); + if (fseek(fp, 0, SEEK_SET)) { + perror("fseek failed"); + return 1; + } + + char* buffer = (char*) malloc(fsize); + + usize read_ret = fread(buffer, 1, fsize, fp); + fclose(fp); + if (read_ret != fsize) { + LOG_FATAL("fread failed read_ret %u != fsize %u", read_ret, fsize); + free(buffer); + return 1; + } + + smcc_lexer_t lexer; + lexer_mem_stream_t mem_stream = {0}; + lexer_stream_t* stream = lexer_mem_stream_init(&mem_stream, buffer, fsize, false); + Assert(stream != null); + stream->name = __FILE__; + stream->name_len = strlen(__FILE__); + lexer_init(&lexer, stream); + lexer_tok_t tok; + + while (1) { + lexer_get_valid_token(&lexer, &tok); + if (tok.type == TOKEN_EOF) { + break; + } + LOG_INFO("token `%s` at %s:%u:%u", get_tok_name(tok.type), tok.loc.name, tok.loc.line, tok.loc.column); + Assert(tok.loc.offset <= fsize); + // LOG_DEBUG("%s", tok.val.str); + // printf("line: %d, column: %d, type: %3d, typename: %s\n", + // lexer.line, lexer.index, tok.type, get_tok_name(tok.type)); + } + + free(buffer); +} diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index d413f92..0000000 --- a/src/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -CC = gcc -CFLAGS = -g -Wall -I.. -LIBS = -Lccompiler -lcc -Lassembler/riscv32 -lasm -Llinker -llk -Lmcode -lmc -L../lib -lcore -CLFAGS += -fsanitize=address - -all: smcc -smcc: cc asm lib mc lk - $(CC) $(CFLAGS) smcc.c $(LIBS) -o smcc - -lib: - make -C ../lib - -asm: - make -C assembler/riscv32 - -mc: - make -C mcode - -cc: - make -C ccompiler - -lk: - make -C linker - -clean: - make -C ../lib clean - make -C assembler/riscv32 clean - make -C ccompiler clean - make -C mcode clean - make -C linker clean diff --git a/src/assembler/assembler.c b/src/assembler/assembler.c deleted file mode 100644 index 6712458..0000000 --- a/src/assembler/assembler.c +++ /dev/null @@ -1,144 +0,0 @@ - -#include -#include -#include - -enum asm_symbol_type{ - DATA_BYTE, - DATA_WORD, - DATA_HALFWORD, - DATA_DOUBLEWORD, - DATA_STRING, - DATA_SYMBLE, - TEXT_LABEL, - UNKNOWN, -}; - -struct asm_symbol_table { - const char* name; - enum asm_symbol_type type; - union { - long long data_doubleword; - long data_word; - short data_halfword; - char data_byte; - char* data_string; - } data; -}; - -static const char comments[] = { - '#', -}; - -static inline int is_blank(char ch) { - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\f' || ch == '\r'; -} - -void parse_line_without_blank(const char* in, const char** out_start, const char** out_end) { - while (*in == '\0' || is_blank(*in)) { - in ++; - } - *out_start = in; - while (*in != '\0') { - if (*in == '\r' || *in == '\n') { - break; - } - for (int i = 0; i < sizeof(comments); i ++) { - if (*in == comments[i]) { - break; - } - } - in ++; - } - - if (*out_end == *out_start) { - goto END; - } - - while (is_blank(*in)) { - in --; - } - -END: - *out_end = in; - return; -} - -void parse_data_symbol(const char* start, const char* end, struct asm_symbol_table* table) { - table->name = start; - while(start < end) { - if (*start == ':') { - - } - } - table->type = UNKNOWN; -} - -#define TMP_BUFF_SIZE 1024 - -enum parse_state { - IN_DATA, - IN_TEXT, - IN_UNKNOWN, -}; -void get_symbol_table(FILE* in) { - enum parse_state state = IN_UNKNOWN; - fseek(in, 0, SEEK_SET); - char buf[TMP_BUFF_SIZE]; - - int current_line = 0; - while (1) { - current_line ++; - char *start = fgets(buf, sizeof(buf), in); - if (start == NULL) { - return; - } - char *end; - parse_line_without_blank(buf, &start, &end); - - if (start == end) { - continue; - } - - if (start[0] == '.') { - // .data .text and so on - if (strcmp(start, ".data") == 0) { - state = IN_DATA; - } else if (strcmp(start, ".text") == 0) { - state = IN_TEXT; - } else { - printf("unknown directive at line %d\n", current_line); - state = IN_UNKNOWN; - } - continue; - } - - switch (state) { - case IN_DATA: - parse_data_symbol(start, end); - break; - case IN_TEXT: - break; - case IN_UNKNOWN: - break; - } - } - -} - -void assembler(FILE* in, FILE* out, char *start_symble) { - char buf[TMP_BUFF_SIZE]; - - char *res = fgets(buf, sizeof(buf), in); - if (res == NULL) { - return; - } - - if (res[0] == '.') { - // maybe .data .text and so on - } else if (res[0] == ' ') { - - } - - -} diff --git a/src/assembler/assembler.h b/src/assembler/assembler.h deleted file mode 100644 index a309207..0000000 --- a/src/assembler/assembler.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __SMCC_ASM_H__ -#define __SMCC_ASM_H__ -// typedef unsigned long long rt_size_t; -// extern void* _syscall_fopen (const char* path); -// extern int _syscall_fread (void* handle, char* buf, rt_size_t size); -// extern int _syscall_fwrite (void* handle, const char* buf, rt_size_t size); -// extern void _syscall_fseek (void* handle, long offset); - - -#include - -#include "riscv32/riscv32_asm.h" -#include "riscv32/riscv32_asm.h" -typedef union asm_prog { - rv32_prog_t rv32; -} asm_prog_t; - -#endif diff --git a/src/assembler/riscv32/Makefile b/src/assembler/riscv32/Makefile deleted file mode 100644 index bf0a0d5..0000000 --- a/src/assembler/riscv32/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -CC = gcc -AR = ar -CFLAGS = -g -Wall -I../../.. - -EXCLUDE = test*.c - -SRCS = $(filter-out $(EXCLUDE), $(wildcard *.c)) -OBJS = $(SRCS:.c=.o) - -libasm.a: $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f libasm.a $(OBJS) diff --git a/src/assembler/riscv32/riscv32_asm.c b/src/assembler/riscv32/riscv32_asm.c deleted file mode 100644 index e0cc3d4..0000000 --- a/src/assembler/riscv32/riscv32_asm.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "riscv32_asm.h" - -void init_rv32_prog(rv32_prog_t* prog, strpool_t* strpool) { - if (strpool == NULL) { - prog->strpool = salloc_alloc(sizeof(strpool_t)); - init_strpool(prog->strpool); - } else { - prog->strpool = strpool; - } - prog->data_base_address = 0; - vector_init(prog->data); - prog->text_base_address = 0; - init_symtab_asm(&prog->symtab); - init_rv32_mcode(&prog->mcode, sizeof(symasm_entry_t)); -} - -void asm_from_file(const char* file_name, rv32_prog_t* prog) { - init_rv32_prog(prog, NULL); - - rt_file_t* fp = rt.fopen(file_name, "r"); - if (fp == NULL) { - LOG_FATAL("Failed to open file %s", file_name); - } - - char buf[1024]; - rt.fread(buf, 1, sizeof(buf), fp); - - -} diff --git a/src/assembler/riscv32/riscv32_asm.h b/src/assembler/riscv32/riscv32_asm.h deleted file mode 100644 index 9356edd..0000000 --- a/src/assembler/riscv32/riscv32_asm.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __SMCC_RISCV32_ASM_H__ -#define __SMCC_RISCV32_ASM_H__ - -#include -#include -#include "symtab_asm.h" -typedef struct rv32_prog { - strpool_t* strpool; - symtab_asm_t symtab; - u32_t text_base_address; - u32_t data_base_address; - VECTOR_HEADER(data, iptr_t); - mcode_rv32_t mcode; -} rv32_prog_t; - -void init_rv32_prog(rv32_prog_t* prog, strpool_t* strpool); - -#endif diff --git a/src/assembler/riscv32/symtab_asm.c b/src/assembler/riscv32/symtab_asm.c deleted file mode 100644 index 0d7ecb1..0000000 --- a/src/assembler/riscv32/symtab_asm.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "symtab_asm.h" - -// /* append label */ -// static inline int -// rv32_append_label(mcode_rv32_t* prog, void* label, u32_t offset) { -// // prog->symtab -// symtab_asm_put(&prog->symtab, label, offset); -// return 0; -// } - -static u32_t hash_func(const symasm_entry_t* key) { - return rt_strhash(key->name); -} - -static int cmp_func(const symasm_entry_t* k1, const symasm_entry_t* k2) { - return rt_strcmp(k1->name, k2->name); -} - -void init_symtab_asm(symtab_asm_t* symtab) { - init_hashtable(&symtab->symtab); - symtab->symtab.hash_func = (u32_t(*)(const void*))hash_func; - symtab->symtab.key_cmp = (int(*)(const void*, const void*))cmp_func; -} - -void symtab_asm_put(symtab_asm_t* symtab, symasm_entry_t* _entry, u32_t address) { - // FIXME maybe memory leak - u32_t* addr = salloc_alloc(sizeof(u32_t)); - if (addr == NULL) { - LOG_FATAL("salloc_alloc failure"); - } - symasm_entry_t* entry = salloc_alloc(sizeof(symasm_entry_t)); - if (entry == NULL) LOG_FATAL("malloc failure"); - *entry = *_entry; - *addr = address; - void* ret = hashtable_set(&symtab->symtab, entry, addr); - if (ret != NULL) { - LOG_ERROR("Symbol %s already exists", entry->name); - } -} - -u32_t* symtab_asm_get(symtab_asm_t* symtab, symasm_entry_t* entry) { - u32_t* addr = hashtable_get(&symtab->symtab, entry); - return addr; -} - -void symtab_asm_destroy(symtab_asm_t* symtab) { - hashtable_destory(&symtab->symtab); -} diff --git a/src/assembler/riscv32/symtab_asm.h b/src/assembler/riscv32/symtab_asm.h deleted file mode 100644 index aa92b39..0000000 --- a/src/assembler/riscv32/symtab_asm.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __SMCC_SYMTAB_ASM_H__ -#define __SMCC_SYMTAB_ASM_H__ - -#include -#include - -typedef enum symasm_attr { - GLOBAL, - LOCAL, -} symasm_attr_t; - -typedef struct symasm_entry { - const char* name; - symasm_attr_t attr; -} symasm_entry_t; - -typedef struct symtab_asm { - hash_table_t symtab; -} symtab_asm_t; - -void init_symtab_asm(symtab_asm_t* symtab); -void symtab_asm_put(symtab_asm_t* symtab, symasm_entry_t* entry, u32_t address); -u32_t* symtab_asm_get(symtab_asm_t* symtab, symasm_entry_t* entry); -void symtab_asm_destroy(symtab_asm_t* symtab); - -#endif diff --git a/src/assembler/riscv32/test.asm.s b/src/assembler/riscv32/test.asm.s deleted file mode 100644 index cb46a3d..0000000 --- a/src/assembler/riscv32/test.asm.s +++ /dev/null @@ -1,62 +0,0 @@ -# This example shows an implementation of the mathematical -# factorial function (! function) to find the factorial value of !7 = 5040. - -.data -argument: .word 7 -str1: .string "Factorial value of " -str2: .string " is " - -.text -main: - lw a0, argument # Load argument from static data - jal ra, fact # Jump-and-link to the 'fact' label - - # Print the result to console - mv a1, a0 - lw a0, argument - jal ra, printResult - - # Exit program - li a7, 10 - ecall - -fact: - addi sp, sp, -16 - sw ra, 8(sp) - sw a0, 0(sp) - addi t0, a0, -1 - bge t0, zero, nfact - - addi a0, zero, 1 - addi sp, sp, 16 - jr x1 - -nfact: - addi a0, a0, -1 - jal ra, fact - addi t1, a0, 0 - lw a0, 0(sp) - lw ra, 8(sp) - addi sp, sp, 16 - mul a0, a0, t1 - ret - -# --- printResult --- -# a0: Value which factorial number was computed from -# a1: Factorial result -printResult: - mv t0, a0 - mv t1, a1 - la a0, str1 - li a7, 4 - ecall - mv a0, t0 - li a7, 1 - ecall - la a0, str2 - li a7, 4 - ecall - mv a0, t1 - li a7, 1 - ecall - ret diff --git a/src/assembler/syscall.h b/src/assembler/syscall.h deleted file mode 100644 index e69de29..0000000 diff --git a/src/ccompiler/Makefile b/src/ccompiler/Makefile deleted file mode 100644 index f25747c..0000000 --- a/src/ccompiler/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# all: cc - -# # run: ccompiler -# # ./ccompiler test.c flat.bin - -# # simple_test: -# # make -C tests/simple - -# cc: frontend middleend backend ccompiler.c test_main.c -# gcc -g ccompiler.c test_main.c -I../ -L./frontend -lfrontend -L./middleend -lmiddleend -L./backend -lbackend -L../lib -lcore -o cc - -# frontend: -# make -C ./frontend - -# middleend: -# make -C ./middleend - -# backend: -# make -C ./backend - -# clean: -# rm -f cc -# make -C ./frontend clean -# make -C ./middleend clean -# make -C ./backend clean - - -# 顶层Makefile修改 -CC = gcc -AR = ar -CFLAGS = -g -Wall -I.. -I../.. -MODULES = frontend middleend backend - -FRONTEND_SUBDIRS = lexer parser parser/ast parser/symtab -MODULES += $(addprefix frontend/, $(FRONTEND_SUBDIRS)) - -MIDDLEEND_SUBDIRS = ir -MODULES += $(addprefix middleend/, $(MIDDLEEND_SUBDIRS)) - -BACKEND_SUBDIRS = riscv32 -MODULES += $(addprefix backend/, $(BACKEND_SUBDIRS)) - -# 自动收集所有子模块源文件 -EXCLUDE = test*.c - -SRCS = $(filter-out $(EXCLUDE), $(wildcard $(addsuffix /*.c,$(MODULES)))) -SRCS += ccompiler.c -OBJS = $(SRCS:.c=.o) - -libcc.a: $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f libcc.a $(OBJS) diff --git a/src/ccompiler/backend/Makefile b/src/ccompiler/backend/Makefile deleted file mode 100644 index 5bc11e5..0000000 --- a/src/ccompiler/backend/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# 编译器设置 -CC = gcc -AR = ar -CFLAGS = -g -Wall -I../.. - -RISCV32_DIR = ./riscv32 - -# 源文件列表 -SRCS = \ - backend.c \ - $(RISCV32_DIR)/riscv32.c - -# 生成目标文件列表 -OBJS = $(SRCS:.c=.o) - -# 最终目标 -TARGET = libbackend.a - -all: $(TARGET) - -$(TARGET): $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f $(OBJS) $(TARGET) - -.PHONY: all clean diff --git a/src/ccompiler/backend/backend.c b/src/ccompiler/backend/backend.c deleted file mode 100644 index 97a9fe5..0000000 --- a/src/ccompiler/backend/backend.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "backend.h" - -int gen_asm_from_ir(ir_prog_t* ir, cc_arch_t arch, asm_prog_t* out_asm) { - switch (arch) { - case CC_ARCH_RISCV32: - // TODO using maroc to choice - init_rv32_prog(&(out_asm->rv32), NULL); - gen_rv32_from_ir(ir, &(out_asm->rv32)); - break; - case CC_ARCH_X86_32: - default: - Panic("Unsupported arch"); - break; - } - return 0; -} - -asm_prog_t* cc_backend(ir_prog_t* ir, cc_backend_conf_t* conf) { - // TODO - asm_prog_t* bin = (asm_prog_t*)salloc_alloc(sizeof(asm_prog_t)); - gen_asm_from_ir(ir, conf->arch, bin); - return bin; -} diff --git a/src/ccompiler/backend/backend.h b/src/ccompiler/backend/backend.h deleted file mode 100644 index 66c1441..0000000 --- a/src/ccompiler/backend/backend.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef __SMCC_CC_BACKEND_H__ -#define __SMCC_CC_BACKEND_H__ - -// TODO Use Maroc to choice architecture -#ifndef __SMCC_CC_NO_RISCV32__ -#include "riscv32/riscv32.h" -#endif - -// #ifndef __SMCC_CC_NO_X86_32__ -// #include "x86_32/x86_32.h" -// #endif - -// TODO 统一 汇编器 接口 -#include -#include "../middleend/ir/ir.h" -typedef enum cc_arch { - CC_ARCH_RISCV32, - CC_ARCH_X86_32 -} cc_arch_t; - -typedef union asm_prog asm_prog_t; -int gen_asm_from_ir(ir_prog_t* ir, cc_arch_t arch, asm_prog_t* asm_prog); - -typedef struct cc_backend_conf { - cc_arch_t arch; -} cc_backend_conf_t; - -asm_prog_t* cc_backend(ir_prog_t* ir, cc_backend_conf_t* conf); - -#endif diff --git a/src/ccompiler/backend/riscv32/README.md b/src/ccompiler/backend/riscv32/README.md deleted file mode 100644 index f72497e..0000000 --- a/src/ccompiler/backend/riscv32/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# 后端代码生成 - -## riscv32i - -> 仿照ripes的syscall,实现了rv32-vm - -### syscall ecall 系统调用 - -```c -// ecall 系统调用函数实现 -#define ECALL_PNT_INT(num) \ - ADDI(REG_A0, REG_X0, num), \ - ADDI(REG_A7, REG_X0, 0x1), \ - ECALL(), - -#define ECALL_PNT_STR(str) \ - ADDI(REG_A0, REG_X0, str), \ - ADDI(REG_A7, REG_X0, 0x4), \ - ECALL(), - -#define ECALL_EXIT(errno) \ - ADDI(REG_A0, REG_X0, errno), \ - ADDI(REG_A7, REG_X0, 10), \ - ECALL(), - -#define ECALL_SCAN_INT(int) \ - ADDI(REG_A7, (1025 + 4)), \ - ECALL(), - -#define ECALL_SCAN_STR(str) \ - ADDI(REG_A0, REG_X0, str), \ - ADDI(REG_A7, REG_X0, (1025 + 5)), \ - ECALL(), - -// 函数声明 -void ecall_pnt_int(int num); -void ecall_pnt_str(char *str); -void ecall_exit(int errno); -int ecall_scani(); -void ecall_scans(char *str); -``` diff --git a/src/ccompiler/backend/riscv32/riscv32.c b/src/ccompiler/backend/riscv32/riscv32.c deleted file mode 100644 index 56d3c5b..0000000 --- a/src/ccompiler/backend/riscv32/riscv32.c +++ /dev/null @@ -1,302 +0,0 @@ -#include "riscv32.h" -#include - -typedef struct { - ir_func_t* func; - int stack_offset; - int stack_base; - int func_idx; - int block_idx; -} gen_ctx_t; - -static inline int stack_pos(ir_node_t* ptr, gen_ctx_t *ctx) { - // ir_func_t *func, int stack_base, int stack_offset - int offset = ctx->stack_base; - for (int i = 0; i < ctx->func->bblocks.size; i ++) { - ir_bblock_t* block = vector_at(ctx->func->bblocks, i); - for (int i = 0; i < block->instrs.size; i++) { - if (vector_at(block->instrs, i) == ptr) { - offset += i * 4; - Assert(offset >= 0 && offset < ctx->stack_offset); - return offset; - } - } - offset += block->instrs.size * 4; - } - Panic("stack pos got error"); - return 0; -} - -static int system_func(const char* name) { - static struct { - const char* name; - int ecall_num; - } defined_func[] = { - {"ecall_pnt_int", 1}, - {"ecall_pnt_char", 11}, - {"ecall_scan_int", 1025 + 4}, - }; - - for (int i = 0; i < sizeof(defined_func)/sizeof(defined_func[0]); i++) { - if (rt_strcmp(name, defined_func[i].name) == 0) { - return defined_func[i].ecall_num; - } - } - return -1; -} - -static int get_node_val(mcode_rv32_t* out_asm, gen_ctx_t* ctx, ir_node_t* ptr, int reg) { - int len = 0; - switch (ptr->tag) { - case IR_NODE_CONST_INT: { - // TODO - rv32_li(out_asm, reg, ptr->data.const_int.val); - // emit_rv32_instr(out_asm, RV_ADDI, reg, reg, 0, ptr->data.const_int.val); - break; - } - default: { - int offset = stack_pos(ptr, ctx); - rv32_lw(out_asm, reg, REG_SP, offset); - break; - } - } - return len; -} - -static int gen_instr(rv32_prog_t* _out_asm, gen_ctx_t* ctx, ir_node_t* instr) { - mcode_rv32_t* out_asm = &_out_asm->mcode; - int idx = 0; - int offset; - char buf[1024]; - symasm_entry_t label; - - switch (instr->tag) { - case IR_NODE_ALLOC: { - // TODO - break; - } - case IR_NODE_LOAD: { - offset = stack_pos(instr->data.load.target, ctx); - // t0 = M[sp + offset] - rv32_lw(out_asm, REG_T0, REG_SP, offset); - break; - } - case IR_NODE_STORE: { - idx += get_node_val(out_asm, ctx, instr->data.store.value, REG_T0); - offset = stack_pos(instr->data.store.target, ctx); - // M[sp + offset] = t0 - rv32_sw(out_asm, REG_T0, REG_SP, offset); - break; - } - case IR_NODE_RET: { - // A0 = S0 - if (instr->data.ret.ret_val != NULL) { - idx += get_node_val(out_asm, ctx, instr->data.ret.ret_val, REG_A0); - } - // ra = M[sp + 0] - rv32_lw(out_asm, REG_RA, REG_SP, 0); - // sp = sp + stack_offset - rv32_addi(out_asm, REG_SP, REG_SP, ctx->stack_offset); - // ret == JALR(REG_X0, REG_RA, 0) - rv32_ret(out_asm); - break; - } - case IR_NODE_OP: { - idx += get_node_val(out_asm, ctx, instr->data.op.lhs, REG_T1); - idx += get_node_val(out_asm, ctx, instr->data.op.rhs, REG_T2); - - rv32_instr_t _instr = { - .rd = REG_T0, - .rs1 = REG_T1, - .rs2 = REG_T2, - .imm = 0 - }; - #define GEN_BIN_OP(type) _instr.instr_type = type, \ - emit_rv32_instr(out_asm, &_instr, EMIT_PUSH_BACK, NULL) - switch (instr->data.op.op) { - case IR_OP_ADD: - GEN_BIN_OP(RV_ADD); - break; - case IR_OP_SUB: - GEN_BIN_OP(RV_SUB); - break; - case IR_OP_MUL: - GEN_BIN_OP(RV_MUL); - break; - case IR_OP_DIV: - GEN_BIN_OP(RV_DIV); - break; - case IR_OP_MOD: - GEN_BIN_OP(RV_REM); - break; - case IR_OP_EQ: - GEN_BIN_OP(RV_XOR); - rv32_seqz(out_asm, REG_T0, REG_T0); - break; - case IR_OP_GE: - GEN_BIN_OP(RV_SLT); - rv32_seqz(out_asm, REG_T0, REG_T0); - break; - case IR_OP_GT: - // SGT(rd, rs1, rs2) SLT(rd, rs2, rs1) - // GENCODE(SGT(REG_T0, REG_T1, REG_T2)); - rv32_slt(out_asm, REG_T0, REG_T2, REG_T1); - break; - case IR_OP_LE: - // GENCODE(SGT(REG_T0, REG_T1, REG_T2)); - rv32_slt(out_asm, REG_T0, REG_T2, REG_T1); - rv32_seqz(out_asm, REG_T0, REG_T0); - break; - case IR_OP_LT: - rv32_slt(out_asm, REG_T0, REG_T1, REG_T2); - break; - case IR_OP_NEQ: - GEN_BIN_OP(RV_XOR); - break; - default: - LOG_ERROR("ERROR gen_instr op in riscv"); - break; - } - offset = stack_pos(instr, ctx); - rv32_sw(out_asm, REG_T0, REG_SP, offset); - break; - } - case IR_NODE_BRANCH: { - get_node_val(out_asm, ctx, instr->data.branch.cond, REG_T0); - - rt.snprintf(buf, sizeof(buf), "L%s%p", instr->data.branch.true_bblock->label, instr->data.branch.true_bblock); - label.name = strpool_intern(_out_asm->strpool, buf); - label.attr = LOCAL; - rv32_bne_l(out_asm, REG_T0, REG_X0, &label); - - rt.snprintf(buf, sizeof(buf), "L%s%p", instr->data.branch.false_bblock->label, instr->data.branch.false_bblock); - label.name = strpool_intern(_out_asm->strpool, buf); - label.attr = LOCAL; - rv32_jal_l(out_asm, REG_X0, &label); - break; - } - case IR_NODE_JUMP: { - // TODO - rt.snprintf(buf, sizeof(buf), "L%s%p", instr->data.jump.target_bblock->label, instr->data.jump.target_bblock); - label.name = strpool_intern(_out_asm->strpool, buf); - label.attr = LOCAL; - rv32_jal_l(out_asm, REG_X0, &label); - break; - } - case IR_NODE_CALL: { - if (instr->data.call.args.size > 8) { - LOG_ERROR("can't add so much params"); - } - int param_regs[8] = { - REG_A0, REG_A1, REG_A2, REG_A3, - REG_A4, REG_A5, REG_A6, REG_A7 - }; - for (int i = 0; i < instr->data.call.args.size; i++) { - ir_node_t* param = vector_at(instr->data.call.args, i); - idx += get_node_val(out_asm, ctx, param, param_regs[i]); - } - - int system_func_idx = system_func(instr->data.call.callee->name); - if (system_func_idx != -1) { - rv32_li(out_asm, REG_A7, system_func_idx); - rv32_ecall(out_asm); - goto CALL_END; - } - - /* - // GENCODES(CALL(0)); - // AUIPC(REG_X1, REG_X0), \ - // JALR(REG_X1, REG_X1, offset) - */ - // TODO CALL - label.name = strpool_intern(_out_asm->strpool, instr->data.call.callee->name); - label.attr = GLOBAL; - rv32_call_l(out_asm, &label); - CALL_END: - offset = stack_pos(instr, ctx); - rv32_sw(out_asm, REG_A0, REG_SP, offset); - break; - } - default: - LOG_ERROR("ERROR gen_instr in riscv"); - } - return idx; -} - -static int gen_block(rv32_prog_t* out_asm, gen_ctx_t* ctx, ir_bblock_t* block) { - symasm_entry_t label; - char buf[1024]; - rt.snprintf(buf, sizeof(buf), "L%s%p", block->label, block); - label.name = strpool_intern(out_asm->strpool, buf); - label.attr = LOCAL; - symtab_asm_put(&out_asm->symtab, &label, out_asm->mcode.code.size); - - for (int i = 0; i < block->instrs.size; i ++) { - gen_instr(out_asm, ctx, vector_at(block->instrs, i)); - } - return 0; -} - -static int gen_func(rv32_prog_t* out_asm, ir_func_t* func) { - gen_ctx_t ctx; - - symasm_entry_t label = { - .name = strpool_intern(out_asm->strpool, func->name), - .attr = GLOBAL, - }; - symtab_asm_put(&out_asm->symtab, &label, out_asm->mcode.code.size); - - int stack_base = 4; - int stack_offset = stack_base; - for (int i = 0; i < func->bblocks.size; i++) { - // TODO every instr push ret val to stack - stack_offset += 4 * (*vector_at(func->bblocks, i)).instrs.size; - } - ctx.func = func; - ctx.stack_base = stack_base; - ctx.stack_offset = stack_offset; - ctx.func_idx = 0; - ctx.block_idx = 0; - // TODO Alignment by 16 - - // sp = sp - stack_offset; - rv32_addi(&out_asm->mcode, REG_SP, REG_SP, -stack_offset); - // M[sp] = ra; - rv32_sw(&out_asm->mcode, REG_RA, REG_SP, 0); - - int param_regs[8] = { - REG_A0, REG_A1, REG_A2, REG_A3, - REG_A4, REG_A5, REG_A6, REG_A7 - }; - if (func->params.size > 8) { - LOG_ERROR("can't add so much params"); - } - for (int i = 0; i < func->params.size; i++) { - int offset = stack_pos(vector_at(func->params, i), &ctx); - // M[sp + offset] = param[idx]; - rv32_sw(&out_asm->mcode, param_regs[i], REG_SP, offset); - } - - for(int i = 0; i < func->bblocks.size; i ++) { - gen_block(out_asm, &ctx ,vector_at(func->bblocks, i)); - } - return 0; -} - - -int gen_rv32_from_ir(ir_prog_t* ir, rv32_prog_t* out_asm) { - init_rv32_prog(out_asm, NULL); - - for(int i = 0; i < ir->funcs.size; i ++) { - gen_func(out_asm, vector_at(ir->funcs, i)); - } - return 0; - - // // Got Main pos; - // for (int i = 0; i < prog->funcs.size; i++) { - // if (strcmp(vector_at(prog->funcs, i)->name, "main") == 0) { - // return jmp_cache[i]; - // } - // } - // LOG_ERROR("main not found"); -} diff --git a/src/ccompiler/backend/riscv32/riscv32.h b/src/ccompiler/backend/riscv32/riscv32.h deleted file mode 100644 index ff85207..0000000 --- a/src/ccompiler/backend/riscv32/riscv32.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __SMCC_CC_RISCV32_H__ -#define __SMCC_CC_RISCV32_H__ - -#include -#include "../../middleend/ir/ir.h" - -int gen_rv32_from_ir(ir_prog_t* ir, rv32_prog_t* out_asm); - -#endif diff --git a/src/ccompiler/ccompiler.c b/src/ccompiler/ccompiler.c deleted file mode 100644 index 4baaa8b..0000000 --- a/src/ccompiler/ccompiler.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "ccompiler.h" - -asm_prog_t* smcc_cc(smcc_cc_t* cc) { - ast_node_t* root = cc_frontend(cc->file, cc->stream, cc->sread); - - // TODO add config - ir_prog_t* prog = cc_middleend(root, &cc->midend_conf); - - // TODO add config - asm_prog_t* asm_prog = cc_backend(prog, &cc->backend_conf); -} diff --git a/src/ccompiler/ccompiler.h b/src/ccompiler/ccompiler.h deleted file mode 100644 index 41958e7..0000000 --- a/src/ccompiler/ccompiler.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __SMCC_CC_H__ -#define __SMCC_CC_H__ - -// TODO -#include "frontend/frontend.h" -#include "middleend/middleend.h" -#include "backend/backend.h" - -typedef struct smcc_cc { - const char *file; - void *stream; - sread_fn sread; - - cc_midend_conf_t midend_conf; - cc_backend_conf_t backend_conf; -} smcc_cc_t; - -typedef union asm_prog asm_prog_t; -asm_prog_t* smcc_cc(smcc_cc_t* cc); - -#endif diff --git a/src/ccompiler/frontend/Makefile b/src/ccompiler/frontend/Makefile deleted file mode 100644 index 9491850..0000000 --- a/src/ccompiler/frontend/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# 编译器设置 -CC = gcc -AR = ar -CFLAGS = -g -Wall -I../.. - -# 源文件路径 -LEXER_DIR = ./lexer -PARSER_DIR = ./parser -AST_DIR = ./parser/ast - -# 源文件列表 -SRCS = \ - frontend.c \ - $(LEXER_DIR)/lexer.c \ - $(LEXER_DIR)/token.c \ - $(PARSER_DIR)/parser.c \ - $(PARSER_DIR)/ast.c \ - $(AST_DIR)/block.c \ - $(AST_DIR)/decl.c \ - $(AST_DIR)/expr.c \ - $(AST_DIR)/func.c \ - $(AST_DIR)/program.c \ - $(AST_DIR)/stmt.c \ - $(AST_DIR)/term.c \ - -# 生成目标文件列表 -OBJS = $(SRCS:.c=.o) - -# 最终目标 -TARGET = libfrontend.a - -all: $(TARGET) - -$(TARGET): $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f $(OBJS) $(TARGET) - -.PHONY: all clean \ No newline at end of file diff --git a/src/ccompiler/frontend/frontend.c b/src/ccompiler/frontend/frontend.c deleted file mode 100644 index b90f8a8..0000000 --- a/src/ccompiler/frontend/frontend.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include "frontend.h" - -ast_node_t* cc_frontend(const char* file, void* stream, sread_fn sread) { - init_lib_core(); - strpool_t strpool; - init_strpool(&strpool); - - cc_lexer_t lexer; - init_lexer(&lexer, file, stream, sread, &strpool); - - symtab_t symtab; - init_symtab(&symtab); - // TODO global scope - symtab_enter_scope(&symtab); - - parser_t parser; - init_parser(&parser, &lexer, &symtab); - parse_prog(&parser); - - // TODO Free the resourse - return parser.root; -} diff --git a/src/ccompiler/frontend/frontend.h b/src/ccompiler/frontend/frontend.h deleted file mode 100644 index ba871ae..0000000 --- a/src/ccompiler/frontend/frontend.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __SMCC_CC_FRONTEND_H__ -#define __SMCC_CC_FRONTEND_H__ - -#include "lexer/lexer.h" -#include "parser/parser.h" -typedef int (*sread_fn)(void *dst_buf, int elem_size, int count, void *stream); -ast_node_t* cc_frontend(const char* file, void* stream, sread_fn sread); - -#endif diff --git a/src/ccompiler/frontend/lexer/lexer.c b/src/ccompiler/frontend/lexer/lexer.c deleted file mode 100644 index 9481ab3..0000000 --- a/src/ccompiler/frontend/lexer/lexer.c +++ /dev/null @@ -1,525 +0,0 @@ -/** - * 仿照LCCompiler的词法分析部分 - * - * 如下为LCC的README in 2025.2 -This hierarchy is the distribution for lcc version 4.2. - -lcc version 3.x is described in the book "A Retargetable C Compiler: -Design and Implementation" (Addison-Wesley, 1995, ISBN 0-8053-1670-1). -There are significant differences between 3.x and 4.x, most notably in -the intermediate code. For details, see -https://drh.github.io/lcc/documents/interface4.pdf. - -VERSION 4.2 IS INCOMPATIBLE WITH EARLIER VERSIONS OF LCC. DO NOT -UNLOAD THIS DISTRIBUTION ON TOP OF A 3.X DISTRIBUTION. - -LCC is a C89 ("ANSI C") compiler designed to be highly retargetable. - -LOG describes the changes since the last release. - -CPYRIGHT describes the conditions under you can use, copy, modify, and -distribute lcc or works derived from lcc. - -doc/install.html is an HTML file that gives a complete description of -the distribution and installation instructions. - -Chris Fraser / cwf@aya.yale.edu -David Hanson / drh@drhanson.net - */ -#include -#include "lexer_log.h" -#include "token.h" -#include "lexer.h" - -static const struct { - const char* name; - enum CSTD_KEYWORD std_type; - cc_tktype_t tok; -} keywords[] = { - #define X(name, std_type, tok, ...) { #name, std_type, tok }, - KEYWORD_TABLE - #undef X -}; - -// by using binary search to find the keyword -static inline int keyword_cmp(const char* name, int len) { - int low = 0; - int high = sizeof(keywords) / sizeof(keywords[0]) - 1; - while (low <= high) { - int mid = (low + high) / 2; - const char *key = keywords[mid].name; - int cmp = 0; - - // 自定义字符串比较逻辑 - for (int i = 0; i < len; i++) { - if (name[i] != key[i]) { - cmp = (unsigned char)name[i] - (unsigned char)key[i]; - break; - } - if (name[i] == '\0') break; // 遇到终止符提前结束 - } - - if (cmp == 0) { - // 完全匹配检查(长度相同) - if (key[len] == '\0') return mid; - cmp = -1; // 当前关键词比输入长 - } - - if (cmp < 0) { - high = mid - 1; - } else { - low = mid + 1; - } - } - return -1; // Not a keyword. -} - -void init_lexer(cc_lexer_t* lexer, const char* file_name, void* stream, lexer_sread_fn sread, strpool_t* strpool) { - lexer->strpool = strpool; - lexer->cur_ptr = lexer->end_ptr = (char*)&(lexer->buffer); - lexer->loc.fname = strpool_intern(lexer->strpool, file_name); - lexer->loc.line = 1; - lexer->loc.col = 1; - - lexer->stream = stream; - lexer->sread = sread; - - rt_memset(lexer->buffer, 0, sizeof(lexer->buffer)); -} - -static void flush_buffer(cc_lexer_t* lexer) { - int num = lexer->end_ptr - lexer->cur_ptr; - for (int i = 0; i < num; i++) { - lexer->buffer[i] = lexer->cur_ptr[i]; - } - lexer->cur_ptr = lexer->buffer; - - int read_size = LEXER_BUFFER_SIZE - num; - // TODO rt_size_t to int maybe lose precision - int got_size = lexer->sread(lexer->buffer + num, 1, read_size, lexer->stream); - if (got_size < 0) { - LEX_ERROR("lexer read error"); - } else if (got_size < read_size) { - lexer->end_ptr += got_size; - lexer->end_ptr[0] = '\0'; // EOF - lexer->end_ptr++; - } else if (got_size == read_size) { - lexer->end_ptr += got_size; - } else { - LEX_ERROR("lexer read error imposible got_size > read_size maybe overflow?"); - } -} - -static void goto_newline(cc_lexer_t* lexer) { - do { - if (lexer->cur_ptr == lexer->end_ptr) { - flush_buffer(lexer); - lexer->cur_ptr--; - } - lexer->cur_ptr++; - } while (*lexer->cur_ptr != '\n' && *lexer->cur_ptr != '\0'); -} - -static void goto_block_comment(cc_lexer_t* lexer) { - while (1) { - if (lexer->end_ptr - lexer->cur_ptr < 2) { - flush_buffer(lexer); - } - - if (lexer->cur_ptr[0] == '\0') { - break; - } else if (lexer->cur_ptr[0] == '*' && lexer->cur_ptr[1] == '/') { - lexer->cur_ptr += 2; - break; - } else { - if (lexer->cur_ptr[0] == '\n') lexer->loc.line++; - lexer->cur_ptr++; - } - } -} - -// TODO escape character not enough -static char got_slash(char* peek) { - switch (*peek) { - case '\\': return '\\'; - case '\'': return '\''; - case '\"': return '\"'; - case '\?': return '\?'; - case '0': return '\0'; - - case 'b': return '\b'; - case 'f': return '\f'; - case 'n': return '\n'; - case 'r': return '\r'; - case 't': return '\t'; - case 'v': return '\v'; - default: break; - } - LEX_ERROR("Unknown escape character"); - return -1; -} - -static void parse_char_literal(cc_lexer_t* lexer, tok_t* token) { - char val = 0; - char* peek = lexer->cur_ptr + 1; - if (*peek == '\\') { - peek++; - val = got_slash(peek); - peek++; - } else { - val = *peek++; - } - - if (*peek++ != '\'') LEX_ERROR("Unclosed character literal"); - lexer->cur_ptr = peek; - token->val.ch = val; -} - -static void parse_string_literal(cc_lexer_t* lexer, tok_t* token) { - char* peek = lexer->cur_ptr + 1; - // TODO string literal size check - static char dest[LEXER_MAX_TOKEN_SIZE + 1]; - int len = 0; - - while (*peek != '"') { - if (peek >= lexer->end_ptr) flush_buffer(lexer); - - if (*peek == '\\') { // 处理转义 - peek++; - *peek = got_slash(peek); - } - - if (len >= LEXER_MAX_TOKEN_SIZE) LEX_ERROR("String too long"); - dest[len++] = *peek++; - } - dest[len] = '\0'; - lexer->cur_ptr = peek + 1; // 1 is `"` - lexer->loc.len = len + 2; // 2 is `"` `"` - - token->val.str = strpool_intern(lexer->strpool, dest); -} - -// FIXME it write by AI maybe error -static void parse_number(cc_lexer_t* lexer, tok_t* token) { - char* peek = lexer->cur_ptr; - int base = 10; - int is_float = 0; - long long int_val = 0; - double float_val = 0.0; - double fraction = 1.0; - - // 判断进制 - if (*peek == '0') { - peek++; - switch (*peek) { - case 'x': - case 'X': - base = 16; - default: - base = 8; - } - } - - // 解析整数部分 - while (1) { - int digit = -1; - if (*peek >= '0' && *peek <= '9') { - digit = *peek - '0'; - } else if (base == 16) { - if (*peek >= 'a' && *peek <= 'f') digit = *peek - 'a' + 10; - else if (*peek >= 'A' && *peek <= 'F') digit = *peek - 'A' + 10; - } - - if (digit < 0 || digit >= base) break; - - if (!is_float) { - int_val = int_val * base + digit; - } else { - float_val = float_val * base + digit; - fraction *= base; - } - peek++; - } - - // 解析浮点数 - if (*peek == '.' && base == 10) { - is_float = 1; - float_val = int_val; - peek++; - - while (*peek >= '0' && *peek <= '9') { - float_val = float_val * 10.0 + (*peek - '0'); - fraction *= 10.0; - peek++; - } - float_val /= fraction; - } - - // 解析科学计数法 - if ((*peek == 'e' || *peek == 'E') && base == 10) { - is_float = 1; - peek++; - // int exp_sign = 1; - int exponent = 0; - - if (*peek == '+') peek++; - else if (*peek == '-') { - // exp_sign = -1; - peek++; - } - - while (*peek >= '0' && *peek <= '9') { - exponent = exponent * 10 + (*peek - '0'); - peek++; - } - // float_val *= pow(10.0, exp_sign * exponent); - } - - // 存储结果 - // TODO - lexer->loc.len = peek - lexer->cur_ptr; - lexer->cur_ptr = peek; - if (is_float) { - token->val.f32 = float_val; - token->sub_type = TOKEN_FLOAT_LITERAL; - } else { - token->val.i = int_val; - token->sub_type = TOKEN_INT_LITERAL; - } -} - -#define GOT_ONE_TOKEN_BUF_SIZE 64 -// /zh/c/language/operator_arithmetic.html -void get_token(cc_lexer_t* lexer, tok_t* token) { - // 需要保证缓冲区始终可读 - if (lexer->end_ptr - lexer->cur_ptr < GOT_ONE_TOKEN_BUF_SIZE) { - flush_buffer(lexer); - } - register char* peek = lexer->cur_ptr; - - cc_tktype_t tk_type = TOKEN_INIT; - ctype_t literal = { 0 }; - - // once step - switch (*peek++) { - case '=': - switch (*peek++) { - case '=': tk_type = TOKEN_EQ; break; - default: peek--, tk_type = TOKEN_ASSIGN; break; - } break; - case '+': - switch (*peek++) { - case '+': tk_type = TOKEN_ADD_ADD; break; - case '=': tk_type = TOKEN_ASSIGN_ADD; break; - default: peek--, tk_type = TOKEN_ADD; break; - } break; - case '-': - switch (*peek++) { - case '-': tk_type = TOKEN_SUB_SUB; break; - case '=': tk_type = TOKEN_ASSIGN_SUB; break; - - case '>': tk_type = TOKEN_DEREF; break; - default: peek--, tk_type = TOKEN_SUB; break; - } break; - case '*': - switch (*peek++) { - case '=': tk_type = TOKEN_ASSIGN_MUL; break; - default: peek--, tk_type = TOKEN_MUL; break; - } break; - case '/': - switch (*peek++) { - case '=': tk_type = TOKEN_ASSIGN_DIV; break; - case '/': { - goto_newline(lexer); - tk_type = TOKEN_LINE_COMMENT; - goto END; - } - case '*': { - lexer->cur_ptr = peek; - goto_block_comment(lexer); - tk_type = TOKEN_BLOCK_COMMENT; - goto END; - } - default: peek--, tk_type = TOKEN_DIV; break; - } break; - case '%': - switch (*peek++) { - case '=': tk_type = TOKEN_ASSIGN_MOD; break; - default: peek--, tk_type = TOKEN_MOD; break; - } break; - case '&': - switch (*peek++) { - case '&': tk_type = TOKEN_AND_AND; break; - case '=': tk_type = TOKEN_ASSIGN_AND; break; - default: peek--, tk_type = TOKEN_AND; break; - } break; - case '|': - switch (*peek++) { - case '|': tk_type = TOKEN_OR_OR; break; - case '=': tk_type = TOKEN_ASSIGN_OR; break; - default: peek--, tk_type = TOKEN_OR; break; - } break; - case '^': - switch (*peek++) { - case '=': tk_type = TOKEN_ASSIGN_XOR; break; - default: peek--, tk_type = TOKEN_XOR; break; - } break; - case '<': - switch (*peek++) { - case '=': tk_type = TOKEN_LE; break; - case '<': tk_type = (*peek == '=') ? (peek++, TOKEN_ASSIGN_L_SH) : TOKEN_L_SH; break; - default: peek--, tk_type = TOKEN_LT; break; - } break; - case '>': - switch (*peek++) { - case '=': tk_type = TOKEN_GE; break; - case '>': tk_type = (*peek == '=') ? (peek++, TOKEN_ASSIGN_R_SH) : TOKEN_R_SH; break; - default: peek--, tk_type = TOKEN_GT; break; - } break; - case '~': - tk_type = TOKEN_BIT_NOT; break; - case '!': - switch (*peek++) { - case '=': tk_type = TOKEN_NEQ; break; - default: peek--, tk_type = TOKEN_NOT; break; - } break; - case '[': - tk_type = TOKEN_L_BRACKET; break; - case ']': - tk_type = TOKEN_R_BRACKET; break; - case '(': - tk_type = TOKEN_L_PAREN; break; - case ')': - tk_type = TOKEN_R_PAREN; break; - case '{': - tk_type = TOKEN_L_BRACE; break; - case '}': - tk_type = TOKEN_R_BRACE; break; - case ';': - tk_type = TOKEN_SEMICOLON; break; - case ',': - tk_type = TOKEN_COMMA; break; - case ':': - tk_type = TOKEN_COLON; break; - case '.': - if (peek[0] == '.' && peek[1] == '.') { - peek += 2; - tk_type = TOKEN_ELLIPSIS; - } else { - tk_type = TOKEN_DOT; - } - break; - case '?': - tk_type = TOKEN_COND; break; - case '\v': case '\r': case '\f': - case ' ': case '\t': - tk_type = TOKEN_BLANK; break; - case '\n': - // you need to flush a newline or blank - lexer->loc.line += 1; - lexer->loc.col = -1; - lexer->loc.len = 1; - tk_type = TOKEN_BLANK; - break; - case '#': - // TODO make line or file comment to change - LEX_WARN("Maroc does not support in lexer rather in preprocessor, it will be ignored"); - goto_newline(lexer); - tk_type = TOKEN_BLANK; - goto END; - case '\0': - // EOF - tk_type = TOKEN_EOF; - goto END; - case '\'': - parse_char_literal(lexer, token); - literal = token->val; - tk_type = TOKEN_CHAR_LITERAL; - goto END; break; - case '"': - parse_string_literal(lexer, token); - literal = token->val; - tk_type = TOKEN_STRING_LITERAL; - goto END; break; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - parse_number(lexer, token); - // TODO Make it easy - literal = token->val; - tk_type = token->sub_type; - goto END; break; - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':case 'Y': case 'Z': - case '_': - // TOKEN_IDENT - if ((*peek == 'L' && *peek == '\'') || (*peek == 'L' && *peek == '"')) { - LEX_ERROR("unsupport wide-character char literal by `L` format"); - } - while (1) { - if (peek == lexer->end_ptr) { - LEX_ERROR("unsupport outof 64 length identifier"); - } - if ((*peek >= 'a' && *peek <= 'z') || (*peek >= 'A' && *peek <= 'Z') || - (*peek == '_') || (*peek >= '0' && *peek <= '9')) { - peek++; - continue; - } - break; - } - - int strlen = peek - lexer->cur_ptr; - int res = keyword_cmp((const char*)lexer->cur_ptr, strlen); - if (res == -1) { - char prev = lexer->cur_ptr[strlen]; - lexer->cur_ptr[strlen] = '\0'; - literal.str = strpool_intern(lexer->strpool, lexer->cur_ptr); - lexer->cur_ptr[strlen] = prev; - tk_type = TOKEN_IDENT; break; - } else { - tk_type = keywords[res].tok; break; - } - default: - LEX_ERROR("unsupport char in sourse code `%c`", *(lexer->cur_ptr)); - break; - } - - lexer->loc.len = peek - lexer->cur_ptr; - lexer->cur_ptr = peek; -END: - lexer->loc.col += lexer->loc.len; - lexer->loc.len = 0; - - token->val = literal; - token->sub_type = tk_type; - token->loc = lexer->loc; -static const tok_basic_type_t tok_type_map[] = { - // 普通token使用#str - #define X(str, basic, tok) [tok] = basic, - TOKEN_TABLE - #undef X - - // 关键字使用#name - #define X(name, std, tok) [tok] = TK_BASIC_KEYWORD, - KEYWORD_TABLE - #undef X -}; - token->type = tok_type_map[tk_type]; - LEX_DEBUG("get token `%s` in %s:%d:%d", get_tok_name(tk_type), - token->loc.fname, token->loc.line, token->loc.col); -} - -// get_token maybe got invalid (with parser) -void get_valid_token(cc_lexer_t* lexer, tok_t* token) { - tok_basic_type_t type; - do { - get_token(lexer, token); - type = token->type; - Assert(type != TK_BASIC_INVALID); - } while (type == TK_BASIC_WHITESPACE || type == TK_BASIC_COMMENT); -} diff --git a/src/ccompiler/frontend/lexer/lexer.h b/src/ccompiler/frontend/lexer/lexer.h deleted file mode 100644 index dc7a58a..0000000 --- a/src/ccompiler/frontend/lexer/lexer.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @file lexer.h - * @brief C语言词法分析器核心数据结构与接口 - */ - -#ifndef __SMCC_CC_LEXER_H__ -#define __SMCC_CC_LEXER_H__ - -#include -#include "token.h" - -#ifndef LEXER_MAX_TOKEN_SIZE -#define LEXER_MAX_TOKEN_SIZE 63 ///< 单个token的最大长度限制 -#endif - -#ifndef LEXER_BUFFER_SIZE -#define LEXER_BUFFER_SIZE 4095 ///< 词法分析缓冲区大小 -#endif - -/** - * @brief 流读取函数原型 - * @param dst_buf 目标缓冲区 - * @param elem_size 元素大小 - * @param count 元素数量 - * @param stream 输入流指针 - * @return 实际读取的元素数量 - */ -typedef int (*lexer_sread_fn)(void *dst_buf, int elem_size, int count, void *stream); - -/** - * @brief 词法分析器核心结构体 - * - * 封装词法分析所需的状态信息和缓冲区管理 - */ -typedef struct cc_lexer { - loc_t loc; ///< 当前解析位置信息(文件名、行列号等) - - char* cur_ptr; ///< 当前扫描指针(指向尚未处理的字符) - char* end_ptr; ///< 缓冲区结束指针(指向最后一个有效字符的下一个位置) - char buffer[LEXER_BUFFER_SIZE+1]; ///< 字符缓冲区(包含NUL终止符) - - lexer_sread_fn sread; ///< 流读取函数指针 - void* stream; ///< 输入流对象指针 - strpool_t* strpool; ///< 字符串池(用于存储标识符等字符串) -} cc_lexer_t; - -/** - * @brief 初始化词法分析器 - * @param[out] lexer 要初始化的词法分析器实例 - * @param[in] file_name 当前解析的源文件名 - * @param[in] stream 输入流对象指针 - * @param[in] sread 自定义流读取函数 - * @param[in] strpool 字符串池实例 - */ -void init_lexer(cc_lexer_t* lexer, const char* file_name, void* stream, - lexer_sread_fn sread, strpool_t* strpool); - -/** - * @brief 获取原始token - * @param[in] lexer 词法分析器实例 - * @param[out] token 输出token存储位置 - * - * 此函数会返回所有类型的token,包括空白符等无效token - */ -void get_token(cc_lexer_t* lexer, tok_t* token); - -/** - * @brief 获取有效token - * @param[in] lexer 词法分析器实例 - * @param[out] token 输出token存储位置 - * - * 此函数会自动跳过空白符等无效token,返回对语法分析有意义的token - */ -void get_valid_token(cc_lexer_t* lexer, tok_t* token); - -#endif \ No newline at end of file diff --git a/src/ccompiler/frontend/lexer/lexer_log.h b/src/ccompiler/frontend/lexer/lexer_log.h deleted file mode 100644 index 21c7aa0..0000000 --- a/src/ccompiler/frontend/lexer/lexer_log.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef __SMCC_LEXER_LOG_H__ -#define __SMCC_LEXER_LOG_H__ - -#include - -#ifndef LEX_LOG_LEVEL -#define LEX_LOG_LEVEL 4 -#endif - -#if LEX_LOG_LEVEL <= 1 -#define LEX_NOTSET( fmt, ...) LOG_NOTSET("LEXER: " fmt, ##__VA_ARGS__) -#else -#define LEX_NOTSET( fmt, ...) -#endif - -#if LEX_LOG_LEVEL <= 2 -#define LEX_DEBUG( fmt, ...) LOG_DEBUG( "LEXER: " fmt, ##__VA_ARGS__) -#else -#define LEX_DEBUG( fmt, ...) -#endif - -#if LEX_LOG_LEVEL <= 3 -#define LEX_INFO( fmt, ...) LOG_INFO( "LEXER: " fmt, ##__VA_ARGS__) -#else -#define LEX_INFO( fmt, ...) -#endif - -#if LEX_LOG_LEVEL <= 4 -#define LEX_WARN( fmt, ...) LOG_WARN( "LEXER: " fmt, ##__VA_ARGS__) -#else -#define LEX_WARN( fmt, ...) -#endif - -#if LEX_LOG_LEVEL <= 5 -#define LEX_ERROR( fmt, ...) LOG_ERROR("LEXER: " fmt, ##__VA_ARGS__) -#else -#define LEX_ERROR( fmt, ...) -#endif - -#if LEX_LOG_LEVEL <= 6 -#define LEX_FATAL( fmt, ...) LOG_FATAL("LEXER: " fmt, ##__VA_ARGS__) -#else -#define LEX_FATAL( fmt, ...) -#endif - -#endif // __SMCC_LEXER_LOG_H__ diff --git a/src/ccompiler/frontend/lexer/token.c b/src/ccompiler/frontend/lexer/token.c deleted file mode 100644 index 08cc5b1..0000000 --- a/src/ccompiler/frontend/lexer/token.c +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include "lexer_log.h" -#include "token.h" - -#define ROUND_IDX(idx) ((idx) % tokbuf->cap) - -tok_t* pop_tok(tok_stream_t* tokbuf) { - if (tokbuf->size == 0) { - LEX_ERROR("no token to pop"); - return NULL; - } - int idx = tokbuf->cur; - tokbuf->cur = ROUND_IDX(idx + 1); - tokbuf->size -= 1; - return tokbuf->buf + idx; -} - -void flush_peek_tok(tok_stream_t* tokbuf) { - tokbuf->peek = tokbuf->cur; -} - -void init_tokbuf(tok_stream_t *tokbuf, void *stream, tok_stream_get_func gettok) { - tokbuf->cur = 0; - tokbuf->end = 0; - tokbuf->peek = 0; - tokbuf->size = 0; - tokbuf->stream = stream; - tokbuf->gettok = gettok; - tokbuf->buf = NULL; - tokbuf->cap = 0; -} - -tok_t *peek_tok(tok_stream_t *tokbuf) { - Assert(tokbuf->size <= tokbuf->cap); - int idx = tokbuf->peek; - tokbuf->peek = ROUND_IDX(idx + 1); - - if (idx == tokbuf->end) { - if (tokbuf->size == tokbuf->cap) { - LEX_ERROR("peek_tok buffer overflow"); - return NULL; - } - if (tokbuf->gettok == NULL) { - LEX_ERROR("peek_tok can not got tok"); - return NULL; - } - tokbuf->gettok(tokbuf->stream, &(tokbuf->buf[idx])); - tokbuf->size++; - tokbuf->end = tokbuf->peek; - } - - return &(tokbuf->buf[idx]); -} - -cc_tktype_t peek_tok_type(tok_stream_t* tokbuf) { - return peek_tok(tokbuf)->sub_type; -} - -int expect_pop_tok(tok_stream_t* tokbuf, cc_tktype_t type) { - flush_peek_tok(tokbuf); - tok_t* tok = peek_tok(tokbuf); - if (tok->sub_type != type) { - LEX_ERROR("expected tok `%s` but got `%s`", get_tok_name(type), get_tok_name(tok->type)); - return 0; - } else { - pop_tok(tokbuf); - } - return 0; -} - -// 生成字符串映射(根据需求选择#str或#name) -static const char* token_strings[] = { - // 普通token使用#str - #define X(str, basic, tok) [tok] = #str, - TOKEN_TABLE - #undef X - - // 关键字使用#name - #define X(name, std, tok) [tok] = #name, - KEYWORD_TABLE - #undef X -}; - -const char* get_tok_name(cc_tktype_t type) { - return token_strings[type]; -} diff --git a/src/ccompiler/frontend/lexer/token.h b/src/ccompiler/frontend/lexer/token.h deleted file mode 100644 index 1811c43..0000000 --- a/src/ccompiler/frontend/lexer/token.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef __SMCC_CC_TOKEN_H__ -#define __SMCC_CC_TOKEN_H__ - -#include - -enum CSTD_KEYWORD { - CSTD_C89, - CSTD_C99, - CEXT_ASM, -}; - -// Using Binary Search To Fast Find Keyword -#define KEYWORD_TABLE \ - X(asm , CEXT_ASM, TOKEN_ASM) \ - X(break , CSTD_C89, TOKEN_BREAK) \ - X(case , CSTD_C89, TOKEN_CASE) \ - X(char , CSTD_C89, TOKEN_CHAR) \ - X(const , CSTD_C89, TOKEN_CONST) \ - X(continue , CSTD_C89, TOKEN_CONTINUE) \ - X(default , CSTD_C89, TOKEN_DEFAULT) \ - X(do , CSTD_C89, TOKEN_DO) \ - X(double , CSTD_C89, TOKEN_DOUBLE) \ - X(else , CSTD_C89, TOKEN_ELSE) \ - X(enum , CSTD_C89, TOKEN_ENUM) \ - X(extern , CSTD_C89, TOKEN_EXTERN) \ - X(float , CSTD_C89, TOKEN_FLOAT) \ - X(for , CSTD_C89, TOKEN_FOR) \ - X(goto , CSTD_C89, TOKEN_GOTO) \ - X(if , CSTD_C89, TOKEN_IF) \ - X(inline , CSTD_C99, TOKEN_INLINE) \ - X(int , CSTD_C89, TOKEN_INT) \ - X(long , CSTD_C89, TOKEN_LONG) \ - X(register , CSTD_C89, TOKEN_REGISTER) \ - X(restrict , CSTD_C99, TOKEN_RESTRICT) \ - X(return , CSTD_C89, TOKEN_RETURN) \ - X(short , CSTD_C89, TOKEN_SHORT) \ - X(signed , CSTD_C89, TOKEN_SIGNED) \ - X(sizeof , CSTD_C89, TOKEN_SIZEOF) \ - X(static , CSTD_C89, TOKEN_STATIC) \ - X(struct , CSTD_C89, TOKEN_STRUCT) \ - X(switch , CSTD_C89, TOKEN_SWITCH) \ - X(typedef , CSTD_C89, TOKEN_TYPEDEF) \ - X(union , CSTD_C89, TOKEN_UNION) \ - X(unsigned , CSTD_C89, TOKEN_UNSIGNED) \ - X(void , CSTD_C89, TOKEN_VOID) \ - X(volatile , CSTD_C89, TOKEN_VOLATILE) \ - X(while , CSTD_C89, TOKEN_WHILE) \ - // KEYWORD_TABLE - -#define TOKEN_TABLE \ - X(init , TK_BASIC_INVALID, TOKEN_INIT) \ - X(EOF , TK_BASIC_EOF, TOKEN_EOF) \ - X(blank , TK_BASIC_WHITESPACE, TOKEN_BLANK) \ - X("==" , TK_BASIC_OPERATOR, TOKEN_EQ) \ - X("=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN) \ - X("++" , TK_BASIC_OPERATOR, TOKEN_ADD_ADD) \ - X("+=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_ADD) \ - X("+" , TK_BASIC_OPERATOR, TOKEN_ADD) \ - X("--" , TK_BASIC_OPERATOR, TOKEN_SUB_SUB) \ - X("-=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_SUB) \ - X("->" , TK_BASIC_OPERATOR, TOKEN_DEREF) \ - X("-" , TK_BASIC_OPERATOR, TOKEN_SUB) \ - X("*=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_MUL) \ - X("*" , TK_BASIC_OPERATOR, TOKEN_MUL) \ - X("/=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_DIV) \ - X("/" , TK_BASIC_OPERATOR, TOKEN_DIV) \ - X("//" , TK_BASIC_COMMENT , TOKEN_LINE_COMMENT) \ - X("/* */" , TK_BASIC_COMMENT , TOKEN_BLOCK_COMMENT) \ - X("%=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_MOD) \ - X("%" , TK_BASIC_OPERATOR, TOKEN_MOD) \ - X("&&" , TK_BASIC_OPERATOR, TOKEN_AND_AND) \ - X("&=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_AND) \ - X("&" , TK_BASIC_OPERATOR, TOKEN_AND) \ - X("||" , TK_BASIC_OPERATOR, TOKEN_OR_OR) \ - X("|=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_OR) \ - X("|" , TK_BASIC_OPERATOR, TOKEN_OR) \ - X("^=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_XOR) \ - X("^" , TK_BASIC_OPERATOR, TOKEN_XOR) \ - X("<<=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_L_SH) \ - X("<<" , TK_BASIC_OPERATOR, TOKEN_L_SH) \ - X("<=" , TK_BASIC_OPERATOR, TOKEN_LE) \ - X("<" , TK_BASIC_OPERATOR, TOKEN_LT) \ - X(">>=" , TK_BASIC_OPERATOR, TOKEN_ASSIGN_R_SH) \ - X(">>" , TK_BASIC_OPERATOR, TOKEN_R_SH) \ - X(">=" , TK_BASIC_OPERATOR, TOKEN_GE) \ - X(">" , TK_BASIC_OPERATOR, TOKEN_GT) \ - X("!" , TK_BASIC_OPERATOR, TOKEN_NOT) \ - X("!=" , TK_BASIC_OPERATOR, TOKEN_NEQ) \ - X("~" , TK_BASIC_OPERATOR, TOKEN_BIT_NOT) \ - X("[" , TK_BASIC_OPERATOR, TOKEN_L_BRACKET) \ - X("]" , TK_BASIC_OPERATOR, TOKEN_R_BRACKET) \ - X("(" , TK_BASIC_OPERATOR, TOKEN_L_PAREN) \ - X(")" , TK_BASIC_OPERATOR, TOKEN_R_PAREN) \ - X("{" , TK_BASIC_OPERATOR, TOKEN_L_BRACE) \ - X("}" , TK_BASIC_OPERATOR, TOKEN_R_BRACE) \ - X(";" , TK_BASIC_OPERATOR, TOKEN_SEMICOLON) \ - X("," , TK_BASIC_OPERATOR, TOKEN_COMMA) \ - X(":" , TK_BASIC_OPERATOR, TOKEN_COLON) \ - X("." , TK_BASIC_OPERATOR, TOKEN_DOT) \ - X("..." , TK_BASIC_OPERATOR, TOKEN_ELLIPSIS) \ - X("?" , TK_BASIC_OPERATOR, TOKEN_COND) \ - X(ident , TK_BASIC_IDENTIFIER, TOKEN_IDENT) \ - X(int_literal , TK_BASIC_LITERAL, TOKEN_INT_LITERAL) \ - X(float_literal , TK_BASIC_LITERAL, TOKEN_FLOAT_LITERAL) \ - X(char_literal , TK_BASIC_LITERAL, TOKEN_CHAR_LITERAL) \ - X(string_literal , TK_BASIC_LITERAL, TOKEN_STRING_LITERAL) \ - // END - -// 定义TokenType枚举 -typedef enum cc_tktype { - // 处理普通token - #define X(str, basic, tok) tok, - TOKEN_TABLE - #undef X - - // 处理关键字(保持原有格式) - #define X(name, std, tok) tok, - KEYWORD_TABLE - #undef X -} cc_tktype_t; - -typedef struct tok_stream { - int cur; - int end; - int peek; - int size; - int cap; - tok_t* buf; - void* stream; - void (*gettok)(void* stream, tok_t* token); -} tok_stream_t; - -typedef void(*tok_stream_get_func)(void* stream, tok_t* token); -void init_tokbuf(tok_stream_t* tokbuf, void* stream, tok_stream_get_func gettok); -tok_t* peek_tok(tok_stream_t* tokbuf); -tok_t* pop_tok(tok_stream_t* tokbuf); -void flush_peek_tok(tok_stream_t* tokbuf); -cc_tktype_t peek_tok_type(tok_stream_t* tokbuf); -int expect_pop_tok(tok_stream_t* tokbuf, cc_tktype_t type); -const char* get_tok_name(cc_tktype_t type); - -#endif diff --git a/src/ccompiler/frontend/parser/ast-gdb.py b/src/ccompiler/frontend/parser/ast-gdb.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/ccompiler/frontend/parser/ast.c b/src/ccompiler/frontend/parser/ast.c deleted file mode 100644 index 8f26038..0000000 --- a/src/ccompiler/frontend/parser/ast.c +++ /dev/null @@ -1,172 +0,0 @@ -#include "ast.h" -ast_node_t* new_ast_node(void) { - ast_node_t* node = rt._malloc(sizeof(ast_node_t)); - init_ast_node(node); - return node; -} - -void init_ast_node(ast_node_t* node) { - node->type = NT_INIT; - - for (int i = 0; i < sizeof(node->children) / sizeof(node->children[0]); i++) { - node->children[i] = NULL; - } -} - -// ast_node_t* find_ast_node(ast_node_t* node, ast_type_t type) { - -// } - -#include -static void pnt_depth(int depth) { - for (int i = 0; i < depth; i++) { - printf(" "); - } -} - -// void pnt_ast(ast_node_t* node, int depth) { -// if (!node) return; -// pnt_depth(depth); -// switch (node->type) { -// case NT_ROOT: -// for (int i = 0; i < node->root.child_size; i++) { -// pnt_ast(node->root.children[i], depth); -// } -// return; - -// case NT_ADD : printf("+ \n"); break; // (expr) + (expr) -// case NT_SUB : printf("- \n"); break; // (expr) - (expr) -// case NT_MUL : printf("* \n"); break; // (expr) * (expr) -// case NT_DIV : printf("/ \n"); break; // (expr) / (expr) -// case NT_MOD : printf("%%\n"); break; // (expr) % (expr) -// case NT_AND : printf("& \n"); break; // (expr) & (expr) -// case NT_OR : printf("| \n"); break; // (expr) | (expr) -// case NT_XOR : printf("^ \n"); break; // (expr) ^ (expr) -// case NT_L_SH : printf("<<\n"); break; // (expr) << (expr) -// case NT_R_SH : printf(">>\n"); break; // (expr) >> (expr) -// case NT_EQ : printf("==\n"); break; // (expr) == (expr) -// case NT_NEQ : printf("!=\n"); break; // (expr) != (expr) -// case NT_LE : printf("<=\n"); break; // (expr) <= (expr) -// case NT_GE : printf(">=\n"); break; // (expr) >= (expr) -// case NT_LT : printf("< \n"); break; // (expr) < (expr) -// case NT_GT : printf("> \n"); break; // (expr) > (expr) -// case NT_AND_AND : printf("&&\n"); break; // (expr) && (expr) -// case NT_OR_OR : printf("||\n"); break; // (expr) || (expr) -// case NT_NOT : printf("! \n"); break; // ! (expr) -// case NT_BIT_NOT : printf("~ \n"); break; // ~ (expr) -// case NT_COMMA : printf(", \n"); break; // expr, expr 逗号运算符 -// case NT_ASSIGN : printf("= \n"); break; // (expr) = (expr) -// // case NT_COND : // (expr) ? (expr) : (expr) - -// case NT_STMT_EMPTY : // ; -// printf(";\n"); -// break; -// case NT_STMT_IF : // if (cond) { ... } [else {...}] -// printf("if"); -// pnt_ast(node->if_stmt.cond, depth+1); -// pnt_ast(node->if_stmt.if_stmt, depth+1); -// if (node->if_stmt.else_stmt) { -// pnt_depth(depth); -// printf("else"); -// pnt_ast(node->if_stmt.else_stmt, depth+1); -// } -// break; -// case NT_STMT_WHILE : // while (cond) { ... } -// printf("while\n"); -// pnt_ast(node->while_stmt.cond, depth+1); -// pnt_ast(node->while_stmt.body, depth+1); -// break; -// case NT_STMT_DOWHILE : // do {...} while (cond) -// printf("do-while\n"); -// pnt_ast(node->do_while_stmt.body, depth+1); -// pnt_ast(node->do_while_stmt.cond, depth+1); -// break; -// case NT_STMT_FOR : // for (init; cond; iter) {...} -// printf("for\n"); -// if (node->for_stmt.init) -// pnt_ast(node->for_stmt.init, depth+1); -// if (node->for_stmt.cond) -// pnt_ast(node->for_stmt.cond, depth+1); -// if (node->for_stmt.iter) -// pnt_ast(node->for_stmt.iter, depth+1); -// pnt_ast(node->for_stmt.body, depth+1); -// break; -// case NT_STMT_SWITCH : // switch (expr) { case ... } -// case NT_STMT_BREAK : // break; -// case NT_STMT_CONTINUE : // continue; -// case NT_STMT_GOTO : // goto label; -// case NT_STMT_CASE : // case const_expr: -// case NT_STMT_DEFAULT : // default: -// case NT_STMT_LABEL : // label: -// break; -// case NT_STMT_BLOCK : // { ... } -// printf("{\n"); -// for (int i = 0; i < node->block.child_size; i++) { -// pnt_ast(node->block.children[i], depth+1); -// } -// pnt_depth(depth); -// printf("}\n"); -// break; -// case NT_STMT_RETURN : // return expr; -// printf("return"); -// if (node->return_stmt.expr_stmt) { -// printf(" "); -// pnt_ast(node->return_stmt.expr_stmt, depth+1); -// } else { -// printf("\n"); -// } -// break; -// case NT_STMT_EXPR : // expr; -// printf("stmt\n"); -// pnt_ast(node->expr_stmt.expr_stmt, depth); -// pnt_depth(depth); -// printf(";\n"); -// break; -// case NT_DECL_VAR : // type name; or type name = expr; -// printf("decl_val\n"); -// break; -// case NT_DECL_FUNC: // type func_name(param_list); -// printf("decl func %s\n", node->func.name->syms.tok.val.str); -// break; -// case NT_FUNC : // type func_name(param_list) {...} -// printf("def func %s\n", node->func.name->syms.tok.val.str); -// // pnt_ast(node->child.func.params, depth); -// pnt_ast(node->func.body, depth); -// // pnt_ast(node->child.func.ret, depth); -// break; -// case NT_PARAM : // 函数形参 -// printf("param\n"); -// case NT_ARG_LIST : // 实参列表(需要与NT_CALL配合) -// printf("arg_list\n"); -// case NT_TERM_CALL : // func (expr) -// printf("call\n"); -// break; -// case NT_TERM_IDENT: -// printf("%s\n", node->syms.tok.val.str); -// break; -// case NT_TERM_VAL : // Terminal Symbols like constant, identifier, keyword -// tok_t * tok = &node->syms.tok; -// switch (tok->type) { -// case TOKEN_CHAR_LITERAL: -// printf("%c\n", tok->val.ch); -// break; -// case TOKEN_INT_LITERAL: -// printf("%d\n", tok->val.i); -// break; -// case TOKEN_STRING_LITERAL: -// printf("%s\n", tok->val.str); -// break; -// default: -// printf("unknown term val\n"); -// break; -// } -// default: -// break; -// } - -// // 通用子节点递归处理 -// if (node->type <= NT_ASSIGN) { // 表达式类统一处理子节点 -// if (node->expr.left) pnt_ast(node->expr.left, depth+1); -// if (node->expr.right) pnt_ast(node->expr.right, depth + 1); -// } -// } diff --git a/src/ccompiler/frontend/parser/ast.h b/src/ccompiler/frontend/parser/ast.h deleted file mode 100644 index c3e74c6..0000000 --- a/src/ccompiler/frontend/parser/ast.h +++ /dev/null @@ -1,190 +0,0 @@ -#ifndef __AST_H__ -#define __AST_H__ - -#include "../lexer/lexer.h" -#include -#include "symtab/symtab.h" -#include "type.h" - -typedef enum { - NT_INIT, - NT_ROOT, // global scope in root node - NT_ADD, // (expr) + (expr) - NT_SUB, // (expr) - (expr) - NT_MUL, // (expr) * (expr) - NT_DIV, // (expr) / (expr) - NT_MOD, // (expr) % (expr) - NT_AND, // (expr) & (expr) - NT_OR, // (expr) | (expr) - NT_XOR, // (expr) ^ (expr) - NT_L_SH, // (expr) << (expr) - NT_R_SH, // (expr) >> (expr) - NT_EQ, // (expr) == (expr) - NT_NEQ, // (expr) != (expr) - NT_LE, // (expr) <= (expr) - NT_GE, // (expr) >= (expr) - NT_LT, // (expr) < (expr) - NT_GT, // (expr) > (expr) - NT_AND_AND, // (expr) && (expr) - NT_OR_OR, // (expr) || (expr) - NT_NOT, // ! (expr) - NT_BIT_NOT, // ~ (expr) - NT_COND, // (expr) ? (expr) : (expr) - NT_COMMA, // expr, expr 逗号运算符 - NT_ASSIGN, // (expr) = (expr) - - NT_ADDRESS, // &expr (取地址) - NT_DEREF, // *expr (解引用) - NT_INDEX, // arr[index] (数组访问) - NT_MEMBER, // struct.member - NT_PTR_MEMBER,// ptr->member - - NT_CAST, // (type)expr 强制类型转换 - NT_SIZEOF, // sizeof(type|expr) -// NT_ALIGNOF, // _Alignof(type) (C11) - - NT_STMT_EMPTY, // ; - NT_STMT_IF, // if (cond) { ... } [else {...}] - NT_STMT_WHILE, // while (cond) { ... } - NT_STMT_DOWHILE, // do {...} while (cond) - NT_STMT_FOR, // for (init; cond; iter) {...} - NT_STMT_SWITCH, // switch (expr) { case ... } - NT_STMT_BREAK, // break; - NT_STMT_CONTINUE, // continue; - NT_STMT_GOTO, // goto label; - NT_STMT_CASE, // case const_expr: - NT_STMT_DEFAULT, // default: - NT_STMT_LABEL, // label: - NT_STMT_BLOCK, // { ... } - NT_STMT_RETURN, // return expr; - NT_STMT_EXPR, // expr; - - NT_BLOCK, - // NT_TYPE_BASE, // 基础类型节点 - // NT_TYPE_PTR, // 指针类型 - // NT_TYPE_ARRAY, // 数组类型 - // NT_TYPE_FUNC, // 函数类型 - // NT_TYPE_QUAL, // 限定符节点 - - NT_DECL_VAR, // type name; or type name = expr; - NT_DECL_FUNC, // type func_name(param_list); - NT_FUNC, // type func_name(param_list) {...} - NT_PARAM, // 函数形参 - NT_ARG_LIST, // 实参列表(需要与NT_CALL配合) - - NT_TERM_CALL, // func (expr) - NT_TERM_VAL, - NT_TERM_IDENT, - NT_TERM_TYPE, -} ast_type_t; - -typedef struct ast_node { - ast_type_t type; - - union { - void *children[6]; - struct { - VECTOR_HEADER(children, struct ast_node *); - } root; - struct { - VECTOR_HEADER(children, struct ast_node *); - } block; - struct { - symtab_key_t key; - struct ast_node * decl_node; - tok_t tok; - } syms; - struct { - VECTOR_HEADER(params, struct ast_node *); - } params; - struct { - struct ast_node * name; - struct ast_node * params; - struct ast_node * func_decl; - } call; - struct { - struct ast_node *type; - struct ast_node *name; - struct ast_node *expr_stmt; // optional - void* data; - } decl_val; - struct { - struct ast_node *ret; - struct ast_node *name; - struct ast_node *params; // array of params - struct ast_node *def; - } decl_func; - struct { - struct ast_node *decl; - struct ast_node *body; // optional - void* data; - } func; - struct { - struct ast_node *left; - struct ast_node *right; - struct ast_node *optional; // optional - } expr; - struct { - struct ast_node *cond; - struct ast_node *if_stmt; - struct ast_node *else_stmt; // optional - } if_stmt; - struct { - struct ast_node *cond; - struct ast_node *body; - } switch_stmt; - struct { - struct ast_node *cond; - struct ast_node *body; - } while_stmt; - struct { - struct ast_node *body; - struct ast_node *cond; - } do_while_stmt; - struct { - struct ast_node *init; - struct ast_node *cond; // optional - struct ast_node *iter; // optional - struct ast_node *body; - } for_stmt; - struct { - struct ast_node *expr_stmt; // optional - } return_stmt; - struct { - struct ast_node *label; - } goto_stmt; - struct { - struct ast_node *label; - } label_stmt; - struct { - struct ast_node *block; - } block_stmt; - struct { - struct ast_node *expr_stmt; - } expr_stmt; - }; -} ast_node_t; - -ast_node_t* new_ast_node(void); -void init_ast_node(ast_node_t* node); -void pnt_ast(ast_node_t* node, int depth); - -typedef struct parser parser_t; -typedef ast_node_t* (*parse_func_t) (parser_t*); - -void parse_prog(parser_t* parser); -ast_node_t* parse_decl(parser_t* parser); -ast_node_t* parse_decl_val(parser_t* parser); - -ast_node_t* parse_block(parser_t* parser); -ast_node_t* parse_stmt(parser_t* parser); -ast_node_t* parse_expr(parser_t* parser); - -ast_node_t* parse_type(parser_t* parser); - -ast_node_t* new_ast_ident_node(tok_t* tok); -ast_node_t* expect_pop_ident(tok_stream_t* tokbuf); - -int peek_decl(tok_stream_t* tokbuf); - -#endif diff --git a/src/ccompiler/frontend/parser/ast/README.md b/src/ccompiler/frontend/parser/ast/README.md deleted file mode 100644 index 6303b46..0000000 --- a/src/ccompiler/frontend/parser/ast/README.md +++ /dev/null @@ -1,18 +0,0 @@ -- ast.c 作为抽象语法树的定义 - -- block.c 作为块的实现主要用于处理作用域,需要符号表 - -- decl.c 作为声明的实现,其中主要携带变量声明,函数声明见 func.c ,需要符号表 - -- func.c 作为函数的实现,其中主要携带函数声明,以及函数定义,需要符号表 - -- expr.c 作为表达式的实现。需要符号表 - -- stmt.c 作为语句的实现。需要表达式类型判断合法性 - -- term.c 作为终结符的实现。需要表达式类型判断合法性 - -- program.c 作为词法分析(语义分析)入口函数,可以根据parser结构生成AST - -其中stmt参考cppreference -其中expr参考AI以及CParser diff --git a/src/ccompiler/frontend/parser/ast/block.c b/src/ccompiler/frontend/parser/ast/block.c deleted file mode 100644 index 60ec425..0000000 --- a/src/ccompiler/frontend/parser/ast/block.c +++ /dev/null @@ -1,49 +0,0 @@ - -#include "../ast.h" -#include "../parser.h" - -#ifndef BLOCK_MAX_NODE -#define BLOCK_MAX_NODE (1024) -#endif - -ast_node_t* new_ast_node_block() { - ast_node_t* node = new_ast_node(); - node->type = NT_BLOCK; - vector_init(node->block.children); - return node; -} - -ast_node_t* parse_block(parser_t* parser) { - symtab_enter_scope(parser->symtab); - tok_stream_t *tokbuf = &parser->tokbuf; - flush_peek_tok(tokbuf); - cc_tktype_t ttype; - ast_node_t* node = new_ast_node_block(); - - expect_pop_tok(tokbuf, TOKEN_L_BRACE); - ast_node_t* child = NULL; - while (1) { - if (peek_decl(tokbuf)) { - child = parse_decl(parser); - vector_push(node->block.children, child); - continue; - } - - flush_peek_tok(tokbuf); - ttype = peek_tok_type(tokbuf); - switch (ttype) { - case TOKEN_R_BRACE: { - pop_tok(tokbuf); - goto END; - } - default: { - child = parse_stmt(parser); - vector_push(node->block.children, child); - break; - } - } - } -END: - symtab_leave_scope(parser->symtab); - return node; -} diff --git a/src/ccompiler/frontend/parser/ast/decl.c b/src/ccompiler/frontend/parser/ast/decl.c deleted file mode 100644 index b66654a..0000000 --- a/src/ccompiler/frontend/parser/ast/decl.c +++ /dev/null @@ -1,98 +0,0 @@ -#include "../ast.h" -#include "../parser.h" - -/** - * 0 false - * 1 true - */ -int peek_decl(tok_stream_t* tokbuf) { - flush_peek_tok(tokbuf); - switch (peek_tok_type(tokbuf)) { - case TOKEN_STATIC: - case TOKEN_EXTERN: - case TOKEN_REGISTER: - case TOKEN_TYPEDEF: - LOG_ERROR("not impliment"); - break; - default: - flush_peek_tok(tokbuf); - } - - switch (peek_tok_type(tokbuf)) { - case TOKEN_VOID: - case TOKEN_CHAR: - case TOKEN_SHORT: - case TOKEN_INT: - case TOKEN_LONG: - case TOKEN_FLOAT: - case TOKEN_DOUBLE: - // FIXME Ptr - return 1; - default: - flush_peek_tok(tokbuf); - } - return 0; -} - -ast_node_t* parse_decl_val(parser_t* parser) { - tok_stream_t* tokbuf = &parser->tokbuf; - cc_tktype_t ttype; - flush_peek_tok(tokbuf); - - ast_node_t* node; - ast_node_t* type_node = parse_type(parser); - flush_peek_tok(tokbuf); - ast_node_t* name_node = new_ast_ident_node(peek_tok(tokbuf)); - - node = new_ast_node(); - node->decl_val.type = type_node; - node->decl_val.name = name_node; - node->type = NT_DECL_VAR; - - type_node->syms.key.uid = parser->symtab->cur_scope->uid; - type_node->syms.key.strp_name = name_node->syms.tok.val.str; - symtab_add(parser->symtab, &type_node->syms.key, node); - - ttype = peek_tok_type(tokbuf); - if (ttype == TOKEN_ASSIGN) { - node->decl_val.expr_stmt = parse_stmt(parser); - if (node->decl_val.expr_stmt->type != NT_STMT_EXPR) { - LOG_ERROR("parser_decl_val want stmt_expr"); - } - } else if (ttype == TOKEN_SEMICOLON) { - pop_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_SEMICOLON); - } else { - LOG_ERROR("parser_decl_val syntax error"); - } - return node; -} - -ast_node_t* parse_decl(parser_t* parser) { - tok_stream_t* tokbuf = &parser->tokbuf; - flush_peek_tok(tokbuf); - cc_tktype_t ttype; - ast_node_t* node; - - if (peek_decl(tokbuf) == 0) { - LOG_ERROR("syntax error expect decl_val TYPE"); - } - if (peek_tok_type(tokbuf) != TOKEN_IDENT) { - LOG_ERROR("syntax error expect decl_val IDENT"); - } - - ttype = peek_tok_type(tokbuf); - switch (ttype) { - case TOKEN_L_PAREN: // ( - return NULL; - break; - case TOKEN_ASSIGN: - case TOKEN_SEMICOLON: - node = parse_decl_val(parser); - break; - default: - LOG_ERROR("syntax error expect decl_val ASSIGN or SEMICOLON"); - return NULL; - } - return node; -} diff --git a/src/ccompiler/frontend/parser/ast/expr.c b/src/ccompiler/frontend/parser/ast/expr.c deleted file mode 100644 index ce658ac..0000000 --- a/src/ccompiler/frontend/parser/ast/expr.c +++ /dev/null @@ -1,425 +0,0 @@ -#include "../ast.h" -#include "../parser.h" - -// Copy from `CParse` -/** - * Operator precedence classes - */ -enum Precedence { - PREC_BOTTOM, - PREC_EXPRESSION, /* , left to right */ - PREC_ASSIGNMENT, /* = += -= *= /= %= <<= >>= &= ^= |= right to left */ - PREC_CONDITIONAL, /* ?: right to left */ - PREC_LOGICAL_OR, /* || left to right */ - PREC_LOGICAL_AND, /* && left to right */ - PREC_OR, /* | left to right */ - PREC_XOR, /* ^ left to right */ - PREC_AND, /* & left to right */ - PREC_EQUALITY, /* == != left to right */ - PREC_RELATIONAL, /* < <= > >= left to right */ - PREC_SHIFT, /* << >> left to right */ - PREC_ADDITIVE, /* + - left to right */ - PREC_MULTIPLICATIVE, /* * / % left to right */ - PREC_CAST, /* (type) right to left */ - PREC_UNARY, /* ! ~ ++ -- + - * & sizeof right to left */ - PREC_POSTFIX, /* () [] -> . left to right */ - PREC_PRIMARY, - PREC_TOP -}; - -enum ParseType { - INFIX_PARSER, - PREFIX_PARSER, -}; - -static ast_node_t *parse_subexpression(tok_stream_t* tokbuf, symtab_t *symtab, enum Precedence prec); -#define NEXT(prec) parse_subexpression(tokbuf, symtab, prec) - -static ast_node_t* gen_node2(ast_node_t* left, ast_node_t* right, - ast_type_t type) { - ast_node_t* node = new_ast_node(); - node->type = type; - node->expr.left = left; - node->expr.right = right; - return node; - // FIXME - - // switch (type) { - // case NT_ADD : printf("+ \n"); break; // (expr) + (expr) - // case NT_SUB : printf("- \n"); break; // (expr) - (expr) - // case NT_MUL : printf("* \n"); break; // (expr) * (expr) - // case NT_DIV : printf("/ \n"); break; // (expr) / (expr) - // case NT_MOD : printf("%%\n"); break; // (expr) % (expr) - // case NT_AND : printf("& \n"); break; // (expr) & (expr) - // case NT_OR : printf("| \n"); break; // (expr) | (expr) - // case NT_XOR : printf("^ \n"); break; // (expr) ^ (expr) - // case NT_L_SH : printf("<<\n"); break; // (expr) << (expr) - // case NT_R_SH : printf(">>\n"); break; // (expr) >> (expr) - // case NT_EQ : printf("==\n"); break; // (expr) == (expr) - // case NT_NEQ : printf("!=\n"); break; // (expr) != (expr) - // case NT_LE : printf("<=\n"); break; // (expr) <= (expr) - // case NT_GE : printf(">=\n"); break; // (expr) >= (expr) - // case NT_LT : printf("< \n"); break; // (expr) < (expr) - // case NT_GT : printf("> \n"); break; // (expr) > (expr) - // case NT_AND_AND : printf("&&\n"); break; // (expr) && (expr) - // case NT_OR_OR : printf("||\n"); break; // (expr) || (expr) - // case NT_NOT : printf("! \n"); break; // ! (expr) - // case NT_BIT_NOT : printf("~ \n"); break; // ~ (expr) - // case NT_COMMA : printf(", \n"); break; // expr, expr 逗号运算符 - // case NT_ASSIGN : printf("= \n"); break; // (expr) = (expr) - // // case NT_COND : // (expr) ? (expr) : (expr) - // } -} - -static ast_node_t* parse_comma(tok_stream_t* tokbuf, symtab_t *symtab, ast_node_t* left) { - ast_node_t* node = new_ast_node(); - node->type = NT_COMMA; - node->expr.left = left; - node->expr.right = NEXT(PREC_EXPRESSION); - return node; -} - -static ast_node_t* parse_assign(tok_stream_t* tokbuf, symtab_t *symtab, ast_node_t* left) { - flush_peek_tok(tokbuf); - cc_tktype_t ttype = peek_tok_type(tokbuf); - pop_tok(tokbuf); - ast_node_t* node = new_ast_node(); - node->type = NT_ASSIGN; - // saved left - node->expr.left = left; - enum Precedence next = PREC_ASSIGNMENT + 1; - switch (ttype) { - case TOKEN_ASSIGN : - left = NEXT(next); - break; - case TOKEN_ASSIGN_ADD : - left = gen_node2(left, NEXT(next), NT_ADD); - break; - case TOKEN_ASSIGN_SUB : - left = gen_node2(left, NEXT(next), NT_SUB); - break; - case TOKEN_ASSIGN_MUL : - left = gen_node2(left, NEXT(next), NT_MUL); - break; - case TOKEN_ASSIGN_DIV : - left = gen_node2(left, NEXT(next), NT_DIV); - break; - case TOKEN_ASSIGN_MOD : - left = gen_node2(left, NEXT(next), NT_MOD); - break; - case TOKEN_ASSIGN_L_SH : - left = gen_node2(left, NEXT(next), NT_L_SH); - break; - case TOKEN_ASSIGN_R_SH : - left = gen_node2(left, NEXT(next), NT_R_SH); - break; - case TOKEN_ASSIGN_AND : - left = gen_node2(left, NEXT(next), NT_AND); - break; - case TOKEN_ASSIGN_OR : - left = gen_node2(left, NEXT(next), NT_OR); - break; - case TOKEN_ASSIGN_XOR : - left = gen_node2(left, NEXT(next), NT_XOR); - break; - default: - LOG_ERROR("unsupported operator"); - break; - } - node->expr.right = left; - return node; -} - -static ast_node_t* parse_cmp(tok_stream_t* tokbuf, symtab_t *symtab, ast_node_t* left) { - flush_peek_tok(tokbuf); - cc_tktype_t ttype = peek_tok_type(tokbuf); - pop_tok(tokbuf); - ast_node_t* node = new_ast_node(); - // saved left - node->expr.left = left; - switch (ttype) { - case TOKEN_EQ: - node->type = NT_EQ; - node->expr.right = NEXT(PREC_EQUALITY); - break; - case TOKEN_NEQ: - node->type = NT_NEQ; - node->expr.right = NEXT(PREC_EQUALITY); - break; - case TOKEN_LT: - node->type = NT_LT; - node->expr.right = NEXT(PREC_RELATIONAL); - break; - case TOKEN_GT: - node->type = NT_GT; - node->expr.right = NEXT(PREC_RELATIONAL); - break; - case TOKEN_LE: - node->type = NT_LE; - node->expr.right = NEXT(PREC_RELATIONAL); - break; - case TOKEN_GE: - node->type = NT_GE; - node->expr.right = NEXT(PREC_RELATIONAL); - break; - default: - LOG_ERROR("invalid operator"); - } - return node; -} - -static ast_node_t* parse_cal(tok_stream_t* tokbuf, symtab_t *symtab, ast_node_t* left) { - flush_peek_tok(tokbuf); - cc_tktype_t ttype = peek_tok_type(tokbuf); - pop_tok(tokbuf); - ast_node_t* node = new_ast_node(); - node->expr.left = left; - switch (ttype) { - case TOKEN_OR_OR: - node->type = NT_OR_OR; - node->expr.right = NEXT(PREC_LOGICAL_OR); - break; - case TOKEN_AND_AND: - node->type = NT_AND_AND; - node->expr.right = NEXT(PREC_LOGICAL_AND); - break; - case TOKEN_OR: - node->type = NT_OR; - node->expr.right = NEXT(PREC_OR); - break; - case TOKEN_XOR: - node->type = NT_XOR; - node->expr.right = NEXT(PREC_XOR); - break; - case TOKEN_AND: - node->type = NT_AND; - node->expr.right = NEXT(PREC_AND); - break; - case TOKEN_L_SH: - node->type = NT_L_SH; - node->expr.right = NEXT(PREC_SHIFT); - break; - case TOKEN_R_SH: - node->type = NT_R_SH; - node->expr.right = NEXT(PREC_SHIFT); - break; - case TOKEN_ADD: - node->type = NT_ADD; - node->expr.right = NEXT(PREC_ADDITIVE); - break; - case TOKEN_SUB: - node->type = NT_SUB; - node->expr.right = NEXT(PREC_ADDITIVE); - break; - case TOKEN_MUL: - node->type = NT_MUL; - node->expr.right = NEXT(PREC_MULTIPLICATIVE); - break; - case TOKEN_DIV: - node->type = NT_DIV; - node->expr.right = NEXT(PREC_MULTIPLICATIVE); - break; - case TOKEN_MOD: - node->type = NT_MOD; - node->expr.right = NEXT(PREC_MULTIPLICATIVE); - break; - default: - break; - } - return node; -} - -static ast_node_t* parse_call(tok_stream_t* tokbuf, symtab_t *symtab, ast_node_t* ident) { - ast_node_t* node = new_ast_node(); - node->type = NT_TERM_CALL; - node->call.name = ident; - node->call.params = new_ast_node(); - vector_init(node->call.params->params.params); - pop_tok(tokbuf); // 跳过 '(' - - cc_tktype_t ttype; - while (1) { - flush_peek_tok(tokbuf); - ttype = peek_tok_type(tokbuf); - if (ttype == TOKEN_R_PAREN) { - break; - } - ast_node_t* param = NEXT(PREC_EXPRESSION); - vector_push(node->call.params->params.params, param); - flush_peek_tok(tokbuf); - ttype = peek_tok_type(tokbuf); - if (ttype == TOKEN_COMMA) pop_tok(tokbuf); - } - pop_tok(tokbuf); // 跳过 ')' - - ast_node_t* sym = symtab_get(symtab, &ident->syms.key); - // TODO check func is match - if (sym == NULL || sym->type != NT_DECL_FUNC) { - LOG_FATAL("function not decl %s", ident->syms.key.strp_name); - } - node->call.name = ident; - node->call.func_decl = sym; - return node; -} - -static ast_node_t* parse_paren(tok_stream_t* tokbuf, symtab_t *symtab, ast_node_t* left) { - flush_peek_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_L_PAREN); - left = NEXT(PREC_EXPRESSION); - flush_peek_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_R_PAREN); - return left; -} - -typedef ast_node_t* (*parse_expr_fun_t)(tok_stream_t*, symtab_t* , ast_node_t*); -static struct expr_prec_table_t { - parse_expr_fun_t parser; - enum Precedence prec; - enum ParseType ptype; -} expr_table [256] = { - [TOKEN_COMMA] = {parse_comma, PREC_EXPRESSION, INFIX_PARSER}, - [TOKEN_ASSIGN] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_ADD] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_SUB] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_MUL] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_DIV] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_MOD] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_L_SH] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_R_SH] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_AND] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_OR] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - [TOKEN_ASSIGN_XOR] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER}, - - [TOKEN_OR_OR] = {parse_cal, PREC_LOGICAL_OR , INFIX_PARSER}, - [TOKEN_AND_AND] = {parse_cal, PREC_LOGICAL_AND, INFIX_PARSER}, - [TOKEN_OR] = {parse_cal, PREC_OR , INFIX_PARSER}, - [TOKEN_XOR] = {parse_cal, PREC_XOR , INFIX_PARSER}, - [TOKEN_AND] = {parse_cal, PREC_AND , INFIX_PARSER}, - - [TOKEN_EQ] = {parse_cmp, PREC_EQUALITY, INFIX_PARSER}, - [TOKEN_NEQ] = {parse_cmp, PREC_EQUALITY, INFIX_PARSER}, - [TOKEN_LT] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER}, - [TOKEN_LE] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER}, - [TOKEN_GT] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER}, - [TOKEN_GE] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER}, - - [TOKEN_L_SH] = {parse_cal, PREC_SHIFT , INFIX_PARSER}, - [TOKEN_R_SH] = {parse_cal, PREC_SHIFT , INFIX_PARSER}, - [TOKEN_ADD] = {parse_cal, PREC_ADDITIVE , INFIX_PARSER}, - [TOKEN_SUB] = {parse_cal, PREC_ADDITIVE , INFIX_PARSER}, - [TOKEN_MUL] = {parse_cal, PREC_MULTIPLICATIVE , INFIX_PARSER}, - [TOKEN_DIV] = {parse_cal, PREC_MULTIPLICATIVE , INFIX_PARSER}, - [TOKEN_MOD] = {parse_cal, PREC_MULTIPLICATIVE , INFIX_PARSER}, - - [TOKEN_NOT] = {NULL, PREC_UNARY, PREFIX_PARSER}, - [TOKEN_BIT_NOT] = {NULL, PREC_UNARY, PREFIX_PARSER}, - [TOKEN_ADD_ADD] = {NULL, PREC_UNARY, PREFIX_PARSER}, - [TOKEN_SUB_SUB] = {NULL, PREC_UNARY, PREFIX_PARSER}, - // + - * & sizeof - - [TOKEN_L_PAREN] = {parse_paren, PREC_POSTFIX, INFIX_PARSER}, -}; - -static ast_node_t *parse_primary_expression(tok_stream_t* tokbuf, symtab_t *symtab) { - flush_peek_tok(tokbuf); - - tok_t* tok = peek_tok(tokbuf); - ast_node_t *node = new_ast_node(); - node->type = NT_TERM_VAL; - node->syms.tok = *tok; - - switch (tok->sub_type) { - case TOKEN_INT_LITERAL: - // node->data.data_type = TYPE_INT; - break; - case TOKEN_FLOAT_LITERAL: - LOG_WARN("float not supported"); - break; - case TOKEN_CHAR_LITERAL: - // node->data.data_type = TYPE_CHAR; - break; - case TOKEN_STRING_LITERAL: - // node->data.data_type = TYPE_POINTER; - case TOKEN_IDENT: - node = expect_pop_ident(tokbuf); - cc_tktype_t ttype = peek_tok_type(tokbuf); - node->syms.key.uid = 0; - node->syms.key.strp_name = tok->val.str; - if (ttype == TOKEN_L_PAREN) { - node = parse_call(tokbuf, symtab, node); - } else { - void *sym = symtab_get(symtab, &node->syms.key); - if (sym == NULL) { - LOG_ERROR("undefined symbol but use %s", tok->val.str); - } - node->type = NT_TERM_IDENT; - node->syms.decl_node = sym; - } - goto END; - default: - return NULL; - } - pop_tok(tokbuf); -END: - return node; -} - -static ast_node_t *parse_subexpression(tok_stream_t* tokbuf, symtab_t *symtab, enum Precedence prec) { - cc_tktype_t ttype; - struct expr_prec_table_t* work; - ast_node_t* left; - - while (1) { - flush_peek_tok(tokbuf); - ttype = peek_tok_type(tokbuf); - work = &expr_table[ttype]; - // FIXME - if (ttype == TOKEN_SEMICOLON || ttype == TOKEN_R_PAREN) { - break; - } - if (work == NULL || work->parser == NULL || work->ptype == PREFIX_PARSER) { - if (work->parser != NULL) { - left = work->parser(tokbuf, symtab, NULL); - } else { - left = parse_primary_expression(tokbuf, symtab); - } - } else if (work->ptype == INFIX_PARSER) { - if (work->parser == NULL) - break; - if (work->prec <= prec) - break; - left = work->parser(tokbuf, symtab, left); - } - // assert(left != NULL); - } - - return left; -} - -ast_node_t* parse_expr(parser_t* parser) { - tok_stream_t* tokbuf = &(parser->tokbuf); - symtab_t *symtab = parser->symtab; - flush_peek_tok(tokbuf); - cc_tktype_t ttype = peek_tok_type(tokbuf); - switch (ttype) { - case TOKEN_NOT: - case TOKEN_AND: - case TOKEN_L_PAREN: - case TOKEN_MUL: - case TOKEN_ADD: - case TOKEN_SUB: - case TOKEN_BIT_NOT: - case TOKEN_AND_AND: - case TOKEN_CHAR_LITERAL: - case TOKEN_INT_LITERAL: - case TOKEN_STRING_LITERAL: - case TOKEN_ADD_ADD: - case TOKEN_SUB_SUB: - case TOKEN_SIZEOF: - case TOKEN_IDENT: - return NEXT(PREC_EXPRESSION); - default: - LOG_ERROR("Want expr but not got %s", get_tok_name(ttype)); - break; - } -} diff --git a/src/ccompiler/frontend/parser/ast/func.c b/src/ccompiler/frontend/parser/ast/func.c deleted file mode 100644 index 010088f..0000000 --- a/src/ccompiler/frontend/parser/ast/func.c +++ /dev/null @@ -1,176 +0,0 @@ -#include "../ast.h" -#include "../parser.h" - -#ifndef FUNC_PARAM_CACHE_SIZE -#define FUNC_PARAM_CACHE_SIZE 32 // 合理初始值,可覆盖99%常见情况 -#endif - -// TODO 语义分析压入符号表 -static void parse_params(parser_t* parser, tok_stream_t* cache, ast_node_t* node) { - flush_peek_tok(cache); - cc_tktype_t ttype; - ast_node_t *params = new_ast_node(); - node->decl_func.params = params; - vector_init(params->params.params); - - int depth = 1; - while (depth) { - ttype = peek_tok_type(cache); - switch (ttype) { - case TOKEN_COMMA: - break; - case TOKEN_ELLIPSIS: - ttype = peek_tok_type(cache); - if (ttype != TOKEN_R_PAREN) { - LOG_ERROR("... must be a last parameter list (expect ')')"); - } - // TODO - LOG_ERROR("not implement"); - break; - case TOKEN_IDENT: - // TODO 静态数组 - flush_peek_tok(cache); - ast_node_t* id_node = new_ast_ident_node(peek_tok(cache)); - ast_node_t* node = new_ast_node(); - node->type = NT_DECL_VAR; - node->decl_val.name = id_node; - // TODO typing sys - node->decl_val.type = NULL; - node->decl_val.expr_stmt = NULL; - node->decl_val.data = NULL; - vector_push(params->params.params, node); - - id_node->syms.key.uid = parser->symtab->cur_scope->uid; - id_node->syms.key.strp_name = id_node->syms.tok.val.str; - symtab_add(parser->symtab, &id_node->syms.key, node); - break; - case TOKEN_L_PAREN: { - depth++; - break; - } - case TOKEN_R_PAREN: { - depth--; - break; - } - default: - break; - // TODO 使用cache的类型解析 - // parse_type(parser); - // TODO type parse - // ttype = peekcachetype(cache); - // ttype = peekcachetype(cache); - // if (ttype != TOKEN_IDENT) { - // node->node_type = NT_DECL_FUNC; - // flush_peek_tok(tokbuf); - // continue; - // } - // LOG_ERROR("function expected ')' or ','\n"); - } - pop_tok(cache); - } -} - -ast_type_t check_is_func_decl(tok_stream_t* tokbuf, tok_stream_t* cache) { - expect_pop_tok(tokbuf, TOKEN_L_PAREN); - int depth = 1; - - while (depth) { - tok_t* tok = peek_tok(tokbuf); - pop_tok(tokbuf); - if (cache->size >= cache->cap - 1) { - LOG_ERROR("function parameter list too long"); - } - cache->buf[cache->size++] = *tok; - switch (tok->sub_type) { - case TOKEN_L_PAREN: - depth++; - break; - case TOKEN_R_PAREN: - depth--; - break; - default: - break; - } - } - cache->end = cache->size; - - switch (peek_tok_type(tokbuf)) { - case TOKEN_SEMICOLON: - pop_tok(tokbuf); - return NT_DECL_FUNC; - case TOKEN_L_BRACE: - return NT_FUNC; - break; - default: - LOG_ERROR("function define or decl need '{' or ';' but you don't got"); - } -} - -static ast_node_t* new_ast_node_funcdecl(ast_node_t* ret, ast_node_t* name) { - ast_node_t* node = new_ast_node(); - node->type = NT_DECL_FUNC; - node->decl_func.ret = ret; - node->decl_func.name = name; - node->decl_func.def = NULL; - return node; -} - -void parse_func(parser_t* parser) { - tok_stream_t* tokbuf = &(parser->tokbuf); - flush_peek_tok(tokbuf); - ast_node_t* ret_node = parse_type(parser); - ast_node_t* name_node = expect_pop_ident(tokbuf); - const char* func_name = name_node->syms.tok.val.str; - ast_node_t* decl = new_ast_node_funcdecl(ret_node, name_node); - - tok_stream_t cache; - init_tokbuf(&cache, NULL, NULL); - cache.cap = FUNC_PARAM_CACHE_SIZE; - tok_t buf[FUNC_PARAM_CACHE_SIZE]; - cache.buf = buf; - - ast_type_t type = check_is_func_decl(&(parser->tokbuf), &cache); - - - name_node->syms.key.uid = parser->symtab->cur_scope->uid; - name_node->syms.key.strp_name = func_name; - ast_node_t* prev = symtab_get(parser->symtab, &name_node->syms.key); - // TODO Change something - if (prev != NULL) { - if (prev->type != NT_DECL_FUNC) { - LOG_ERROR("the symbol duplicate old is %d, new is func", prev->type); - } - // TODO check redeclare func is match - if (type == NT_FUNC) { - // TODO Free decl; - rt._free(decl); - decl = prev; - goto FUNC; - } - return; - } - symtab_add(parser->symtab, &name_node->syms.key, decl); - vector_push(parser->root->root.children, decl); - if (type == NT_DECL_FUNC) { - return; - } - -FUNC: - // 该data临时用于判断是否重复定义 - if (decl->decl_func.def != NULL) { - LOG_ERROR("redefinition of function %s", func_name); - } - - ast_node_t* node = new_ast_node(); - node->type = NT_FUNC; - node->func.decl = decl; - node->func.data = NULL; - - decl->decl_func.def = node; - symtab_enter_scope(parser->symtab); - parse_params(parser, &cache, decl); - node->func.body = parse_block(parser); - symtab_leave_scope(parser->symtab); - - vector_push(parser->root->root.children, node); -} diff --git a/src/ccompiler/frontend/parser/ast/program.c b/src/ccompiler/frontend/parser/ast/program.c deleted file mode 100644 index 63478fa..0000000 --- a/src/ccompiler/frontend/parser/ast/program.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "../ast.h" -#include "../parser.h" - -#ifndef PROG_MAX_NODE_SIZE -#define PROG_MAX_NODE_SIZE (1024 * 4) -#endif - -void parse_func(parser_t* parser); - -void parse_prog(parser_t* parser) { - /** - * Program := (Declaration | Definition)* - * same as - * Program := Declaration* Definition* - */ - tok_stream_t *tokbuf = &(parser->tokbuf); - parser->root = new_ast_node(); - ast_node_t* node; - parser->root->type = NT_ROOT; - vector_init(parser->root->root.children); - while (1) { - flush_peek_tok(tokbuf); - if (peek_tok_type(tokbuf) == TOKEN_EOF) { - break; - } - node = parse_decl(parser); - if (node == NULL) { - parse_func(parser); - } else { - vector_push(parser->root->root.children, node); - } - } - return; -} diff --git a/src/ccompiler/frontend/parser/ast/stmt.c b/src/ccompiler/frontend/parser/ast/stmt.c deleted file mode 100644 index 3280526..0000000 --- a/src/ccompiler/frontend/parser/ast/stmt.c +++ /dev/null @@ -1,246 +0,0 @@ -#include "../ast.h" -#include "../parser.h" - -ast_node_t* parse_stmt(parser_t* parser) { - tok_stream_t* tokbuf = &parser->tokbuf; - flush_peek_tok(tokbuf); - cc_tktype_t ttype = peek_tok_type(tokbuf); - ast_node_t* node = new_ast_node(); - switch (ttype) { - case TOKEN_IF: { - /** - * if (exp) stmt - * if (exp) stmt else stmt - */ - pop_tok(tokbuf); - - expect_pop_tok(tokbuf, TOKEN_L_PAREN); - node->if_stmt.cond = parse_expr(parser); - flush_peek_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_R_PAREN); - - node->if_stmt.if_stmt = parse_stmt(parser); - ttype = peek_tok_type(tokbuf); - if (ttype == TOKEN_ELSE) { - pop_tok(tokbuf); - node->if_stmt.else_stmt = parse_stmt(parser); - } else { - node->if_stmt.else_stmt = NULL; - } - node->type = NT_STMT_IF; - break; - } - case TOKEN_SWITCH: { - /** - * switch (exp) stmt - */ - pop_tok(tokbuf); - - expect_pop_tok(tokbuf, TOKEN_L_PAREN); - node->switch_stmt.cond = parse_expr(parser); - expect_pop_tok(tokbuf, TOKEN_R_PAREN); - - node->switch_stmt.body = parse_stmt(parser); - node->type = NT_STMT_SWITCH; - break; - } - case TOKEN_WHILE: { - /** - * while (exp) stmt - */ - pop_tok(tokbuf); - - expect_pop_tok(tokbuf, TOKEN_L_PAREN); - node->while_stmt.cond = parse_expr(parser); - expect_pop_tok(tokbuf, TOKEN_R_PAREN); - - node->while_stmt.body = parse_stmt(parser); - node->type = NT_STMT_WHILE; - break; - } - case TOKEN_DO: { - /** - * do stmt while (exp) - */ - pop_tok(tokbuf); - node->do_while_stmt.body = parse_stmt(parser); - ttype = peek_tok_type(tokbuf); - if (ttype != TOKEN_WHILE) { - LOG_ERROR("expected while after do"); - } - pop_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_L_PAREN); - node->do_while_stmt.cond = parse_expr(parser); - expect_pop_tok(tokbuf, TOKEN_R_PAREN); - node->type = NT_STMT_DOWHILE; - break; - } - case TOKEN_FOR: { - /** - * for (init; [cond]; [iter]) stmt - */ - // node->children.stmt.for_stmt.init - pop_tok(tokbuf); - ttype = peek_tok_type(tokbuf); - if (ttype != TOKEN_L_PAREN) { - LOG_ERROR("expected ( after for"); - } - pop_tok(tokbuf); - - // init expr or init decl_var - // TODO need add this feature - if (peek_decl(tokbuf)) { - node->for_stmt.init = parse_decl_val(parser); - } else { - node->for_stmt.init = parse_expr(parser); - expect_pop_tok(tokbuf, TOKEN_SEMICOLON); - } - - // cond expr or null - ttype = peek_tok_type(tokbuf); - if (ttype != TOKEN_SEMICOLON) { - node->for_stmt.cond = parse_expr(parser); - expect_pop_tok(tokbuf, TOKEN_SEMICOLON); - } else { - node->for_stmt.cond = NULL; - pop_tok(tokbuf); - } - - // iter expr or null - ttype = peek_tok_type(tokbuf); - if (ttype != TOKEN_R_PAREN) { - node->for_stmt.iter = parse_expr(parser); - expect_pop_tok(tokbuf, TOKEN_R_PAREN); - } else { - node->for_stmt.iter = NULL; - pop_tok(tokbuf); - } - - node->for_stmt.body = parse_stmt(parser); - node->type = NT_STMT_FOR; - break; - } - case TOKEN_BREAK: { - /** - * break ; - */ - // TODO check 导致外围 for、while 或 do-while 循环或 switch 语句终止。 - pop_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_SEMICOLON); - - node->type = NT_STMT_BREAK; - break; - } - case TOKEN_CONTINUE: { - /** - * continue ; - */ - // TODO check 导致跳过整个 for、 while 或 do-while 循环体的剩余部分。 - pop_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_SEMICOLON); - - node->type = NT_STMT_CONTINUE; - break; - } - case TOKEN_RETURN: { - /** - * return [exp] ; - */ - // TODO 终止当前函数并返回指定值给调用方函数。 - pop_tok(tokbuf); - ttype = peek_tok_type(tokbuf); - if (ttype != TOKEN_SEMICOLON) { - node->return_stmt.expr_stmt = parse_expr(parser); - flush_peek_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_SEMICOLON); - } else { - node->return_stmt.expr_stmt = NULL; - pop_tok(tokbuf); - } - node->type = NT_STMT_RETURN; - break; - } - case TOKEN_GOTO: { - /** - * goto label ; - */ - // TODO check label 将控制无条件转移到所欲位置。 - //在无法用约定的构造将控制转移到所欲位置时使用。 - pop_tok(tokbuf); - // find symbol table - ttype = peek_tok_type(tokbuf); - if (ttype != TOKEN_IDENT) { - LOG_ERROR("expect identifier after goto"); - } - expect_pop_tok(tokbuf, TOKEN_SEMICOLON); - // TODO filling label - node->goto_stmt.label = expect_pop_ident(tokbuf); - node->type = NT_STMT_GOTO; - break; - } - case TOKEN_SEMICOLON: { - /** - * ; - * empty stmt using by : - * while () ; - * if () ; - * for () ; - */ - pop_tok(tokbuf); - node->type = NT_STMT_EMPTY; - break; - } - case TOKEN_L_BRACE: { - /** - * stmt_block like: { (decl_var | stmt) ... } - */ - node->block_stmt.block = parse_block(parser); - node->type = NT_STMT_BLOCK; - break; - } - case TOKEN_IDENT: { - // TODO label goto - if (peek_tok_type(tokbuf) != TOKEN_COLON) { - goto EXP; - } - node->label_stmt.label = expect_pop_ident(tokbuf); - expect_pop_tok(tokbuf, TOKEN_COLON); - node->type = NT_STMT_LABEL; - break; - } - case TOKEN_CASE: { - // TODO label switch - pop_tok(tokbuf); - LOG_ERROR("unimplemented switch label"); - node->label_stmt.label = parse_expr(parser); - // TODO 该表达式为const int - expect_pop_tok(tokbuf, TOKEN_COLON); - node->type = NT_STMT_CASE; - break; - } - case TOKEN_DEFAULT: { - // TODO label switch default - pop_tok(tokbuf); - expect_pop_tok(tokbuf, TOKEN_COLON); - node->type = NT_STMT_DEFAULT; - break; - } - default: { - /** - * exp ; - */ - EXP: - node->expr_stmt.expr_stmt = parse_expr(parser); - flush_peek_tok(tokbuf); - ttype = peek_tok_type(tokbuf); - if (ttype != TOKEN_SEMICOLON) { - LOG_ERROR("exp must end with \";\""); - } - pop_tok(tokbuf); - node->type = NT_STMT_EXPR; - break; - } - - } - return node; -} diff --git a/src/ccompiler/frontend/parser/ast/term.c b/src/ccompiler/frontend/parser/ast/term.c deleted file mode 100644 index ac782a7..0000000 --- a/src/ccompiler/frontend/parser/ast/term.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "../ast.h" -#include "../parser.h" -#include "../type.h" - -ast_node_t* new_ast_ident_node(tok_t* tok) { - if (tok->sub_type != TOKEN_IDENT) { - LOG_ERROR("syntax error: want identifier but got %d", tok->sub_type); - } - ast_node_t* node = new_ast_node(); - node->type = NT_TERM_IDENT; - node->syms.tok = *tok; - node->syms.decl_node = NULL; - return node; -} - -ast_node_t* expect_pop_ident(tok_stream_t* tokbuf) { - flush_peek_tok(tokbuf); - tok_t* tok = peek_tok(tokbuf); - ast_node_t* node = new_ast_ident_node(tok); - pop_tok(tokbuf); - return node; -} - -ast_node_t* parse_type(parser_t* parser) { - tok_stream_t* tokbuf = &parser->tokbuf; - flush_peek_tok(tokbuf); - cc_tktype_t ttype = peek_tok_type(tokbuf); - data_type_t dtype; - switch(ttype) { - case TOKEN_VOID: dtype = TYPE_VOID; break; - case TOKEN_CHAR: dtype = TYPE_CHAR; break; - case TOKEN_SHORT: dtype = TYPE_SHORT; break; - case TOKEN_INT: dtype = TYPE_INT; break; - case TOKEN_LONG: dtype = TYPE_LONG; break; - case TOKEN_FLOAT: dtype = TYPE_FLOAT; break; - case TOKEN_DOUBLE: dtype = TYPE_DOUBLE; break; - default: - LOG_ERROR("无效的类型说明符"); - } - - ast_node_t* node = new_ast_node(); - node->type = NT_TERM_TYPE; - // TODO added by disable warning, will add typing system - dtype += 1; - pop_tok(tokbuf); - - if (peek_tok_type(tokbuf) == TOKEN_MUL) { - pop_tok(tokbuf); - } - return node; -} diff --git a/src/ccompiler/frontend/parser/ast/type.c b/src/ccompiler/frontend/parser/ast/type.c deleted file mode 100644 index 582abc1..0000000 --- a/src/ccompiler/frontend/parser/ast/type.c +++ /dev/null @@ -1,136 +0,0 @@ -// #include "../parser.h" -// #include "../type.h" - -// enum TypeParseState { -// TPS_BASE_TYPE, // 解析基础类型 (int/char等) -// TPS_QUALIFIER, // 解析限定符 (const/volatile) -// TPS_POINTER, // 解析指针 (*) -// TPS_ARRAY, // 解析数组维度 ([n]) -// TPS_FUNC_PARAMS // 解析函数参数列表 -// }; - -// ast_node_t* parse_type(parser_t* p) { -// ast_node_t* type_root = new_ast_node(); -// ast_node_t* current = type_root; -// current->type = NT_TYPE_BASE; - -// enum TypeParseState state = TPS_QUALIFIER; -// int pointer_level = 0; - -// while (1) { -// tok_type_t t = peektoktype(p); - -// switch (state) { -// // 基础类型解析 (int, char等) -// case TPS_BASE_TYPE: -// if (is_base_type(t)) { -// // current->data.data_type = token_to_datatype(t); -// pop_tok(p); -// state = TPS_POINTER; -// } else { -// error("Expected type specifier"); -// } -// break; - -// // 类型限定符 (const/volatile) -// case TPS_QUALIFIER: -// if (t == TOKEN_CONST || t == TOKEN_VOLATILE) { -// ast_node_t* qual_node = new_ast_node(); -// qual_node->type = NT_TYPE_QUAL; -// qual_node->data.data_type = t; // 复用data_type字段存储限定符 -// current->child.decl.type = qual_node; -// current = qual_node; -// pop_tok(p); -// } else { -// state = TPS_BASE_TYPE; -// } -// break; - -// // 指针解析 (*) -// case TPS_POINTER: -// if (t == TOKEN_MUL) { -// ast_node_t* ptr_node = new_ast_node(); -// ptr_node->type = NT_TYPE_PTR; -// current->child.decl.type = ptr_node; -// current = ptr_node; -// pointer_level++; -// pop_tok(p); -// } else { -// state = TPS_ARRAY; -// } -// break; - -// // 数组维度 ([n]) -// case TPS_ARRAY: -// if (t == TOKEN_L_BRACKET) { -// pop_tok(p); // 吃掉[ -// ast_node_t* arr_node = new_ast_node(); -// arr_node->type = NT_TYPE_ARRAY; - -// // 解析数组大小(仅语法检查) -// if (peektoktype(p) != TOKEN_R_BRACKET) { -// parse_expr(p); // 不计算实际值 -// } - -// expecttok(p, TOKEN_R_BRACKET); -// current->child.decl.type = arr_node; -// current = arr_node; -// } else { -// state = TPS_FUNC_PARAMS; -// } -// break; - -// // 函数参数列表 -// case TPS_FUNC_PARAMS: -// if (t == TOKEN_L_PAREN) { -// ast_node_t* func_node = new_ast_node(); -// func_node->type = NT_TYPE_FUNC; -// current->child.decl.type = func_node; - -// // 解析参数列表(仅结构,不验证类型) -// parse_param_list(p, func_node); -// current = func_node; -// } else { -// return type_root; // 类型解析结束 -// } -// break; -// } -// } -// } - -// // 判断是否是基础类型 -// static int is_base_type(tok_type_t t) { -// return t >= TOKEN_VOID && t <= TOKEN_DOUBLE; -// } - -// // // 转换token到数据类型(简化版) -// // static enum DataType token_to_datatype(tok_type_t t) { -// // static enum DataType map[] = { -// // [TOKEN_VOID] = DT_VOID, -// // [TOKEN_CHAR] = DT_CHAR, -// // [TOKEN_INT] = DT_INT, -// // // ...其他类型映射 -// // }; -// // return map[t]; -// // } - -// // 解析参数列表(轻量级) -// static void parse_param_list(parser_t* p, ast_node_t* func) { -// expecttok(p, TOKEN_L_PAREN); - -// while (peektoktype(p) != TOKEN_R_PAREN) { -// ast_node_t* param = parse_type(p); // 递归解析类型 - -// // 允许可选参数名(仅语法检查) -// if (peektoktype(p) == TOKEN_IDENT) { -// pop_tok(p); // 吃掉参数名 -// } - -// if (peektoktype(p) == TOKEN_COMMA) { -// pop_tok(p); -// } -// } - -// expecttok(p, TOKEN_R_PAREN); -// } - diff --git a/src/ccompiler/frontend/parser/parser.c b/src/ccompiler/frontend/parser/parser.c deleted file mode 100644 index 7b7635c..0000000 --- a/src/ccompiler/frontend/parser/parser.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include "parser.h" -#include "type.h" - -void init_parser(parser_t* parser, cc_lexer_t* lexer, symtab_t* symtab) { - init_lib_core(); - - parser->cur_node = NULL; - parser->root = NULL; - - parser->lexer = lexer; - parser->symtab = symtab; - init_tokbuf(&parser->tokbuf, lexer, (tok_stream_get_func)get_valid_token); - parser->tokbuf.cap = sizeof(parser->TokenBuffer) / sizeof(parser->TokenBuffer[0]); - parser->tokbuf.buf = parser->TokenBuffer; -} - -void run_parser(parser_t* parser) { - parse_prog(parser); -} diff --git a/src/ccompiler/frontend/parser/parser.h b/src/ccompiler/frontend/parser/parser.h deleted file mode 100644 index e244b29..0000000 --- a/src/ccompiler/frontend/parser/parser.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __PARSER_H__ -#define __PARSER_H__ - -#include "../lexer/lexer.h" -#include "symtab/symtab.h" -#include "ast.h" -#define PARSER_MAX_TOKEN_QUEUE 16 - -typedef struct parser { - ast_node_t* root; - ast_node_t* cur_node; - - cc_lexer_t* lexer; - symtab_t* symtab; - tok_stream_t tokbuf; - tok_t TokenBuffer[PARSER_MAX_TOKEN_QUEUE]; - int err_level; -} parser_t; - -void init_parser(parser_t* parser, cc_lexer_t* lexer, symtab_t* symtab); -void run_parser(parser_t* parser); - -#endif diff --git a/src/ccompiler/frontend/parser/symtab/symtab.c b/src/ccompiler/frontend/parser/symtab/symtab.c deleted file mode 100644 index 8a53194..0000000 --- a/src/ccompiler/frontend/parser/symtab/symtab.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "symtab.h" - -static u32_t hash_func(const void* _key) { - const symtab_key_t* key = (symtab_key_t*)_key; - return rt_strhash(key->strp_name); -} - -static int key_cmp(const void* _key1, const void* _key2) { - const symtab_key_t* key1 = (symtab_key_t*)_key1; - const symtab_key_t* key2 = (symtab_key_t*)_key2; - if (rt_strcmp(key1->strp_name, key2->strp_name) == 0) { - return 0; - } - return 1; -} - -void init_symtab(symtab_t* symtab) { - symtab->cur_scope = NULL; - symtab->gid = 1; - init_hashtable(&symtab->global_table); - symtab->global_table.hash_func = hash_func; - symtab->global_table.key_cmp = key_cmp; - init_hashtable(&symtab->local_table); - symtab->local_table.hash_func = hash_func; - symtab->local_table.key_cmp = key_cmp; -} - -void symtab_destroy(symtab_t* symtab) { - // TODO -} - -void symtab_enter_scope(symtab_t* symtab) { - scope_t *scope = (scope_t*)salloc_alloc(sizeof(scope_t)); - scope->parent = symtab->cur_scope; - scope->uid = symtab->gid++; - init_hashtable(&scope->table); - - scope->table.hash_func = hash_func; - scope->table.key_cmp = key_cmp; - symtab->cur_scope = scope; -} - -void symtab_leave_scope(symtab_t* symtab) { - Assert(symtab->cur_scope != NULL); - scope_t *parent = symtab->cur_scope->parent; - hashtable_destory(&symtab->cur_scope->table); - salloc_free(symtab->cur_scope); - symtab->cur_scope = parent; -} - -void* symtab_get(symtab_t* symtab, symtab_key_t* key) { - for (scope_t* scope = symtab->cur_scope; scope != NULL; scope = scope->parent) { - void* val = hashtable_get(&scope->table, key); - if (val != NULL) { - return val; - } - } - return NULL; -} -void* symtab_add(symtab_t* symtab, symtab_key_t* key, void* val) { - return hashtable_set(&symtab->cur_scope->table, key, val); -} diff --git a/src/ccompiler/frontend/parser/symtab/symtab.h b/src/ccompiler/frontend/parser/symtab/symtab.h deleted file mode 100644 index 176dd7f..0000000 --- a/src/ccompiler/frontend/parser/symtab/symtab.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef __SMCC_SYMTABL_H__ -#define __SMCC_SYMTABL_H__ - -#include -#include -#include - -// FIXME 架构上可能有更好的方式解决 - -typedef struct symtab_key { - const char* strp_name; - int uid; -} symtab_key_t; - -typedef struct scope { - int uid; - struct scope* parent; - hash_table_t table; -} scope_t; - -typedef struct symtab { - hash_table_t global_table; - hash_table_t local_table; - scope_t* cur_scope; - int gid; // global id for generating unique scope id -} symtab_t; - -void init_symtab(symtab_t* symtab); -void symtab_destroy(symtab_t* symtab); - -void symtab_enter_scope(symtab_t* symtab); -void symtab_leave_scope(symtab_t* symtab); - -void* symtab_get(symtab_t* symtab, symtab_key_t* key); - -// WARNING key and val need you save, especially val -void* symtab_add(symtab_t* symtab, symtab_key_t* key, void* val); - -#endif diff --git a/src/ccompiler/frontend/parser/type.h b/src/ccompiler/frontend/parser/type.h deleted file mode 100644 index b45a991..0000000 --- a/src/ccompiler/frontend/parser/type.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __TYPE_H__ -#define __TYPE_H__ - -#include "../lexer/token.h" - -typedef enum { - TYPE_VOID, - TYPE_CHAR, - TYPE_SHORT, - TYPE_INT, - TYPE_LONG, - TYPE_LONG_LONG, - TYPE_FLOAT, - TYPE_DOUBLE, - TYPE_LONG_DOUBLE, - - // prefix - TYPE_SIGNED, - TYPE_UNSIGNED, - // TYPE_BOOL, - // TYPE_COMPLEX, - // TYPE_IMAGINARY, - - TYPE_ENUM, - TYPE_ARRAY, - TYPE_STRUCT, - TYPE_UNION, - TYPE_FUNCTION, - TYPE_POINTER, - TYPE_ATOMIC, - - TYPE_TYPEDEF, -} data_type_t; - -#endif diff --git a/src/ccompiler/middleend/Makefile b/src/ccompiler/middleend/Makefile deleted file mode 100644 index c75405e..0000000 --- a/src/ccompiler/middleend/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# 编译器设置 -CC = gcc -AR = ar -CFLAGS = -g -Wall -I../.. - -IR_DIR = ./ir - -# 源文件列表 -SRCS = \ - middleend.c \ - $(IR_DIR)/ir.c \ - $(IR_DIR)/ir_ast.c \ - $(IR_DIR)/ir_lib.c \ - $(IR_DIR)/ir_type.c - -# 生成目标文件列表 -OBJS = $(SRCS:.c=.o) - -# 最终目标 -TARGET = libmiddleend.a - -all: $(TARGET) - -$(TARGET): $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f $(OBJS) $(TARGET) - -.PHONY: all clean diff --git a/src/ccompiler/middleend/ir/ir.c b/src/ccompiler/middleend/ir/ir.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/ccompiler/middleend/ir/ir.h b/src/ccompiler/middleend/ir/ir.h deleted file mode 100644 index 5eb193f..0000000 --- a/src/ccompiler/middleend/ir/ir.h +++ /dev/null @@ -1,152 +0,0 @@ -// ir_core.h -#ifndef IR_CORE_H -#define IR_CORE_H - -#include - -// 错误码定义 -typedef enum { - IR_EC_SUCCESS = 0, // 成功 - IR_EC_MEMORY_ERROR, // 内存分配失败 - IR_EC_TYPE_MISMATCH, // 类型不匹配 - IR_EC_INVALID_OPERAND, // 无效操作数 - IR_EC_DUPLICATE_SYMBOL, // 符号重定义 -} ir_ecode_t; - -typedef struct { - enum { - IR_TYPE_INT32, - IR_TYPE_PTR, - IR_TYPE_ARRAY, - IR_TYPE_FUNC, - IR_TYPE_VOID, - } tag; - union { - struct { - struct ir_type *base; - rt_size_t len; - } arr; - struct { - struct ir_type *ret; - struct ir_type **params; - rt_size_t param_cnt; - } func; - }; -} ir_type_t; - -typedef struct ir_node ir_node_t; - -typedef struct ir_bblock { - const char *label; - VECTOR_HEADER(instrs, ir_node_t*); - // ir_arr_t used_by; -} ir_bblock_t; // basic block - -typedef struct { - const char *name; - ir_type_t *type; - VECTOR_HEADER(params, ir_node_t*); - VECTOR_HEADER(bblocks, ir_bblock_t*); -} ir_func_t; - -typedef struct { - VECTOR_HEADER(global, ir_node_t*); - VECTOR_HEADER(funcs, ir_func_t*); - VECTOR_HEADER(extern_funcs, ir_func_t*); -} ir_prog_t; - -typedef enum ir_node_tag { - IR_NODE_NULL, - IR_NODE_CONST_INT, - IR_NODE_ALLOC, - IR_NODE_LOAD, - IR_NODE_STORE, - IR_NODE_GET_PTR, - IR_NODE_OP, - IR_NODE_BRANCH, - IR_NODE_JUMP, - IR_NODE_CALL, - IR_NODE_RET, -} ir_node_tag_t; - -struct ir_node { - const ir_type_t* type; - const char* name; - VECTOR_HEADER(used_by, ir_node_t*); - ir_node_tag_t tag; - union { - struct { - int32_t val; - } const_int; - struct { - ir_node_t* target; - } load; - struct { - ir_node_t* target; - ir_node_t* value; - } store; - struct { - ir_node_t* src_addr; - ir_node_t* offset; - } get_ptr; - struct { - enum { - /// Not equal to. - IR_OP_NEQ, - /// Equal to. - IR_OP_EQ, - /// Greater than. - IR_OP_GT, - /// Less than. - IR_OP_LT, - /// Greater than or equal to. - IR_OP_GE, - /// Less than or equal to. - IR_OP_LE, - /// Addition. - IR_OP_ADD, - /// Subtraction. - IR_OP_SUB, - /// Multiplication. - IR_OP_MUL, - /// Division. - IR_OP_DIV, - /// Modulo. - IR_OP_MOD, - /// Bitwise AND. - IR_OP_AND, - /// Bitwise OR. - IR_OP_OR, - /// Bitwise XOR. - IR_OP_XOR, - /// Bitwise NOT. - IR_OP_NOT, - /// Shift left logical. - IR_OP_SHL, - /// Shift right logical. - IR_OP_SHR, - /// Shift right arithmetic. - IR_OP_SAR, - } op; - ir_node_t* lhs; - ir_node_t* rhs; - } op; - struct { - ir_node_t* cond; - ir_bblock_t* true_bblock; - ir_bblock_t* false_bblock; - } branch; - struct { - ir_bblock_t* target_bblock; - } jump; - struct { - ir_func_t* callee; - VECTOR_HEADER(args, ir_node_t*); - } call; - struct { - ir_node_t* ret_val; - } ret; - } data; -}; - -#endif // IR_CORE_H diff --git a/src/ccompiler/middleend/ir/ir_ast.c b/src/ccompiler/middleend/ir/ir_ast.c deleted file mode 100644 index 62a27dc..0000000 --- a/src/ccompiler/middleend/ir/ir_ast.c +++ /dev/null @@ -1,446 +0,0 @@ -#include "ir.h" -#include "ir_lib.h" -#include "ir_type.h" -#include "../../frontend/frontend.h" -#include "../../frontend/parser/ast.h" - -// 上下文结构,记录生成过程中的状态 -typedef struct { - ir_func_t* cur_func; // 当前处理的函数 - ir_bblock_t* cur_block; // 当前基本块 -} IRGenContext; -IRGenContext ctx; -ir_prog_t prog; -void _gen_ir_from_ast(ast_node_t* node); - -static void emit_instr(ir_bblock_t* block, ir_node_t* node) { - if (block == NULL) block = ctx.cur_block; - vector_push(block->instrs, node); - // return &(vector_at(block->instrs, block->instrs.size - 1)); -} - -static ir_node_t* emit_br(ir_node_t* cond, ir_bblock_t* trueb, ir_bblock_t* falseb) { - ir_node_t* br = new_ir_node(NULL, IR_NODE_BRANCH); - emit_instr(NULL, br); - br->data.branch.cond = cond; - br->data.branch.true_bblock = trueb; - br->data.branch.false_bblock = falseb; - return br; -} - -static ir_node_t* gen_ir_expr(ast_node_t* node); - -static ir_node_t* gen_ir_term(ast_node_t* node) { - switch (node->type) { - case NT_TERM_VAL: { - ir_node_t* ir = new_ir_node(NULL, IR_NODE_CONST_INT); - ir->data.const_int.val = node->syms.tok.val.i; - return ir; - } - case NT_TERM_IDENT: { - ir_node_t* decl = node->syms.decl_node->decl_val.data; - return decl; - } - case NT_TERM_CALL: { - ir_node_t* call = new_ir_node(NULL, IR_NODE_CALL); - call->data.call.callee = node->call.func_decl->decl_func.def->func.data; - - for (int i = 0; i < node->call.params->params.params.size; i++) { - ast_node_t* param = vector_at(node->call.params->params.params, i); - ir_node_t *tmp = gen_ir_expr(param); - vector_push(call->data.call.args, tmp); - } - - emit_instr(NULL, call); - return call; - } - default: { - Panic("gen_ir_expr: unknown node type"); - } - } - TODO(); - return NULL; -} - -static ir_node_t* gen_ir_expr(ast_node_t* node) { - // term node - switch (node->type) { - case NT_TERM_VAL: - case NT_TERM_IDENT: - case NT_TERM_CALL: - return gen_ir_term(node); - default: - break; - } - - ir_node_t* lhs = gen_ir_expr(node->expr.left); - ir_node_t* rhs = node->expr.right ? gen_ir_expr(node->expr.right) : NULL; - if (node->type == NT_COMMA) { - return rhs; - } - - ir_node_t* instr = NULL; - vector_push(lhs->used_by, instr); - if (rhs) { vector_push(rhs->used_by, instr); } - - ir_node_t* ret; -#define BINOP(operand) do { \ - instr = new_ir_node(NULL, IR_NODE_OP); \ - instr->data.op.op = operand; \ - instr->data.op.lhs = lhs; \ - instr->data.op.rhs = rhs; \ - ret = instr; \ - } while (0) - - switch (node->type) { - case NT_ADD: { - // (expr) + (expr) - BINOP(IR_OP_ADD); break; - } - case NT_SUB: { - // (expr) - (expr) - BINOP(IR_OP_SUB); break; - } - case NT_MUL: { - // (expr) * (expr) - BINOP(IR_OP_MUL); break; - } - case NT_DIV: { - // (expr) / (expr) - BINOP(IR_OP_DIV); break; - } - case NT_MOD: { - // (expr) % (expr) - BINOP(IR_OP_MOD); break; - } - case NT_AND: { - // (expr) & (expr) - BINOP(IR_OP_AND); break; - } - case NT_OR: { - // (expr) | (expr) - BINOP(IR_OP_OR); break; - } - case NT_XOR: { - // (expr) ^ (expr) - BINOP(IR_OP_XOR); break; - } - case NT_BIT_NOT: { - // ~ (expr) - // TODO - // BINOP(IR_OP_NOT); - break; - } - case NT_L_SH: { - // (expr) << (expr) - BINOP(IR_OP_SHL); - break; - } - case NT_R_SH: { - // (expr) >> (expr) - BINOP(IR_OP_SHR); // Shift right logical. - // TODO - // BINOP(IR_OP_SAR); // Shift right arithmetic. - break; - } - case NT_EQ: { - // (expr) == (expr) - BINOP(IR_OP_EQ); break; - } - case NT_NEQ: { - // (expr) != (expr) - BINOP(IR_OP_NEQ); break; - } - case NT_LE: { - // (expr) <= (expr) - BINOP(IR_OP_LE); break; - } - case NT_GE: { - // (expr) >= (expr) - BINOP(IR_OP_GE); break; - } - case NT_LT: { - // (expr) < (expr) - BINOP(IR_OP_LT); break; - } - case NT_GT: { - // (expr) > (expr) - BINOP(IR_OP_GE); break; - } - case NT_AND_AND:// (expr) && (expr) - LOG_ERROR("unimpliment"); - break; - case NT_OR_OR:// (expr) || (expr) - LOG_ERROR("unimpliment"); - break; - case NT_NOT: { - // ! (expr) - instr = new_ir_node(NULL, IR_NODE_OP); - instr->data.op.op = IR_OP_EQ, - instr->data.op.lhs = &node_zero, - instr->data.op.rhs = lhs, - ret = instr; - break; - } - case NT_ASSIGN: { - // (expr) = (expr) - instr = new_ir_node(NULL, IR_NODE_STORE); - instr->data.store.target = lhs; - instr->data.store.value = rhs; - ret = rhs; - break; - } - // case NT_COND: // (expr) ? (expr) : (expr) - default: { - // TODO self error msg - LOG_ERROR("Unsupported IR generation for AST node type %d", node->type); - break; - } - } - emit_instr(NULL, instr); - return ret; -} - -static void gen_ir_func(ast_node_t* node, ir_func_t* func) { - Assert(node->type == NT_FUNC); - ir_bblock_t *entry = new_ir_bblock("entry"); - vector_push(func->bblocks, entry); - - vector_push(prog.funcs, func); - IRGenContext prev_ctx = ctx; - ctx.cur_func = func; - ctx.cur_block = entry; - - ast_node_t* params = node->func.decl->decl_func.params; - for (int i = 0; i < params->params.params.size; i ++) { - ast_node_t* param = params->params.params.data[i]; - ir_node_t* decl = new_ir_node(param->decl_val.name->syms.tok.val.str, IR_NODE_ALLOC); - emit_instr(entry, decl); - vector_push(func->params, decl); - // TODO Typing system - decl->type = &type_i32; - param->decl_val.data = decl; - } - _gen_ir_from_ast(node->func.body); - - ctx = prev_ctx; -} - -void gen_ir_jmp(ast_node_t* node) { - ir_bblock_t *bblocks[3]; - for (int i = 0; i < sizeof(bblocks)/sizeof(bblocks[0]); i++) { - bblocks[i] = new_ir_bblock(NULL); - vector_push(ctx.cur_func->bblocks, bblocks[i]); - } - -#define NEW_IR_JMP(name, block) do { \ - name = new_ir_node(NULL, IR_NODE_JUMP); \ - name->data.jump.target_bblock = block; \ - } while (0) - - switch (node->type) { - case NT_STMT_IF: { - ir_bblock_t* trueb = bblocks[0]; - trueb->label = "if_true"; - ir_bblock_t* falseb = bblocks[1]; - falseb->label = "if_false"; - ir_bblock_t* endb = bblocks[2]; - endb->label = "if_end"; - ir_node_t* jmp; - - // cond - ir_node_t *cond = gen_ir_expr(node->if_stmt.cond); - emit_br(cond, trueb, falseb); - - // true block - ctx.cur_block = trueb; - _gen_ir_from_ast(node->if_stmt.if_stmt); - - // else block - if (node->if_stmt.else_stmt != NULL) { - ctx.cur_block = falseb; - _gen_ir_from_ast(node->if_stmt.else_stmt); - ir_node_t* jmp; - - ctx.cur_block = endb; - NEW_IR_JMP(jmp, ctx.cur_block); - emit_instr(falseb, jmp); - } else { - ctx.cur_block = falseb; - } - NEW_IR_JMP(jmp, ctx.cur_block); - emit_instr(trueb, jmp); - break; - } - case NT_STMT_WHILE: { - ir_bblock_t* entryb = bblocks[0]; - ir_bblock_t* bodyb = bblocks[1]; - ir_bblock_t* endb = bblocks[2]; - - ir_node_t* entry; - NEW_IR_JMP(entry, entryb); - emit_instr(NULL, entry); - - // Entry: - ctx.cur_block = entryb; - ir_node_t *cond = gen_ir_expr(node->while_stmt.cond); - emit_br(cond, bodyb, endb); - - // Body: - ir_node_t* jmp; - ctx.cur_block = bodyb; - _gen_ir_from_ast(node->while_stmt.body); - NEW_IR_JMP(jmp, entryb); - emit_instr(NULL, jmp); - - // End: - ctx.cur_block = endb; - break; - } - case NT_STMT_DOWHILE: { - ir_bblock_t* entryb = bblocks[0]; - ir_bblock_t* bodyb = bblocks[1]; - ir_bblock_t* endb = bblocks[2]; - - ir_node_t* entry; - NEW_IR_JMP(entry, bodyb); - emit_instr(NULL, entry); - - // Body: - ctx.cur_block = bodyb; - _gen_ir_from_ast(node->do_while_stmt.body); - ir_node_t* jmp; - NEW_IR_JMP(jmp, entryb); - emit_instr(NULL, jmp); - - // Entry: - ctx.cur_block = entryb; - ir_node_t *cond = gen_ir_expr(node->do_while_stmt.cond); - emit_br(cond, bodyb, endb); - - // End: - ctx.cur_block = endb; - break; - } - case NT_STMT_FOR: { - ir_bblock_t* entryb = bblocks[0]; - ir_bblock_t* bodyb = bblocks[1]; - ir_bblock_t* endb = bblocks[2]; - - if (node->for_stmt.init) { - _gen_ir_from_ast(node->for_stmt.init); - } - ir_node_t* entry; - NEW_IR_JMP(entry, entryb); - emit_instr(NULL, entry); - - // Entry: - ctx.cur_block = entryb; - if (node->for_stmt.cond) { - ir_node_t *cond = gen_ir_expr(node->for_stmt.cond); - emit_br(cond, bodyb, endb); - } else { - ir_node_t* jmp; - NEW_IR_JMP(jmp, bodyb); - } - - // Body: - ctx.cur_block = bodyb; - _gen_ir_from_ast(node->for_stmt.body); - if (node->for_stmt.iter) { - gen_ir_expr(node->for_stmt.iter); - } - ir_node_t* jmp; - NEW_IR_JMP(jmp, entryb); - emit_instr(NULL, jmp); - - // End: - ctx.cur_block = endb; - break; - } - default: - LOG_ERROR("ir jmp can't hit here"); - } -} - -ir_prog_t* gen_ir_from_ast(ast_node_t* root) { - Assert(root->type == NT_ROOT); - for (int i = 0; i < root->root.children.size; i ++) { - _gen_ir_from_ast(root->root.children.data[i]); - } - // _gen_ir_from_ast(root); - return &prog; -} - -void _gen_ir_from_ast(ast_node_t* node) { - switch (node->type) { - case NT_DECL_FUNC: { - ir_func_t* func = new_ir_func(node->decl_func.name->syms.tok.val.str, &type_i32); - if (node->decl_func.def == NULL) { - ast_node_t* def = new_ast_node(); - def->func.body = NULL; - def->func.decl = node; - node->decl_func.def = def; - vector_push(prog.extern_funcs, func); - } - node->decl_func.def->func.data = func; - break; - } - case NT_FUNC: { - gen_ir_func(node, node->func.data); - break; - } - case NT_STMT_RETURN: { - ir_node_t* ret = NULL; - if (node->return_stmt.expr_stmt != NULL) { - ret = gen_ir_expr(node->return_stmt.expr_stmt); - } - ir_node_t* ir = new_ir_node(NULL, IR_NODE_RET); - ir->data.ret.ret_val = ret; - emit_instr(NULL, ir); - - ir_bblock_t* block = new_ir_bblock(NULL); - ctx.cur_block = block; - vector_push(ctx.cur_func->bblocks, block); - break; - } - case NT_STMT_BLOCK: { - _gen_ir_from_ast(node->block_stmt.block); - break; - } - case NT_BLOCK: { - for (int i = 0; i < node->block.children.size; i ++) { - _gen_ir_from_ast(node->block.children.data[i]); - } - break; - } - case NT_STMT_IF: - case NT_STMT_WHILE: - case NT_STMT_DOWHILE: - case NT_STMT_FOR: - gen_ir_jmp(node); - break; - case NT_DECL_VAR: { - ir_node_t* ir = new_ir_node(node->decl_val.name->syms.tok.val.str, IR_NODE_ALLOC); - emit_instr(NULL, ir); - // TODO Typing system - ir->type = &type_i32; - node->decl_val.data = ir; - if (node->decl_val.expr_stmt != NULL) { - _gen_ir_from_ast(node->decl_val.expr_stmt); - } - break; - } - case NT_STMT_EXPR: { - gen_ir_expr(node->expr_stmt.expr_stmt); - break; - } - case NT_STMT_EMPTY: { - break; - } - default: - // TODO: 错误处理 - LOG_ERROR("unknown node type"); - break; - } -} diff --git a/src/ccompiler/middleend/ir/ir_ast.h b/src/ccompiler/middleend/ir/ir_ast.h deleted file mode 100644 index 0a4488f..0000000 --- a/src/ccompiler/middleend/ir/ir_ast.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __IR_AST_H__ -#define __IR_AST_H__ - -#include "ir.h" -typedef struct ast_node ast_node_t; -ir_prog_t* gen_ir_from_ast(ast_node_t* node); - -#endif // diff --git a/src/ccompiler/middleend/ir/ir_dump.c b/src/ccompiler/middleend/ir/ir_dump.c deleted file mode 100644 index df1ca95..0000000 --- a/src/ccompiler/middleend/ir/ir_dump.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "ir.h" -#include "ir_lib.h" -#include "ir_type.h" -#include -#include - -typedef struct ir_dump { - FILE* fp; -} ir_dump_t; - - -void dump_ir_node(ir_node_t* node, ir_dump_t* dump) { - fprintf(dump->fp, "%%%p", node); - switch (node->tag) { - case IR_NODE_ALLOC: { - node->type = NULL; - // fprintf(dump->fp, "%p\n", ); - break; - } - case IR_NODE_BRANCH: { - node->data.branch.cond = NULL; - node->data.branch.true_bblock = NULL; - node->data.branch.false_bblock = NULL; - break; - } - case IR_NODE_CALL: { - vector_init(node->data.call.args); - node->data.call.callee = NULL; - break; - } - case IR_NODE_CONST_INT: { - node->data.const_int.val = 0; - break; - } - case IR_NODE_JUMP: { - node->data.jump.target_bblock = NULL; - break; - } - case IR_NODE_LOAD: { - node->data.load.target = NULL; - break; - } - case IR_NODE_STORE: { - node->data.store.target = NULL; - node->data.store.value = NULL; - break; - } - case IR_NODE_OP: { - node->data.op.op = 0; - node->data.op.lhs = NULL; - node->data.op.rhs = NULL; - break; - } - case IR_NODE_RET: { - node->data.ret.ret_val = NULL; - break; - } - case IR_NODE_GET_PTR: { - } - default: { - assert(0); - } - } -} - -void dump_ir_bblock(ir_bblock_t* block) { - -} - -void dump_ir_func(ir_func_t* func) { - -} - -void dump_ir_prog(ir_prog_t* prog) { - -} diff --git a/src/ccompiler/middleend/ir/ir_lib.c b/src/ccompiler/middleend/ir/ir_lib.c deleted file mode 100644 index a72019e..0000000 --- a/src/ccompiler/middleend/ir/ir_lib.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "ir.h" - -// FIXME using stdlib.h -#include - -static int total_alloc = 0; -typedef union ir_alloc_item { - ir_node_t node; - ir_bblock_t bblock; - ir_func_t func; - ir_prog_t prog; -} ir_alloc_item_t; - -ir_alloc_item_t* alloc_item() { - return malloc(sizeof(ir_alloc_item_t)); -} - -void free_item(ir_alloc_item_t* item) { - return free(item); -} - -ir_node_t* new_ir_node(const char* name, ir_node_tag_t tag) { - ir_node_t* node = (ir_node_t*)alloc_item(); - node->name = name; - node->type = NULL; - node->tag = tag; - switch (tag) { - case IR_NODE_ALLOC: { - node->type = NULL; - break; - } - case IR_NODE_BRANCH: { - node->data.branch.cond = NULL; - node->data.branch.true_bblock = NULL; - node->data.branch.false_bblock = NULL; - break; - } - case IR_NODE_CALL: { - vector_init(node->data.call.args); - node->data.call.callee = NULL; - break; - } - case IR_NODE_CONST_INT: { - node->data.const_int.val = 0; - break; - } - case IR_NODE_JUMP: { - node->data.jump.target_bblock = NULL; - break; - } - case IR_NODE_LOAD: { - node->data.load.target = NULL; - break; - } - case IR_NODE_STORE: { - node->data.store.target = NULL; - node->data.store.value = NULL; - break; - } - case IR_NODE_OP: { - node->data.op.op = 0; - node->data.op.lhs = NULL; - node->data.op.rhs = NULL; - break; - } - case IR_NODE_RET: { - node->data.ret.ret_val = NULL; - break; - } - case IR_NODE_GET_PTR: { - } - default: { - exit(0); - } - } - vector_init(node->used_by); - return node; -} - -void free_irnode() { - -} - -ir_bblock_t* new_ir_bblock(const char* name) { - ir_bblock_t* block = (ir_bblock_t*)alloc_item(); - block->label = name; - vector_init(block->instrs); - return block; -} - -void free_irbblock() { - -} - -ir_func_t* new_ir_func(const char* name, ir_type_t* type) { - ir_func_t* func = (ir_func_t*)alloc_item(); - func->name = name; - func->type = type; - vector_init(func->params); - vector_init(func->bblocks); - return func; -} - -void free_irfunc() { - -} - -ir_prog_t* new_ir_prog() { - ir_prog_t* prog = (ir_prog_t*)alloc_item(); - vector_init(prog->global); - vector_init(prog->funcs); - vector_init(prog->extern_funcs); - return prog; -} - -void free_irprog() { - -} diff --git a/src/ccompiler/middleend/ir/ir_lib.h b/src/ccompiler/middleend/ir/ir_lib.h deleted file mode 100644 index e969c1c..0000000 --- a/src/ccompiler/middleend/ir/ir_lib.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __IR_LIB_H__ -#define __IR_LIB_H__ - -#include "ir.h" -ir_node_t* new_ir_node(const char* name, ir_node_tag_t tag); -ir_bblock_t* new_ir_bblock(const char* name); -ir_func_t* new_ir_func(const char* name, ir_type_t* type); - -#endif \ No newline at end of file diff --git a/src/ccompiler/middleend/ir/ir_type.c b/src/ccompiler/middleend/ir/ir_type.c deleted file mode 100644 index 2453747..0000000 --- a/src/ccompiler/middleend/ir/ir_type.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "ir.h" - -ir_type_t type_i32 = { - .tag = IR_TYPE_INT32, -}; - -ir_node_t node_zero = { - .tag = IR_NODE_CONST_INT, - .data.const_int = { - .val = 0, - }, -}; diff --git a/src/ccompiler/middleend/ir/ir_type.h b/src/ccompiler/middleend/ir/ir_type.h deleted file mode 100644 index b666a02..0000000 --- a/src/ccompiler/middleend/ir/ir_type.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __IR_TYPE_H__ -#define __IR_TYPE_H__ - -#include "ir.h" -extern ir_type_t type_i32; -extern ir_node_t node_zero; - -#endif \ No newline at end of file diff --git a/src/ccompiler/middleend/middleend.c b/src/ccompiler/middleend/middleend.c deleted file mode 100644 index 046613a..0000000 --- a/src/ccompiler/middleend/middleend.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "middleend.h" - -ir_prog_t* cc_middleend(ast_node_t* root, cc_midend_conf_t* conf) { - return gen_ir_from_ast(root); -} diff --git a/src/ccompiler/middleend/middleend.h b/src/ccompiler/middleend/middleend.h deleted file mode 100644 index d0f7c11..0000000 --- a/src/ccompiler/middleend/middleend.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __SMCC_CC_MIDDLEEND_H__ -#define __SMCC_CC_MIDDLEEND_H__ - -#include "ir/ir.h" -#include "ir/ir_ast.h" - -typedef struct cc_midend_conf { - // cc_arch_t arch; -} cc_midend_conf_t; -// TODO add some feature to cc_middleend like optimization -ir_prog_t* cc_middleend(ast_node_t* root, cc_midend_conf_t* conf); - -#endif // __SMCC_MIDDLEEND_H__ diff --git a/src/ccompiler/middleend/reg_alloc.c b/src/ccompiler/middleend/reg_alloc.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/ccompiler/middleend/reg_alloc.h b/src/ccompiler/middleend/reg_alloc.h deleted file mode 100644 index 560a35c..0000000 --- a/src/ccompiler/middleend/reg_alloc.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __REG_ALLOC_H__ -#define __REG_ALLOC_H__ - -typedef struct { - -} reg_alloc_t; - -#endif \ No newline at end of file diff --git a/src/linker/Makefile b/src/linker/Makefile deleted file mode 100644 index dc9711b..0000000 --- a/src/linker/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -CC = gcc -AR = ar -CFLAGS = -g -Wall -I.. -I../.. -MODULES = . - -EXCLUDE = **/test*.c -SRCS = $(filter-out $(EXCLUDE), $(wildcard $(addsuffix /*.c,$(MODULES)))) -OBJS = $(SRCS:.c=.o) - -liblk.a: $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f liblk.a $(OBJS) diff --git a/src/linker/header.h b/src/linker/header.h deleted file mode 100644 index f366a27..0000000 --- a/src/linker/header.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __HEADER -#define __HEADER -#include -#include -#include - -rv32_prog_t* gen_rv32_crt(); -rv32_prog_t* link_rv32_prog(rv32_prog_t* progs[]); - -#endif diff --git a/src/linker/riscv32_crt.c b/src/linker/riscv32_crt.c deleted file mode 100644 index 7f27e48..0000000 --- a/src/linker/riscv32_crt.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "header.h" -#include -#include -// #include - -rv32_prog_t* gen_rv32_crt() { - rv32_prog_t* _crt = (rv32_prog_t*)salloc_alloc(sizeof(rv32_prog_t)); - init_rv32_prog(_crt, NULL); - - mcode_rv32_t *crt = &_crt->mcode; - rv32_li(crt, REG_SP, 0x1000); - rv32_li(crt, REG_RA, 0x0); - - // call main - symasm_entry_t entry = { - .name = "main", - .attr = GLOBAL - }; - rv32_call_l(crt, &entry); - - // ecall exit2 - rv32_li(crt, REG_A7, 93); - rv32_ecall(crt); - return _crt; -} diff --git a/src/linker/riscv32_linker.c b/src/linker/riscv32_linker.c deleted file mode 100644 index e719954..0000000 --- a/src/linker/riscv32_linker.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "header.h" - -static u32_t text_addr = 0; -#define FOREACH_PROGS(progs) for (rv32_prog_t **pprog = progs, *prog = *progs; \ - *pprog != NULL; prog = *(++pprog)) -int append(const void *key, void *value, void *context) { - symtab_asm_t* symtab = (symtab_asm_t*) context; - symasm_entry_t* entry = (symasm_entry_t*) key; - u32_t* addr = (u32_t*) value; - *addr += text_addr; // size to index - if (entry->attr == GLOBAL) { - symtab_asm_put(symtab, entry, *addr); - } - return 1; -} - -rv32_prog_t* link_rv32_prog(rv32_prog_t* progs[]) { - symtab_asm_t symtab; - init_symtab_asm(&symtab); - - rv32_prog_t* exe = salloc_alloc(sizeof(rv32_prog_t)); - init_rv32_prog(exe, NULL); - - FOREACH_PROGS(progs) { - prog->text_base_address = text_addr; - text_addr += prog->mcode.code.size; - } - - exe->mcode.code.data = salloc_alloc(text_addr); - exe->mcode.code.cap = text_addr; - exe->mcode.code.size = text_addr; - text_addr = 0; - FOREACH_PROGS(progs) { - rt_memcpy(exe->mcode.code.data + text_addr, prog->mcode.code.data, prog->mcode.code.size); - // FIXME - hashtable_foreach(&prog->symtab.symtab, append, &symtab); - text_addr += prog->mcode.code.size; - } - - // find by self - FOREACH_PROGS(progs) { - for (int i = 0; i < prog->mcode.rinstrs.size; i++) { - u32_t* paddr; - u32_t addr; - rotated_instr_t *rinstr = &(prog->mcode.rinstrs.data[i]); - paddr = symtab_asm_get(&prog->symtab, rinstr->target); - if (paddr == NULL) { - paddr = symtab_asm_get(&symtab, rinstr->target); - if (paddr == NULL) { - LOG_FATAL("linker: %s not found", ((symasm_entry_t*)rinstr->target)->name); - } - addr = *paddr; - } else { - addr = *paddr; - // addr += prog->text_base_address; - } - // FIXME - int cur_idx = prog->text_base_address + rinstr->pos; - rinstr->instr.imm += addr - cur_idx; - emit_rv32_instr(&exe->mcode, &rinstr->instr, cur_idx, NULL); - - // symtab_get(&prog->symtab, &prog->rinstrs.data[i].target); - } - } - - return exe; -} diff --git a/src/mcode/Makefile b/src/mcode/Makefile deleted file mode 100644 index 0296614..0000000 --- a/src/mcode/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -CC = gcc -AR = ar -CFLAGS = -g -Wall -I../.. -MODULES = . riscv32 - -EXCLUDE = test*.c -SRCS = $(filter-out $(EXCLUDE), $(wildcard $(addsuffix /*.c,$(MODULES)))) -OBJS = $(SRCS:.c=.o) - -libmc.a: $(OBJS) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -f libmc.a $(OBJS) diff --git a/src/mcode/reloc_symtab.c b/src/mcode/reloc_symtab.c deleted file mode 100644 index d195e58..0000000 --- a/src/mcode/reloc_symtab.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "reloc_symtab.h" - -// /* append label */ -// static inline int -// rv32_append_label(mcode_rv32_t* prog, void* label, u32_t offset) { -// // prog->symtab -// symtab_asm_put(&prog->symtab, label, offset); -// return 0; -// } - -// static u32_t hash_func(const symasm_entry_t* key) { -// return rt_strhash(key->name); -// } - -// static int cmp_func(const symasm_entry_t* k1, const symasm_entry_t* k2) { -// return rt_strcmp(k1->name, k2->name); -// } - -// void init_symtab_asm(symtab_asm_t* symtab) { -// init_hashtable(&symtab->symtab); -// symtab->symtab.hash_func = (u32_t(*)(const void*))hash_func; -// symtab->symtab.key_cmp = (int(*)(const void*, const void*))cmp_func; -// } - -// void symtab_asm_put(symtab_asm_t* symtab, symasm_entry_t* _entry, u32_t address) { -// // FIXME maybe memory leak -// u32_t* addr = salloc_alloc(sizeof(u32_t)); -// if (addr == NULL) { -// LOG_FATAL("salloc_alloc failure"); -// } -// symasm_entry_t* entry = salloc_alloc(sizeof(symasm_entry_t)); -// if (entry == NULL) LOG_FATAL("malloc failure"); -// *entry = *_entry; -// *addr = address; -// void* ret = hashtable_set(&symtab->symtab, entry, addr); -// if (ret != NULL) { -// LOG_ERROR("Symbol %s already exists", entry->name); -// } -// } - -// u32_t* symtab_asm_get(symtab_asm_t* symtab, symasm_entry_t* entry) { -// u32_t* addr = hashtable_get(&symtab->symtab, entry); -// return addr; -// } - -// void symtab_asm_destroy(symtab_asm_t* symtab) { -// hashtable_destory(&symtab->symtab); -// } diff --git a/src/mcode/reloc_symtab.h b/src/mcode/reloc_symtab.h deleted file mode 100644 index de761ed..0000000 --- a/src/mcode/reloc_symtab.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __SMCC_RELOC_SYMTAB_H__ -#define __SMCC_RELOC_SYMTAB_H__ -// Relocation Symbol Table - -#include -#include - -typedef enum reloc_attr { - RELOC_ATTR_GLOBAL, - RELOC_ATTR_LOCAL, -} reloc_attr_t; - -typedef struct reloc_entry { - const char* name; - reloc_attr_t attr; -} reloc_entry_t; - -typedef struct reloc_symtab { - hash_table_t reloctab; -} reloc_symtab_t; - -void init_symtab_asm(reloc_symtab_t* symtab); -void symtab_asm_put(reloc_symtab_t* symtab, reloc_entry_t* entry, u32_t address); -u32_t* symtab_asm_get(reloc_symtab_t* symtab, reloc_entry_t* entry); -void symtab_asm_destroy(reloc_symtab_t* symtab); - -#endif diff --git a/src/mcode/riscv32/riscv32_def.h b/src/mcode/riscv32/riscv32_def.h deleted file mode 100644 index 349c769..0000000 --- a/src/mcode/riscv32/riscv32_def.h +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef __SMCC_RISCV32_DEF_H__ -#define __SMCC_RISCV32_DEF_H__ - -#include - -typedef enum rv_ext { - RV32_I_EXT, - RV32_M_EXT, -} rv_ext_t; - -typedef enum rv_fmt { - RV_R_TYPE, - RV_I_TYPE, - RV_S_TYPE, - RV_B_TYPE, - RV_U_TYPE, - RV_J_TYPE, -} rv_fmt_t; - -typedef enum rv_instr_type { - /* U type */ - RV_AUIPC, - RV_LUI, - /* J type */ - RV_JAL, - /* I type */ - RV_JALR, - // Load - RV_LB, - RV_LH, - RV_LW, - RV_LBU, - RV_LHU, - // Imm arithmetic - RV_ADDI, - RV_SLTI, - RV_SLTIU, - RV_XORI, - RV_ORI, - RV_ANDI, - // Imm shift - RV_SLLI, - RV_SRLI, - RV_SRAI, - /* B type */ - RV_BEQ, - RV_BNE, - RV_BLT, - RV_BGE, - RV_BLTU, - RV_BGEU, - /* S type */ - RV_SB, - RV_SH, - RV_SW, - /* R type */ - RV_ADD, - RV_SUB, - RV_SLL, - RV_SLT, - RV_SLTU, - RV_XOR, - RV_SRL, - RV_SRA, - RV_OR, - RV_AND, - /* I type (system) */ - RV_FENCE, - RV_ECALL, - RV_EBREAK, - - /* M-Extention */ - RV_MUL, - RV_DIV, - RV_REM, - // RV_MULH, - // RV_DIVU, - // RV_REMU, - // RV_MULHSU, - // RV_MULHU, - // RV_DIVU, - // RV_REMU, - // RV_MULHSU, - // RV_MULHU, - RV_INSTR_TYPE_COUNT, -} rv_instr_type_t; - -typedef enum rv_reg { - REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7, - REG_X8, REG_X9, REG_X10, REG_X11, REG_X12, REG_X13, REG_X14, REG_X15, - REG_X16, REG_X17, REG_X18, REG_X19, REG_X20, REG_X21, REG_X22, REG_X23, - REG_X24, REG_X25, REG_X26, REG_X27, REG_X28, REG_X29, REG_X30, REG_X31, - REG_ZERO = REG_X0, REG_RA = REG_X1, REG_SP = REG_X2, REG_GP = REG_X3, - REG_TP = REG_X4, REG_T0 = REG_X5, REG_T1 = REG_X6, REG_T2 = REG_X7, - REG_S0 = REG_X8, REG_S1 = REG_X9, REG_A0 = REG_X10, REG_A1 = REG_X11, - REG_A2 = REG_X12, REG_A3 = REG_X13, REG_A4 = REG_X14, REG_A5 = REG_X15, - REG_A6 = REG_X16, REG_A7 = REG_X17, REG_S2 = REG_X18, REG_S3 = REG_X19, - REG_S4 = REG_X20, REG_S5 = REG_X21, REG_S6 = REG_X22, REG_S7 = REG_X23, - REG_S8 = REG_X24, REG_S9 = REG_X25, REG_S10 = REG_X26, REG_S11 = REG_X27, - REG_T3 = REG_X28, REG_T4 = REG_X29, REG_T5 = REG_X30, REG_T6 = REG_X31, - REG_NULL = 0, -} rv_reg_t; - -#define RV_F3(f3) (f3 << 12) -#define RV_F7(f7) (f7 << 25) - -typedef struct rv32_instr { - rv_ext_t extention; - rv_fmt_t format; - rv_instr_type_t instr_type; - const char* instr_name; - u32_t instr; - u32_t imm; - rv_reg_t rd; - rv_reg_t rs1; - rv_reg_t rs2; -} rv32_instr_t; - -#define RV_INSTRUCTIONS \ - X(RV32_I_EXT, RV_R_TYPE, RV_ADD, "add", 0x33 | RV_F3(0x0) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_ADDI, "addi", 0x13 | RV_F3(0x0)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_AND, "and", 0x33 | RV_F3(0x7) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_ANDI, "andi", 0x13 | RV_F3(0x7)) \ - X(RV32_I_EXT, RV_U_TYPE, RV_AUIPC, "auipc", 0x17) \ - X(RV32_I_EXT, RV_B_TYPE, RV_BEQ, "beq", 0x63 | RV_F3(0x0)) \ - X(RV32_I_EXT, RV_B_TYPE, RV_BGE, "bge", 0x63 | RV_F3(0x5)) \ - X(RV32_I_EXT, RV_B_TYPE, RV_BGEU, "bgeu", 0x63 | RV_F3(0x7)) \ - X(RV32_I_EXT, RV_B_TYPE, RV_BLT, "blt", 0x63 | RV_F3(0x4)) \ - X(RV32_I_EXT, RV_B_TYPE, RV_BLTU, "bltu", 0x63 | RV_F3(0x6)) \ - X(RV32_I_EXT, RV_B_TYPE, RV_BNE, "bne", 0x63 | RV_F3(0x1)) \ - X(RV32_M_EXT, RV_R_TYPE, RV_DIV, "div", 0x33 | RV_F3(0x0) | RV_F7(0x05)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_ECALL, "ecall", 0x73) \ - X(RV32_I_EXT, RV_I_TYPE, RV_EBREAK,"ebreak",0x100073) \ - X(RV32_I_EXT, RV_J_TYPE, RV_JAL, "jal", 0x6F) \ - X(RV32_I_EXT, RV_I_TYPE, RV_JALR, "jalr", 0x67) \ - X(RV32_I_EXT, RV_I_TYPE, RV_LB, "lb", 0x03 | RV_F3(0x0)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_LBU, "lbu", 0x03 | RV_F3(0x4)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_LH, "lh", 0x03 | RV_F3(0x1)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_LHU, "lhu", 0x03 | RV_F3(0x5)) \ - X(RV32_I_EXT, RV_U_TYPE, RV_LUI, "lui", 0x37) \ - X(RV32_I_EXT, RV_I_TYPE, RV_LW, "lw", 0x03 | RV_F3(0x2)) \ - X(RV32_M_EXT, RV_R_TYPE, RV_MUL, "mul", 0x33 | RV_F3(0x0) | RV_F7(0x01)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_OR, "or", 0x33 | RV_F3(0x6) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_ORI, "ori", 0x13 | RV_F3(0x6)) \ - X(RV32_M_EXT, RV_R_TYPE, RV_REM, "rem", 0x33 | RV_F3(0x0) | RV_F7(0x07)) \ - X(RV32_I_EXT, RV_S_TYPE, RV_SB, "sb", 0x23 | RV_F3(0x0)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_SLL, "sll", 0x33 | RV_F3(0x1) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_SLLI, "slli", 0x13 | RV_F3(0x0)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_SLT, "slt", 0x33 | RV_F3(0x2) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_SLTI, "slti", 0x13 | RV_F3(0x2)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_SLTIU, "sltiu", 0x13 | RV_F3(0x3)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_SLTU, "sltu", 0x33 | RV_F3(0x3) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_S_TYPE, RV_SH, "sh", 0x23 | RV_F3(0x1)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_SRA, "sra", 0x33 | RV_F3(0x5) | RV_F7(0x20)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_SRAI, "srai", 0x13 | RV_F3(0x5)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_SRL, "srl", 0x33 | RV_F3(0x5) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_SRLI, "srli", 0x13 | RV_F3(0x5)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_SUB, "sub", 0x33 | RV_F3(0x0) | RV_F7(0x20)) \ - X(RV32_I_EXT, RV_S_TYPE, RV_SW, "sw", 0x23 | RV_F3(0x2)) \ - X(RV32_I_EXT, RV_R_TYPE, RV_XOR, "xor", 0x33 | RV_F3(0x4) | RV_F7(0x00)) \ - X(RV32_I_EXT, RV_I_TYPE, RV_XORI, "xori", 0x13 | RV_F3(0x4)) \ - // End - -#endif diff --git a/src/mcode/riscv32/riscv32_instr.h b/src/mcode/riscv32/riscv32_instr.h deleted file mode 100644 index 89f327f..0000000 --- a/src/mcode/riscv32/riscv32_instr.h +++ /dev/null @@ -1,939 +0,0 @@ -#ifndef __SMCC_RISCV32_INSTR_H__ -#define __SMCC_RISCV32_INSTR_H__ - -#include "riscv32_mcode.h" -#include "riscv32_def.h" - -#define EMIT_PUSH_BACK (-1) -/* base integer instruction */ - -/** - * ADD - * - * add rd, rs1, rs2 - * - * rd = rs1 + rs2 - */ -static inline int -rv32_add(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_ADD, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * ADD Immediate - * - * addi rd, rs1, imm - * - * rd = rs1 + imm (imm maybe cut) - */ -static inline int -rv32_addi(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_ADDI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * AND - * - * and rd, rs1, rs2 - * - * rd = rs1 & rs2 - */ -static inline int -rv32_and(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_AND, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * AND Immediate - * - * andi rd, rs1, imm - * - * rd = rs1 & imm (imm maybe cut) - */ -static inline int -rv32_andi(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_ANDI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Add Upper Immediate to PC - * - * auipc rd, imm{12} - * - * rd = PC + imm{12} - */ -static inline int -rv32_auipc(mcode_rv32_t* prog, rv_reg_t rd, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_AUIPC, - .rd = rd, - .rs1 = REG_NULL, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Branch EQual - * - * beq rs1, rs2, imm - * - * if (rs1 == rs2) PC += imm{alian2} - */ -static inline int -rv32_beq(mcode_rv32_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_BEQ, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Branch Greater than or Equal - * - * bge rs1, rs2, imm - * - * if (rs1 >= rs2) PC += imm{alian2} - */ -static inline int -rv32_bge(mcode_rv32_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_BGE, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Branch Greater than or Equal Unsigned - * - * bgeu rs1, rs2, imm - * - * if (rs1 >= u rs2) PC += imm{alian2} - */ -static inline int -rv32_bgeu(mcode_rv32_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_BGEU, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Branch Less Than - * - * blt rs1, rs2, imm - * - * if (rs1 < rs2) PC += imm{alian2} - */ -static inline int -rv32_blt(mcode_rv32_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_BLT, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Branch Less Than Unsigned - * - * bltu rs1, rs2, imm - * - * if (rs1 < u rs2) PC += imm{alian2} - */ -static inline int -rv32_bltu(mcode_rv32_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_BLTU, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Branch Not Equal - * - * bne rs1, rs2, imm - * - * if (rs1 != rs2) PC += imm{alian2} - */ -static inline int -rv32_bne(mcode_rv32_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_BNE, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Environment BREAK - * - * ebreak - * - * Transfer control to debugger - */ -static inline int -rv32_ebreak(mcode_rv32_t* prog) { - rv32_instr_t instr = { - .instr_type = RV_EBREAK, - .rd = REG_NULL, - .rs1 = REG_NULL, - .rs2 = REG_NULL, - .imm = 1, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Environment CALL - * - * ecall - * - * Transfer control to operating system - */ -static inline int -rv32_ecall(mcode_rv32_t* prog) { - rv32_instr_t instr = { - .instr_type = RV_ECALL, - .rd = REG_NULL, - .rs1 = REG_NULL, - .rs2 = REG_NULL, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Jump And Link - * - * jal rd, imm - * - * rd = PC + 4; PC += imm{alian2} - */ -static inline int -rv32_jal(mcode_rv32_t* prog, rv_reg_t rd, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_JAL, - .rd = rd, - .rs1 = REG_NULL, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Jump And Link Register - * - * jalr rd, rs1, imm - * - * rd = PC + 4; PC = rs1 + imm - */ -static inline int -rv32_jalr(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_JALR, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Load Byte - * - * lb rd, rs1, imm - * - * rd = (8 bit) M[rs1+imm] - */ -static inline int -rv32_lb(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_LB, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Load Byte Unsigned - * - * lbu rd, rs1, imm - * - * rd = (8 bit) M[rs1+imm] - */ -static inline int -rv32_lbu(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_LBU, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * ld - * lh - * lhu - */ - -/** - * Load Upper Immediate - * - * lui rd, imm - * - * rd = (32 bit) imm - */ -static inline int -rv32_lui(mcode_rv32_t* prog, rv_reg_t rd, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_LUI, - .rd = rd, - .rs1 = REG_NULL, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Load Word - * - * lw rd, rs1, imm - * - * rd = (32 bit) M[rs1+imm] - */ -static inline int -rv32_lw(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_LW, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Load Word Unsigned - * - * lwu rd, rs1, imm - * - * rd = (32 bit) u M[rs1+imm] - */ -// static inline int -// rv32_lwu(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { -// return emit_rv32_instr(prog, RV_LWU, rd, rs1, REG_NULL, imm); -// } - -/** - * OR - * - * or rd, rs1, rs2 - * - * rd = rs1 | rs2 - */ -static inline int -rv32_or(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_OR, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * OR Immediate - * - * ori rd, rs1, imm - * - * rd = rs1 | imm (imm maybe cut) - */ -static inline int -rv32_ori(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_ORI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Store Byte - * - * sb rs2, rs1, imm - * - * M[rs1+imm] = rs2 - */ -static inline int -rv32_sb(mcode_rv32_t* prog, rv_reg_t rs2, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_SB, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * sd sh - */ - -/** - * Shift Left - * - * sll rd, rs1, rs2 - * - * rd = rs1 << rs2 - */ -static inline int -rv32_sll(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_SLL, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Shift Left Immediate - * - * slli rd, rs1, imm - * - * rd = rs1 << imm (maybe cut it) - */ -static inline int -rv32_slli(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_SLLI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Set Less Than - * - * slt rd, rs1, rs2 - * - * rd = (rs1 < rs2) ? 1 : 0 - */ -static inline int -rv32_slt(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_SLT, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Set Less Than Immediate - * - * slti rd, rs1, imm - * - * rd = (rs1 < imm) ? 1 : 0 - */ -static inline int -rv32_slti(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_SLTI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Set Less Than Immediate Unsigned - * - * sltiu rd, rs1, imm - * - * rd = (u rs1 < imm) ? 1 : 0 - */ -static inline int -rv32_sltiu(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_SLTIU, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Set Less Than Unsigned - * - * sltu rd, rs1, rs2 - * - * rd = (u rs1 < u rs2) ? 1 : 0 - */ -static inline int -rv32_sltu(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_SLTU, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Shift Right Arithmetic - * - * sra rd, rs1, rs2 - * - * rd = rs1 >> rs2 - */ -static inline int -rv32_sra(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_SRA, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Shift Right Arithmetic Immediate - * - * srai rd, rs1, imm - * - * rd = rs1 >> imm (maybe cut it) - */ -static inline int -rv32_srai(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_SRAI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Shift Right Logical - * - * srl rd, rs1, rs2 - * - * rd = (u rs1) >> rs2 - */ -static inline int -rv32_srl(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_SRL, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Shift Right Logical Immediate - * - * srli rd, rs1, imm - * - * rd = (u rs1) >> imm (maybe cut it) - */ -static inline int -rv32_srli(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_SRLI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * SUBtract - * - * sub rd, rs1, rs2 - * - * rd = rs1 - rs2 - */ -static inline int -rv32_sub(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_SUB, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * Store Word - * - * sw rs2, rs1, imm - * - * M[rs1+imm] = rs2 - */ -static inline int -rv32_sw(mcode_rv32_t* prog, rv_reg_t rs2, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_SW, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * XOR - * - * xor rd, rs1, rs2 - * - * rd = rs1 ^ rs2 - */ -static inline int -rv32_xor(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) { - rv32_instr_t instr = { - .instr_type = RV_XOR, - .rd = rd, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/** - * XOR Immediate - * - * xori rd, rs1, imm - * - * rd = rs1 ^ imm (maybe cut it) - */ -static inline int -rv32_xori(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) { - rv32_instr_t instr = { - .instr_type = RV_XORI, - .rd = rd, - .rs1 = rs1, - .rs2 = REG_NULL, - .imm = imm, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL); -} - -/* pseudo instruction */ - -/** - * branch equal zero - * - * beqz rs1, imm - * - * if (rs1 == 0) PC += imm{alian2} - */ -static inline int -rv32_beqz(mcode_rv32_t* prog, rv_reg_t rs1, u32_t imm) { - return rv32_beq(prog, rs1, REG_X0, imm); -} - -/** - * branch not equal zero - * - * bnez rs1, imm - * - * if (rs1 != 0) PC += imm{alian2} - */ -static inline int -rv32_bnez(mcode_rv32_t* prog, rv_reg_t rs1, u32_t imm) { - return rv32_bne(prog, rs1, REG_X0, imm); -} - -/** - * fabs.s fabs.d - * fmv.s fmv.d - * fneg.s fneg.d - */ - -/** - * jump - * - * j imm - * - * PC = imm{alian2} - */ -static inline int -rv32_j(mcode_rv32_t* prog, u32_t imm) { - // TODO - return rv32_jal(prog, REG_X0, imm); -} - -/** - * jump register - * - * jr rs1 - * - * PC = rs1 - */ -static inline int -rv32_jr(mcode_rv32_t* prog, rv_reg_t rs1) { - // TODO - return rv32_jalr(prog, REG_X0, rs1, 0); -} - -/** - * load address - * - * la rd - * - * rd = address - */ -static inline int -rv32_la(mcode_rv32_t* prog, rv_reg_t rd, u32_t imm) { - // TODO - return rv32_auipc(prog, rd, imm); -} - -/** - * load immediate - * - * li rd, imm - * - * rd = imm - */ -static inline int -rv32_li(mcode_rv32_t* prog, rv_reg_t rd, i32_t imm) { - if (imm >= -2048 && imm <= 2047) { - return rv32_addi(prog, rd, REG_X0, imm); - } else { - int ret = rv32_lui(prog, rd, imm); - rv32_addi(prog, rd, rd, imm); - return ret; - } -} - -/** - * move - * - * mv rd, rs1 - * - * rd = rs1 - */ -static inline int -rv32_mv(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1) { - return rv32_addi(prog, rd, rs1, 0); -} - -/** - * negate - * - * neg rd, rs1 - * - * rd = -rs1 - */ -static inline int -rv32_neg(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1) { - return rv32_sub(prog, rd, REG_X0, rs1); -} - -/** - * no opreation - * - * nop - * - * x0 = x0 - */ -static inline int -rv32_nop(mcode_rv32_t* prog) { - return rv32_addi(prog, REG_X0, REG_X0, 0); -} - -/** - * not - * - * not rd, rs1 - * - * rd = ~rs1 - */ -static inline int -rv32_not(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1) { - return rv32_xori(prog, rd, rs1, ~0); -} - -/** - * return - * - * ret - * - * PC = ra - */ -static inline int -rv32_ret(mcode_rv32_t* prog) { - return rv32_jalr(prog, REG_X0, REG_RA, 0); -} - -/** - * set equal zero - * - * seqz rd, rs1 - * - * rd = (rs1 == 0) ? 1 : 0 - */ -static inline int -rv32_seqz(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1) { - // TODO - return rv32_sltiu(prog, rd, rs1, 1); -} - -/** - * set not equal zero - * - * snez rd, rs1 - * - * rd = (rs1 != 0) ? 1 : 0 - */ -static inline int -rv32_snez(mcode_rv32_t* prog, rv_reg_t rd, rv_reg_t rs1) { - // TODO - return rv32_sltu(prog, rd, rs1, REG_X0); -} - -/* instruction of rotated (using label) */ -static inline int -rv32_bne_l(mcode_rv32_t* prog, rv_reg_t rs1, rv_reg_t rs2, void* label) { - rv32_instr_t instr = { - .instr_type = RV_BNE, - .rd = REG_NULL, - .rs1 = rs1, - .rs2 = rs2, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, label); -} - -static inline int -rv32_jal_l(mcode_rv32_t* prog, rv_reg_t rd, void* label) { - rv32_instr_t instr = { - .instr_type = RV_JAL, - .rd = rd, - .rs1 = REG_NULL, - .rs2 = REG_NULL, - .imm = 0, - }; - return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, label); -} - -static inline int -rv32_call_l(mcode_rv32_t* prog, void* label) { - rv32_instr_t instr; - - instr.instr_type = RV_JAL; - instr.rd = REG_NULL; - instr.rs1 = REG_NULL; - instr.rs2 = REG_NULL; - instr.imm = 0; - - int ret = rv32_auipc(prog, REG_RA, REG_X0); - - instr.instr_type = RV_JALR; - instr.rd = REG_RA; - instr.rs1 = REG_RA; - instr.rs2 = REG_NULL; - instr.imm = 4; - - emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, label); - - return ret; -} - -#endif diff --git a/src/mcode/riscv32/riscv32_mcode.c b/src/mcode/riscv32/riscv32_mcode.c deleted file mode 100644 index 07ae5a2..0000000 --- a/src/mcode/riscv32/riscv32_mcode.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "riscv32_mcode.h" - -static const rv32_instr_t rv32_instrs[] = { - #define X(ext, fmt, type, name, op) \ - [type] = { \ - .extention = ext, \ - .format = fmt, \ - .instr_type = type, \ - .instr_name = name, \ - .instr = op \ - }, - RV_INSTRUCTIONS - #undef X -}; - -static inline int valid_reg(rv_reg_t reg) { - // return reg >= REG_X0 && reg <= REG_X31; - return !(reg & ~0x1F); -} - -void init_rv32_mcode(mcode_rv32_t* mcode, int target_size) { - vector_init(mcode->code); - vector_init(mcode->rinstrs); - mcode->target_size = target_size; -} - -int decode_rv32_instr(u32_t instr, rv32_instr_t *out) { - return 0; -} - -int emit_rv32_instr(mcode_rv32_t* mcode, rv32_instr_t *instr, u32_t idx, void *target) { - // TODO check out input data - instr->extention = rv32_instrs[instr->instr_type].extention; - instr->format = rv32_instrs[instr->instr_type].format; - instr->instr_name = rv32_instrs[instr->instr_type].instr_name; - instr->instr = rv32_instrs[instr->instr_type].instr; - - switch (instr->format) { - case RV_R_TYPE: - instr->instr |= (instr->rd << 7) | (instr->rs1 << 15) | (instr->rs2 << 20); - break; - case RV_I_TYPE: - instr->instr |= (instr->rd << 7) | (instr->rs1 << 15) | (instr->imm << 20); - break; - case RV_S_TYPE: - instr->instr |= (instr->rs1 << 15) | (instr->rs2 << 20) | - ((instr->imm & 0x1F) << 7) | ((instr->imm & 0xFE0) << 20); - break; - case RV_B_TYPE: - instr->instr |= (instr->rs1 << 15) | (instr->rs2 << 20) | - (((instr->imm) >> 12) & 0x1) << 31 | /* imm[12:12] -> instr[31:31] */ \ - (((instr->imm) >> 5) & 0x3F) << 25 | /* imm[10:5] -> instr[30:25] */ \ - (((instr->imm) >> 1) & 0xF) << 8 | /* imm[4:1] -> instr[11:8] */ \ - (((instr->imm) >> 11) & 0x1) << 7; /* imm[11:11] -> instr[7:7] */ - break; - case RV_U_TYPE: - instr->instr |= (instr->rd << 7) | (instr->imm & 0xFFFFF000); - break; - case RV_J_TYPE: - instr->instr |= (instr->rd << 7) | - (((instr->imm) >> 20) & 0x1) << 31 | /* imm[20:20] -> instr[31:31] */ \ - (((instr->imm) >> 1) & 0x3FF)<< 21 | /* imm[10:1] -> instr[30:21] */ \ - (((instr->imm) >> 11) & 0x1) << 20 | /* imm[11:11] -> instr[20:20] */ \ - (((instr->imm) >> 12) & 0xFF) << 12; /* imm[19:12] -> instr[19:12] */ - break; - default: - Panic("Invalid instruction format"); - } - - if (target != NULL) { - Assert(idx == -1); - void* ptr; - if (mcode->target_size >= 0) { - ptr = salloc_alloc(mcode->target_size); - if (ptr == NULL) { - LOG_FATAL("malloc failure"); - } - rt_memcpy(ptr, target, mcode->target_size); - } else { - ptr = target; - } - rotated_instr_t rinstr = { - .instr = *instr, - .pos = mcode->code.size, - .target = ptr, - }; - vector_push(mcode->rinstrs, rinstr); - // GOTO idx == -1 - } - - if (idx == -1) { - // 32bit instr - int idx = mcode->code.size; - vector_push(mcode->code, 0); - vector_push(mcode->code, 0); - vector_push(mcode->code, 0); - vector_push(mcode->code, 0); - u32_t* pinstr = (u32_t*)(&vector_at(mcode->code, idx)); - *pinstr = instr->instr; - return idx; - } else if (idx >= 0 && idx < mcode->code.size) { - *(u32_t*)&vector_at(mcode->code, idx) = instr->instr; - return mcode->code.size - 1; - } else { - Panic("Invalid instruction index"); - } - return -1; -} diff --git a/src/mcode/riscv32/riscv32_mcode.h b/src/mcode/riscv32/riscv32_mcode.h deleted file mode 100644 index 62b7a7e..0000000 --- a/src/mcode/riscv32/riscv32_mcode.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __SMCC_ASM_RISCV32_H__ -#define __SMCC_ASM_RISCV32_H__ - -#include -#include -#include "riscv32_def.h" - -typedef struct rotated_instr { - u32_t pos; - void* target; - rv32_instr_t instr; -} rotated_instr_t; - -typedef struct mcode_rv32 { - VECTOR_HEADER(rinstrs, rotated_instr_t); - VECTOR_HEADER(code, u8_t); - int target_size; -} mcode_rv32_t; - -void init_rv32_mcode(mcode_rv32_t* mcode, int target_size); -int emit_rv32_instr(mcode_rv32_t* mcode, rv32_instr_t *instr, u32_t idx, void *target); - -#endif diff --git a/src/pprocesser/main.c b/src/pprocesser/main.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/smcc.c b/src/smcc.c deleted file mode 100644 index 4cc8697..0000000 --- a/src/smcc.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include "linker/header.h" -#include - -int main(int argc, char** argv) { - // gcc rv32ima_codegen.c -o rv32gen.exe - init_lib_core(); - log_set_level(NULL, LOG_LEVEL_ERROR | LOG_LEVEL_WARN | LOG_LEVEL_FATAL); - - const char* infilename = ".test.c"; - const char* outfilename = "flat.bin"; - if (argc >= 2) { - infilename = argv[1]; - } - if (argc >= 3) { - outfilename = argv[2]; - } - FILE* in = fopen(infilename, "r"); - FILE* out = fopen(outfilename, "wb"); - if (in == NULL || out == NULL) { - printf("Failed to open file\n"); - return 1; - } - - smcc_cc_t cc = { - .file = infilename, - .sread = (sread_fn)fread, - .stream = in, - }; - - asm_prog_t *prog = smcc_cc(&cc); - // gen_rv32_instr(&prog->rv32); - - rv32_prog_t* rv32_crt = gen_rv32_crt(); - rv32_prog_t* rv32_progs[3] = { - rv32_crt, - &prog->rv32, - NULL, - }; - - rv32_prog_t* code = link_rv32_prog(rv32_progs); - - // Flatbin fmt - fwrite(code->mcode.code.data, 1, code->mcode.code.size, out); - // fwrite(code->data.data, sizeof(u8_t)code->text.size, 1, out); - - fclose(in); - fclose(out); - return 0; -} diff --git a/tests/cbuild/cbuild.toml b/tests/cbuild/cbuild.toml new file mode 100644 index 0000000..a281d84 --- /dev/null +++ b/tests/cbuild/cbuild.toml @@ -0,0 +1,2 @@ +[package] +name = "test_cbuild" diff --git a/tests/cbuild/include/add.h b/tests/cbuild/include/add.h new file mode 100644 index 0000000..a889a66 --- /dev/null +++ b/tests/cbuild/include/add.h @@ -0,0 +1,6 @@ +#ifndef __ADD_H__ +#define __ADD_H__ + +int add(int a, int b); + +#endif diff --git a/tests/cbuild/src/add.c b/tests/cbuild/src/add.c new file mode 100644 index 0000000..082fc1e --- /dev/null +++ b/tests/cbuild/src/add.c @@ -0,0 +1,3 @@ +int add(int a, int b) { + return a + b; +} \ No newline at end of file diff --git a/tests/cbuild/src/main.c b/tests/cbuild/src/main.c new file mode 100644 index 0000000..0481c63 --- /dev/null +++ b/tests/cbuild/src/main.c @@ -0,0 +1,8 @@ +#include +#include + +int main() { + printf("Hello World!\n"); + printf("Add 1 + 1 = %d\n", add(1, 1)); + return 0; +} diff --git a/tests/cbuild/tests/test_main.c b/tests/cbuild/tests/test_main.c new file mode 100644 index 0000000..7837bfc --- /dev/null +++ b/tests/cbuild/tests/test_main.c @@ -0,0 +1,10 @@ +int add(int a, int b); +#include + +int main() { + int a = 1; + int b = 2; + int c = add(a, b); + assert(c == 3); + return 0; +} diff --git a/tests/simple/01_return.c b/tests/simple/01_return.c new file mode 100644 index 0000000..9fd5dff --- /dev/null +++ b/tests/simple/01_return.c @@ -0,0 +1,3 @@ +int main() { + return 65536; +} diff --git a/tests/simple/02_decl_expr.c b/tests/simple/02_decl_expr.c new file mode 100644 index 0000000..50796b1 --- /dev/null +++ b/tests/simple/02_decl_expr.c @@ -0,0 +1,8 @@ +int main() { + int a; + int b; + a = 1 + 2 * 3; + b = 7; + a = a - b + 1; + return a; +} diff --git a/tests/simple/03_decl_init.c b/tests/simple/03_decl_init.c new file mode 100644 index 0000000..45ad750 --- /dev/null +++ b/tests/simple/03_decl_init.c @@ -0,0 +1,6 @@ + +int main() { + int x = 10; + x = x + 1; + return x; +} \ No newline at end of file diff --git a/tests/simple/04_if.c b/tests/simple/04_if.c new file mode 100644 index 0000000..af66af7 --- /dev/null +++ b/tests/simple/04_if.c @@ -0,0 +1,10 @@ +int main(void) { + int a; + a = 1; + if (a) { + a = 1; + } else { + a = 2; + } + return a; +} diff --git a/tests/simple/05_else.c b/tests/simple/05_else.c new file mode 100644 index 0000000..6cda761 --- /dev/null +++ b/tests/simple/05_else.c @@ -0,0 +1,10 @@ +int main(void) { + int a; + a = 0; + if (a) { + a = 1; + } else { + a = 2; + } + return a; +} \ No newline at end of file diff --git a/tests/simple/06_fcall.c b/tests/simple/06_fcall.c new file mode 100644 index 0000000..18f817c --- /dev/null +++ b/tests/simple/06_fcall.c @@ -0,0 +1,9 @@ +int add(int, int); + +int main(void) { + return add(1, 2); +} + +int add(int a, int b) { + return a + b; +} diff --git a/tests/simple/07_while.c b/tests/simple/07_while.c new file mode 100644 index 0000000..93d6cd4 --- /dev/null +++ b/tests/simple/07_while.c @@ -0,0 +1,5 @@ +int main() { + int i = 0; + while (i < 10) i = i + 1; + return i; +} diff --git a/tests/simple/08_do_while.c b/tests/simple/08_do_while.c new file mode 100644 index 0000000..a5e3245 --- /dev/null +++ b/tests/simple/08_do_while.c @@ -0,0 +1,12 @@ +// #include + +int main() { + int i = 0; + int pow = 1; + do { + pow = pow * 2; + i = i + 1; + } while(i < 7); + // printf("%d", pow); + return pow; +} diff --git a/tests/simple/09_for.c b/tests/simple/09_for.c new file mode 100644 index 0000000..7095227 --- /dev/null +++ b/tests/simple/09_for.c @@ -0,0 +1,7 @@ +int main() { + int num = 0; + for (int i = 0; i < 10; i += 1) { + num = num + 1; + } + return num; +} diff --git a/tests/simple/10_main.c b/tests/simple/10_main.c new file mode 100644 index 0000000..045475b --- /dev/null +++ b/tests/simple/10_main.c @@ -0,0 +1,7 @@ +int add(int a, int b) { + return a + b; +} + +int main(void) { + return add(1, 2); +} diff --git a/tests/simple/11_recursive.c b/tests/simple/11_recursive.c new file mode 100644 index 0000000..e526db0 --- /dev/null +++ b/tests/simple/11_recursive.c @@ -0,0 +1,18 @@ +// #include + +int factorial(int num); + +int main() { + int num = 5; + int result = factorial(num); + // printf("%d", result); + return result; +} + +int factorial(int num) { + if (num == 0) { + return 1; + } else { + return num * factorial(num - 1); + } +} diff --git a/tests/simple/Makefile b/tests/simple/Makefile new file mode 100644 index 0000000..3b05474 --- /dev/null +++ b/tests/simple/Makefile @@ -0,0 +1,30 @@ +# VM := ../../rv32-vm +# CC := ../../ccompiler +# STD_CC := gcc + +# TESTS := $(wildcard *.c) + +# # 定义所有测试目标 +# TEST_TARGETS := $(patsubst %.c, %_test, $(TESTS)) + +# all: $(TEST_TARGETS) + +# %_test: %.c +# @$(STD_CC) -g -o $@ $< +# @$(CC) $< flat.bin +# @./$@ ; ret_gcc=$$? +# @$(VM) flat.bin ; ret_vm=$$? +# @echo "Testing $@" +# @if [ $$ret_gcc -eq $$ret_vm ]; then \ +# echo "$@ passed"; \ +# else \ +# echo "$@ failed: GCC returned $$ret_gcc, VM returned $$ret_vm"; \ +# exit 1; \ +# fi + +# clean: +# rm -f $(TEST_TARGETS) flat.bin + +# .PHONY: all clean +all: + python test.py \ No newline at end of file diff --git a/tests/simple/hard_01.c b/tests/simple/hard_01.c new file mode 100644 index 0000000..85ad74e --- /dev/null +++ b/tests/simple/hard_01.c @@ -0,0 +1,6 @@ +int main() { + int a, b; + a = 1; + b = 2; + return a + b; +} \ No newline at end of file diff --git a/tests/simple/test.py b/tests/simple/test.py new file mode 100644 index 0000000..3dbd496 --- /dev/null +++ b/tests/simple/test.py @@ -0,0 +1,86 @@ +import subprocess +import os +from pathlib import Path + +# 配置参数 +TEST_DIR = Path(".") +CC_PATH = Path("../../src/smcc.exe") +VM_PATH = Path("../rv32-vm.exe") +WORKSPACE = Path(".") # 测试工作目录 + +# 测试用例映射表(示例) +TEST_CASE_MAP = { + "./01_return.c": 65536, + "./02_decl_expr.c": 1, + "./03_decl_init.c": 11, + "./04_if.c": 1, + "./05_else.c": 2, + "./06_fcall.c": 3, + "./07_while.c": 10, + "./08_do_while.c": 128, + "./09_for.c": 10, + "./10_main.c": 3, + "./11_recursive.c": 120, +} + +def run_command(cmd, capture_stderr=True): + """执行命令并捕获stderr""" + result = subprocess.run( + cmd, + cwd=WORKSPACE, + stderr=subprocess.PIPE if capture_stderr else None, + text=True, + timeout=1, + ) + return result.stderr.strip() if capture_stderr else None + +def run_test(test_file, expected): + print(f"\nTesting {test_file}...") + + # 1. 编译生成flat.bin + compile_cmd = [str(CC_PATH), str(test_file)] + compile_err = run_command(compile_cmd) + + if not (WORKSPACE / "flat.bin").exists(): + print(f" Compilation failed: {compile_err}") + return False + + # 2. 执行虚拟机 + vm_cmd = [str(VM_PATH), "flat.bin"] + + # 3. 解析返回值(假设最后一行是返回值) + try: + vm_err = run_command(vm_cmd) + actual = int(vm_err.split()[-1]) + except (ValueError, IndexError) as e: + print(f" Invalid VM output: {vm_err}") + return False + except subprocess.TimeoutExpired: + print(" Timeout expired") + return False + + # 4. 验证结果 + if actual == expected: + print(f" PASSED {test_file}") + return True + else: + print(f" FAILED: Expected {expected}, got {actual}") + return False + +def main(): + passed = 0 + total = 0 + + for test_file, expected in TEST_CASE_MAP.items(): + total += 1 + if run_test(TEST_DIR / test_file, expected): + passed += 1 + + # 清理中间文件 + if (WORKSPACE / "flat.bin").exists(): + os.remove(WORKSPACE / "flat.bin") + + print(f"\nTest Summary: {passed}/{total} passed") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/wc.py b/wc.py new file mode 100644 index 0000000..27a6805 --- /dev/null +++ b/wc.py @@ -0,0 +1,48 @@ +"""统计目录下C/C++文件的行数(write by AI)""" +import os + +def count_lines(file_path): + """统计单个文件的代码行数""" + try: + with open(file_path, 'rb') as f: # 二进制模式读取避免编码问题 + return sum(1 for _ in f) + except UnicodeDecodeError: + print(f"警告:无法解码文件 {file_path}(可能不是文本文件)") + return 0 + except Exception as e: + print(f"读取 {file_path} 出错: {str(e)}") + return 0 + +def scan_files(directory, exclude_dirs=None): + """扫描目录获取所有C/C++文件""" + if exclude_dirs is None: + exclude_dirs = ['.git', 'venv', '__pycache__'] # 默认排除的目录 + + c_files = [] + for root, dirs, files in os.walk(directory): + # 跳过排除目录 + dirs[:] = [d for d in dirs if d not in exclude_dirs] + + for file in files: + if file.endswith(('.c', '.h')): + full_path = os.path.join(root, file) + c_files.append(full_path) + return c_files + +def main(): + """main function""" + target_dir = input("请输入要扫描的目录路径(留空为当前目录): ") or '.' + + files = scan_files(target_dir) + total_lines = 0 + + print("\n统计结果:") + for idx, file in enumerate(files, 1): + lines = count_lines(file) + total_lines += lines + print(f"{idx:4d}. {file} ({lines} 行)") + + print(f"\n总计: {len(files)} 个C/C++文件,共 {total_lines} 行代码") + +if __name__ == "__main__": + main()