├── .clang-format ├── .clang-tidy ├── .github └── workflows │ └── test.yml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── cierra ├── .envrc ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── bacon.toml ├── cierra │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── parser │ │ │ ├── cst2ast.rs │ │ │ ├── mod.rs │ │ │ ├── taurus.rs │ │ │ └── taurus │ │ │ │ ├── Taurus.interp │ │ │ │ ├── Taurus.tokens │ │ │ │ ├── TaurusLexer.interp │ │ │ │ ├── TaurusLexer.tokens │ │ │ │ ├── tauruslexer.rs │ │ │ │ ├── tauruslistener.rs │ │ │ │ └── taurusparser.rs │ │ ├── subst.rs │ │ ├── taurus.rs │ │ ├── utils.rs │ │ └── vcgen.rs │ └── tests │ │ ├── parser.rs │ │ ├── parser │ │ ├── a.c │ │ ├── array.c │ │ ├── bool2int.c │ │ ├── cmp.c │ │ ├── collision.c │ │ ├── comment.c │ │ ├── div.c │ │ ├── do_while.c │ │ ├── mod.c │ │ ├── verify.c │ │ └── while.c │ │ └── snapshots │ │ ├── parser__a.snap │ │ ├── parser__array.snap │ │ ├── parser__bool2int.snap │ │ ├── parser__cmp.snap │ │ ├── parser__collision.snap │ │ ├── parser__comment.snap │ │ ├── parser__div.snap │ │ ├── parser__do_while.snap │ │ ├── parser__mod.snap │ │ ├── parser__verify.snap │ │ └── parser__while.snap ├── flake.lock ├── flake.nix ├── generate.sh ├── grammar │ ├── COPYING │ └── parser │ │ ├── .antlr │ │ ├── Taurus.interp │ │ ├── Taurus.tokens │ │ ├── TaurusLexer.interp │ │ ├── TaurusLexer.java │ │ ├── TaurusLexer.tokens │ │ └── TaurusParser.java │ │ └── Taurus.g4 ├── macro │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── src │ │ ├── generator.rs │ │ ├── lib.rs │ │ ├── parser.rs │ │ └── value.rs │ └── tests │ │ ├── snapshot.rs │ │ └── snapshots │ │ ├── snapshot__bracket_1.snap │ │ ├── snapshot__bracket_2.snap │ │ ├── snapshot__expand.snap │ │ ├── snapshot__long.snap │ │ ├── snapshot__long_nested.snap │ │ ├── snapshot__nested.snap │ │ ├── snapshot__simple.snap │ │ ├── snapshot__symbol_ex.snap │ │ └── snapshot__unquote.snap ├── rustfmt.toml └── sexp │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ ├── lib.rs │ ├── serializer.rs │ └── utils.rs ├── cmake ├── 3rd.cmake ├── add_header.cmake ├── compile_config.cmake ├── config.h.in └── functions.cmake ├── doc └── CMakeLists.txt ├── src ├── CMakeLists.txt ├── codegen │ └── codegen.cpp ├── error │ └── error.cpp ├── include │ ├── ast.h │ ├── codegen.h │ ├── common.h │ ├── error.h │ ├── init.h │ ├── ir.h │ ├── irast.h │ ├── irgen.h │ ├── irlexer.h │ ├── irparser.h │ ├── irtoken.h │ ├── lexical.h │ ├── log.h │ ├── lowirgen.h │ ├── parser.h │ ├── scanner.h │ ├── token.h │ ├── type.h │ ├── typechecker.h │ └── utils.h ├── init.cpp ├── ir │ ├── irgen.cpp │ ├── irlexer.cpp │ ├── irparser.cpp │ └── lowirgen.cpp ├── lexical │ ├── lexical.cpp │ └── token.cpp ├── log.cpp ├── main.cpp ├── parser │ ├── parser.cpp │ └── type.cpp ├── scanner │ └── scanner.cpp ├── test │ ├── test_demo.c │ ├── test_lexical.c │ ├── test_parser.c │ └── test_tyck.c └── typechecker │ ├── eval.cpp │ └── typechecker.cpp ├── test ├── CMakeLists.txt ├── system_test │ └── CMakeLists.txt └── unit_test │ ├── CMakeLists.txt │ └── buffer_base_test.cpp └── tools └── cppcheck-suppressions.xml /.clang-format: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # CMakeLists.txt for Simple-XX/SimpleCompiler. 6 | 7 | --- 8 | # @version clang-format version 15 9 | # @see https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 使用 LLVM 规范 11 | BasedOnStyle: LLVM 12 | ... 13 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # .clang-tidy for Simple-XX/SimpleCompiler. 6 | 7 | --- 8 | Checks: '-*,\ 9 | bugprone-*,\ 10 | clang-analyzer-*,\ 11 | cppcoreguidelines-*,\ 12 | hicpp-*,\ 13 | llvm-*,\ 14 | misc-* 15 | modernize-*,\ 16 | performance-*,\ 17 | portability-*,\ 18 | readability-*,\ 19 | -cppcoreguidelines-pro-type-reinterpret-cast,\ 20 | -bugprone-easily-swappable-parameters,\ 21 | -llvm-header-guard,\ 22 | -misc-non-private-member-variables-in-classes,\ 23 | -cppcoreguidelines-non-private-member-variables-in-classes,\ 24 | -readability-identifier-length,\ 25 | -cppcoreguidelines-avoid-non-const-global-variables' 26 | HeaderFilterRegex: '^${sourceDir}/src' 27 | AnalyzeTemporaryDtors: true 28 | ... 29 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | defaults: 10 | run: 11 | working-directory: ./cierra 12 | 13 | name: Test 14 | 15 | jobs: 16 | fmt: 17 | name: Fmt 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | name: Checkout 🛎️ 22 | - uses: dtolnay/rust-toolchain@nightly 23 | name: Setup Cargo Toolchain 🛎️ 24 | with: 25 | components: rustfmt 26 | - run: cargo fmt --all -- --check 27 | name: Check Code Format 🔧 28 | 29 | lint: 30 | name: Lint 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v3 34 | name: Checkout 🛎️ 35 | - uses: dtolnay/rust-toolchain@stable 36 | name: Setup Cargo Toolchain 🛎️ 37 | with: 38 | components: clippy 39 | - uses: Swatinem/rust-cache@v2 40 | - run: cargo clippy --tests 41 | name: Run Clippy Lints 🔨 42 | 43 | test: 44 | name: Test 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v3 48 | name: Checkout 🛎️ 49 | - uses: dtolnay/rust-toolchain@stable 50 | name: Setup Cargo Toolchain 🛎️ 51 | - uses: Swatinem/rust-cache@v2 52 | - run: cargo test 53 | name: Running Tests 🚀 54 | 55 | build-nix: 56 | name: Build on Nix 57 | runs-on: ubuntu-latest 58 | steps: 59 | - uses: actions/checkout@v3 60 | name: Checkout 🛎️ 61 | - uses: cachix/install-nix-action@v18 62 | name: Install Nix 🛎️ 63 | - uses: cachix/cachix-action@v11 64 | name: Setup Cachix 🛎️ 65 | with: 66 | name: monadlab 67 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 68 | - run: nix build 69 | name: Build on Nix 🚀 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # .gitignore for Simple-XX/SimpleCompiler. 6 | 7 | # Prerequisites 8 | *.d 9 | 10 | # Compiled Object files 11 | *.slo 12 | *.lo 13 | *.o 14 | *.obj 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Compiled Dynamic libraries 21 | *.so 22 | *.dylib 23 | *.dll 24 | 25 | # Fortran module files 26 | *.mod 27 | *.smod 28 | 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | *.lib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | 40 | # VSCode 41 | .vscode 42 | 43 | build 44 | 45 | *.o 46 | .DS_Store 47 | build 48 | cmake-build* 49 | .gdbinit 50 | tools/opensbi/build 51 | .vscode 52 | .idea 53 | 3rd 54 | Doxyfile 55 | src/include/config.h 56 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # CMakeLists.txt for Simple-XX/SimpleCompiler. 6 | 7 | # 设置最小 cmake 版本 8 | cmake_minimum_required(VERSION 3.27 FATAL_ERROR) 9 | 10 | # 设置项目名与版本 11 | project(SimpleCompiler 12 | VERSION 0.0.1) 13 | 14 | # 禁止原地编译 15 | if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR}) 16 | # 如果你看到这句话,cmake 此时已经在根目录下生成了一些临时文件,你需要删除它们 17 | # CMakeFiles, CMakeCache.txt 18 | message( 19 | FATAL_ERROR 20 | "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there." 21 | ) 22 | endif () 23 | 24 | # 设置辅助 cmake 脚本路径 25 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 26 | 27 | # 导入函数 28 | include(functions) 29 | # 导入第三方依赖 30 | include(3rd) 31 | # 导入编译配置 32 | include(compile_config) 33 | 34 | # 日志文件路径 35 | set(LOG_FILE_PATH "${EXECUTABLE_OUTPUT_PATH}/logs/SimpleCompilerLog.log") 36 | # 日志文件大小 37 | set(LOG_FILE_MAX_SIZE 1024*1024*4) 38 | # 日志文件数量 39 | set(LOG_FILE_MAX_COUNT 8) 40 | # 生成配置头文件 41 | configure_file( 42 | "${PROJECT_SOURCE_DIR}/cmake/config.h.in" 43 | "${PROJECT_SOURCE_DIR}/src/include/config.h" 44 | ) 45 | 46 | # 添加要编译的目录 47 | add_subdirectory(src) 48 | add_subdirectory(test) 49 | add_subdirectory(doc) 50 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 27, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "std", 11 | "description": "This preset makes sure the project actually builds with at least the specified standard", 12 | "hidden": true, 13 | "cacheVariables": { 14 | "CMAKE_C_EXTENSIONS": "OFF", 15 | "CMAKE_C_STANDARD": "17", 16 | "CMAKE_C_STANDARD_REQUIRED": "ON", 17 | "CMAKE_CXX_EXTENSIONS": "OFF", 18 | "CMAKE_CXX_STANDARD": "20", 19 | "CMAKE_CXX_STANDARD_REQUIRED": "ON" 20 | } 21 | }, 22 | { 23 | "name": "configurePresets_base", 24 | "hidden": true, 25 | "inherits": [ 26 | "std" 27 | ], 28 | "displayName": "configurePresets_base", 29 | "description": "base configurePresets", 30 | "binaryDir": "${sourceDir}/build", 31 | "cacheVariables": { 32 | "CMAKE_EXPORT_COMPILE_COMMANDS": { 33 | "type": "BOOL", 34 | "value": "ON" 35 | }, 36 | "EXECUTABLE_OUTPUT_PATH": { 37 | "type": "STRING", 38 | "value": "${sourceDir}/build/bin" 39 | }, 40 | "LIBRARY_OUTPUT_PATH": { 41 | "type": "STRING", 42 | "value": "${sourceDir}/build/lib" 43 | }, 44 | "COVERAGE_OUTPUT_DIR": { 45 | "type": "STRING", 46 | "value": "${sourceDir}/build/coverage" 47 | } 48 | } 49 | }, 50 | { 51 | "name": "build", 52 | "hidden": false, 53 | "inherits": [ 54 | "configurePresets_base" 55 | ], 56 | "displayName": "build", 57 | "description": "build" 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zone.N 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpileCompiler 2 | 3 | SimpileCompiler 4 | 编译器 5 | 6 | ## 使用方法 7 | 8 | ```shell 9 | git clone https://github.com/Simple-XX/SimpleCompiler.git 10 | cd SimpleCompiler 11 | mkdir build 12 | cd build 13 | cmake .. 14 | # 查看帮助信息 15 | ./bin/SimpleCompiler -h 16 | # 词法检查,会将结果输出到命令行窗口 17 | ./bin/SimpleCompiler ../src/test/test_lexical.c -o 1 18 | ``` 19 | 20 | ## 参考资料 21 | 22 | - 自己动手写编译器、连接器,王博俊 张宇 清华大学出版社 23 | 24 | - [怎样写一个解释器](http://www.yinwang.org/blog-cn/2012/08/01/interpreter) 25 | 26 | - 自己动手构造编译系统 编译、汇编与链接,范志东 张琼声 机械工业出版社 27 | 28 | -------------------------------------------------------------------------------- /cierra/.envrc: -------------------------------------------------------------------------------- 1 | nix_direnv_watch_file ./generate.sh 2 | use flake -------------------------------------------------------------------------------- /cierra/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | generator/ 13 | 14 | .direnv 15 | -------------------------------------------------------------------------------- /cierra/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["cierra", "macro", "sexp"] -------------------------------------------------------------------------------- /cierra/README.md: -------------------------------------------------------------------------------- 1 | # Cierra 2 | 3 | A simple deductive prover. 4 | 5 | ## Get Started 6 | 7 | ### Nix 8 | 9 | Make sure you have [direnv](https://github.com/nix-community/nix-direnv) installed. 10 | 11 | #### VSCode 12 | 13 | 1. Install the [Direnv](https://marketplace.visualstudio.com/items?itemName=mkhl.direnv) extension. 14 | 15 | 2. Allow `.envrc`. 16 | 17 | #### Clion 18 | 19 | 1. Install the [Direnv integration](https://plugins.jetbrains.com/plugin/15285-direnv-integration) plugin. 20 | 21 | 2. Allow `.envrc`. 22 | 23 | 3. Restart IDE. `Import direnv` when you are prompted to. 24 | 25 | 4. Set `Language and Frameworks > Rust > Toolchain location` to `/nix/store/`. 26 | 27 | 5. Clear caches and restart IDE. Remember to `Import direnv` each time you start the IDE. 28 | 29 | ### Manual 30 | 31 | Ensure you have the latest stable rust toolchain. 32 | 33 | Optionally, 34 | 35 | - install the nightly rust toolchain because our rustfmt configuration is nightly only. 36 | - install jre to generate grammar files. 37 | 38 | ## Development 39 | 40 | ### Regenerate grammar files 41 | 42 | If you've configured the project with `Nix`, 43 | 44 | ```bash 45 | $ generate 46 | ``` 47 | 48 | otherwise, 49 | 50 | ```bash 51 | $ ./generate.sh 52 | ``` 53 | 54 | > Please ensure the [antlr-rust fork](https://github.com/rrevenantt/antlr4rust/releases) of `antlr` is installed and available at `antlr4`. 55 | 56 | ### Format code 57 | 58 | If you've configured the project with `Nix`, 59 | 60 | ```bash 61 | $ format 62 | ``` 63 | 64 | otherwise, 65 | 66 | ```bash 67 | $ cargo +nightly fmt 68 | ``` 69 | 70 | ## License 71 | 72 | If not otherwise specified, all files in this repository are licensed under the [GPLv3](./LICENSE) license, 73 | except for files in the following folders: 74 | 75 | - `sexp` - [Apache-2.0](./sexp/LICENSE-APACHE) or [MIT](./sexp/LICENSE-MIT) 76 | - `macro` - [Apache-2.0](./macro/LICENSE-APACHE) or [MIT](./macro/LICENSE-MIT) 77 | 78 | Files under the `grammar` folder are adopted from [CMinor-Verifier](https://github.com/thufv/CMinor-Verifier) project 79 | and licensed under [GPLv3](./grammar/COPYING). 80 | All modifications are stated in comments of the files. 81 | 82 | ## References 83 | 84 | - [CMinor-Verifier](https://github.com/thufv/CMinor-Verifier) - Course lab project of [Software Analysis and Verification (zh-cn)](https://feihe.github.io/ProgramVerification/s22), Tsinghua University. -------------------------------------------------------------------------------- /cierra/bacon.toml: -------------------------------------------------------------------------------- 1 | # This is a configuration file for the bacon tool 2 | # More info at https://github.com/Canop/bacon 3 | 4 | default_job = "clippy" 5 | 6 | [keybindings] 7 | k = "scroll-lines(-1)" 8 | j = "scroll-lines(1)" 9 | c = "job:clippy" 10 | t = "job:test" 11 | f = "job:fix" 12 | shift-F9 = "toggle-backtrace" 13 | ctrl-r = "toggle-raw-output" 14 | ctrl-u = "scroll-page(-1)" 15 | ctrl-d = "scroll-page(1)" 16 | 17 | [jobs] 18 | 19 | [jobs.clippy] 20 | command = ["cargo", "clippy", "--workspace", "--tests", "--color", "always", "--", "-W", "clippy::all", "-W", "clippy::nursery", "-W", "clippy::pedantic"] 21 | watch = ["src", "tests"] 22 | need_stdout = false 23 | 24 | [jobs.test] 25 | command = ["cargo", "test", "--color", "always"] 26 | need_stdout = true 27 | watch = ["tests"] 28 | 29 | [jobs.doc] 30 | command = ["cargo", "doc", "--color", "always", "--no-deps"] 31 | need_stdout = false 32 | 33 | [jobs.fix] 34 | command = ["cargo", "clippy", "--fix", "--allow-staged", "--allow-dirty", "--workspace", "--tests", "--color", "always", "--", "-W", "clippy::all", "-W", "clippy::nursery", "-W", "clippy::pedantic"] 35 | need_stdout = false 36 | on_success = "job:clippy" -------------------------------------------------------------------------------- /cierra/cierra/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cierra" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [[bin]] 7 | name = "cierra-cli" 8 | path = "src/main.rs" 9 | 10 | [lib] 11 | name = "cierra" 12 | path = "src/lib.rs" 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | antlr-rust = "0.3.0-beta" 17 | array-init = "2.0" 18 | clap = { version = "4.0", features = ["derive"] } 19 | derive_more = "0.99.17" 20 | internment = "0.7" 21 | itertools = "0.10" 22 | sexp = { path = "../sexp" } 23 | sexp_macro = { path = "../macro" } 24 | tracing = "0.1" 25 | tracing-subscriber = "0.3" 26 | 27 | [dev-dependencies] 28 | glob = "0.3" 29 | insta = "1.19" 30 | -------------------------------------------------------------------------------- /cierra/cierra/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::missing_panics_doc, clippy::module_name_repetitions)] 2 | 3 | #[macro_use] 4 | mod utils; 5 | mod parser; 6 | pub mod subst; 7 | pub mod taurus; 8 | pub mod vcgen; 9 | -------------------------------------------------------------------------------- /cierra/cierra/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, path::PathBuf}; 2 | 3 | use cierra::{ 4 | taurus::{Symbol, Taurus}, 5 | vcgen::{func_to_vc, Context}, 6 | }; 7 | use clap::Parser; 8 | use sexp::SexpDisplay; 9 | 10 | #[derive(Debug, Parser)] 11 | struct Args { 12 | filename: PathBuf, 13 | entrypoint: Symbol, 14 | } 15 | 16 | fn main() { 17 | tracing_subscriber::fmt::init(); 18 | let args = Args::parse(); 19 | let src = fs::read_to_string(args.filename).unwrap(); 20 | let prog = Taurus::parse(&src); 21 | let func = prog.find_func(args.entrypoint).unwrap().clone(); 22 | let mut ctx = Context::default(); 23 | let vc = func_to_vc(&mut ctx, func); 24 | eprintln!("{}", vc.pretty_display()); 25 | } 26 | -------------------------------------------------------------------------------- /cierra/cierra/src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod taurus; 2 | // mod visitors; 3 | pub mod cst2ast; 4 | -------------------------------------------------------------------------------- /cierra/cierra/src/parser/taurus.rs: -------------------------------------------------------------------------------- 1 | pub mod tauruslexer; 2 | pub mod tauruslistener; 3 | pub mod taurusparser; 4 | -------------------------------------------------------------------------------- /cierra/cierra/src/parser/taurus/Taurus.tokens: -------------------------------------------------------------------------------- 1 | VOID=1 2 | STRUCT=2 3 | LPAR=3 4 | RPAR=4 5 | LBRACE=5 6 | RBRACE=6 7 | COMMA=7 8 | SEMICOLON=8 9 | LBRACKET=9 10 | RBRACKET=10 11 | PERIOD=11 12 | INT=12 13 | FLOAT=13 14 | BOOL=14 15 | IF=15 16 | ELSE=16 17 | BREAK=17 18 | CONTINUE=18 19 | RETURN=19 20 | WHILE=20 21 | DO=21 22 | FOR=22 23 | ASSIGN=23 24 | EQ=24 25 | NE=25 26 | LE=26 27 | LT=27 28 | GE=28 29 | GT=29 30 | ADD=30 31 | MINUS=31 32 | MUL=32 33 | DIV=33 34 | NOT=34 35 | MOD=35 36 | AND=36 37 | OR=37 38 | EXPR_TRUE=38 39 | EXPR_FALSE=39 40 | ANNO_TRUE=40 41 | ANNO_FALSE=41 42 | RESULT=42 43 | LENGTH=43 44 | OLD=44 45 | WITH=45 46 | IMPLY=46 47 | EQUIV=47 48 | XOR=48 49 | FORALL=49 50 | EXISTS=50 51 | BOOLEAN=51 52 | INTEGER=52 53 | REAL=53 54 | REQUIRES=54 55 | DECREASES=55 56 | ENSURES=56 57 | ASSERT=57 58 | LOOP=58 59 | INVARIANT=59 60 | VARIANT=60 61 | PREDICATE=61 62 | VALID=62 63 | APOSTROPHE=63 64 | INT_CONSTANT=64 65 | FLOAT_CONSTANT=65 66 | IDENT=66 67 | COMMENT=67 68 | LINE_COMMENT=68 69 | ANNOT_START=69 70 | ANNOT_END=70 71 | LINE_ANNOT_START=71 72 | AT=72 73 | LINEEND=73 74 | WS=74 75 | 'void'=1 76 | 'struct'=2 77 | '('=3 78 | ')'=4 79 | '{'=5 80 | '}'=6 81 | ','=7 82 | ';'=8 83 | '['=9 84 | ']'=10 85 | '.'=11 86 | 'int'=12 87 | 'float'=13 88 | 'bool'=14 89 | 'if'=15 90 | 'else'=16 91 | 'break'=17 92 | 'continue'=18 93 | 'return'=19 94 | 'while'=20 95 | 'do'=21 96 | 'for'=22 97 | '='=23 98 | '=='=24 99 | '!='=25 100 | '<='=26 101 | '<'=27 102 | '>='=28 103 | '>'=29 104 | '+'=30 105 | '-'=31 106 | '*'=32 107 | '/'=33 108 | '!'=34 109 | '%'=35 110 | '&&'=36 111 | '||'=37 112 | 'true'=38 113 | 'false'=39 114 | '\\true'=40 115 | '\\false'=41 116 | '\\result'=42 117 | '\\length'=43 118 | '\\old'=44 119 | '\\with'=45 120 | '==>'=46 121 | '<==>'=47 122 | '^^'=48 123 | '\\forall'=49 124 | '\\exists'=50 125 | 'boolean'=51 126 | 'integer'=52 127 | 'real'=53 128 | 'requires'=54 129 | 'decreases'=55 130 | 'ensures'=56 131 | 'assert'=57 132 | 'loop'=58 133 | 'invariant'=59 134 | 'variant'=60 135 | 'predicate'=61 136 | '\\valid'=62 137 | '..'=63 138 | '/*@'=69 139 | '*/'=70 140 | '//@'=71 141 | '@'=72 142 | -------------------------------------------------------------------------------- /cierra/cierra/src/parser/taurus/TaurusLexer.tokens: -------------------------------------------------------------------------------- 1 | VOID=1 2 | STRUCT=2 3 | LPAR=3 4 | RPAR=4 5 | LBRACE=5 6 | RBRACE=6 7 | COMMA=7 8 | SEMICOLON=8 9 | LBRACKET=9 10 | RBRACKET=10 11 | PERIOD=11 12 | INT=12 13 | FLOAT=13 14 | BOOL=14 15 | IF=15 16 | ELSE=16 17 | BREAK=17 18 | CONTINUE=18 19 | RETURN=19 20 | WHILE=20 21 | DO=21 22 | FOR=22 23 | ASSIGN=23 24 | EQ=24 25 | NE=25 26 | LE=26 27 | LT=27 28 | GE=28 29 | GT=29 30 | ADD=30 31 | MINUS=31 32 | MUL=32 33 | DIV=33 34 | NOT=34 35 | MOD=35 36 | AND=36 37 | OR=37 38 | EXPR_TRUE=38 39 | EXPR_FALSE=39 40 | ANNO_TRUE=40 41 | ANNO_FALSE=41 42 | RESULT=42 43 | LENGTH=43 44 | OLD=44 45 | WITH=45 46 | IMPLY=46 47 | EQUIV=47 48 | XOR=48 49 | FORALL=49 50 | EXISTS=50 51 | BOOLEAN=51 52 | INTEGER=52 53 | REAL=53 54 | REQUIRES=54 55 | DECREASES=55 56 | ENSURES=56 57 | ASSERT=57 58 | LOOP=58 59 | INVARIANT=59 60 | VARIANT=60 61 | PREDICATE=61 62 | VALID=62 63 | APOSTROPHE=63 64 | INT_CONSTANT=64 65 | FLOAT_CONSTANT=65 66 | IDENT=66 67 | COMMENT=67 68 | LINE_COMMENT=68 69 | ANNOT_START=69 70 | ANNOT_END=70 71 | LINE_ANNOT_START=71 72 | AT=72 73 | LINEEND=73 74 | WS=74 75 | 'void'=1 76 | 'struct'=2 77 | '('=3 78 | ')'=4 79 | '{'=5 80 | '}'=6 81 | ','=7 82 | ';'=8 83 | '['=9 84 | ']'=10 85 | '.'=11 86 | 'int'=12 87 | 'float'=13 88 | 'bool'=14 89 | 'if'=15 90 | 'else'=16 91 | 'break'=17 92 | 'continue'=18 93 | 'return'=19 94 | 'while'=20 95 | 'do'=21 96 | 'for'=22 97 | '='=23 98 | '=='=24 99 | '!='=25 100 | '<='=26 101 | '<'=27 102 | '>='=28 103 | '>'=29 104 | '+'=30 105 | '-'=31 106 | '*'=32 107 | '/'=33 108 | '!'=34 109 | '%'=35 110 | '&&'=36 111 | '||'=37 112 | 'true'=38 113 | 'false'=39 114 | '\\true'=40 115 | '\\false'=41 116 | '\\result'=42 117 | '\\length'=43 118 | '\\old'=44 119 | '\\with'=45 120 | '==>'=46 121 | '<==>'=47 122 | '^^'=48 123 | '\\forall'=49 124 | '\\exists'=50 125 | 'boolean'=51 126 | 'integer'=52 127 | 'real'=53 128 | 'requires'=54 129 | 'decreases'=55 130 | 'ensures'=56 131 | 'assert'=57 132 | 'loop'=58 133 | 'invariant'=59 134 | 'variant'=60 135 | 'predicate'=61 136 | '\\valid'=62 137 | '..'=63 138 | '/*@'=69 139 | '*/'=70 140 | '//@'=71 141 | '@'=72 142 | -------------------------------------------------------------------------------- /cierra/cierra/src/subst.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | taurus::{ 3 | logic::{ArithTerm, Binder, Pred, PredCmp}, 4 | Symbol, 5 | }, 6 | utils::{boxed, mk_subst}, 7 | }; 8 | 9 | pub trait Substitutable { 10 | /// Returns `self[y/x]`. 11 | #[must_use] 12 | fn subst(&self, y: ArithTerm, x: Symbol) -> Self; 13 | } 14 | 15 | impl Substitutable for Pred { 16 | fn subst(&self, y: ArithTerm, x: Symbol) -> Self { 17 | let c_subst = mk_subst(y.clone(), x); 18 | let p_subst = mk_subst(y, x); 19 | match self { 20 | Self::TT => Self::TT, 21 | Self::Bot => Self::Bot, 22 | Self::Cmp(c) => Self::Cmp(c_subst(c)), 23 | Self::Call(..) => todo!("Inter-procedural substitution"), 24 | Self::Con(p1, p2) => Self::Con(boxed(p_subst(p1)), boxed(p_subst(p2))), 25 | Self::Dis(p1, p2) => Self::Dis(boxed(p_subst(p1)), boxed(p_subst(p2))), 26 | Self::Imply(p1, p2) => Self::Imply(boxed(p_subst(p1)), boxed(p_subst(p2))), 27 | Self::Iff(p1, p2) => Self::Iff(boxed(p_subst(p1)), boxed(p_subst(p2))), 28 | Self::Not(p) => Self::Not(boxed(p_subst(p))), 29 | Self::Xor(p1, p2) => Self::Xor(boxed(p_subst(p1)), boxed(p_subst(p2))), 30 | Self::Length { .. } => todo!("Length substitution"), 31 | Self::Quant(q, xs, p) => Self::Quant( 32 | *q, 33 | xs.clone(), 34 | if xs.iter().any(|Binder(_ty, x_)| *x_ == x) { 35 | p.clone() 36 | } else { 37 | boxed(p_subst(p)) 38 | }, 39 | ), 40 | } 41 | } 42 | } 43 | 44 | impl Substitutable for PredCmp { 45 | fn subst(&self, y: ArithTerm, x: Symbol) -> Self { 46 | let subst = mk_subst(y.clone(), x); 47 | let subst_ = mk_subst(y, x); 48 | match self { 49 | Self::Cmp(op, e1, e2) => Self::Cmp(*op, boxed(subst(e1)), subst_(e2)), 50 | Self::ArithTerm(t) => Self::ArithTerm(subst_(t)), 51 | } 52 | } 53 | } 54 | 55 | impl Substitutable for ArithTerm { 56 | #[allow(clippy::many_single_char_names)] 57 | fn subst(&self, y: ArithTerm, x: Symbol) -> Self { 58 | use ArithTerm::{Binary, Const, Mem, Read, Res, Store, Unary, Var}; 59 | let subst = mk_subst(y.clone(), x); 60 | match self { 61 | Var(x_) if x == *x_ => y, 62 | Var(_) | Res | Const(_) => self.clone(), 63 | Read(e, i) => Read(boxed(subst(e)), boxed(subst(i))), 64 | Store(e, i, v) => Store(boxed(subst(e)), boxed(subst(i)), boxed(subst(v))), 65 | Mem(e, m) => Mem(boxed(subst(e)), *m), 66 | Unary(op, e) => Unary(*op, boxed(subst(e))), 67 | Binary(op, e1, e2) => Binary(*op, boxed(subst(e1)), boxed(subst(e2))), 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cierra/cierra/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | subst::Substitutable, 3 | taurus::{logic::ArithTerm, Symbol}, 4 | }; 5 | 6 | pub fn mk_subst(y: ArithTerm, x: Symbol) -> impl Fn(&T) -> T + 'static 7 | where 8 | T: Substitutable, 9 | { 10 | move |t| t.subst(y.clone(), x) 11 | } 12 | 13 | pub fn boxed(b: T) -> Box { 14 | Box::new(b) 15 | } 16 | 17 | #[macro_export] 18 | macro_rules! vect { 19 | (@push $v:ident, ..$e:expr, $($tt:tt)+) => { 20 | { 21 | $v.extend($e); 22 | vect!(@push $v, $($tt)+) 23 | } 24 | }; 25 | (@push $v:ident, $e:expr, $($tt:tt)+) => { 26 | { 27 | #[allow(clippy::vec_init_then_push)] 28 | $v.push($e); 29 | vect!(@push $v, $($tt)+) 30 | } 31 | }; 32 | (@push $v:ident, .. $e:expr) => { 33 | $v.extend($e) 34 | }; 35 | (@push $v:ident, $e:expr) => { 36 | { 37 | #[allow(clippy::vec_init_then_push)] 38 | $v.push($e) 39 | } 40 | }; 41 | ([$($tt:tt)+]) => { 42 | { 43 | let mut v = Vec::new(); 44 | vect!(@push v, $($tt)+); 45 | v 46 | } 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /cierra/cierra/tests/parser.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use cierra::taurus::Taurus; 4 | use insta::assert_debug_snapshot; 5 | 6 | #[test] 7 | fn must_parse() { 8 | let test_files = glob::glob("tests/parser/*.c").unwrap(); 9 | for file in test_files { 10 | let path = file.unwrap(); 11 | let name = path.file_stem().unwrap().to_str().unwrap(); 12 | let content = fs::read_to_string(&path).unwrap(); 13 | let ast = Taurus::parse(&content); 14 | assert_debug_snapshot!(name, ast); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/a.c: -------------------------------------------------------------------------------- 1 | //@ predicate sorted(integer a[]) = \forall integer i; 0 <= i < \length(a) - 1 ==> a[i] <= a[i + 1]; 2 | 3 | /*@ 4 | requires sorted(a); 5 | */ 6 | int search(int a[], int x) { 7 | return 0; 8 | } -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/array.c: -------------------------------------------------------------------------------- 1 | /*@ requires \valid(a+(0..len)) && len >= 2; 2 | @ ensures \result == a[0] + a[1] + a[2]; 3 | */ 4 | int fun(int a[], int len) { 5 | return a[0] + a[1] + a[2]; 6 | } 7 | -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/bool2int.c: -------------------------------------------------------------------------------- 1 | //@ ensures \result == 2; 2 | int main() { 3 | return (1 == 1) + (0 != 1); 4 | } -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/cmp.c: -------------------------------------------------------------------------------- 1 | /*@ 2 | requires \true; 3 | ensures \result <= a <= b; 4 | */ 5 | int cmp(int a, int b) { 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/collision.c: -------------------------------------------------------------------------------- 1 | //@ ensures \true; 2 | void collide() { 3 | int x = 0; 4 | int y = 0; 5 | //@ loop invariant x <= 5; 6 | while (x < 5) { 7 | int y = x; 8 | x = y + 1; 9 | } 10 | //@ assert y == 0; 11 | //@ assert x == 5; 12 | } -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/comment.c: -------------------------------------------------------------------------------- 1 | // 2 | /**/ 3 | //123 4 | /*233*/ 5 | -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/div.c: -------------------------------------------------------------------------------- 1 | //@ ensures \result == 1; 2 | int main() { 3 | return 5 / 3; 4 | } 5 | -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/do_while.c: -------------------------------------------------------------------------------- 1 | /*@ 2 | @ requires \true; 3 | @ ensures \true; 4 | */ 5 | void fun() { 6 | int x = 0; 7 | //@ loop invariant x < 1; 8 | do 9 | { 10 | x = x + 1; 11 | } while(x < 1); 12 | //@ assert x == 1; 13 | } -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/mod.c: -------------------------------------------------------------------------------- 1 | //@ ensures \result == 2; 2 | int main() { 3 | return 5 % -3; 4 | } 5 | -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/verify.c: -------------------------------------------------------------------------------- 1 | /*@ requires n >= 0; 2 | @ ensures \true; 3 | */ 4 | int verify(int n) { 5 | int x = n; 6 | int y = 0; 7 | /*@ loop invariant x >= 0; 8 | @ loop invariant x + y == n; 9 | */ 10 | while (x > 0) { 11 | x = x - 1; 12 | y = y + 1; 13 | } 14 | //@ assert y == n; 15 | return y; 16 | } -------------------------------------------------------------------------------- /cierra/cierra/tests/parser/while.c: -------------------------------------------------------------------------------- 1 | /*@ requires \true; 2 | @ ensures \true; 3 | */ 4 | void fun() { 5 | int x = 0; 6 | //@ loop invariant x < 1; 7 | while (x < 1) 8 | x = x + 1; 9 | //@ assert x == 1; 10 | } 11 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__a.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Pred( 8 | PredDef { 9 | name: Symbol( 10 | "sorted", 11 | ), 12 | vars: [ 13 | ( 14 | Array( 15 | Int, 16 | ), 17 | Symbol( 18 | "a", 19 | ), 20 | ), 21 | ], 22 | body: Quant( 23 | Forall, 24 | [ 25 | Binder( 26 | Int, 27 | Symbol( 28 | "i", 29 | ), 30 | ), 31 | ], 32 | Imply( 33 | Cmp( 34 | Cmp( 35 | Lt, 36 | Cmp( 37 | Le, 38 | ArithTerm( 39 | Const( 40 | Int( 41 | 0, 42 | ), 43 | ), 44 | ), 45 | Var( 46 | Symbol( 47 | "i", 48 | ), 49 | ), 50 | ), 51 | Binary( 52 | Minus, 53 | Var( 54 | Symbol( 55 | "a", 56 | ), 57 | ), 58 | Const( 59 | Int( 60 | 1, 61 | ), 62 | ), 63 | ), 64 | ), 65 | ), 66 | Cmp( 67 | Cmp( 68 | Le, 69 | ArithTerm( 70 | Read( 71 | Var( 72 | Symbol( 73 | "a", 74 | ), 75 | ), 76 | Var( 77 | Symbol( 78 | "i", 79 | ), 80 | ), 81 | ), 82 | ), 83 | Read( 84 | Var( 85 | Symbol( 86 | "a", 87 | ), 88 | ), 89 | Binary( 90 | Plus, 91 | Var( 92 | Symbol( 93 | "i", 94 | ), 95 | ), 96 | Const( 97 | Int( 98 | 1, 99 | ), 100 | ), 101 | ), 102 | ), 103 | ), 104 | ), 105 | ), 106 | ), 107 | }, 108 | ), 109 | Func( 110 | FuncDef { 111 | name: Symbol( 112 | "search", 113 | ), 114 | contract: Contract { 115 | requires: [ 116 | Call( 117 | Symbol( 118 | "sorted", 119 | ), 120 | [ 121 | Arith( 122 | Var( 123 | Symbol( 124 | "a", 125 | ), 126 | ), 127 | ), 128 | ], 129 | ), 130 | ], 131 | ensures: [], 132 | }, 133 | ret_ty: Atom( 134 | Int, 135 | ), 136 | vars: [ 137 | ( 138 | Array( 139 | Int, 140 | ), 141 | Symbol( 142 | "a", 143 | ), 144 | ), 145 | ( 146 | Atom( 147 | Int, 148 | ), 149 | Symbol( 150 | "x", 151 | ), 152 | ), 153 | ], 154 | decl_stmts: [ 155 | Stmt( 156 | Ret( 157 | Some( 158 | Const( 159 | Int( 160 | 0, 161 | ), 162 | ), 163 | ), 164 | ), 165 | ), 166 | ], 167 | }, 168 | ), 169 | ], 170 | } 171 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__array.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "fun", 11 | ), 12 | contract: Contract { 13 | requires: [ 14 | Con( 15 | Length { 16 | base: Symbol( 17 | "a", 18 | ), 19 | start: 0, 20 | end: Var( 21 | Symbol( 22 | "len", 23 | ), 24 | ), 25 | }, 26 | Cmp( 27 | Cmp( 28 | Ge, 29 | ArithTerm( 30 | Var( 31 | Symbol( 32 | "len", 33 | ), 34 | ), 35 | ), 36 | Const( 37 | Int( 38 | 2, 39 | ), 40 | ), 41 | ), 42 | ), 43 | ), 44 | ], 45 | ensures: [ 46 | Cmp( 47 | Cmp( 48 | Eq, 49 | ArithTerm( 50 | Res, 51 | ), 52 | Binary( 53 | Plus, 54 | Binary( 55 | Plus, 56 | Read( 57 | Var( 58 | Symbol( 59 | "a", 60 | ), 61 | ), 62 | Const( 63 | Int( 64 | 0, 65 | ), 66 | ), 67 | ), 68 | Read( 69 | Var( 70 | Symbol( 71 | "a", 72 | ), 73 | ), 74 | Const( 75 | Int( 76 | 1, 77 | ), 78 | ), 79 | ), 80 | ), 81 | Read( 82 | Var( 83 | Symbol( 84 | "a", 85 | ), 86 | ), 87 | Const( 88 | Int( 89 | 2, 90 | ), 91 | ), 92 | ), 93 | ), 94 | ), 95 | ), 96 | ], 97 | }, 98 | ret_ty: Atom( 99 | Int, 100 | ), 101 | vars: [ 102 | ( 103 | Array( 104 | Int, 105 | ), 106 | Symbol( 107 | "a", 108 | ), 109 | ), 110 | ( 111 | Atom( 112 | Int, 113 | ), 114 | Symbol( 115 | "len", 116 | ), 117 | ), 118 | ], 119 | decl_stmts: [ 120 | Stmt( 121 | Ret( 122 | Some( 123 | Binary( 124 | Arith( 125 | Plus, 126 | ), 127 | Binary( 128 | Arith( 129 | Plus, 130 | ), 131 | Read( 132 | Var( 133 | Symbol( 134 | "a", 135 | ), 136 | ), 137 | Const( 138 | Int( 139 | 0, 140 | ), 141 | ), 142 | ), 143 | Read( 144 | Var( 145 | Symbol( 146 | "a", 147 | ), 148 | ), 149 | Const( 150 | Int( 151 | 1, 152 | ), 153 | ), 154 | ), 155 | ), 156 | Read( 157 | Var( 158 | Symbol( 159 | "a", 160 | ), 161 | ), 162 | Const( 163 | Int( 164 | 2, 165 | ), 166 | ), 167 | ), 168 | ), 169 | ), 170 | ), 171 | ), 172 | ], 173 | }, 174 | ), 175 | ], 176 | } 177 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__bool2int.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "main", 11 | ), 12 | contract: Contract { 13 | requires: [], 14 | ensures: [ 15 | Cmp( 16 | Cmp( 17 | Eq, 18 | ArithTerm( 19 | Res, 20 | ), 21 | Const( 22 | Int( 23 | 2, 24 | ), 25 | ), 26 | ), 27 | ), 28 | ], 29 | }, 30 | ret_ty: Atom( 31 | Int, 32 | ), 33 | vars: [], 34 | decl_stmts: [ 35 | Stmt( 36 | Ret( 37 | Some( 38 | Binary( 39 | Arith( 40 | Plus, 41 | ), 42 | Binary( 43 | Logic( 44 | Cmp( 45 | Eq, 46 | ), 47 | ), 48 | Const( 49 | Int( 50 | 1, 51 | ), 52 | ), 53 | Const( 54 | Int( 55 | 1, 56 | ), 57 | ), 58 | ), 59 | Binary( 60 | Logic( 61 | Cmp( 62 | Neq, 63 | ), 64 | ), 65 | Const( 66 | Int( 67 | 0, 68 | ), 69 | ), 70 | Const( 71 | Int( 72 | 1, 73 | ), 74 | ), 75 | ), 76 | ), 77 | ), 78 | ), 79 | ), 80 | ], 81 | }, 82 | ), 83 | ], 84 | } 85 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__cmp.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "cmp", 11 | ), 12 | contract: Contract { 13 | requires: [ 14 | TT, 15 | ], 16 | ensures: [ 17 | Cmp( 18 | Cmp( 19 | Le, 20 | Cmp( 21 | Le, 22 | ArithTerm( 23 | Res, 24 | ), 25 | Var( 26 | Symbol( 27 | "a", 28 | ), 29 | ), 30 | ), 31 | Var( 32 | Symbol( 33 | "b", 34 | ), 35 | ), 36 | ), 37 | ), 38 | ], 39 | }, 40 | ret_ty: Atom( 41 | Int, 42 | ), 43 | vars: [ 44 | ( 45 | Atom( 46 | Int, 47 | ), 48 | Symbol( 49 | "a", 50 | ), 51 | ), 52 | ( 53 | Atom( 54 | Int, 55 | ), 56 | Symbol( 57 | "b", 58 | ), 59 | ), 60 | ], 61 | decl_stmts: [ 62 | Stmt( 63 | Ret( 64 | Some( 65 | Const( 66 | Int( 67 | 0, 68 | ), 69 | ), 70 | ), 71 | ), 72 | ), 73 | ], 74 | }, 75 | ), 76 | ], 77 | } 78 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__collision.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "collide", 11 | ), 12 | contract: Contract { 13 | requires: [], 14 | ensures: [ 15 | TT, 16 | ], 17 | }, 18 | ret_ty: Void, 19 | vars: [], 20 | decl_stmts: [ 21 | Decl( 22 | Decl { 23 | ty: Atom( 24 | Int, 25 | ), 26 | name: Symbol( 27 | "x", 28 | ), 29 | value: Some( 30 | Const( 31 | Int( 32 | 0, 33 | ), 34 | ), 35 | ), 36 | }, 37 | ), 38 | Decl( 39 | Decl { 40 | ty: Atom( 41 | Int, 42 | ), 43 | name: Symbol( 44 | "y", 45 | ), 46 | value: Some( 47 | Const( 48 | Int( 49 | 0, 50 | ), 51 | ), 52 | ), 53 | }, 54 | ), 55 | Stmt( 56 | While( 57 | LoopAnnot { 58 | invariants: [ 59 | Cmp( 60 | Cmp( 61 | Le, 62 | ArithTerm( 63 | Var( 64 | Symbol( 65 | "x", 66 | ), 67 | ), 68 | ), 69 | Const( 70 | Int( 71 | 5, 72 | ), 73 | ), 74 | ), 75 | ), 76 | ], 77 | variants: [], 78 | }, 79 | Binary( 80 | Logic( 81 | Cmp( 82 | Lt, 83 | ), 84 | ), 85 | Var( 86 | Symbol( 87 | "x", 88 | ), 89 | ), 90 | Const( 91 | Int( 92 | 5, 93 | ), 94 | ), 95 | ), 96 | Block( 97 | [ 98 | Decl( 99 | Decl { 100 | ty: Atom( 101 | Int, 102 | ), 103 | name: Symbol( 104 | "y", 105 | ), 106 | value: Some( 107 | Var( 108 | Symbol( 109 | "x", 110 | ), 111 | ), 112 | ), 113 | }, 114 | ), 115 | Stmt( 116 | Assign( 117 | Var( 118 | Symbol( 119 | "x", 120 | ), 121 | Binary( 122 | Arith( 123 | Plus, 124 | ), 125 | Var( 126 | Symbol( 127 | "y", 128 | ), 129 | ), 130 | Const( 131 | Int( 132 | 1, 133 | ), 134 | ), 135 | ), 136 | ), 137 | ), 138 | ), 139 | ], 140 | ), 141 | ), 142 | ), 143 | Stmt( 144 | Assert( 145 | Cmp( 146 | Cmp( 147 | Eq, 148 | ArithTerm( 149 | Var( 150 | Symbol( 151 | "y", 152 | ), 153 | ), 154 | ), 155 | Const( 156 | Int( 157 | 0, 158 | ), 159 | ), 160 | ), 161 | ), 162 | ), 163 | ), 164 | Stmt( 165 | Assert( 166 | Cmp( 167 | Cmp( 168 | Eq, 169 | ArithTerm( 170 | Var( 171 | Symbol( 172 | "x", 173 | ), 174 | ), 175 | ), 176 | Const( 177 | Int( 178 | 5, 179 | ), 180 | ), 181 | ), 182 | ), 183 | ), 184 | ), 185 | ], 186 | }, 187 | ), 188 | ], 189 | } 190 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__comment.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [], 7 | } 8 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__div.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "main", 11 | ), 12 | contract: Contract { 13 | requires: [], 14 | ensures: [ 15 | Cmp( 16 | Cmp( 17 | Eq, 18 | ArithTerm( 19 | Res, 20 | ), 21 | Const( 22 | Int( 23 | 1, 24 | ), 25 | ), 26 | ), 27 | ), 28 | ], 29 | }, 30 | ret_ty: Atom( 31 | Int, 32 | ), 33 | vars: [], 34 | decl_stmts: [ 35 | Stmt( 36 | Ret( 37 | Some( 38 | Binary( 39 | Arith( 40 | Mul, 41 | ), 42 | Const( 43 | Int( 44 | 5, 45 | ), 46 | ), 47 | Const( 48 | Int( 49 | 3, 50 | ), 51 | ), 52 | ), 53 | ), 54 | ), 55 | ), 56 | ], 57 | }, 58 | ), 59 | ], 60 | } 61 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__do_while.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "fun", 11 | ), 12 | contract: Contract { 13 | requires: [ 14 | TT, 15 | ], 16 | ensures: [ 17 | TT, 18 | ], 19 | }, 20 | ret_ty: Void, 21 | vars: [], 22 | decl_stmts: [ 23 | Decl( 24 | Decl { 25 | ty: Atom( 26 | Int, 27 | ), 28 | name: Symbol( 29 | "x", 30 | ), 31 | value: Some( 32 | Const( 33 | Int( 34 | 0, 35 | ), 36 | ), 37 | ), 38 | }, 39 | ), 40 | Stmt( 41 | Do( 42 | LoopAnnot { 43 | invariants: [ 44 | Cmp( 45 | Cmp( 46 | Lt, 47 | ArithTerm( 48 | Var( 49 | Symbol( 50 | "x", 51 | ), 52 | ), 53 | ), 54 | Const( 55 | Int( 56 | 1, 57 | ), 58 | ), 59 | ), 60 | ), 61 | ], 62 | variants: [], 63 | }, 64 | Binary( 65 | Logic( 66 | Cmp( 67 | Lt, 68 | ), 69 | ), 70 | Var( 71 | Symbol( 72 | "x", 73 | ), 74 | ), 75 | Const( 76 | Int( 77 | 1, 78 | ), 79 | ), 80 | ), 81 | Block( 82 | [ 83 | Stmt( 84 | Assign( 85 | Var( 86 | Symbol( 87 | "x", 88 | ), 89 | Binary( 90 | Arith( 91 | Plus, 92 | ), 93 | Var( 94 | Symbol( 95 | "x", 96 | ), 97 | ), 98 | Const( 99 | Int( 100 | 1, 101 | ), 102 | ), 103 | ), 104 | ), 105 | ), 106 | ), 107 | ], 108 | ), 109 | ), 110 | ), 111 | Stmt( 112 | Empty, 113 | ), 114 | Stmt( 115 | Assert( 116 | Cmp( 117 | Cmp( 118 | Eq, 119 | ArithTerm( 120 | Var( 121 | Symbol( 122 | "x", 123 | ), 124 | ), 125 | ), 126 | Const( 127 | Int( 128 | 1, 129 | ), 130 | ), 131 | ), 132 | ), 133 | ), 134 | ), 135 | ], 136 | }, 137 | ), 138 | ], 139 | } 140 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__mod.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "main", 11 | ), 12 | contract: Contract { 13 | requires: [], 14 | ensures: [ 15 | Cmp( 16 | Cmp( 17 | Eq, 18 | ArithTerm( 19 | Res, 20 | ), 21 | Const( 22 | Int( 23 | 2, 24 | ), 25 | ), 26 | ), 27 | ), 28 | ], 29 | }, 30 | ret_ty: Atom( 31 | Int, 32 | ), 33 | vars: [], 34 | decl_stmts: [ 35 | Stmt( 36 | Ret( 37 | Some( 38 | Binary( 39 | Arith( 40 | Mul, 41 | ), 42 | Const( 43 | Int( 44 | 5, 45 | ), 46 | ), 47 | Unary( 48 | Neg, 49 | Const( 50 | Int( 51 | 3, 52 | ), 53 | ), 54 | ), 55 | ), 56 | ), 57 | ), 58 | ), 59 | ], 60 | }, 61 | ), 62 | ], 63 | } 64 | -------------------------------------------------------------------------------- /cierra/cierra/tests/snapshots/parser__while.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: cierra/tests/parser.rs 3 | expression: ast 4 | --- 5 | Taurus { 6 | defs: [ 7 | Func( 8 | FuncDef { 9 | name: Symbol( 10 | "fun", 11 | ), 12 | contract: Contract { 13 | requires: [ 14 | TT, 15 | ], 16 | ensures: [ 17 | TT, 18 | ], 19 | }, 20 | ret_ty: Void, 21 | vars: [], 22 | decl_stmts: [ 23 | Decl( 24 | Decl { 25 | ty: Atom( 26 | Int, 27 | ), 28 | name: Symbol( 29 | "x", 30 | ), 31 | value: Some( 32 | Const( 33 | Int( 34 | 0, 35 | ), 36 | ), 37 | ), 38 | }, 39 | ), 40 | Stmt( 41 | While( 42 | LoopAnnot { 43 | invariants: [ 44 | Cmp( 45 | Cmp( 46 | Lt, 47 | ArithTerm( 48 | Var( 49 | Symbol( 50 | "x", 51 | ), 52 | ), 53 | ), 54 | Const( 55 | Int( 56 | 1, 57 | ), 58 | ), 59 | ), 60 | ), 61 | ], 62 | variants: [], 63 | }, 64 | Binary( 65 | Logic( 66 | Cmp( 67 | Lt, 68 | ), 69 | ), 70 | Var( 71 | Symbol( 72 | "x", 73 | ), 74 | ), 75 | Const( 76 | Int( 77 | 1, 78 | ), 79 | ), 80 | ), 81 | Assign( 82 | Var( 83 | Symbol( 84 | "x", 85 | ), 86 | Binary( 87 | Arith( 88 | Plus, 89 | ), 90 | Var( 91 | Symbol( 92 | "x", 93 | ), 94 | ), 95 | Const( 96 | Int( 97 | 1, 98 | ), 99 | ), 100 | ), 101 | ), 102 | ), 103 | ), 104 | ), 105 | Stmt( 106 | Assert( 107 | Cmp( 108 | Cmp( 109 | Eq, 110 | ArithTerm( 111 | Var( 112 | Symbol( 113 | "x", 114 | ), 115 | ), 116 | ), 117 | Const( 118 | Int( 119 | 1, 120 | ), 121 | ), 122 | ), 123 | ), 124 | ), 125 | ), 126 | ], 127 | }, 128 | ), 129 | ], 130 | } 131 | -------------------------------------------------------------------------------- /cierra/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "antlr4-rust-unwrapped": { 4 | "flake": false, 5 | "locked": { 6 | "narHash": "sha256-apLSrQqqLKXiKVT8LAxO3i4LbZQqvj0b8AicoyYpuE4=", 7 | "type": "file", 8 | "url": "https://github.com/rrevenantt/antlr4rust/releases/download/antlr4-4.8-2-Rust0.3.0-beta/antlr4-4.8-2-SNAPSHOT-complete.jar" 9 | }, 10 | "original": { 11 | "type": "file", 12 | "url": "https://github.com/rrevenantt/antlr4rust/releases/download/antlr4-4.8-2-Rust0.3.0-beta/antlr4-4.8-2-SNAPSHOT-complete.jar" 13 | } 14 | }, 15 | "flake-utils": { 16 | "locked": { 17 | "lastModified": 1659877975, 18 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", 19 | "owner": "numtide", 20 | "repo": "flake-utils", 21 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", 22 | "type": "github" 23 | }, 24 | "original": { 25 | "owner": "numtide", 26 | "repo": "flake-utils", 27 | "type": "github" 28 | } 29 | }, 30 | "flake-utils_2": { 31 | "locked": { 32 | "lastModified": 1659877975, 33 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", 34 | "owner": "numtide", 35 | "repo": "flake-utils", 36 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", 37 | "type": "github" 38 | }, 39 | "original": { 40 | "owner": "numtide", 41 | "repo": "flake-utils", 42 | "type": "github" 43 | } 44 | }, 45 | "naersk": { 46 | "inputs": { 47 | "nixpkgs": "nixpkgs" 48 | }, 49 | "locked": { 50 | "lastModified": 1662220400, 51 | "narHash": "sha256-9o2OGQqu4xyLZP9K6kNe1pTHnyPz0Wr3raGYnr9AIgY=", 52 | "owner": "nix-community", 53 | "repo": "naersk", 54 | "rev": "6944160c19cb591eb85bbf9b2f2768a935623ed3", 55 | "type": "github" 56 | }, 57 | "original": { 58 | "owner": "nix-community", 59 | "repo": "naersk", 60 | "type": "github" 61 | } 62 | }, 63 | "nixpkgs": { 64 | "locked": { 65 | "lastModified": 1666688649, 66 | "narHash": "sha256-i1Tq2VgXbEZKgjM2p2OqZdxcnK4FZjRZ9Oy4Ewx8gjA=", 67 | "owner": "NixOS", 68 | "repo": "nixpkgs", 69 | "rev": "03a00f66fc4e893dccba1579df6d0c83852e1c2c", 70 | "type": "github" 71 | }, 72 | "original": { 73 | "id": "nixpkgs", 74 | "type": "indirect" 75 | } 76 | }, 77 | "nixpkgs_2": { 78 | "locked": { 79 | "lastModified": 1666688649, 80 | "narHash": "sha256-i1Tq2VgXbEZKgjM2p2OqZdxcnK4FZjRZ9Oy4Ewx8gjA=", 81 | "owner": "NixOS", 82 | "repo": "nixpkgs", 83 | "rev": "03a00f66fc4e893dccba1579df6d0c83852e1c2c", 84 | "type": "github" 85 | }, 86 | "original": { 87 | "owner": "NixOS", 88 | "ref": "nixpkgs-unstable", 89 | "repo": "nixpkgs", 90 | "type": "github" 91 | } 92 | }, 93 | "nixpkgs_3": { 94 | "locked": { 95 | "lastModified": 1665296151, 96 | "narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=", 97 | "owner": "NixOS", 98 | "repo": "nixpkgs", 99 | "rev": "14ccaaedd95a488dd7ae142757884d8e125b3363", 100 | "type": "github" 101 | }, 102 | "original": { 103 | "owner": "NixOS", 104 | "ref": "nixpkgs-unstable", 105 | "repo": "nixpkgs", 106 | "type": "github" 107 | } 108 | }, 109 | "root": { 110 | "inputs": { 111 | "antlr4-rust-unwrapped": "antlr4-rust-unwrapped", 112 | "flake-utils": "flake-utils", 113 | "naersk": "naersk", 114 | "nixpkgs": "nixpkgs_2", 115 | "rust-overlay": "rust-overlay" 116 | } 117 | }, 118 | "rust-overlay": { 119 | "inputs": { 120 | "flake-utils": "flake-utils_2", 121 | "nixpkgs": "nixpkgs_3" 122 | }, 123 | "locked": { 124 | "lastModified": 1666667027, 125 | "narHash": "sha256-MUJa0T8j5cy3eE70hoL1KW52VfTcXm4VbwvdF5scs1g=", 126 | "owner": "oxalica", 127 | "repo": "rust-overlay", 128 | "rev": "79b6e66bb76537c96707703f08630765e46148d1", 129 | "type": "github" 130 | }, 131 | "original": { 132 | "owner": "oxalica", 133 | "repo": "rust-overlay", 134 | "type": "github" 135 | } 136 | } 137 | }, 138 | "root": "root", 139 | "version": 7 140 | } 141 | -------------------------------------------------------------------------------- /cierra/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | flake-utils.url = "github:numtide/flake-utils"; 4 | naersk.url = "github:nix-community/naersk"; 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 | rust-overlay.url = "github:oxalica/rust-overlay"; 7 | antlr4-rust-unwrapped = { 8 | url = "https://github.com/rrevenantt/antlr4rust/releases/download/antlr4-4.8-2-Rust0.3.0-beta/antlr4-4.8-2-SNAPSHOT-complete.jar"; 9 | flake = false; 10 | }; 11 | }; 12 | 13 | outputs = { self, flake-utils, naersk, nixpkgs, rust-overlay, antlr4-rust-unwrapped }: 14 | flake-utils.lib.eachDefaultSystem (system: 15 | let 16 | overlays = [ (import rust-overlay) ]; 17 | pkgs = (import nixpkgs) { 18 | inherit system overlays; 19 | }; 20 | 21 | naersk' = pkgs.callPackage naersk { }; 22 | 23 | rustMinimal = pkgs.rust-bin.stable.latest.default; 24 | rustStable = pkgs.rust-bin.stable.latest.default.override { 25 | extensions = [ "rust-src" ]; 26 | }; 27 | rustNightly = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal.override { 28 | extensions = [ "rustfmt" ]; 29 | }); 30 | 31 | generateScript = builtins.readFile ./generate.sh; 32 | format = pkgs.writeShellApplication { 33 | name = "format"; 34 | runtimeInputs = [ rustNightly ]; 35 | text = '' 36 | cargo fmt "$@" 37 | ''; 38 | }; 39 | antlr4-rust = pkgs.stdenv.mkDerivation { 40 | name = "antlr4-rust"; 41 | pname = "antlr"; 42 | src = antlr4-rust-unwrapped; 43 | dontUnpack = true; 44 | nativeBuildInputs = [ pkgs.makeWrapper ]; 45 | installPhase = '' 46 | runHook preInstall 47 | 48 | jar_name="''${src##*/}" 49 | 50 | mkdir -p "$out"/{share/java,bin} 51 | cp "$src" "$out/share/java/$jar_name" 52 | makeWrapper ${pkgs.jre}/bin/java "$out/bin/antlr" \ 53 | --add-flags "-cp $out/share/java/$jar_name org.antlr.v4.Tool" \ 54 | --set _JAVA_OPTIONS '-Xmx500M' 55 | makeWrapper ${pkgs.jre}/bin/java "$out/bin/grun" \ 56 | --add-flags "-cp $out/share/java/$jar_name org.antlr.v4.gui.TestRig" 57 | ln -s "$out/bin/antlr"{,4} 58 | 59 | runHook postInstall 60 | ''; 61 | }; 62 | generate = pkgs.writeShellApplication { 63 | name = "generate"; 64 | runtimeInputs = [ antlr4-rust ]; 65 | text = generateScript; 66 | }; 67 | 68 | darwinBuildInputs = pkgs.lib.optionals pkgs.stdenv.isDarwin [ pkgs.darwin.libiconv pkgs.darwin.Security ]; 69 | additionalBuildInputs = with pkgs; [ jre ] ++ darwinBuildInputs; 70 | devBuildInputs = [ antlr4-rust rustStable pkgs.rust-analyzer pkgs.shellcheck format generate ]; 71 | 72 | in 73 | rec { 74 | # For `nix build` & `nix run`: 75 | packages = rec { 76 | cierra = naersk'.buildPackage rec { 77 | src = ./.; 78 | cargo = rustMinimal; 79 | rustc = rustMinimal; 80 | nativeBuildInputs = additionalBuildInputs; 81 | override = _: { 82 | preBuild = '' 83 | cp -r ${src}/grammar . 84 | ${pkgs.lib.getExe generate} 85 | ''; 86 | }; 87 | }; 88 | default = cierra; 89 | }; 90 | 91 | # For `nix develop`: 92 | devShell = pkgs.mkShell { 93 | nativeBuildInputs = devBuildInputs ++ additionalBuildInputs; 94 | }; 95 | 96 | apps = rec { 97 | default = cierra; 98 | cierra = flake-utils.lib.mkApp { 99 | drv = self.packages.${system}.cierra; 100 | }; 101 | }; 102 | } 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /cierra/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | function to_rust_path() { 5 | local path=$1 6 | 7 | local relative_path=${path#grammar/} 8 | local relative_parent_path=${relative_path%/*} 9 | local base_name=${relative_path##*/} 10 | local base_name_without_extension=${base_name%.*} 11 | local lower_base_name_without_extension=${base_name_without_extension,,} 12 | 13 | local rust_path="cierra/src/${relative_parent_path}/${lower_base_name_without_extension}" 14 | echo "$rust_path" 15 | } 16 | 17 | grammars=(grammar/**/*.g4) 18 | echo "Generating Rust files from ${#grammars[@]} grammars..." 19 | for grammar in "${grammars[@]}"; do 20 | rust_path=$(to_rust_path "$grammar") 21 | echo "Generating $rust_path from $grammar" 22 | 23 | abs_rust_path="$(pwd)/$rust_path" 24 | grammar_name="${grammar##*/}" 25 | 26 | mkdir -p "$rust_path" 27 | pushd "${grammar%/*}" >/dev/null 28 | antlr4 -Dlanguage=Rust -o "$abs_rust_path" "$grammar_name" 29 | popd >/dev/null 30 | done 31 | -------------------------------------------------------------------------------- /cierra/grammar/parser/.antlr/Taurus.tokens: -------------------------------------------------------------------------------- 1 | VOID=1 2 | STRUCT=2 3 | LPAR=3 4 | RPAR=4 5 | LBRACE=5 6 | RBRACE=6 7 | COMMA=7 8 | SEMICOLON=8 9 | LBRACKET=9 10 | RBRACKET=10 11 | PERIOD=11 12 | INT=12 13 | FLOAT=13 14 | BOOL=14 15 | IF=15 16 | ELSE=16 17 | BREAK=17 18 | CONTINUE=18 19 | RETURN=19 20 | WHILE=20 21 | DO=21 22 | FOR=22 23 | ASSIGN=23 24 | EQ=24 25 | NE=25 26 | LE=26 27 | LT=27 28 | GE=28 29 | GT=29 30 | ADD=30 31 | MINUS=31 32 | MUL=32 33 | DIV=33 34 | NOT=34 35 | MOD=35 36 | AND=36 37 | OR=37 38 | EXPR_TRUE=38 39 | EXPR_FALSE=39 40 | ANNO_TRUE=40 41 | ANNO_FALSE=41 42 | RESULT=42 43 | LENGTH=43 44 | OLD=44 45 | WITH=45 46 | IMPLY=46 47 | EQUIV=47 48 | XOR=48 49 | FORALL=49 50 | EXISTS=50 51 | BOOLEAN=51 52 | INTEGER=52 53 | REAL=53 54 | REQUIRES=54 55 | DECREASES=55 56 | ENSURES=56 57 | ASSERT=57 58 | LOOP=58 59 | INVARIANT=59 60 | VARIANT=60 61 | PREDICATE=61 62 | VALID=62 63 | APOSTROPHE=63 64 | INT_CONSTANT=64 65 | FLOAT_CONSTANT=65 66 | IDENT=66 67 | COMMENT=67 68 | LINE_COMMENT=68 69 | ANNOT_START=69 70 | ANNOT_END=70 71 | LINE_ANNOT_START=71 72 | AT=72 73 | LINEEND=73 74 | WS=74 75 | 'void'=1 76 | 'struct'=2 77 | '('=3 78 | ')'=4 79 | '{'=5 80 | '}'=6 81 | ','=7 82 | ';'=8 83 | '['=9 84 | ']'=10 85 | '.'=11 86 | 'int'=12 87 | 'float'=13 88 | 'bool'=14 89 | 'if'=15 90 | 'else'=16 91 | 'break'=17 92 | 'continue'=18 93 | 'return'=19 94 | 'while'=20 95 | 'do'=21 96 | 'for'=22 97 | '='=23 98 | '=='=24 99 | '!='=25 100 | '<='=26 101 | '<'=27 102 | '>='=28 103 | '>'=29 104 | '+'=30 105 | '-'=31 106 | '*'=32 107 | '/'=33 108 | '!'=34 109 | '%'=35 110 | '&&'=36 111 | '||'=37 112 | 'true'=38 113 | 'false'=39 114 | '\\true'=40 115 | '\\false'=41 116 | '\\result'=42 117 | '\\length'=43 118 | '\\old'=44 119 | '\\with'=45 120 | '==>'=46 121 | '<==>'=47 122 | '^^'=48 123 | '\\forall'=49 124 | '\\exists'=50 125 | 'boolean'=51 126 | 'integer'=52 127 | 'real'=53 128 | 'requires'=54 129 | 'decreases'=55 130 | 'ensures'=56 131 | 'assert'=57 132 | 'loop'=58 133 | 'invariant'=59 134 | 'variant'=60 135 | 'predicate'=61 136 | '\\valid'=62 137 | '..'=63 138 | '/*@'=69 139 | '*/'=70 140 | '//@'=71 141 | '@'=72 142 | -------------------------------------------------------------------------------- /cierra/grammar/parser/.antlr/TaurusLexer.tokens: -------------------------------------------------------------------------------- 1 | VOID=1 2 | STRUCT=2 3 | LPAR=3 4 | RPAR=4 5 | LBRACE=5 6 | RBRACE=6 7 | COMMA=7 8 | SEMICOLON=8 9 | LBRACKET=9 10 | RBRACKET=10 11 | PERIOD=11 12 | INT=12 13 | FLOAT=13 14 | BOOL=14 15 | IF=15 16 | ELSE=16 17 | BREAK=17 18 | CONTINUE=18 19 | RETURN=19 20 | WHILE=20 21 | DO=21 22 | FOR=22 23 | ASSIGN=23 24 | EQ=24 25 | NE=25 26 | LE=26 27 | LT=27 28 | GE=28 29 | GT=29 30 | ADD=30 31 | MINUS=31 32 | MUL=32 33 | DIV=33 34 | NOT=34 35 | MOD=35 36 | AND=36 37 | OR=37 38 | EXPR_TRUE=38 39 | EXPR_FALSE=39 40 | ANNO_TRUE=40 41 | ANNO_FALSE=41 42 | RESULT=42 43 | LENGTH=43 44 | OLD=44 45 | WITH=45 46 | IMPLY=46 47 | EQUIV=47 48 | XOR=48 49 | FORALL=49 50 | EXISTS=50 51 | BOOLEAN=51 52 | INTEGER=52 53 | REAL=53 54 | REQUIRES=54 55 | DECREASES=55 56 | ENSURES=56 57 | ASSERT=57 58 | LOOP=58 59 | INVARIANT=59 60 | VARIANT=60 61 | PREDICATE=61 62 | VALID=62 63 | APOSTROPHE=63 64 | INT_CONSTANT=64 65 | FLOAT_CONSTANT=65 66 | IDENT=66 67 | COMMENT=67 68 | LINE_COMMENT=68 69 | ANNOT_START=69 70 | ANNOT_END=70 71 | LINE_ANNOT_START=71 72 | AT=72 73 | LINEEND=73 74 | WS=74 75 | 'void'=1 76 | 'struct'=2 77 | '('=3 78 | ')'=4 79 | '{'=5 80 | '}'=6 81 | ','=7 82 | ';'=8 83 | '['=9 84 | ']'=10 85 | '.'=11 86 | 'int'=12 87 | 'float'=13 88 | 'bool'=14 89 | 'if'=15 90 | 'else'=16 91 | 'break'=17 92 | 'continue'=18 93 | 'return'=19 94 | 'while'=20 95 | 'do'=21 96 | 'for'=22 97 | '='=23 98 | '=='=24 99 | '!='=25 100 | '<='=26 101 | '<'=27 102 | '>='=28 103 | '>'=29 104 | '+'=30 105 | '-'=31 106 | '*'=32 107 | '/'=33 108 | '!'=34 109 | '%'=35 110 | '&&'=36 111 | '||'=37 112 | 'true'=38 113 | 'false'=39 114 | '\\true'=40 115 | '\\false'=41 116 | '\\result'=42 117 | '\\length'=43 118 | '\\old'=44 119 | '\\with'=45 120 | '==>'=46 121 | '<==>'=47 122 | '^^'=48 123 | '\\forall'=49 124 | '\\exists'=50 125 | 'boolean'=51 126 | 'integer'=52 127 | 'real'=53 128 | 'requires'=54 129 | 'decreases'=55 130 | 'ensures'=56 131 | 'assert'=57 132 | 'loop'=58 133 | 'invariant'=59 134 | 'variant'=60 135 | 'predicate'=61 136 | '\\valid'=62 137 | '..'=63 138 | '/*@'=69 139 | '*/'=70 140 | '//@'=71 141 | '@'=72 142 | -------------------------------------------------------------------------------- /cierra/macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sexp_macro" 3 | version = "0.0.0" 4 | authors = ["LightQuantum "] 5 | description = "A `cargo generate` template for quick-starting a procedural macro crate" 6 | keywords = ["template", "proc_macro", "procmacro"] 7 | edition = "2018" 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [dependencies] 13 | proc-macro2 = "1.0" 14 | quote = "1" 15 | syn = "1.0" 16 | 17 | [dev-dependencies] 18 | insta = "1.21" 19 | sexp = { path = "../sexp" } 20 | -------------------------------------------------------------------------------- /cierra/macro/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /cierra/macro/README.md: -------------------------------------------------------------------------------- 1 | # Sexp macro 2 | 3 | This is an auxiliary library for macros related to smt-lib v2 flavored S-expressions. 4 | 5 | Most part of its code is adopted from [lexpr-rs](https://github.com/rotty/lexpr-rs). 6 | 7 | The modified version is licensed under either of Apache License, Version 2.0 or MIT license, just like the original one. 8 | -------------------------------------------------------------------------------- /cierra/macro/src/generator.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, TokenStream}; 2 | use quote::{quote, ToTokens}; 3 | 4 | use crate::value::Value; 5 | 6 | struct GenerateCtx { 7 | value: Value, 8 | s: Ident, 9 | atom_break: usize, 10 | list_break: usize, 11 | } 12 | 13 | impl GenerateCtx { 14 | pub fn with(&self, value: Value) -> Self { 15 | Self { value, s: self.s.clone(), atom_break: self.atom_break, list_break: self.list_break } 16 | } 17 | } 18 | 19 | impl ToTokens for GenerateCtx { 20 | fn to_tokens(&self, tokens: &mut TokenStream) { 21 | let Self { value, s, atom_break, list_break } = self; 22 | let expanded = match value { 23 | Value::Literal(lit) => quote! { #s.put_atom(&format!("{:?}", #lit), #atom_break); }, 24 | Value::Symbol(name) => quote! { #s.put_atom(&format!("{}", #name), #atom_break); }, 25 | Value::Unquoted(tt) => quote! { #s.put_atom(&(#tt).to_string(), #atom_break); }, 26 | Value::List(elements) => { 27 | let pushes = 28 | elements.iter().map(|elem| self.with(elem.clone()).into_token_stream()); 29 | quote! { 30 | #s.begin_list(#list_break); 31 | #(#pushes)* 32 | #s.end_list(); 33 | } 34 | } 35 | Value::Bracket(elements) => { 36 | let count = elements.len(); 37 | let elements = elements.iter().enumerate().map(|(idx, elem)| { 38 | match (idx == 0, idx == count - 1) { 39 | (true, true) => format!("[{}]", elem), 40 | (true, false) => format!("[{}", elem), 41 | (false, true) => format!("{}]", elem), 42 | (false, false) => elem.to_string(), 43 | } 44 | }); 45 | quote! { 46 | #( #s.put_atom(#elements, #atom_break); )* 47 | } 48 | } 49 | Value::Nested(tt) => quote! { 50 | ::fmt(&#tt, #s); 51 | }, 52 | Value::Expand(tt) => quote! { 53 | #[allow(unused_parens)] 54 | for elem in #tt { 55 | ::fmt(elem, #s); 56 | } 57 | }, 58 | }; 59 | tokens.extend(expanded); 60 | // tokens.extend(quote! { 61 | // |#s: &mut ::sise::Serializer| { 62 | // #expanded 63 | // } 64 | // }); 65 | } 66 | } 67 | 68 | pub fn generate(s: Ident, value: Value, atom_break: usize, list_break: usize) -> TokenStream { 69 | GenerateCtx { value, s, atom_break, list_break }.into_token_stream() 70 | } 71 | -------------------------------------------------------------------------------- /cierra/macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | use proc_macro2::Ident; 5 | use quote::quote; 6 | use syn::{ 7 | parse::{Parse, ParseStream}, 8 | parse_macro_input, 9 | Token, 10 | }; 11 | 12 | mod generator; 13 | mod parser; 14 | mod value; 15 | 16 | struct Input { 17 | s: Ident, 18 | _comma: Token![,], 19 | e: proc_macro2::TokenStream, 20 | } 21 | 22 | impl Parse for Input { 23 | fn parse(input: ParseStream) -> syn::Result { 24 | Ok(Self { s: input.parse()?, _comma: input.parse()?, e: input.parse()? }) 25 | } 26 | } 27 | 28 | /// Example of [function-like procedural macro][1]. 29 | /// 30 | /// [1]: https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros 31 | #[proc_macro] 32 | pub fn sexp(input: TokenStream) -> TokenStream { 33 | let input = parse_macro_input!(input as Input); 34 | 35 | let tokens = match parser::parse(input.e) { 36 | Ok(value) => generator::generate(input.s, value, usize::MAX, 0), 37 | Err(e) => { 38 | let msg = format!("could not parse s-expression: {:?}", e); 39 | quote! { compile_error!(#msg) } 40 | } 41 | }; 42 | 43 | tokens.into() 44 | } 45 | -------------------------------------------------------------------------------- /cierra/macro/src/parser.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Delimiter, Punct, Spacing, TokenStream, TokenTree}; 2 | 3 | use crate::value::Value; 4 | 5 | #[derive(Debug)] 6 | struct Parser { 7 | tokens: Vec, 8 | index: usize, 9 | } 10 | 11 | #[derive(Debug)] 12 | pub enum ParseError { 13 | UnexpectedDelimiter(Delimiter), 14 | UnexpectedEnd, 15 | } 16 | 17 | impl Parser { 18 | fn new(tokens: Vec) -> Self { 19 | Self { tokens, index: 0 } 20 | } 21 | 22 | fn next_token(&mut self) -> Option<&TokenTree> { 23 | if self.index == self.tokens.len() { 24 | return None; 25 | } 26 | let token = &self.tokens[self.index]; 27 | self.index += 1; 28 | Some(token) 29 | } 30 | 31 | fn token(&mut self) -> Result<&TokenTree, ParseError> { 32 | self.next_token().ok_or(ParseError::UnexpectedEnd) 33 | } 34 | 35 | fn peek(&mut self) -> Option<&TokenTree> { 36 | if self.index == self.tokens.len() { 37 | return None; 38 | } 39 | Some(&self.tokens[self.index]) 40 | } 41 | 42 | fn parse_puncts(&mut self, first: &Punct) -> Result { 43 | let mut buf = String::from(first.as_char()); 44 | let mut spacing = first.spacing(); 45 | while spacing == Spacing::Joint { 46 | let punct = self.token()?; 47 | match punct { 48 | TokenTree::Punct(punct) => { 49 | buf.push(punct.as_char()); 50 | spacing = punct.spacing(); 51 | } 52 | _ => break, 53 | } 54 | } 55 | Ok(buf) 56 | } 57 | 58 | fn parse(&mut self) -> Result { 59 | let first_punct = match self.token()? { 60 | TokenTree::Punct(punct) => punct, 61 | TokenTree::Literal(literal) => return Ok(Value::Literal(literal.clone())), 62 | TokenTree::Ident(ident) => return Ok(Value::Symbol(ident.to_string())), 63 | TokenTree::Group(group) => { 64 | return match group.delimiter() { 65 | Delimiter::Parenthesis => parse_list(group.stream()), 66 | Delimiter::Bracket => parse_bracket(group.stream()), 67 | delim => Err(ParseError::UnexpectedDelimiter(delim)), 68 | }; 69 | } 70 | } 71 | .clone(); 72 | match self.parse_puncts(&first_punct)?.as_str() { 73 | "," => Ok(Value::Unquoted(self.token()?.clone())), 74 | "%" => Ok(Value::Nested(self.token()?.clone())), 75 | "..." => Ok(Value::Expand(self.token()?.clone())), 76 | c => Ok(Value::Symbol(c.to_string())), 77 | } 78 | } 79 | } 80 | 81 | fn parse_bracket(tokens: TokenStream) -> Result { 82 | let mut elements = vec![]; 83 | let mut parser = Parser::new(tokens.into_iter().collect()); 84 | while parser.peek().is_some() { 85 | elements.push(parser.parse()?); 86 | } 87 | Ok(Value::Bracket( 88 | elements 89 | .into_iter() 90 | .filter_map(|elem| match elem { 91 | Value::Symbol(x) => Some(x), 92 | _ => None, 93 | }) 94 | .collect(), 95 | )) 96 | } 97 | 98 | fn parse_list(tokens: TokenStream) -> Result { 99 | let mut elements = vec![]; 100 | let mut parser = Parser::new(tokens.into_iter().collect()); 101 | while parser.peek().is_some() { 102 | elements.push(parser.parse()?); 103 | } 104 | Ok(Value::List(elements)) 105 | } 106 | 107 | pub fn parse(tokens: TokenStream) -> Result { 108 | let mut parser = Parser::new(tokens.into_iter().collect()); 109 | parser.parse() 110 | } 111 | -------------------------------------------------------------------------------- /cierra/macro/src/value.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone)] 2 | pub enum Value { 3 | Literal(proc_macro2::Literal), 4 | Symbol(String), 5 | Unquoted(proc_macro2::TokenTree), 6 | Nested(proc_macro2::TokenTree), 7 | Expand(proc_macro2::TokenTree), 8 | List(Vec), 9 | Bracket(Vec), 10 | } 11 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshot.rs: -------------------------------------------------------------------------------- 1 | use insta::assert_display_snapshot; 2 | use sexp::{Serializer, SerializerStyle, SexpDisplay}; 3 | use sexp_macro::sexp; 4 | 5 | struct Plain(F) 6 | where 7 | F: FnOnce(&mut Serializer); 8 | 9 | impl SexpDisplay for Plain 10 | where 11 | F: FnOnce(&mut Serializer), 12 | { 13 | fn fmt(&self, s: &mut Serializer) { 14 | sexp!(s, (assert 1 2)); 15 | } 16 | } 17 | 18 | macro_rules! plain { 19 | ($($tt: tt)*) => { 20 | Plain(|s| {sexp!(s, $($tt)*);}) 21 | } 22 | } 23 | 24 | #[test] 25 | fn snapshot() { 26 | snapshot_sexp("simple", |s| { 27 | sexp!(s, (assert 1 2)); 28 | }); 29 | snapshot_sexp("symbol_ex", |s| { 30 | sexp!(s, (= y (+ x 1))); 31 | }); 32 | let p = plain!((not (= x 0))); 33 | let q = plain!((> y 1)); 34 | snapshot_sexp("nested", |s| { 35 | sexp!(s, (=> %p %q)); 36 | }); 37 | snapshot_sexp("bracket_1", |s| { 38 | sexp!(s, (forall [x y z] (=> (and (<= x y) (<= y z)) (<= x z)))); 39 | }); 40 | snapshot_sexp("bracket_2", |s| { 41 | sexp!(s, (forall [x] (= x x))); 42 | }); 43 | let l: [Box; 2] = [Box::new(plain!((not (= x 0)))), Box::new(plain!((> y 1)))]; 44 | snapshot_sexp("expand", |s| { 45 | sexp!(s, (test ...(&l))); 46 | }); 47 | snapshot_sexp("long", |s| { 48 | sexp!(s, (assert 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)); 49 | }); 50 | snapshot_sexp("long_nested", |s| { 51 | sexp!(s, (assert (not (=> (choice (+ (choice (8 9 10)) (assume (- (choice (8 (choice (8 9 10)) (choice (+ (assume (- (choice (8 (choice (+ (assume (- (choice (8 9 10)))))) 10)))))) 10)))))))))); 52 | }); 53 | } 54 | 55 | fn snapshot_sexp(name: &str, f: impl FnOnce(&mut Serializer)) { 56 | let mut buf = String::new(); 57 | let mut s = Serializer::new(SerializerStyle { line_break: "\n", indentation: " " }, &mut buf); 58 | f(&mut s); 59 | s.finish(true); 60 | assert_display_snapshot!(name, buf); 61 | } 62 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__bracket_1.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (forall [x y z] 6 | (=> 7 | (and 8 | (<= x y) 9 | (<= y z) 10 | ) 11 | (<= x z) 12 | ) 13 | ) 14 | 15 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__bracket_2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (forall [x] 6 | (= x x) 7 | ) 8 | 9 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__expand.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (test 6 | (assert 1 2) 7 | (assert 1 2) 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__long.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (assert 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) 6 | 7 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__long_nested.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (assert 6 | (not 7 | (=> 8 | (choice 9 | (+ 10 | (choice 11 | (8 9 10) 12 | ) 13 | (assume 14 | (- 15 | (choice 16 | (8 17 | (choice 18 | (8 9 10) 19 | ) 20 | (choice 21 | (+ 22 | (assume 23 | (- 24 | (choice 25 | (8 26 | (choice 27 | (+ 28 | (assume 29 | (- 30 | (choice 31 | (8 9 10) 32 | ) 33 | ) 34 | ) 35 | ) 36 | ) 10 37 | ) 38 | ) 39 | ) 40 | ) 41 | ) 42 | ) 10 43 | ) 44 | ) 45 | ) 46 | ) 47 | ) 48 | ) 49 | ) 50 | ) 51 | ) 52 | 53 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__nested.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (=> 6 | (assert 1 2) 7 | (assert 1 2) 8 | ) 9 | 10 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (assert 1 2) 6 | 7 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__symbol_ex.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (= y 6 | (+ x 1) 7 | ) 8 | 9 | -------------------------------------------------------------------------------- /cierra/macro/tests/snapshots/snapshot__unquote.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: macro/tests/snapshot.rs 3 | expression: buf 4 | --- 5 | (=> 6 | (not 7 | (= x 0) 8 | ) 9 | (> y 1) 10 | ) 11 | 12 | -------------------------------------------------------------------------------- /cierra/rustfmt.toml: -------------------------------------------------------------------------------- 1 | unstable_features = true 2 | 3 | version = "Two" 4 | 5 | group_imports = "StdExternalCrate" 6 | imports_layout = "HorizontalVertical" 7 | imports_granularity = "Crate" 8 | reorder_imports = true 9 | 10 | use_small_heuristics = "Max" 11 | 12 | wrap_comments = true 13 | 14 | reorder_impl_items = true 15 | condense_wildcard_suffixes = true 16 | enum_discrim_align_threshold = 20 17 | use_field_init_shorthand = true 18 | 19 | format_strings = true 20 | format_code_in_doc_comments = true 21 | format_macro_matchers = true 22 | 23 | ignore = [ 24 | "cierra/src/parser/taurus" 25 | ] -------------------------------------------------------------------------------- /cierra/sexp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sexp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /cierra/sexp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /cierra/sexp/README.md: -------------------------------------------------------------------------------- 1 | # Sexp 2 | 3 | This is an auxiliary library for generating smt-lib v2 flavored S-expressions. 4 | 5 | Most part of its code is adopted from [rust-sise](https://github.com/eduardosm/rust-sise). 6 | 7 | The modified version is licensed under either of Apache License, Version 2.0 or MIT license, just like the original one. -------------------------------------------------------------------------------- /cierra/sexp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::missing_panics_doc, clippy::module_name_repetitions)] 2 | 3 | pub use serializer::{Serializer, SerializerStyle}; 4 | 5 | mod serializer; 6 | mod utils; 7 | 8 | pub trait SexpDisplay { 9 | fn fmt(&self, s: &mut Serializer); 10 | fn display(&self, style: SerializerStyle) -> String { 11 | let mut buf = String::new(); 12 | let mut s = Serializer::new(style, &mut buf); 13 | self.fmt(&mut s); 14 | s.finish(true); 15 | buf 16 | } 17 | fn pretty_display(&self) -> String { 18 | self.display(SerializerStyle { line_break: "\n", indentation: " " }) 19 | } 20 | fn plain_display(&self) -> String { 21 | self.display(SerializerStyle { line_break: " ", indentation: "" }) 22 | } 23 | } 24 | 25 | impl SexpDisplay for &T 26 | where 27 | T: SexpDisplay, 28 | { 29 | fn fmt(&self, s: &mut Serializer) { 30 | SexpDisplay::fmt(&**self, s); 31 | } 32 | } 33 | 34 | impl SexpDisplay for &mut T 35 | where 36 | T: SexpDisplay, 37 | { 38 | fn fmt(&self, s: &mut Serializer) { 39 | SexpDisplay::fmt(&**self, s); 40 | } 41 | } 42 | 43 | impl SexpDisplay for Box 44 | where 45 | T: SexpDisplay, 46 | { 47 | fn fmt(&self, s: &mut Serializer) { 48 | SexpDisplay::fmt(&**self, s); 49 | } 50 | } 51 | 52 | impl SexpDisplay for &dyn SexpDisplay { 53 | fn fmt(&self, s: &mut Serializer) { 54 | SexpDisplay::fmt(&**self, s); 55 | } 56 | } 57 | 58 | impl SexpDisplay for Box { 59 | fn fmt(&self, s: &mut Serializer) { 60 | SexpDisplay::fmt(&**self, s); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cierra/sexp/src/serializer.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::check_atom; 2 | 3 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 4 | pub struct SerializerStyle<'a> { 5 | pub line_break: &'a str, 6 | pub indentation: &'a str, 7 | } 8 | 9 | /// Serializes into a possibly multi-line string. 10 | /// 11 | /// # Example 12 | /// 13 | /// Compact (single line) example: 14 | /// 15 | /// ``` 16 | /// let style = sexp::SerializerStyle { line_break: "\n", indentation: " " }; 17 | /// 18 | /// let mut result = String::new(); 19 | /// let mut serializer = sexp::Serializer::new(style, &mut result); 20 | /// 21 | /// serializer.begin_list(usize::MAX); 22 | /// serializer.put_atom("example", usize::MAX); 23 | /// serializer.begin_list(usize::MAX); 24 | /// serializer.put_atom("1", usize::MAX); 25 | /// serializer.put_atom("2", usize::MAX); 26 | /// serializer.put_atom("3", usize::MAX); 27 | /// serializer.end_list(); 28 | /// serializer.begin_list(usize::MAX); 29 | /// serializer.put_atom("a", usize::MAX); 30 | /// serializer.put_atom("b", usize::MAX); 31 | /// serializer.put_atom("c", usize::MAX); 32 | /// serializer.end_list(); 33 | /// serializer.end_list(); 34 | /// serializer.finish(false); 35 | /// 36 | /// let expected_result = "(example (1 2 3) (a b c))"; 37 | /// assert_eq!(result, expected_result); 38 | /// ``` 39 | /// 40 | /// Full multi-line example: 41 | /// 42 | /// ``` 43 | /// let style = sexp::SerializerStyle { line_break: "\n", indentation: " " }; 44 | /// 45 | /// let mut result = String::new(); 46 | /// let mut serializer = sexp::Serializer::new(style, &mut result); 47 | /// 48 | /// serializer.begin_list(usize::MAX); 49 | /// serializer.put_atom("example", 0); 50 | /// serializer.begin_list(0); 51 | /// serializer.put_atom("1", 0); 52 | /// serializer.put_atom("2", 0); 53 | /// serializer.put_atom("3", 0); 54 | /// serializer.end_list(); 55 | /// serializer.begin_list(0); 56 | /// serializer.put_atom("a", 0); 57 | /// serializer.put_atom("b", 0); 58 | /// serializer.put_atom("c", 0); 59 | /// serializer.end_list(); 60 | /// serializer.end_list(); 61 | /// serializer.finish(true); 62 | /// 63 | /// let expected_result = "(\n example\n (\n 1\n 2\n 3\n )\n (\n a\n b\n c\n )\n)\n"; 64 | /// assert_eq!(result, expected_result); 65 | /// ``` 66 | /// 67 | /// You can write specified nodes in a single line: 68 | /// 69 | /// ``` 70 | /// let style = sexp::SerializerStyle { line_break: "\n", indentation: " " }; 71 | /// 72 | /// let mut result = String::new(); 73 | /// let mut serializer = sexp::Serializer::new(style, &mut result); 74 | /// 75 | /// serializer.begin_list(0); 76 | /// serializer.put_atom("example", usize::MAX); 77 | /// serializer.begin_list(0); 78 | /// // Write the three atoms in a single line. 79 | /// serializer.put_atom("1", usize::MAX); 80 | /// serializer.put_atom("2", usize::MAX); 81 | /// serializer.put_atom("3", usize::MAX); 82 | /// serializer.end_list(); 83 | /// serializer.begin_list(0); 84 | /// // Write the three atoms in a single line. 85 | /// serializer.put_atom("a", usize::MAX); 86 | /// serializer.put_atom("b", usize::MAX); 87 | /// serializer.put_atom("c", usize::MAX); 88 | /// serializer.end_list(); 89 | /// serializer.end_list(); 90 | /// serializer.finish(true); 91 | /// 92 | /// let expected_result = "(example\n (1 2 3)\n (a b c)\n)\n"; 93 | /// assert_eq!(result, expected_result); 94 | /// ``` 95 | pub struct Serializer<'a, 'b> { 96 | style: SerializerStyle<'a>, 97 | out: &'b mut String, 98 | state: State, 99 | } 100 | 101 | enum State { 102 | Beginning, 103 | Writing(WritingState), 104 | Finished, 105 | } 106 | 107 | struct WritingState { 108 | stack: Vec, 109 | list_beginning: bool, 110 | current_list_line_broken: bool, 111 | line_len: usize, 112 | } 113 | 114 | struct StackItem { 115 | line_broken: bool, 116 | } 117 | 118 | impl<'a, 'b> Serializer<'a, 'b> { 119 | pub fn new(style: SerializerStyle<'a>, out: &'b mut String) -> Self { 120 | Self { style, out, state: State::Beginning } 121 | } 122 | 123 | fn write_indent(indentation: &str, n: usize, out: &mut String) -> usize { 124 | let prev_len = out.len(); 125 | for _ in 0..n { 126 | out.push_str(indentation); 127 | } 128 | out.len() - prev_len 129 | } 130 | 131 | pub fn put_atom(&mut self, atom: &str, break_line_at: usize) { 132 | assert!(check_atom(atom), "invalid atom {:?}", atom); 133 | 134 | match self.state { 135 | State::Beginning => { 136 | self.out.push_str(atom); 137 | self.state = State::Finished; 138 | } 139 | State::Writing(ref mut state) => { 140 | if state.line_len < break_line_at { 141 | if !state.list_beginning { 142 | self.out.push(' '); 143 | state.line_len += 1; 144 | } 145 | self.out.push_str(atom); 146 | state.line_len += atom.len(); 147 | } else { 148 | self.out.push_str(self.style.line_break); 149 | let indent_len = 150 | Self::write_indent(self.style.indentation, state.stack.len() + 1, self.out); 151 | self.out.push_str(atom); 152 | state.current_list_line_broken = true; 153 | state.line_len = indent_len + atom.len(); 154 | } 155 | state.list_beginning = false; 156 | } 157 | State::Finished => panic!("writing already finished"), 158 | } 159 | } 160 | 161 | #[allow(clippy::branches_sharing_code)] 162 | pub fn begin_list(&mut self, break_line_at: usize) { 163 | match self.state { 164 | State::Beginning => { 165 | self.out.push('('); 166 | self.state = State::Writing(WritingState { 167 | stack: Vec::new(), 168 | list_beginning: true, 169 | current_list_line_broken: false, 170 | line_len: 1, 171 | }); 172 | } 173 | State::Writing(ref mut state) => { 174 | if state.line_len < break_line_at { 175 | if !state.list_beginning { 176 | self.out.push(' '); 177 | state.line_len += 1; 178 | } 179 | self.out.push('('); 180 | state.line_len += 1; 181 | } else { 182 | self.out.push_str(self.style.line_break); 183 | state.line_len = 184 | Self::write_indent(self.style.indentation, state.stack.len() + 1, self.out); 185 | self.out.push('('); 186 | state.current_list_line_broken = true; 187 | state.line_len += 1; 188 | } 189 | 190 | state.stack.push(StackItem { line_broken: state.current_list_line_broken }); 191 | state.list_beginning = true; 192 | state.current_list_line_broken = false; 193 | } 194 | State::Finished => panic!("writing already finished"), 195 | } 196 | } 197 | 198 | pub fn end_list(&mut self) { 199 | match self.state { 200 | State::Beginning => panic!("no list to end"), 201 | State::Writing(ref mut state) => { 202 | if state.current_list_line_broken { 203 | self.out.push_str(self.style.line_break); 204 | state.line_len = 205 | Self::write_indent(self.style.indentation, state.stack.len(), self.out); 206 | } 207 | self.out.push(')'); 208 | state.line_len += 1; 209 | 210 | if let Some(previous) = state.stack.pop() { 211 | state.current_list_line_broken |= previous.line_broken; 212 | state.list_beginning = false; 213 | } else { 214 | self.state = State::Finished; 215 | } 216 | } 217 | State::Finished => panic!("writing already finished"), 218 | } 219 | } 220 | 221 | pub fn finish(self, insert_line_break: bool) { 222 | match self.state { 223 | State::Finished => { 224 | if insert_line_break { 225 | self.out.push_str(self.style.line_break); 226 | } 227 | } 228 | _ => panic!("writing not finished yet"), 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /cierra/sexp/src/utils.rs: -------------------------------------------------------------------------------- 1 | /// Returns whether `chr` is a valid atom character outside a 2 | /// string (i.e. one of `:atomchar:` documented at `TreeNode::Atom`). 3 | #[inline] 4 | pub const fn is_atom_chr(chr: char) -> bool { 5 | matches!( 6 | chr, 7 | '!' | '#' 8 | | '$' 9 | | '%' 10 | | '&' 11 | | '*' 12 | | '+' 13 | | '-' 14 | | '.' 15 | | '/' 16 | | '0'..='9' 17 | | ':' 18 | | '<' 19 | | '=' 20 | | '>' 21 | | '?' 22 | | '@' 23 | | 'A'..='Z' 24 | | '_' 25 | | 'a'..='z' 26 | | '~' 27 | | '[' 28 | | ']' 29 | | ' ' 30 | ) 31 | } 32 | 33 | /// Returns whether `chr` is a valid atom character inside a 34 | /// string, excluding `"` and `\` (i.e. one of `:stringchar:` 35 | /// documented at `TreeNode::Atom`). 36 | #[inline] 37 | pub const fn is_atom_string_chr(chr: char) -> bool { 38 | matches!(chr, ' '..='~' if chr != '"' && chr != '\\') 39 | } 40 | 41 | /// Checks whether `atom` is a valid atom (i.e. matches the regular 42 | /// expression documented at `TreeNode::Atom`). 43 | pub fn check_atom(atom: &str) -> bool { 44 | if atom.is_empty() { 45 | // Empty atom 46 | return false; 47 | } 48 | 49 | let mut iter = atom.chars(); 50 | let mut in_string = false; 51 | loop { 52 | if in_string { 53 | match iter.next() { 54 | Some('"') => in_string = false, 55 | Some('\\') => match iter.next() { 56 | Some('"' | '\\') => {} 57 | Some(chr) if is_atom_string_chr(chr) => {} 58 | // Invalid character or unfinished string 59 | Some(_) | None => return false, 60 | }, 61 | Some(chr) if is_atom_string_chr(chr) => {} 62 | // Invalid character or Unfinished string 63 | Some(_) | None => return false, 64 | } 65 | } else { 66 | match iter.next() { 67 | None => return true, 68 | Some('"') => in_string = true, 69 | Some(chr) if is_atom_chr(chr) => {} 70 | // Invalid character 71 | Some(_) => return false, 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /cmake/3rd.cmake: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # 3rd.cmake for Simple-XX/SimpleCompiler. 6 | # 依赖管理 7 | 8 | # 设置依赖下载路径 9 | set(CPM_SOURCE_CACHE ${PROJECT_SOURCE_DIR}/3rd) 10 | # 优先使用本地文件 11 | set(CPM_USE_LOCAL_PACKAGES True) 12 | # https://github.com/cpm-cmake/CPM.cmake 13 | # -------- get_cpm.cmake -------- 14 | set(CPM_DOWNLOAD_VERSION 0.38.2) 15 | 16 | if (CPM_SOURCE_CACHE) 17 | set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 18 | elseif (DEFINED ENV{CPM_SOURCE_CACHE}) 19 | set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 20 | else () 21 | set(CPM_DOWNLOAD_LOCATION "${PROJECT_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 22 | endif () 23 | 24 | # Expand relative path. This is important if the provided path contains a tilde (~) 25 | get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) 26 | 27 | function(download_cpm) 28 | message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") 29 | file(DOWNLOAD 30 | https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake 31 | ${CPM_DOWNLOAD_LOCATION} 32 | ) 33 | endfunction() 34 | 35 | if (NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) 36 | download_cpm() 37 | else () 38 | # resume download if it previously failed 39 | file(READ ${CPM_DOWNLOAD_LOCATION} check) 40 | if ("${check}" STREQUAL "") 41 | download_cpm() 42 | endif () 43 | unset(check) 44 | endif () 45 | 46 | include(${CPM_DOWNLOAD_LOCATION}) 47 | # -------- get_cpm.cmake -------- 48 | 49 | # https://github.com/google/googletest 50 | CPMAddPackage( 51 | NAME googletest 52 | GITHUB_REPOSITORY google/googletest 53 | GIT_TAG v1.14.0 54 | VERSION 1.14.0 55 | OPTIONS 56 | "INSTALL_GTEST OFF" 57 | "gtest_force_shared_crt ON" 58 | ) 59 | 60 | # https://github.com/TheLartians/PackageProject.cmake 61 | CPMAddPackage("gh:TheLartians/PackageProject.cmake@1.11.0") 62 | 63 | # https://github.com/cpm-cmake/CPMLicenses.cmake 64 | # 保持在 CPMAddPackage 的最后 65 | CPMAddPackage( 66 | NAME CPMLicenses.cmake 67 | GITHUB_REPOSITORY cpm-cmake/CPMLicenses.cmake 68 | VERSION 0.0.7 69 | ) 70 | if (CPMLicenses.cmake_ADDED) 71 | cpm_licenses_create_disclaimer_target( 72 | write-licenses "${CMAKE_CURRENT_SOURCE_DIR}/3rd/LICENSE" "${CPM_PACKAGES}" 73 | ) 74 | endif () 75 | # make 时自动在 3rd 文件夹下生成 LICENSE 文件 76 | add_custom_target(3rd_licenses 77 | ALL 78 | COMMAND 79 | make 80 | write-licenses 81 | ) 82 | 83 | # doxygen 84 | find_package(Doxygen 85 | REQUIRED dot) 86 | if (NOT DOXYGEN_FOUND) 87 | message(FATAL_ERROR "Doxygen not found.\n" 88 | "Following https://www.doxygen.nl/index.html to install.") 89 | endif () 90 | 91 | # cppcheck 92 | find_program(CPPCHECK_EXE NAMES cppcheck) 93 | if (NOT CPPCHECK_EXE) 94 | message(FATAL_ERROR "cppcheck not found.\n" 95 | "Following https://cppcheck.sourceforge.io to install.") 96 | endif () 97 | add_custom_target(cppcheck 98 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 99 | COMMENT "Run cppcheck on ${PROJECT_BINARY_DIR}/compile_commands.json ..." 100 | COMMAND 101 | ${CPPCHECK_EXE} 102 | --enable=all 103 | --project=${PROJECT_BINARY_DIR}/compile_commands.json 104 | --suppress-xml=${PROJECT_SOURCE_DIR}/tools/cppcheck-suppressions.xml 105 | --output-file=${PROJECT_BINARY_DIR}/cppcheck_report.log 106 | ) 107 | 108 | # 获取全部源文件 109 | file(GLOB_RECURSE ALL_SOURCE_FILES 110 | ${PROJECT_SOURCE_DIR}/src/*.h 111 | ${PROJECT_SOURCE_DIR}/src/*.hpp 112 | ${PROJECT_SOURCE_DIR}/src/*.c 113 | ${PROJECT_SOURCE_DIR}/src/*.cpp 114 | ${PROJECT_SOURCE_DIR}/test/*.h 115 | ${PROJECT_SOURCE_DIR}/test/*.hpp 116 | ${PROJECT_SOURCE_DIR}/test/*.c 117 | ${PROJECT_SOURCE_DIR}/test/*.cpp 118 | ) 119 | 120 | # clang-tidy 121 | find_program(CLANG_TIDY_EXE NAMES clang-tidy) 122 | if (NOT CLANG_TIDY_EXE) 123 | message(FATAL_ERROR "clang-tidy not found.\n" 124 | "Following https://clang.llvm.org/extra/clang-tidy to install.") 125 | endif () 126 | add_custom_target(clang-tidy 127 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 128 | COMMENT "Run clang-tidy on ${ALL_SOURCE_FILES} ..." 129 | COMMAND 130 | ${CLANG_TIDY_EXE} 131 | --config-file=${PROJECT_SOURCE_DIR}/.clang-tidy 132 | -p=${PROJECT_BINARY_DIR} 133 | ${ALL_SOURCE_FILES} 134 | > ${PROJECT_BINARY_DIR}/clang_tidy_report.log 2>&1 135 | ) 136 | 137 | # clang-format 138 | find_program(CLANG_FORMAT_EXE NAMES clang-format) 139 | if (NOT CLANG_FORMAT_EXE) 140 | message(FATAL_ERROR "clang-format not found.\n" 141 | "Following https://clang.llvm.org/docs/ClangFormat.html to install.") 142 | endif () 143 | add_custom_target(clang-format 144 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 145 | COMMENT "Run clang-format on ${ALL_SOURCE_FILES} ..." 146 | COMMAND ${CLANG_FORMAT_EXE} -i -style=file ${ALL_SOURCE_FILES} 147 | ) 148 | 149 | # genhtml 生成测试覆盖率报告网页 150 | find_program(GENHTML_EXE genhtml) 151 | if (NOT GENHTML_EXE) 152 | message(FATAL_ERROR "genhtml not found.\n" 153 | "Following https://github.com/linux-test-project/lcov to install.") 154 | endif () 155 | 156 | # lcov 生成测试覆盖率报告 157 | find_program(LCOV_EXE lcov) 158 | if (NOT LCOV_EXE) 159 | message(FATAL_ERROR "lcov not found.\n" 160 | "Following https://github.com/linux-test-project/lcov to install.") 161 | endif () 162 | 163 | find_package(spdlog REQUIRED) 164 | if (NOT spdlog_FOUND) 165 | message(FATAL_ERROR "spdlog not found.\n" 166 | "Following https://github.com/gabime/spdlog to install.") 167 | endif () 168 | -------------------------------------------------------------------------------- /cmake/add_header.cmake: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # add_header.cmake for Simple-XX/SimpleCompiler. 6 | # 将头文件路径添加到 _target 的搜索路径中 7 | 8 | function(add_header_3rd _target) 9 | target_include_directories(${_target} PRIVATE 10 | ${tinyobjloader_SOURCE_DIR}) 11 | endfunction() 12 | -------------------------------------------------------------------------------- /cmake/compile_config.cmake: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # compile_config.cmake for Simple-XX/SimpleCompiler. 6 | # 配置信息 7 | 8 | # 编译选项 9 | list(APPEND DEFAULT_COMPILE_OPTIONS 10 | -Wall 11 | -Wextra 12 | $<$:-O3;-Werror> 13 | $<$:-O0;-g;-ggdb> 14 | ) 15 | 16 | list(APPEND DEFAULT_LINK_LIB 17 | spdlog::spdlog 18 | ) 19 | -------------------------------------------------------------------------------- /cmake/config.h.in: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file config.h 4 | * @brief 项目配置 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N创建文件 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_CONFIG_H 18 | #define SIMPLECOMPILER_CONFIG_H 19 | 20 | #include 21 | #include 22 | 23 | namespace SimpleCompiler { 24 | 25 | /// 日志文件路径 26 | static const std::string LOG_FILE_PATH = 27 | std::string("@LOG_FILE_PATH@"); 28 | /// 日志文件大小 29 | static constexpr const size_t LOG_FILE_MAX_SIZE = @LOG_FILE_MAX_SIZE@; 30 | /// 日志文件数量 31 | static constexpr const size_t LOG_FILE_MAX_COUNT = @LOG_FILE_MAX_COUNT@; 32 | 33 | } // namespace SimpleCompiler 34 | 35 | #endif /* SIMPLECOMPILER_CONFIG_H */ 36 | -------------------------------------------------------------------------------- /cmake/functions.cmake: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # functions.cmake for Simple-XX/SimpleCompiler. 6 | # 辅助函数 7 | 8 | # 添加测试覆盖率 target 9 | # DEPENDS 要生成的 targets 10 | # SOURCE_DIR 源码路径 11 | # BINARY_DIR 二进制文件路径 12 | # EXCLUDE_DIR 要排除的目录 13 | function(add_coverage_target) 14 | # 解析参数 15 | set(options) 16 | set(one_value_keywords SOURCE_DIR BINARY_DIR) 17 | set(multi_value_keywords DEPENDS EXCLUDE_DIR) 18 | cmake_parse_arguments( 19 | ARG "${options}" "${one_value_keywords}" "${multi_value_keywords}" ${ARGN} 20 | ) 21 | 22 | # 不检查的目录 23 | list(APPEND EXCLUDES --exclude) 24 | foreach (_item ${ARG_EXCLUDE_DIR}) 25 | list(APPEND EXCLUDES '${_item}') 26 | endforeach () 27 | 28 | # 添加 target 29 | add_custom_target(coverage DEPENDS ${ARG_DEPENDS} 30 | COMMAND ${CMAKE_CTEST_COMMAND} 31 | ) 32 | # 在 coverage 执行完毕后生成报告 33 | add_custom_command(TARGET coverage 34 | COMMENT "Generating coverage report ..." 35 | POST_BUILD 36 | WORKING_DIRECTORY ${ARG_BINARY_DIR} 37 | COMMAND ${CMAKE_COMMAND} -E make_directory ${COVERAGE_OUTPUT_DIR} 38 | COMMAND ${LCOV_EXE} 39 | -c 40 | -o ${COVERAGE_OUTPUT_DIR}/coverage.info 41 | -d ${ARG_BINARY_DIR} 42 | -b ${ARG_SOURCE_DIR} 43 | --no-external 44 | ${EXCLUDES} 45 | --rc lcov_branch_coverage=1 46 | COMMAND ${GENHTML_EXE} 47 | ${COVERAGE_OUTPUT_DIR}/coverage.info 48 | -o ${COVERAGE_OUTPUT_DIR} 49 | --branch-coverage 50 | ) 51 | endfunction() 52 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # CMakeLists.txt for Simple-XX/SimpleCompiler. 6 | 7 | # 设置项目名与版本 8 | project( 9 | doc 10 | VERSION 0.0.1 11 | ) 12 | 13 | # 设置 doxygen 相关参数 14 | set(DOXYGEN_HAVE_DOT YES) 15 | set(DOXYGEN_DOT_MULTI_TARGETS YES) 16 | set(DOXYGEN_GENERATE_LATEX NO) 17 | set(DOXYGEN_PROJECT_NAME ${SimpleRenderer_PROJECT_NAME}) 18 | set(DOXYGEN_PROJECT_NUMBER ${SimpleRenderer_PROJECT_VERSION}) 19 | set(DOXYGEN_PROJECT_BRIEF ${PROJECT_DESCRIPTION}) 20 | set(DOXYGEN_RECURSIVE YES) 21 | set(DOXYGEN_EXCLUDE_PATTERNS */3rd/*, */.vscode/*, */.idea/*, */.github/*, */.git/*, */build*/*, */cmake-/*) 22 | set(DOXYGEN_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) 23 | set(DOXYGEN_EXTRACT_ALL YES) 24 | set(DOXYGEN_EXTRACT_PRIVATE YES) 25 | set(DOXYGEN_EXTRACT_STATIC YES) 26 | set(DOXYGEN_EXTRACT_LOCAL_CLASSES YES) 27 | set(DOXYGEN_SOURCE_BROWSER YES) 28 | set(DOXYGEN_INLINE_SOURCES YES) 29 | set(DOXYGEN_ALPHABETICAL_INDEX YES) 30 | set(DOXYGEN_GENERATE_TREEVIEW YES) 31 | set(DOXYGEN_ENABLE_PREPROCESSING YES) 32 | set(DOXYGEN_CLASS_DIAGRAMS YES) 33 | set(DOXYGEN_CLASS_GRAPH YES) 34 | set(DOXYGEN_GRAPHICAL_HIERARCHY YES) 35 | set(DOXYGEN_CALLER_GRAPH YES) 36 | set(DOXYGEN_CALL_GRAPH YES) 37 | set(DOXYGEN_UML_LOOK YES) 38 | set(DOXYGEN_HTML_TIMESTAMP YES) 39 | 40 | # 创建 target 并通过 VERBATIM 将 cmake 参数传递给 doxygen 41 | doxygen_add_docs(doc 42 | COMMENT "Generating docs at ${PROJECT_BINARY_DIR}/html/index.html ..." 43 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 44 | ${SimpleRenderer_SOURCE_DIR} 45 | ) 46 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # CMakeLists.txt for Simple-XX/SimpleCompiler. 6 | 7 | add_executable(SimpleCompiler 8 | codegen/codegen.cpp 9 | error/error.cpp 10 | ir/irgen.cpp 11 | ir/irlexer.cpp 12 | ir/irparser.cpp 13 | ir/lowirgen.cpp 14 | lexical/lexical.cpp 15 | lexical/token.cpp 16 | parser/parser.cpp 17 | parser/type.cpp 18 | scanner/scanner.cpp 19 | typechecker/eval.cpp 20 | typechecker/typechecker.cpp 21 | log.cpp 22 | init.cpp 23 | main.cpp 24 | ) 25 | 26 | target_include_directories(SimpleCompiler PRIVATE 27 | include 28 | ) 29 | 30 | target_compile_options(SimpleCompiler PRIVATE 31 | ${DEFAULT_COMPILE_OPTIONS} 32 | ) 33 | 34 | target_link_libraries(SimpleCompiler PRIVATE 35 | ${DEFAULT_LINK_LIB} 36 | ) 37 | -------------------------------------------------------------------------------- /src/error/error.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file error.cpp 4 | * @brief 错误处理 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include "error.h" 18 | #include "log.h" 19 | 20 | Pos::Pos(unsigned int _l, unsigned int _c) : line(_l), col(_c) { return; } 21 | 22 | Pos::~Pos() { return; } 23 | 24 | Error::Error(const std::string &_f) : filename(_f) { 25 | err_no = 0; 26 | pos = new Pos(1, 1); 27 | return; 28 | } 29 | 30 | Error::~Error() { 31 | if (pos != NULL) { 32 | delete pos; 33 | } 34 | return; 35 | } 36 | 37 | void Error::set_line(unsigned int _l) { 38 | pos->line = _l; 39 | return; 40 | } 41 | 42 | void Error::set_col(unsigned int _c) { 43 | pos->col = _c; 44 | return; 45 | } 46 | 47 | void Error::set_err_no(int _e) { 48 | err_no = _e; 49 | return; 50 | } 51 | 52 | int Error::get_err_no() const { return err_no; } 53 | 54 | Pos *Error::get_pos() const { return pos; } 55 | 56 | void Error::display_err() const { 57 | SPDLOG_LOGGER_ERROR(SCLOG, "[ERROR] File: {}, Line: {}, Pos: {}", filename, 58 | pos->line, pos->col); 59 | return; 60 | } 61 | -------------------------------------------------------------------------------- /src/include/codegen.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file codegen.h 4 | * @brief codegen 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_CODEGEN_H 18 | #define SIMPLECOMPILER_CODEGEN_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | class CodeGen { 25 | std::map funcStack; 26 | std::string currentFunc; 27 | std::istream &cinstream; 28 | 29 | public: 30 | void Generate(std::string &_code); 31 | CodeGen(std::istream &_cinstream) : cinstream(_cinstream) {} 32 | }; 33 | 34 | #endif /* SIMPLECOMPILER_CODEGEN_H */ 35 | -------------------------------------------------------------------------------- /src/include/common.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file common.h 4 | * @brief common 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_COMMON_H 18 | #define SIMPLECOMPILER_COMMON_H 19 | 20 | #include "ast.h" 21 | #include "codegen.h" 22 | #include "error.h" 23 | #include "init.h" 24 | #include "ir.h" 25 | #include "irast.h" 26 | #include "irgen.h" 27 | #include "irlexer.h" 28 | #include "irparser.h" 29 | #include "lexical.h" 30 | #include "lowirgen.h" 31 | #include "parser.h" 32 | #include "scanner.h" 33 | #include "typechecker.h" 34 | #include "utils.h" 35 | 36 | class Init; 37 | class Error; 38 | class Lexer; 39 | class Scanner; 40 | class Parser; 41 | class TypeChecker; 42 | class IRGenerator; 43 | class IRLexer; 44 | class IRParser; 45 | class LowIRGenerator; 46 | class Codegen; 47 | 48 | extern Error *error; 49 | 50 | extern const char *tokenName[]; 51 | 52 | #endif /* SIMPLECOMPILER_COMMON_H */ 53 | -------------------------------------------------------------------------------- /src/include/error.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file error.h 4 | * @brief error 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_ERROR_H 18 | #define SIMPLECOMPILER_ERROR_H 19 | 20 | #include 21 | #include 22 | 23 | class Pos { 24 | public: 25 | Pos(unsigned int _l, unsigned int _c); 26 | ~Pos(); 27 | // 保存当前行号 28 | unsigned int line; 29 | // 保存当前列号 30 | unsigned int col; 31 | }; 32 | 33 | // 错误处理 34 | class Error { 35 | private: 36 | // 保存当前文件名 37 | const std::string filename; 38 | // 保存当前错误号 39 | int err_no; 40 | // 保存错误位置 41 | Pos *pos; 42 | 43 | public: 44 | Error(const std::string &_f); 45 | virtual ~Error(); 46 | void set_line(unsigned int _l); 47 | void set_col(unsigned int _c); 48 | void set_err_no(int _e); 49 | int get_err_no() const; 50 | Pos *get_pos() const; 51 | virtual void display_err() const; 52 | }; 53 | 54 | #endif /* SIMPLECOMPILER_ERROR_H */ 55 | -------------------------------------------------------------------------------- /src/include/init.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file init.h 4 | * @brief init 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_INIT_H 18 | #define SIMPLECOMPILER_INIT_H 19 | 20 | #include 21 | #include 22 | 23 | // 源文件 24 | extern std::vector src_files; 25 | // 输出文件 26 | extern std::string dest_file; 27 | 28 | class Init { 29 | private: 30 | // 绝对路径 31 | std::string abs_path; 32 | // 路径缓存大小 33 | static const int PATH_BUFFER = 1024; 34 | // 路径缓存 35 | char abs_path_buffer[PATH_BUFFER]; 36 | // 用于接收选项 37 | int index; 38 | int c; 39 | 40 | public: 41 | Init(); 42 | ~Init(); 43 | int init(int &_argc, char **&_argv); 44 | }; 45 | 46 | #endif /* SIMPLECOMPILER_INIT_H */ 47 | -------------------------------------------------------------------------------- /src/include/ir.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file ir.h 4 | * @brief ir 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_IR_H 18 | #define SIMPLECOMPILER_IR_H 19 | 20 | enum IRToken { 21 | VARDECL_IR, 22 | NUMBER_IR, 23 | SYMBOL_IR, 24 | LSB_IR, 25 | RSB_IR, 26 | ASSIGN_IR, 27 | FUNCEND_IR, 28 | END_IR, 29 | GOTO_IR, 30 | IF_IR, 31 | PARAM_IR, 32 | CALL_IR, 33 | RETURN_IR, 34 | OP_IR, 35 | COLON_IR, 36 | LOGICOP_IR, 37 | COMMENT_IR 38 | }; 39 | 40 | #endif /* SIMPLECOMPILER_IR_H */ -------------------------------------------------------------------------------- /src/include/irast.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file irast.h 4 | * @brief irast 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_IRAST_H 18 | #define SIMPLECOMPILER_IRAST_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "ir.h" 29 | #include "utils.h" 30 | 31 | class LowIRGenerator; 32 | 33 | class BaseIR { 34 | public: 35 | int lineno; 36 | BaseIR(int l) : lineno(l) {} 37 | virtual ~BaseIR() = default; 38 | virtual std::string Generate(LowIRGenerator &_generator, 39 | std::string &_code) = 0; 40 | }; 41 | 42 | using IRPtr = std::unique_ptr; 43 | using IRPtrList = std::vector; 44 | 45 | class DeclIR : public BaseIR { 46 | VarType varType; 47 | int size; 48 | std::string name; 49 | 50 | public: 51 | DeclIR(VarType _type, int _size, std::string _name, int _line) 52 | : BaseIR(_line), varType(_type), size(_size), name(std::move(_name)) {} 53 | 54 | VarType getType() const { return varType; } 55 | int getSize() const { return size; } 56 | const std::string &getName() const { return name; } 57 | 58 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 59 | }; 60 | 61 | class InitIR : public BaseIR { 62 | VarType varType; 63 | std::string name; 64 | int pos; 65 | int val; 66 | 67 | public: 68 | InitIR(VarType _type, std::string _name, int _p, int _v, int _line) 69 | : BaseIR(_line), varType(_type), name(std::move(_name)), pos(_p), 70 | val(_v) {} 71 | 72 | VarType getType() const { return varType; } 73 | int getPos() const { return pos; } 74 | int getVal() const { return val; } 75 | const std::string &getName() const { return name; } 76 | 77 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 78 | }; 79 | 80 | class FuncDefIR : public BaseIR { 81 | std::string name; 82 | int paramNum; 83 | IRPtr body; 84 | 85 | public: 86 | FuncDefIR(std::string _name, int _param, IRPtr _body, int _line) 87 | : BaseIR(_line), name(std::move(_name)), paramNum(_param), 88 | body(std::move(_body)) {} 89 | 90 | const std::string &getName() const { return name; } 91 | int getParamNum() const { return paramNum; } 92 | const IRPtr &getBody() const { return body; } 93 | 94 | virtual std::string Generate(LowIRGenerator &generator, std::string &code); 95 | }; 96 | 97 | class StatementsIR : public BaseIR { 98 | IRPtrList stmts; 99 | 100 | public: 101 | StatementsIR(IRPtrList _stmt, int _line) 102 | : BaseIR(_line), stmts(std::move(_stmt)) {} 103 | 104 | const IRPtrList &getStmts() const { return stmts; } 105 | 106 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 107 | }; 108 | 109 | class BinaryExpIR : public BaseIR { 110 | IRPtr lhs; // right value 111 | IRPtr rhs; // right value 112 | Operator op; 113 | 114 | public: 115 | BinaryExpIR(IRPtr _l, IRPtr _r, Operator _op, int _line) 116 | : BaseIR(_line), lhs(std::move(_l)), rhs(std::move(_r)), op(_op) {} 117 | 118 | const IRPtr &getLHS() const { return lhs; } 119 | const IRPtr &getRHS() const { return rhs; } 120 | const Operator &getOp() const { return op; } 121 | 122 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 123 | }; 124 | 125 | class UnaryExpIR : public BaseIR { 126 | Operator op; 127 | IRPtr exp; // right value; 128 | 129 | public: 130 | UnaryExpIR(IRPtr _exp, Operator _op, int _line) 131 | : BaseIR(_line), op(_op), exp(std::move(_exp)) {} 132 | 133 | const IRPtr &getExp() const { return exp; } 134 | const Operator &getOp() const { return op; } 135 | 136 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 137 | }; 138 | 139 | class AssignIR : public BaseIR { 140 | IRPtr lhs; 141 | IRPtr rhs; 142 | 143 | public: 144 | AssignIR(IRPtr _l, IRPtr _r, int _line) 145 | : BaseIR(_line), lhs(std::move(_l)), rhs(std::move(_r)) {} 146 | 147 | const IRPtr &getLHS() const { return lhs; } 148 | const IRPtr &getRHS() const { return rhs; } 149 | 150 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 151 | }; 152 | 153 | class CondGotoIR : public BaseIR { 154 | IRPtr cond; 155 | int labelNum; // label 156 | 157 | public: 158 | CondGotoIR(IRPtr _cond, int _num, int _line) 159 | : BaseIR(_line), cond(std::move(_cond)), labelNum(_num) {} 160 | 161 | const IRPtr &getCond() const { return cond; } 162 | int getLabel() const { return labelNum; } 163 | 164 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 165 | }; 166 | 167 | class LValIR : public BaseIR { 168 | VarType varType; 169 | std::string name; 170 | IRPtr pos; 171 | 172 | public: 173 | LValIR(VarType _var, std::string _n, int _line, IRPtr _p = nullptr) 174 | : BaseIR(_line), varType(_var), name(std::move(_n)), pos(std::move(_p)) {} 175 | 176 | VarType getType() const { return varType; } 177 | const IRPtr &getPos() const { return pos; } 178 | const std::string &getName() const { return name; } 179 | 180 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 181 | }; 182 | 183 | class GotoIR : public BaseIR { 184 | int label; // label 185 | 186 | public: 187 | GotoIR(int _l, int _line) : BaseIR(_line), label(_l) {} 188 | 189 | int getLabel() const { return label; } 190 | 191 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 192 | }; 193 | 194 | class Label : public BaseIR { 195 | int num; 196 | 197 | public: 198 | Label(int _n, int _line) : BaseIR(_line), num(_n) {} 199 | 200 | int getNum() const { return num; } 201 | 202 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 203 | }; 204 | 205 | class ParamListIR : public BaseIR { 206 | IRPtrList params; // right value; 207 | 208 | public: 209 | ParamListIR(IRPtrList _p, int _line) : BaseIR(_line), params(std::move(_p)) {} 210 | 211 | const IRPtrList &getParams() const { return params; } 212 | 213 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 214 | }; 215 | 216 | class FuncCallIR : public BaseIR { 217 | std::string funcName; 218 | 219 | public: 220 | FuncCallIR(std::string _name, int _line) 221 | : BaseIR(_line), funcName(std::move(_name)) {} 222 | 223 | const std::string &getName() const { return funcName; } 224 | 225 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 226 | }; 227 | 228 | class ReturnIR : public BaseIR { 229 | IRPtr ret; // right value or null 230 | 231 | public: 232 | ReturnIR(IRPtr _r, int _line) : BaseIR(_line), ret(std::move(_r)) {} 233 | 234 | const IRPtr &getReturnValue() const { return ret; } 235 | 236 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 237 | }; 238 | 239 | class RightValIR : public BaseIR { 240 | IRToken type; 241 | std::string name; 242 | int value; 243 | 244 | public: 245 | RightValIR(IRToken _t, std::string _name, int _line) 246 | : BaseIR(_line), type(_t), name(std::move(_name)) {} 247 | RightValIR(IRToken _t, int val, int line) 248 | : BaseIR(line), type(_t), value(val) {} 249 | 250 | const IRToken &getType() const { return type; } 251 | int getVal() const { return value; } 252 | const std::string &getName() const { return name; } 253 | 254 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 255 | }; 256 | 257 | class ProgramIR : public BaseIR { 258 | IRPtrList nodes; 259 | 260 | public: 261 | ProgramIR(IRPtrList _n, int _line) : BaseIR(_line), nodes(std::move(_n)) {} 262 | 263 | const IRPtrList &getNodes() const { return nodes; } 264 | 265 | virtual std::string Generate(LowIRGenerator &_generator, std::string &_code); 266 | }; 267 | 268 | #endif /* SIMPLECOMPILER_IRAST_H */ 269 | -------------------------------------------------------------------------------- /src/include/irgen.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file irgen.h 4 | * @brief irgen 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_IRGEN_H 18 | #define SIMPLECOMPILER_IRGEN_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "ast.h" 25 | #include "utils.h" 26 | 27 | class IRGenerator { 28 | private: 29 | int t_num; 30 | int T_num; 31 | int l_num; 32 | int cur_break_l; 33 | int cur_continue_l; 34 | int currentDepth; 35 | int currentBlock; 36 | std::string currentFunc; 37 | 38 | std::map> BlockSymbolTable; 39 | std::vector parentBlock; 40 | std::map FuncTable; 41 | std::vector ReverseSymbolTable; 42 | 43 | public: 44 | IRGenerator(std::map _FuncTable, 45 | const std::map> &_BlockVars) 46 | : FuncTable(std::move(_FuncTable)) { 47 | for (auto &item1 : _BlockVars) { 48 | for (auto &item2 : item1.second) { 49 | if (item2.second.isConst && item2.second.type == VarType::var_t) 50 | continue; 51 | BlockSymbolTable[item1.first][item2.first] = 52 | GenVar(item2.second.name, item2.second.type, item2.second.dims); 53 | } 54 | } 55 | 56 | t_num = 0; 57 | T_num = 0; 58 | l_num = 0; 59 | cur_break_l = -1; 60 | cur_continue_l = -1; 61 | currentDepth = 0; 62 | currentBlock = 0; 63 | currentFunc = ""; 64 | } 65 | 66 | void GenerateValue(const std::string &_varName, int &_idx, int _indx, 67 | InitValAST *_init, std::vector _dim, int _i, 68 | std::string &_code); 69 | 70 | void GenVarDecl(VarDeclAST &_varDecl, std::string &_code); 71 | 72 | std::string GenId(ProcessedIdAST &_id, std::string &_code); 73 | 74 | std::string GenNumber(NumAST &_num, std::string &_code); 75 | 76 | std::string GenVarDef(VarDefAST &_varDef, std::string &_code); 77 | 78 | std::string GenAssign(AssignAST &_assign, std::string &_code); 79 | 80 | std::string GenBinaryExp(BinaryAST &_exp, std::string &_code); 81 | 82 | std::string GenInitVal(InitValAST &_init, std::string &_code); 83 | 84 | static std::string op2char(Operator _op); 85 | 86 | void GenBlock(BlockAST &_block, std::string &_code); 87 | 88 | std::string GenFuncCall(FuncCallAST &_func, std::string &_code); 89 | 90 | std::string GenLVal(LValAST &_lval, std::string &_code); 91 | 92 | std::string GenUnaryExp(UnaryAST &_exp, std::string &_code); 93 | 94 | std::string GenLAndExp(BinaryAST &_exp, std::string &_code); 95 | 96 | std::string GenLOrExp(BinaryAST &_exp, std::string &_code); 97 | 98 | void GenFuncDef(FuncDefAST &_funcDef, std::string &_code); 99 | 100 | void GenCompUnit(CompUnitAST &_unit, std::string &_code); 101 | 102 | void GenIfElse(IfAST &_stmt, std::string &_code); 103 | 104 | void GenWhile(WhileAST &_stmt, std::string &_code); 105 | 106 | void GenControl(ControlAST &_stmt, std::string &_code); 107 | 108 | void GenStmt(StmtAST &_stmt, std::string &_code); 109 | }; 110 | 111 | #endif /* SIMPLECOMPILER_IRGEN_H */ 112 | -------------------------------------------------------------------------------- /src/include/irlexer.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file irlexer.h 4 | * @brief irlexer 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_IRLEXER_H 18 | #define SIMPLECOMPILER_IRLEXER_H 19 | 20 | #include 21 | #include 22 | 23 | #include "ir.h" 24 | #include "type.h" 25 | 26 | class IRLexer { 27 | public: 28 | std::istream &cinstream; 29 | IRLexer(std::istream &_cinstream) : cinstream(_cinstream){}; 30 | ~IRLexer() = default; 31 | 32 | IRToken NextIRToken(); 33 | Operator getOp() { return op; } 34 | std::string getName() { return name; } 35 | int getVal() const { return value; } 36 | int getLineno() const { return lineno; } 37 | 38 | private: 39 | int value; 40 | std::string name; 41 | Operator op; 42 | int lineno; 43 | 44 | IRToken ParseNum(); 45 | IRToken ParseSymbol(); 46 | IRToken ParseComment(); 47 | }; 48 | 49 | #endif /* SIMPLECOMPILER_IRLEXER_H */ 50 | -------------------------------------------------------------------------------- /src/include/irparser.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file irparser.h 4 | * @brief irparser 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_IRPARSER_H 18 | #define SIMPLECOMPILER_IRPARSER_H 19 | 20 | #include 21 | 22 | #include "ir.h" 23 | #include "irast.h" 24 | #include "irlexer.h" 25 | 26 | class IRParser { 27 | public: 28 | IRParser(std::istream &_cinstream) : lexer(_cinstream) {} 29 | 30 | void NextIRToken(); 31 | 32 | IRPtr ParseDecl(); 33 | 34 | IRPtr ParseInit(); 35 | 36 | IRPtr ParseFuncDef(); 37 | 38 | IRPtr ParseStatements(); 39 | 40 | IRPtr ParseAssign(); 41 | 42 | IRPtr ParseCondGoto(); 43 | 44 | IRPtr ParseLVal(); 45 | 46 | IRPtr ParseGoto(); 47 | 48 | IRPtr ParseLabel(); 49 | 50 | IRPtr ParseParams(); 51 | 52 | IRPtr ParseFuncCall(); 53 | 54 | IRPtr ParseReturn(); 55 | 56 | IRPtr ParseProgram(); 57 | 58 | private: 59 | IRLexer lexer; 60 | IRToken current; 61 | }; 62 | 63 | #endif /* SIMPLECOMPILER_IRPARSER_H */ 64 | -------------------------------------------------------------------------------- /src/include/irtoken.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Simple-XX/SimpleCompiler/ccb5391268e2aeafe54280884b6380d44dd12f12/src/include/irtoken.h -------------------------------------------------------------------------------- /src/include/lexical.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file lexical.h 4 | * @brief lexical 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_LEXICAL_H 18 | #define SIMPLECOMPILER_LEXICAL_H 19 | 20 | #include "error.h" 21 | #include "scanner.h" 22 | #include "token.h" 23 | 24 | extern Error *error; 25 | 26 | // blank 条件 27 | #define COND_BLANK ((ch == ' ') || (ch == '\n') || (ch == '\t') || (ch == '\r')) 28 | // identifier 条件 29 | #define COND_IDENTIFIER \ 30 | ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_')) 31 | // number 条件 32 | #define COND_NUMBER (((ch >= '0') && (ch <= '9'))) 33 | // separator 条件 34 | #define COND_SEPARATOR \ 35 | ((ch == '(') || (ch == ')') || (ch == '{') || (ch == '}') || (ch == ',') || \ 36 | (ch == ':') || (ch == ';') || (ch == '[') || (ch == ']')) 37 | // operation 条件 38 | #define COND_OPERATION \ 39 | ((ch == '=') || (ch == '+') || (ch == '-') || (ch == '*') || (ch == '/') || \ 40 | (ch == '%') || (ch == '|') || (ch == '&') || (ch == '^') || (ch == '!') || \ 41 | (ch == '>') || (ch == '<')) 42 | 43 | // 词法分析 44 | class Lexer { 45 | private: 46 | // 扫描器对象,用于从文件中获取字符 47 | Scanner &scanner; 48 | // 关键字 49 | static keywords_t keywords; 50 | // 当前字符 51 | char ch; 52 | // 保存结果 53 | token_base_t *token; 54 | // 扫描 55 | bool scan(char _need = 0); 56 | // 返回错误号 57 | int err(); 58 | // 空白字符 59 | void blank(); 60 | // 标识符,包括关键字 61 | void identifier(); 62 | // 数字 63 | void number(); 64 | // 字符 65 | void character(); 66 | // 字符串 67 | void str(); 68 | // 分界符 69 | void separator(); 70 | // 操作符 71 | void operation(); 72 | 73 | public: 74 | Lexer(Scanner &_sc); 75 | ~Lexer(); 76 | 77 | token_base_t *lexing(); 78 | bool is_done() const; 79 | }; 80 | 81 | #endif /* SIMPLECOMPILER_LEXICAL_H */ 82 | -------------------------------------------------------------------------------- /src/include/log.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file log.h 4 | * @brief 日志封装 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-27 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-27Zone.N创建文件 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_LOG_H 18 | #define SIMPLECOMPILER_LOG_H 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include "config.h" 25 | 26 | extern std::shared_ptr SCLOG; 27 | 28 | void log_init(void); 29 | 30 | #endif /* SIMPLECOMPILER_LOG_H */ 31 | -------------------------------------------------------------------------------- /src/include/lowirgen.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file lowirgen.h 4 | * @brief lowirgen 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_LOWIRGEN_H 18 | #define SIMPLECOMPILER_LOWIRGEN_H 19 | 20 | #include 21 | #include 22 | 23 | #include "ir.h" 24 | #include "irast.h" 25 | 26 | class LowIRGenerator { 27 | private: 28 | std::string currentFunc; 29 | int v_num; 30 | int reg_num; 31 | 32 | struct Variable { 33 | int v_num; 34 | VarType varType; 35 | Variable() {} 36 | Variable(int num, VarType type) : v_num(num), varType(type) {} 37 | }; 38 | 39 | struct StackVar { 40 | int pos_min; 41 | int pos_max; 42 | VarType type; 43 | std::string func; 44 | StackVar() {} 45 | StackVar(int _p_min, int _p_max, std::string _f, VarType _t) 46 | : pos_min(_p_min), pos_max(_p_max), type(_t), func(std::move(_f)) {} 47 | }; 48 | 49 | std::map globalVars; 50 | std::map funcStack; 51 | std::map varStack; 52 | 53 | public: 54 | LowIRGenerator() { 55 | v_num = 0; 56 | currentFunc = ""; 57 | reg_num = 1; 58 | } 59 | 60 | std::string op2char(Operator _op); 61 | 62 | std::string GenDecl(DeclIR &_decl, std::string &_code); 63 | 64 | std::string GenInit(InitIR &_init, std::string &_code); 65 | 66 | std::string GenFuncDef(FuncDefIR &_funcDef, std::string &_code); 67 | 68 | std::string GenStatements(StatementsIR &_stmts, std::string &_code); 69 | 70 | std::string GenBinaryExp(BinaryExpIR &_binary, std::string &_code); 71 | 72 | std::string GenUnaryExp(UnaryExpIR &_unary, std::string &_code); 73 | 74 | std::string GenAssign(AssignIR &_assign, std::string &_code); 75 | 76 | std::string GenCondGoto(CondGotoIR &_cond, std::string &_code); 77 | 78 | std::string GenLVal(LValIR &_lval, std::string &_code); 79 | 80 | std::string GenGoto(GotoIR &_gt, std::string &_code); 81 | 82 | std::string GenLabel(Label &_label, std::string &_code); 83 | 84 | std::string GenParamList(ParamListIR &_params, std::string &_code); 85 | 86 | std::string GenFuncCall(FuncCallIR &_funcCall, std::string &_code); 87 | 88 | std::string GenReturn(ReturnIR &_ret, std::string &_code); 89 | 90 | std::string GenRightVal(RightValIR &_rightval, std::string &_code); 91 | 92 | std::string GenProgram(ProgramIR &_program, std::string &_code); 93 | }; 94 | 95 | #endif /* SIMPLECOMPILER_LOWIRGEN_H */ 96 | -------------------------------------------------------------------------------- /src/include/parser.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file parser.h 4 | * @brief parser 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_PARSER_H 18 | #define SIMPLECOMPILER_PARSER_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "ast.h" 27 | #include "error.h" 28 | #include "lexical.h" 29 | #include "token.h" 30 | 31 | extern Error *error; 32 | 33 | // 语法分析 34 | class Parser { 35 | private: 36 | // 词法分析器 37 | Lexer &lexer; 38 | // 超前查看的 token 39 | token_base_t *token; 40 | // 获取下一个 token 41 | void next(); 42 | // 匹配指定 token_base_t 43 | bool match_token(tag_t _tag); 44 | 45 | // 程序 46 | ASTPtr program(); 47 | // 语句 48 | ASTPtr statement(); 49 | 50 | // 一元表达式 51 | ASTPtr unary(); 52 | // 二元表达式 53 | ASTPtr binary(const std::function &_parser, 54 | std::initializer_list _ops); 55 | ASTPtr binary_add(); 56 | ASTPtr binary_mul(); 57 | ASTPtr binary_relation(); 58 | ASTPtr binary_eq(); 59 | ASTPtr binary_and(); 60 | ASTPtr binary_or(); 61 | 62 | // If then else 63 | ASTPtr if_else(); 64 | // while loop 65 | ASTPtr while_loop(); 66 | // block 67 | ASTPtr block(); 68 | 69 | // var declare 70 | ASTPtr var_decl(); 71 | // var definition 72 | ASTPtr var_def(bool _isConst); 73 | // initial value 74 | ASTPtr init_val(); 75 | 76 | // function def 77 | ASTPtr function_def(); 78 | 79 | public: 80 | Parser(Lexer &_lex); 81 | ~Parser(); 82 | // 进行解析 83 | ASTPtr parsing(); 84 | bool is_done() const; 85 | }; 86 | 87 | #endif /* SIMPLECOMPILER_PARSER_H */ 88 | -------------------------------------------------------------------------------- /src/include/scanner.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file scanner.h 4 | * @brief scanner 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_SCANNER_H 18 | #define SIMPLECOMPILER_SCANNER_H 19 | 20 | #include 21 | #include 22 | 23 | #include "error.h" 24 | 25 | extern Error *error; 26 | 27 | // 扫描器 28 | class Scanner { 29 | private: 30 | // 输入流 31 | std::ifstream fin; 32 | // 前一个读到的字符 33 | char prev_char; 34 | // 当前读到的字符 35 | char curr_char; 36 | // 扫描缓冲区长度 37 | static const int SCAN_BUFFER = 128; 38 | // 扫描缓冲区 39 | char scan_buf[SCAN_BUFFER]; 40 | // 实际读取到的字节数 41 | int real_buf_len; 42 | // 缓冲区读取位置 43 | int pos_read_buf; 44 | 45 | public: 46 | // 读一个文件 47 | Scanner(const std::string &_filename); 48 | ~Scanner(); 49 | // 扫描并返回字符 50 | char scan(); 51 | // 返回前一个字符 52 | char get_prev_char(); 53 | // 文件是否结束 54 | bool is_done(); 55 | }; 56 | 57 | #endif /* SIMPLECOMPILER_SCANNER_H */ 58 | -------------------------------------------------------------------------------- /src/include/token.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file token.h 4 | * @brief token 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_TOKEN_H 18 | #define SIMPLECOMPILER_TOKEN_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | /** 25 | * 支持的类型枚举 26 | */ 27 | enum tag_t : ssize_t { 28 | // 错误 29 | ERR = -2, 30 | // 结束标识 31 | END = EOF, 32 | 33 | /// @name 关键字 keyword 34 | /// @{ 35 | /// int 36 | KW_INT = 0, 37 | /// char 38 | KW_CHAR, 39 | /// void 40 | KW_VOID, 41 | /// const 42 | KW_CONST, 43 | /// if 44 | KW_IF, 45 | /// else 46 | KW_ELSE, 47 | /// while 48 | KW_WHILE, 49 | /// for 50 | KW_FOR, 51 | /// break 52 | KW_BREAK, 53 | /// continue 54 | KW_CONTINUE, 55 | /// return 56 | KW_RETURN, 57 | /// @} 58 | 59 | /// @name 类型标识 60 | /// 标识符 61 | ID, 62 | /// int 63 | NUM, 64 | /// char 65 | CHAR, 66 | /// str 67 | STR, 68 | /// @} 69 | 70 | /// @name 操作符 operator 71 | /// = 72 | ASSIGN, 73 | /// + 74 | ADD, 75 | /// - 76 | SUB, 77 | /// * 78 | MUL, 79 | /// / 80 | DIV, 81 | /// % 82 | MOD, 83 | /// | 84 | ORBIT, 85 | /// & 86 | ANDBIT, 87 | /// ^ 88 | EORBIT, 89 | /// && 90 | AND, 91 | /// || 92 | OR, 93 | /// ! 94 | NOT, 95 | /// > 96 | GT, 97 | /// >= 98 | GE, 99 | /// < 100 | LT, 101 | /// <= 102 | LE, 103 | /// == 104 | EQU, 105 | /// != 106 | NEQU, 107 | /// @} 108 | 109 | /// @name 分界符 separator 110 | /// ( 111 | LPAREN, 112 | /// ) 113 | RPAREN, 114 | /// { 115 | LBRACE, 116 | /// } 117 | RBRACE, 118 | /// [ 119 | LBRACKET, 120 | /// ] 121 | RBRACKET, 122 | /// , 123 | COMMA, 124 | /// : 125 | COLON, 126 | /// ; 127 | SEMICON, 128 | /// @} 129 | }; 130 | 131 | /** 132 | * 基类 133 | */ 134 | class token_base_t { 135 | public: 136 | /// 保存的 tag 137 | tag_t tag; 138 | 139 | /** 140 | * 构造函数 141 | * @param _tag tag 142 | */ 143 | explicit token_base_t(tag_t _tag); 144 | 145 | /// @name 默认构造/析构函数 146 | /// @{ 147 | token_base_t() = default; 148 | token_base_t(const token_base_t &_token) = default; 149 | token_base_t(token_base_t &&_token) = default; 150 | auto operator=(const token_base_t &_token) -> token_base_t & = default; 151 | auto operator=(token_base_t &&_token) -> token_base_t & = default; 152 | virtual ~token_base_t() = default; 153 | /// @} 154 | 155 | /** 156 | * 获得当前 token 的名称字符串 157 | * @return const std::string tag 名 158 | */ 159 | [[nodiscard]] virtual auto to_string() const -> const std::string; 160 | }; 161 | 162 | /** 163 | * 标识符(变量名) 164 | */ 165 | class token_identifier_t : public token_base_t { 166 | public: 167 | /// 标识符名称 168 | std::string name; 169 | 170 | /** 171 | * 构造函数 172 | * @param _name 标识符名称 173 | */ 174 | explicit token_identifier_t(std::string _name); 175 | 176 | /// @name 默认构造/析构函数 177 | /// @{ 178 | token_identifier_t() = default; 179 | token_identifier_t(const token_identifier_t &_token_identifier) = default; 180 | token_identifier_t(token_identifier_t &&_token_identifier) = default; 181 | auto operator=(const token_identifier_t &_token_identifier) 182 | -> token_identifier_t & = default; 183 | auto operator=(token_identifier_t &&_token_identifier) 184 | -> token_identifier_t & = default; 185 | ~token_identifier_t() = default; 186 | /// @} 187 | 188 | /** 189 | * 获得当前 token 的名称字符串 190 | * @return const std::string tag 名+标识符名称 191 | */ 192 | [[nodiscard]] auto to_string() const -> const std::string override; 193 | }; 194 | 195 | /** 196 | * 数字 197 | */ 198 | class token_num_t : public token_base_t { 199 | public: 200 | /// 数值 201 | int num_val; 202 | 203 | /** 204 | * 构造函数 205 | * @param _num_val 数值 206 | */ 207 | explicit token_num_t(int _num_val); 208 | 209 | /// @name 默认构造/析构函数 210 | /// @{ 211 | token_num_t() = default; 212 | token_num_t(const token_num_t &_num) = default; 213 | token_num_t(token_num_t &&_num) = default; 214 | auto operator=(const token_num_t &_num) -> token_num_t & = default; 215 | auto operator=(token_num_t &&_num) -> token_num_t & = default; 216 | ~token_num_t() = default; 217 | /// @} 218 | 219 | /** 220 | * 获得当前 token 的名称字符串 221 | * @return const std::string tag 名+数值 222 | */ 223 | [[nodiscard]] auto to_string() const -> const std::string override; 224 | }; 225 | 226 | /** 227 | * 字符 228 | */ 229 | class token_char_t : public token_base_t { 230 | public: 231 | /// 字符值 232 | char char_val; 233 | 234 | /** 235 | * 构造函数 236 | * @param _char_val 字符值 237 | */ 238 | explicit token_char_t(char _char_val); 239 | 240 | /// @name 默认构造/析构函数 241 | /// @{ 242 | token_char_t() = default; 243 | token_char_t(const token_char_t &_char) = default; 244 | token_char_t(token_char_t &&_char) = default; 245 | auto operator=(const token_char_t &_char) -> token_char_t & = default; 246 | auto operator=(token_char_t &&_char) -> token_char_t & = default; 247 | ~token_char_t() = default; 248 | /// @} 249 | 250 | /** 251 | * 获得当前 token 的名称字符串 252 | * @return const std::string tag 名+字符 253 | */ 254 | [[nodiscard]] auto to_string() const -> const std::string override; 255 | }; 256 | 257 | /** 258 | * 字符串 259 | */ 260 | class token_string_t : public token_base_t { 261 | public: 262 | /// 字符串值 263 | std::string string_val; 264 | 265 | /** 266 | * 构造函数 267 | * @param _string_val 字符串值 268 | */ 269 | explicit token_string_t(const std::string &_string_val); 270 | 271 | /// @name 默认构造/析构函数 272 | /// @{ 273 | token_string_t() = default; 274 | token_string_t(const token_string_t &_str) = default; 275 | token_string_t(token_string_t &&_str) = default; 276 | auto operator=(const token_string_t &_str) -> token_string_t & = default; 277 | auto operator=(token_string_t &&_str) -> token_string_t & = default; 278 | ~token_string_t() = default; 279 | /// @} 280 | 281 | /** 282 | * 获得当前 token 的名称字符串 283 | * @return const std::string tag 名+字符串 284 | */ 285 | [[nodiscard]] auto to_string() const -> const std::string override; 286 | }; 287 | 288 | /** 289 | * 关键字 290 | */ 291 | class keywords_t { 292 | public: 293 | /** 294 | * 构造函数 295 | */ 296 | keywords_t(); 297 | 298 | /// @name 默认构造/析构函数 299 | /// @{ 300 | keywords_t(const keywords_t &_keywords) = default; 301 | keywords_t(keywords_t &&_keywords) = default; 302 | auto operator=(const keywords_t &_keywords) -> keywords_t & = default; 303 | auto operator=(keywords_t &&_keywords) -> keywords_t & = default; 304 | ~keywords_t() = default; 305 | /// @} 306 | 307 | /** 308 | * 通过字符串获取 tag 309 | * @param _name 字符串 310 | * @return tag_t tag 名 311 | */ 312 | tag_t get_tag(const std::string &_name); 313 | 314 | private: 315 | /// 关键字-tag 表 316 | std::unordered_map> keywords; 317 | }; 318 | 319 | #endif /* SIMPLECOMPILER_TOKEN_H */ 320 | -------------------------------------------------------------------------------- /src/include/type.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file type.h 4 | * @brief type 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_TYPE_H 18 | #define SIMPLECOMPILER_TYPE_H 19 | 20 | #include "token.h" 21 | 22 | enum Type { int_t, char_t, void_t }; 23 | 24 | enum VarType { array_t, var_t }; 25 | 26 | enum Operator { 27 | add_op, 28 | sub_op, 29 | mul_op, 30 | div_op, 31 | mod_op, 32 | orbit_op, 33 | andbit_op, 34 | eorbit_op, 35 | and_op, 36 | or_op, 37 | not_op, 38 | gt_op, 39 | ge_op, 40 | lt_op, 41 | le_op, 42 | equ_op, 43 | nequ_op, 44 | ERROR 45 | }; 46 | 47 | enum Control { continue_c, break_c, return_c }; 48 | 49 | Operator tag_to_op(tag_t _t); 50 | 51 | std::string op_to_string(Operator _p); 52 | 53 | std::string type_to_string(Type _t); 54 | 55 | std::string vartype_to_string(VarType _t); 56 | 57 | #endif /* SIMPLECOMPILER_TYPE_H */ 58 | -------------------------------------------------------------------------------- /src/include/typechecker.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file typechecker.h 4 | * @brief typechecker 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_TYCK_H 18 | #define SIMPLECOMPILER_TYCK_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "ast.h" 27 | #include "token.h" 28 | #include "utils.h" 29 | 30 | class TypeCheck { 31 | private: 32 | int currentBlock; 33 | std::string currentFunc; 34 | 35 | public: 36 | std::vector parentBlock; 37 | 38 | std::map FuncTable; 39 | std::map> BlockVars; 40 | 41 | TypeCheck() { 42 | currentFunc = ""; 43 | currentBlock = 0; 44 | 45 | FuncTable["getint"] = Function("getint", Type::int_t, std::vector{}); 46 | FuncTable["getch"] = Function("getch", Type::int_t, std::vector{}); 47 | FuncTable["getarray"] = 48 | Function("getarray", Type::int_t, 49 | std::vector{ 50 | Var("a", VarType::array_t, false, std::vector{0})}); 51 | FuncTable["putint"] = 52 | Function("putint", Type::void_t, 53 | std::vector{Var("a", VarType::var_t, false)}); 54 | FuncTable["putch"] = 55 | Function("putch", Type::void_t, 56 | std::vector{Var("a", VarType::var_t, false)}); 57 | FuncTable["putarray"] = 58 | Function("putarray", Type::void_t, 59 | std::vector{ 60 | Var("a", VarType::var_t, false), 61 | Var("b", VarType::array_t, false, std::vector{0})}); 62 | } 63 | 64 | ~TypeCheck() = default; 65 | 66 | bool FillInValue(int *_memory, InitValAST *_init, std::vector &_dim, 67 | size_t _i); 68 | 69 | std::unique_ptr EvalVarDecl(VarDeclAST &_varDecl); 70 | 71 | std::unique_ptr EvalId(IdAST &_id); 72 | 73 | std::unique_ptr EvalVarDef(VarDefAST &_varDef); 74 | 75 | std::unique_ptr EvalFuncCall(FuncCallAST &_func); 76 | 77 | std::unique_ptr EvalBlock(BlockAST &_block); 78 | 79 | std::unique_ptr EvalIfElse(IfAST &_stmt); 80 | 81 | std::unique_ptr EvalWhile(WhileAST &_stmt); 82 | 83 | std::unique_ptr EvalControl(ControlAST &_stmt); 84 | 85 | std::unique_ptr EvalAssign(AssignAST &_assign); 86 | 87 | ASTPtr EvalLVal(LValAST &_lval); 88 | 89 | ASTPtr EvalAddExp(BinaryAST &_exp); 90 | 91 | ASTPtr EvalMulExp(BinaryAST &_exp); 92 | 93 | ASTPtr EvalUnaryExp(UnaryAST &_exp); 94 | 95 | std::unique_ptr EvalFuncDef(FuncDefAST &_funcDef); 96 | 97 | ASTPtr EvalRelExp(BinaryAST &_exp); 98 | 99 | ASTPtr EvalLAndExp(BinaryAST &_exp); 100 | 101 | ASTPtr EvalLOrExp(BinaryAST &_exp); 102 | 103 | std::unique_ptr EvalCompUnit(CompUnitAST &_unit); 104 | 105 | ASTPtr EvalEqExp(BinaryAST &_exp); 106 | 107 | std::unique_ptr EvalStmt(StmtAST &_stmt); 108 | 109 | std::unique_ptr EvalInitVal(InitValAST &_init); 110 | 111 | std::unique_ptr EvalNumber(NumAST &_num); 112 | 113 | std::unique_ptr EvalProcessedId(ProcessedIdAST &_id); 114 | 115 | std::unique_ptr EvalEmpty(); 116 | }; 117 | 118 | #endif /* SIMPLECOMPILER_TYCK_H */ 119 | -------------------------------------------------------------------------------- /src/include/utils.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file utils.h 4 | * @brief utils 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #ifndef SIMPLECOMPILER_UTILS_H 18 | #define SIMPLECOMPILER_UTILS_H 19 | 20 | #include "ast.h" 21 | #include "token.h" 22 | 23 | struct GenVar { 24 | std::string name; 25 | VarType argType; 26 | std::vector dims; 27 | std::string id; 28 | 29 | GenVar() {} 30 | GenVar(std::string _n, VarType _t, std::vector _d = std::vector{}, 31 | std::string _id = "") 32 | : name(std::move(_n)), argType(_t), dims(std::move(_d)), 33 | id(std::move(_id)) {} 34 | }; 35 | 36 | struct Var { 37 | std::string name; 38 | VarType type; 39 | bool isConst; 40 | std::vector dims; 41 | int val; 42 | 43 | Var() {} 44 | Var(std::string _n, VarType _t, bool _c, 45 | std::vector _d = std::vector{}, int _v = 0) 46 | : name(std::move(_n)), type(_t), isConst(_c), dims(std::move(_d)), 47 | val(_v) {} 48 | }; 49 | 50 | struct Function { 51 | std::string funcName; 52 | Type funcType; 53 | std::vector argTable; 54 | 55 | Function() {} 56 | Function(std::string _n, Type _t, std::vector _a) 57 | : funcName(std::move(_n)), funcType(_t), argTable(std::move(_a)) {} 58 | }; 59 | 60 | #endif /* SIMPLECOMPILER_UTILS_H */ 61 | -------------------------------------------------------------------------------- /src/init.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file init.cpp 4 | * @brief 初始化 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "init.h" 23 | #include "log.h" 24 | 25 | // 命令行参数解析相关定义 26 | extern int optind, opterr, optopt; 27 | extern char *optarg; 28 | static const int LEXICAL_OPT = 256; 29 | static struct option long_options[] = { 30 | {"help", no_argument, NULL, 'h'}, 31 | {"version", no_argument, NULL, 'v'}, 32 | {"output", required_argument, NULL, 'o'}, 33 | {"lexical", optional_argument, NULL, LEXICAL_OPT}, 34 | }; 35 | 36 | Init::Init() { 37 | // 初始化目录信息 38 | abs_path = getcwd(abs_path_buffer, 256); 39 | abs_path += "/"; 40 | index = 0; 41 | c = 0; 42 | log_init(); 43 | return; 44 | } 45 | 46 | Init::~Init() { return; } 47 | 48 | int Init::init(int &_argc, char **&_argv) { 49 | while ((c = getopt_long(_argc, _argv, "hvo:", long_options, &index)) != EOF) { 50 | switch (c) { 51 | // 显示帮助信息 52 | case 'h': 53 | SPDLOG_LOGGER_INFO( 54 | SCLOG, "SimpleCompiler v0.01\nCopyright(C) Simple-XX 2023\n" 55 | "命令格式:[源文件[源文件] -o 输出文件 [选项]][-h|-v]\n" 56 | "\t源文件\t\t必须是以.c结尾的文件\n" 57 | "\t-o\t\t指定输出文件\n" 58 | "\t--lexical[指定文件(可选)]\t显示词法分析过程\n" 59 | "\t-h\t\t显示帮助信息\n" 60 | "\t-v\t\t显示版本信息"); 61 | break; 62 | // 显示版本信息 63 | case 'v': 64 | SPDLOG_LOGGER_INFO(SCLOG, 65 | "SimpleCompiler v0.01\nCopyright(C) Simple-XX 2023\n"); 66 | break; 67 | case 'o': 68 | // 寻找源文件,寻找区间 _argv[1:-2] 69 | for (int i = 1; i < _argc - 2; i++) { 70 | // 添加源文件 71 | if (strstr(_argv[i], ".c")) { 72 | src_files.push_back(abs_path + _argv[i]); 73 | } 74 | } 75 | // 设置输出文件 76 | dest_file = abs_path + optarg; 77 | break; 78 | 79 | case LEXICAL_OPT: 80 | SPDLOG_LOGGER_INFO(SCLOG, "输出词法分析结果,可指定输出到文件\n" 81 | "[--lexical 输出文件]"); 82 | break; 83 | // 表示选项不支持 84 | case '?': 85 | SPDLOG_LOGGER_WARN(SCLOG, "unknow option"); 86 | break; 87 | default: 88 | SPDLOG_LOGGER_WARN(SCLOG, "bikbib"); 89 | break; 90 | } 91 | } 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /src/ir/irlexer.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file irlexer.cpp 4 | * @brief ir lexer 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include 18 | 19 | #include "irlexer.h" 20 | #include "irtoken.h" 21 | 22 | IRToken IRLexer::ParseNum() { 23 | char c = cinstream.peek(); 24 | int val = 0; 25 | if (c == '0') { 26 | c = cinstream.get(); 27 | c = cinstream.peek(); 28 | if (c == 'x' || c == 'X') { 29 | c = cinstream.get(); 30 | while (true) { 31 | c = cinstream.peek(); 32 | if (c >= '0' && c <= '9') { 33 | c = cinstream.get(); 34 | val = val * 16 + c - '0'; 35 | } else if (c >= 'A' && c <= 'F') { 36 | c = cinstream.get(); 37 | val = val * 16 + c - 'A' + 10; 38 | } else if (c >= 'a' && c <= 'f') { 39 | c = cinstream.get(); 40 | val = val * 16 + c - 'a' + 10; 41 | } else { 42 | value = val; 43 | return IRToken::NUMBER_IR; 44 | } 45 | } 46 | } else { 47 | while (true) { 48 | if (c >= '0' && c <= '7') { 49 | c = cinstream.get(); 50 | val = val * 8 + c - '0'; 51 | } else { 52 | value = val; 53 | return IRToken::NUMBER_IR; 54 | } 55 | c = cinstream.peek(); 56 | } 57 | } 58 | } else { 59 | while (c >= '0' && c <= '9') { 60 | c = cinstream.get(); 61 | val = val * 10 + (int)(c - '0'); 62 | c = cinstream.peek(); 63 | } 64 | value = val; 65 | return IRToken::NUMBER_IR; 66 | } 67 | } 68 | 69 | IRToken IRLexer::ParseSymbol() { 70 | std::string s; 71 | char c; 72 | while (true) { 73 | c = cinstream.get(); 74 | s += c; 75 | c = cinstream.peek(); 76 | if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || 77 | (c >= '0' && c <= '9'))) { 78 | if (s == "var") { 79 | return IRToken::VARDECL_IR; 80 | } 81 | if (s == "end") { 82 | return IRToken::FUNCEND_IR; 83 | } 84 | if (s == "if") { 85 | return IRToken::IF_IR; 86 | } 87 | if (s == "goto") { 88 | return IRToken::GOTO_IR; 89 | } 90 | if (s == "return") { 91 | return IRToken::RETURN_IR; 92 | } 93 | if (s == "call") { 94 | return IRToken::CALL_IR; 95 | } 96 | if (s == "param") { 97 | return IRToken::PARAM_IR; 98 | } 99 | name = s; 100 | return IRToken::SYMBOL_IR; 101 | } 102 | } 103 | } 104 | 105 | IRToken IRLexer::ParseComment() { 106 | char c; 107 | c = cinstream.get(); 108 | while ((c = cinstream.peek()) != '\n') { 109 | c = cinstream.get(); 110 | } 111 | return IRToken::COMMENT_IR; 112 | } 113 | 114 | IRToken IRLexer::NextIRToken() { 115 | char c; 116 | while (true) { 117 | c = cinstream.peek(); 118 | if (c >= '0' && c <= '9') { 119 | return ParseNum(); 120 | } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_')) { 121 | return ParseSymbol(); 122 | } else if (c == EOF) { 123 | return IRToken::END_IR; 124 | } else if (c <= 32) { 125 | if (c == '\n') { 126 | lineno++; 127 | } 128 | c = cinstream.get(); 129 | continue; 130 | } else { 131 | switch (c) { 132 | case '+': { 133 | c = cinstream.get(); 134 | op = Operator::add_op; 135 | return IRToken::OP_IR; 136 | } 137 | case '-': { 138 | c = cinstream.get(); 139 | op = Operator::sub_op; 140 | return IRToken::OP_IR; 141 | } 142 | case '*': { 143 | c = cinstream.get(); 144 | op = Operator::mul_op; 145 | return IRToken::OP_IR; 146 | } 147 | case '/': { 148 | c = cinstream.get(); 149 | c = cinstream.peek(); 150 | if (c == '/') { 151 | return ParseComment(); 152 | } 153 | op = Operator::div_op; 154 | return IRToken::OP_IR; 155 | } 156 | case '%': { 157 | c = cinstream.get(); 158 | op = Operator::mod_op; 159 | return IRToken::OP_IR; 160 | } 161 | case '>': { 162 | c = cinstream.get(); 163 | c = cinstream.peek(); 164 | if (c == '=') { 165 | c = cinstream.get(); 166 | op = Operator::ge_op; 167 | return IRToken::LOGICOP_IR; 168 | } else { 169 | op = Operator::gt_op; 170 | return IRToken::LOGICOP_IR; 171 | } 172 | } 173 | case '<': { 174 | c = cinstream.get(); 175 | c = cinstream.peek(); 176 | if (c == '=') { 177 | c = cinstream.get(); 178 | op = Operator::le_op; 179 | return IRToken::LOGICOP_IR; 180 | } else { 181 | op = Operator::lt_op; 182 | return IRToken::LOGICOP_IR; 183 | } 184 | } 185 | case '=': { 186 | c = cinstream.get(); 187 | c = cinstream.peek(); 188 | if (c == '=') { 189 | c = cinstream.get(); 190 | op = Operator::equ_op; 191 | return IRToken::LOGICOP_IR; 192 | } else { 193 | return IRToken::ASSIGN_IR; 194 | } 195 | } 196 | case '!': { 197 | c = cinstream.get(); 198 | c = cinstream.peek(); 199 | if (c == '=') { 200 | c = cinstream.get(); 201 | op = Operator::nequ_op; 202 | return IRToken::LOGICOP_IR; 203 | } else { 204 | op = Operator::not_op; 205 | return IRToken::OP_IR; 206 | } 207 | } 208 | case '[': { 209 | c = cinstream.get(); 210 | return IRToken::LSB_IR; 211 | } 212 | case ']': { 213 | c = cinstream.get(); 214 | return IRToken::RSB_IR; 215 | } 216 | case ':': { 217 | c = cinstream.get(); 218 | return IRToken::COLON_IR; 219 | } 220 | default: 221 | exit(23); 222 | } 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/lexical/lexical.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file lexical.cpp 4 | * @brief lexical 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "lexical.h" 21 | #include "log.h" 22 | 23 | keywords_t Lexer::keywords; 24 | 25 | Lexer::Lexer(Scanner &_sc) : scanner(_sc) { 26 | ch = ' '; 27 | token = NULL; 28 | return; 29 | } 30 | 31 | Lexer::~Lexer() { 32 | if (token != NULL) { 33 | delete token; 34 | } 35 | return; 36 | } 37 | 38 | bool Lexer::scan(char _need) { 39 | ch = scanner.scan(); 40 | if (_need) { 41 | if (ch != _need) 42 | return false; 43 | ch = scanner.scan(); 44 | return true; 45 | } 46 | return true; 47 | } 48 | 49 | void Lexer::blank() { 50 | token_base_t *t = NULL; 51 | // 跳过空字符 52 | do { 53 | scan(); 54 | } while (COND_BLANK); 55 | token = t; 56 | return; 57 | } 58 | 59 | void Lexer::identifier() { 60 | token_base_t *t = NULL; 61 | // 标识符 62 | while (COND_IDENTIFIER) { 63 | std::string name = ""; 64 | do { 65 | // 记录字符 66 | name.push_back(ch); 67 | scan(); 68 | } while (COND_IDENTIFIER || (ch >= '0' && ch <= '9')); 69 | // 判断是不是关键字 70 | tag_t tag = keywords.get_tag(name); 71 | // 如果不是,则为标识符 72 | if (tag == ID) { 73 | t = new token_identifier_t(name); 74 | } 75 | // 如果是 76 | else { 77 | t = new token_base_t(tag); 78 | } 79 | } 80 | token = t; 81 | return; 82 | } 83 | 84 | void Lexer::number() { 85 | token_base_t *t = NULL; 86 | int val = 0; 87 | // 十进制数 88 | do { 89 | // 计算数字 90 | val = val * 10 + ch - '0'; 91 | } while (scan(), (ch >= '0' && ch <= '9')); 92 | t = new token_num_t(val); 93 | token = t; 94 | return; 95 | } 96 | 97 | void Lexer::character() { 98 | token_base_t *t = NULL; 99 | do { 100 | // 过滤掉第一个单引号 101 | if (ch == '\'') { 102 | continue; 103 | } 104 | // 文件结束或换行 105 | else if ((ch == '\n') || (ch == EOF)) { 106 | t = new token_base_t(ERR); 107 | error->set_err_no(ERR); 108 | error->display_err(); 109 | break; 110 | } 111 | // 转义字符 112 | else if (ch == '\\') { 113 | scan(); 114 | if (ch == 'n') { 115 | t = new token_char_t('\n'); 116 | } else if (ch == 't') { 117 | t = new token_char_t('\t'); 118 | } else if (ch == '0') { 119 | t = new token_char_t('\0'); 120 | } else if (ch == '\'') { 121 | t = new token_char_t('\''); 122 | } else if (ch == '\\') { 123 | t = new token_char_t('\\'); 124 | } else if ((ch == EOF) || (ch == '\n')) { 125 | t = new token_base_t(ERR); 126 | error->set_err_no(ERR); 127 | error->display_err(); 128 | break; 129 | } 130 | // 其它的不转义 131 | else { 132 | t = new token_char_t(ch); 133 | } 134 | } 135 | // 一般情况 136 | else { 137 | t = new token_char_t(ch); 138 | } 139 | } while (scan('\'') == false); 140 | token = t; 141 | return; 142 | } 143 | 144 | void Lexer::str() { 145 | token_base_t *t = NULL; 146 | std::string s = ""; 147 | do { 148 | // 过滤掉第一个双引号 149 | if (ch == '"') { 150 | continue; 151 | } 152 | // 直接结束 153 | else if ((ch == '\n') || (ch == EOF)) { 154 | t = new token_base_t(ERR); 155 | error->set_err_no(ERR); 156 | error->display_err(); 157 | break; 158 | } 159 | // 转义字符 160 | if (ch == '\\') { 161 | scan(); 162 | if (ch == 'n') { 163 | s.push_back('\n'); 164 | } else if (ch == 't') { 165 | s.push_back('\t'); 166 | } else if (ch == '0') { 167 | s.push_back('\0'); 168 | } else if (ch == '"') { 169 | s.push_back('"'); 170 | } else if (ch == '\\') { 171 | s.push_back('\\'); 172 | } else if (ch == '\n') { 173 | ; 174 | } else if (ch == EOF) { 175 | t = new token_base_t(ERR); 176 | error->set_err_no(ERR); 177 | error->display_err(); 178 | break; 179 | } 180 | // 其它的不转义 181 | else { 182 | s.push_back(ch); 183 | } 184 | } 185 | // 一般情况 186 | else { 187 | s.push_back(ch); 188 | } 189 | } while (scan('"') == false); 190 | // 最终字符串 191 | if (t == NULL) { 192 | t = new token_string_t(s); 193 | } 194 | token = t; 195 | return; 196 | } 197 | 198 | void Lexer::separator() { 199 | token_base_t *t = NULL; 200 | switch (ch) { 201 | case '(': 202 | t = new token_base_t(LPAREN); 203 | break; 204 | case ')': 205 | t = new token_base_t(RPAREN); 206 | break; 207 | case '{': 208 | t = new token_base_t(LBRACE); 209 | break; 210 | case '}': 211 | t = new token_base_t(RBRACE); 212 | break; 213 | case '[': 214 | t = new token_base_t(LBRACKET); 215 | break; 216 | case ']': 217 | t = new token_base_t(RBRACKET); 218 | break; 219 | case ',': 220 | t = new token_base_t(COMMA); 221 | break; 222 | case ':': 223 | t = new token_base_t(COLON); 224 | break; 225 | case ';': 226 | t = new token_base_t(SEMICON); 227 | break; 228 | default: 229 | t = new token_base_t(ERR); // 错误的词法记号 230 | } 231 | scan(); 232 | token = t; 233 | return; 234 | } 235 | 236 | void Lexer::operation() { 237 | token_base_t *t = NULL; 238 | switch (ch) { 239 | case '=': 240 | t = new token_base_t(scan('=') ? EQU : ASSIGN); 241 | break; 242 | case '+': 243 | t = new token_base_t(ADD); 244 | scan(); 245 | break; 246 | case '-': 247 | t = new token_base_t(SUB); 248 | scan(); 249 | break; 250 | case '*': 251 | t = new token_base_t(MUL); 252 | scan(); 253 | break; 254 | case '/': 255 | scan(); 256 | // 单行注释 257 | if (ch == '/') { 258 | while (ch != '\n' && ch != EOF) 259 | scan(); 260 | } 261 | // 多行注释 262 | else if (ch == '*') { 263 | while (scan(EOF) == false) { 264 | if (ch == '*') { 265 | if (scan('/')) 266 | break; 267 | } 268 | } 269 | // 没有闭合 270 | if (ch == EOF) { 271 | SPDLOG_LOGGER_ERROR(SCLOG, "多行注释未正常结束"); 272 | } 273 | } 274 | // 否则为除号 275 | else 276 | t = new token_base_t(DIV); 277 | break; 278 | case '%': 279 | t = new token_base_t(MOD); 280 | scan(); 281 | break; 282 | case '|': 283 | t = new token_base_t(scan('|') ? OR : ORBIT); 284 | break; 285 | case '&': 286 | t = new token_base_t(scan('&') ? AND : ANDBIT); 287 | break; 288 | case '^': 289 | t = new token_base_t(EORBIT); 290 | scan(); 291 | break; 292 | case '!': 293 | t = new token_base_t(scan('=') ? NEQU : NOT); 294 | break; 295 | case '>': 296 | t = new token_base_t(scan('=') ? GE : GT); 297 | break; 298 | case '<': 299 | t = new token_base_t(scan('=') ? LE : LT); 300 | break; 301 | // 除此以外是错误的 302 | default: 303 | t = new token_base_t(ERR); 304 | error->set_err_no(ERR); 305 | error->display_err(); 306 | scan(); 307 | } 308 | token = t; 309 | return; 310 | } 311 | 312 | token_base_t *Lexer::lexing() { 313 | // 字符不为空且没有出错时 314 | while ((is_done() == false)) { 315 | if (COND_BLANK) 316 | blank(); 317 | else if (COND_IDENTIFIER) 318 | identifier(); 319 | else if (COND_NUMBER) { 320 | number(); 321 | } else if (ch == '\'') { 322 | character(); 323 | } else if (ch == '"') { 324 | this->str(); 325 | } else if (COND_SEPARATOR) { 326 | separator(); 327 | } else if (COND_OPERATION) { 328 | operation(); 329 | } else { 330 | token = new token_base_t(ERR); 331 | error->set_err_no(ERR); 332 | error->display_err(); 333 | return token; 334 | } 335 | // 更新 token 内容 336 | if (token != NULL && token->tag != ERR) { 337 | return token; 338 | } 339 | } 340 | token = new token_base_t(END); 341 | return token; 342 | } 343 | 344 | bool Lexer::is_done() const { 345 | return scanner.is_done() || (error->get_err_no() < 0); 346 | } 347 | -------------------------------------------------------------------------------- /src/lexical/token.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file token.cpp 4 | * @brief token 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include "token.h" 18 | 19 | /// 与 tag_t 对应的 token 名称 20 | static const std::string tokenName[] = { 21 | "INT", "CHAR", "VOID", "CONST", "IF", "ELSE", "WHILE", 22 | "FOR", "BREAK", "CONTINUE", "RETURN", "ID", "NUM", "CH", 23 | "STR", "ASSIGN", "ADD", "SUB", "MUL", "DIV", "MOD", 24 | "ORBIT", "ANDBIT", "EORBIT", "AND", "OR", "NOT", "GT", 25 | "GE", "LT", "LE", "EQU", "NEQU", "LPAREN", "RPAREN", 26 | "LBRACE", "RBRACE", "LBRACKET", "RBRACKET", "COMMA", "COLON", "SEMICON", 27 | }; 28 | 29 | token_base_t::token_base_t(tag_t _tag) : tag(_tag) {} 30 | 31 | auto token_base_t::to_string() const -> const std::string { 32 | return tokenName[tag]; 33 | } 34 | 35 | token_identifier_t::token_identifier_t(std::string _name) 36 | : token_base_t(ID), name(_name) {} 37 | 38 | const std::string token_identifier_t::to_string() const { 39 | return token_base_t::to_string() + "(" + name + ")"; 40 | } 41 | 42 | token_num_t::token_num_t(int _num_val) : token_base_t(NUM), num_val(_num_val) {} 43 | 44 | const std::string token_num_t::to_string() const { 45 | return token_base_t::to_string() + "(" + std::to_string(num_val) + ")"; 46 | } 47 | 48 | token_char_t::token_char_t(char _char_val) 49 | : token_base_t(CHAR), char_val(_char_val) {} 50 | 51 | const std::string token_char_t::to_string() const { 52 | return token_base_t::to_string() + "(" + std::to_string(char_val) + ")"; 53 | } 54 | 55 | token_string_t::token_string_t(const std::string &_string_val) 56 | : token_base_t(STR), string_val(_string_val) {} 57 | 58 | const std::string token_string_t::to_string() const { 59 | return token_base_t::to_string() + "(" + string_val + ")"; 60 | } 61 | 62 | keywords_t::keywords_t() { 63 | keywords["int"] = KW_INT; 64 | keywords["char"] = KW_CHAR; 65 | keywords["void"] = KW_VOID; 66 | keywords["const"] = KW_CONST; 67 | keywords["if"] = KW_IF; 68 | keywords["else"] = KW_ELSE; 69 | keywords["while"] = KW_WHILE; 70 | keywords["for"] = KW_FOR; 71 | keywords["break"] = KW_BREAK; 72 | keywords["continue"] = KW_CONTINUE; 73 | keywords["return"] = KW_RETURN; 74 | } 75 | 76 | tag_t keywords_t::get_tag(const std::string &_name) { 77 | // 如果没有在关键字表中则说明是一个标识符 78 | return keywords.find(_name) != keywords.end() ? keywords[_name] : ID; 79 | } 80 | -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file log.cpp 4 | * @brief 日志封装 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-27 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-27Zone.N创建文件 14 | *
15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "log.h" 27 | 28 | std::shared_ptr SCLOG = nullptr; 29 | 30 | /// @note 在 win 下使用时需要在程序结束时使用 `spdlog::shutdown()` 回收资源 31 | void log_init(void) { 32 | try { 33 | spdlog::init_thread_pool(65536, 1); 34 | auto stdout_sink = std::make_shared(); 35 | auto rotating_sink = std::make_shared( 36 | SimpleCompiler::LOG_FILE_PATH, SimpleCompiler::LOG_FILE_MAX_SIZE, 37 | SimpleCompiler::LOG_FILE_MAX_COUNT); 38 | std::vector sinks{stdout_sink, rotating_sink}; 39 | auto logger = std::make_shared( 40 | "multi_sink", sinks.begin(), sinks.end(), spdlog::thread_pool(), 41 | spdlog::async_overflow_policy::block); 42 | // [年-月-日 时:分:秒.毫秒] [文件名:行号] [日志级别以彩色大写输出 8 43 | // 字符右对齐] 内容 44 | logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%s:%# %!] [%^%l%$] %v"); 45 | spdlog::register_logger(logger); 46 | spdlog::flush_on(spdlog::level::trace); 47 | 48 | SCLOG = spdlog::get("multi_sink"); 49 | } catch (const spdlog::spdlog_ex &e) { 50 | std::printf("Log initialization failed: %s\n", e.what()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file main.cpp 4 | * @brief 入口 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "common.h" 23 | #include "log.h" 24 | 25 | // TODO: 由 init 处理 26 | // 源文件 27 | std::vector src_files; 28 | // 输出文件 29 | std::string dest_file = ""; 30 | 31 | Error *error = NULL; 32 | 33 | int main(int _argc, char **_argv) { 34 | // 初始化 35 | // 包括与命令行的交互、获取要操作的文件等 36 | Init initer; 37 | initer.init(_argc, _argv); 38 | // 逐个打开文件 39 | for (const auto &i : src_files) { 40 | SPDLOG_LOGGER_INFO(SCLOG, "打开文件:{}", i); 41 | error = new Error(i); 42 | Scanner scanner(i); 43 | Lexer lexer(scanner); 44 | Parser parser(lexer); 45 | ASTPtr prog = parser.parsing(); 46 | SPDLOG_LOGGER_INFO(SCLOG, "[AST]:\n{}", prog->to_string()); 47 | TypeCheck checker = TypeCheck(); 48 | ASTPtr root = prog->Eval(checker); 49 | if (!root) { 50 | SPDLOG_LOGGER_ERROR(SCLOG, "Type check error"); 51 | exit(2); 52 | } 53 | std::map FuncTable = checker.FuncTable; 54 | std::map> BlockVars = checker.BlockVars; 55 | 56 | IRGenerator generator = 57 | IRGenerator(std::move(FuncTable), std::move(BlockVars)); 58 | std::string irout; 59 | root->GenerateIR(generator, irout); 60 | SPDLOG_LOGGER_INFO(SCLOG, "[IR]:\n{}", irout); 61 | 62 | std::istringstream stream_stmt(irout); 63 | IRParser irparser = IRParser(stream_stmt); 64 | IRPtr irroot = irparser.ParseProgram(); 65 | LowIRGenerator lowirgenerator = LowIRGenerator(); 66 | std::string lowirout; 67 | irroot->Generate(lowirgenerator, lowirout); 68 | SPDLOG_LOGGER_INFO(SCLOG, "[LOW IR]:\n{}", lowirout); 69 | 70 | std::istringstream stream_stmt2(lowirout); 71 | CodeGen codegenerator = CodeGen(stream_stmt2); 72 | std::string riscV; 73 | codegenerator.Generate(riscV); 74 | SPDLOG_LOGGER_INFO(SCLOG, "[RISCV]:\n{}", riscV); 75 | } 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /src/parser/type.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file type.cpp 4 | * @brief 类型处理 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include "type.h" 18 | 19 | // convert tag to operator 20 | 21 | Operator tag_to_op(tag_t _t) { 22 | switch (_t) { 23 | case tag_t::ADD: 24 | return add_op; 25 | case tag_t::SUB: 26 | return sub_op; 27 | case tag_t::MUL: 28 | return mul_op; 29 | case tag_t::DIV: 30 | return div_op; 31 | case tag_t::MOD: 32 | return mod_op; 33 | case tag_t::ORBIT: 34 | return orbit_op; 35 | case tag_t::ANDBIT: 36 | return andbit_op; 37 | case tag_t::EORBIT: 38 | return eorbit_op; 39 | case tag_t::AND: 40 | return and_op; 41 | case tag_t::OR: 42 | return or_op; 43 | case tag_t::NOT: 44 | return not_op; 45 | case tag_t::GT: 46 | return gt_op; 47 | case tag_t::GE: 48 | return ge_op; 49 | case tag_t::LT: 50 | return lt_op; 51 | case tag_t::LE: 52 | return le_op; 53 | case tag_t::EQU: 54 | return equ_op; 55 | case tag_t::NEQU: 56 | return nequ_op; 57 | default: 58 | return ERROR; 59 | } 60 | } 61 | 62 | std::string type_to_string(Type _t) { 63 | switch (_t) { 64 | case Type::int_t: 65 | return "INT"; 66 | case Type::char_t: 67 | return "CHAR"; 68 | case Type::void_t: 69 | return "VOID"; 70 | default: 71 | return "ERROR"; 72 | } 73 | } 74 | 75 | std::string vartype_to_string(VarType _t) { 76 | switch (_t) { 77 | case VarType::var_t: 78 | return "var"; 79 | case VarType::array_t: 80 | return "array"; 81 | default: 82 | return "ERROR"; 83 | } 84 | } 85 | 86 | std::string op_to_string(Operator _p) { 87 | switch (_p) { 88 | case Operator::add_op: 89 | return "add"; 90 | case Operator::sub_op: 91 | return "sub"; 92 | case Operator::mul_op: 93 | return "mul"; 94 | case Operator::div_op: 95 | return "div"; 96 | case Operator::mod_op: 97 | return "mod"; 98 | case Operator::orbit_op: 99 | return "orbit"; 100 | case Operator::andbit_op: 101 | return "andbit"; 102 | case Operator::eorbit_op: 103 | return "eorbit"; 104 | case Operator::and_op: 105 | return "and"; 106 | case Operator::or_op: 107 | return "or"; 108 | case Operator::not_op: 109 | return "not"; 110 | case Operator::gt_op: 111 | return "gt"; 112 | case Operator::ge_op: 113 | return "ge"; 114 | case Operator::lt_op: 115 | return "lt"; 116 | case Operator::le_op: 117 | return "le"; 118 | case Operator::equ_op: 119 | return "equ"; 120 | case Operator::nequ_op: 121 | return "nequ"; 122 | default: 123 | exit(97); 124 | } 125 | } -------------------------------------------------------------------------------- /src/scanner/scanner.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file scanner.cpp 4 | * @brief 读入 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include 18 | 19 | #include "log.h" 20 | #include "scanner.h" 21 | 22 | Scanner::Scanner(const std::string &_filename) { 23 | fin.open(_filename, std::ios::in); 24 | if (fin.is_open() == false) { 25 | SPDLOG_LOGGER_ERROR(SCLOG, "Open file failed"); 26 | } 27 | prev_char = ' '; 28 | curr_char = ' '; 29 | real_buf_len = 0; 30 | pos_read_buf = -1; 31 | return; 32 | } 33 | 34 | Scanner::~Scanner() { 35 | if (fin.is_open()) { 36 | fin.close(); 37 | } 38 | return; 39 | } 40 | 41 | // 扫描 42 | char Scanner::scan() { 43 | // 缓冲区已经读取完 44 | if (pos_read_buf == real_buf_len - 1) { 45 | // 重新读取 46 | fin.read(scan_buf, SCAN_BUFFER); 47 | // 读到了多少数据 48 | real_buf_len = fin.gcount(); 49 | // 文件读完了 50 | if (real_buf_len == 0) { 51 | fin.close(); 52 | // 重置读取位置 53 | pos_read_buf = -1; 54 | return EOF; 55 | } 56 | // 重置读取位置 57 | pos_read_buf = -1; 58 | } 59 | // 读取位置++ 60 | pos_read_buf++; 61 | // 获取对应位置的字符 62 | curr_char = scan_buf[pos_read_buf]; 63 | // 如果为结束符则返回 64 | if (curr_char == EOF) { 65 | fin.close(); 66 | return EOF; 67 | } 68 | // 如果是换行符,就把当前行 +1,列重置 69 | if (curr_char == '\n') { 70 | error->set_line(++(error->get_pos()->line)); 71 | error->set_col(1); 72 | } 73 | // 否则列 +1 74 | else { 75 | error->set_col(++(error->get_pos()->col)); 76 | } 77 | // 否则设置 prev_char 78 | prev_char = curr_char; 79 | // 然后返回 80 | return curr_char; 81 | } 82 | 83 | char Scanner::get_prev_char() { return prev_char; } 84 | 85 | // 若文件关闭则视为已完成 86 | // 已完成返回 true 87 | bool Scanner::is_done() { return !fin.is_open(); } 88 | -------------------------------------------------------------------------------- /src/test/test_demo.c: -------------------------------------------------------------------------------- 1 | // This file is a part of Simple-XX/SimpleCompiler 2 | // (https://github.com/Simple-XX/SimpleCompiler). 3 | // 4 | // test_demo.c for Simple-XX/SimpleCompiler. 5 | 6 | int foo(int a) { return foo(a - 1); } 7 | 8 | int main() { 9 | int a = 10; 10 | a = a * foo(a); 11 | return 0; 12 | } -------------------------------------------------------------------------------- /src/test/test_lexical.c: -------------------------------------------------------------------------------- 1 | 2 | // This file is a part of Simple-XX/SimpleCompiler 3 | // (https://github.com/Simple-XX/SimpleCompiler). 4 | // 5 | // test_lexical.c for Simple-XX/SimpleCompiler. 6 | 7 | // keywords 8 | int 9 | char 10 | void 11 | const 12 | if 13 | else 14 | while 15 | for 16 | break 17 | continue 18 | return 19 | 20 | // identifier 21 | adffadf fdafa232 22 | fa_efa _fwf2_ 23 | 24 | // operator 25 | + 26 | - 27 | * 28 | / 29 | = 30 | == 31 | & 32 | && 33 | | 34 | || 35 | ^ 36 | ! 37 | != 38 | > 39 | < 40 | >= 41 | <= 42 | && | || ^ ! != > 43 | 44 | // separator 45 | ( 46 | ) 47 | { 48 | } 49 | [ 50 | ] 51 | , 52 | : 53 | ; 54 | 55 | // combination 56 | int a = 124; 57 | char bb = 's'; 58 | int c = 2; 59 | int d = a * b; 60 | 61 | // real program 62 | int main() { 63 | int num1 = 12; 64 | int arr[114514]; 65 | int *pointer; 66 | scanf("%d", &num1); 67 | for (int i = 0; i < 10; i++) { 68 | printf("num1 = %d\n", num1); 69 | } 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /src/test/test_parser.c: -------------------------------------------------------------------------------- 1 | 2 | // This file is a part of Simple-XX/SimpleCompiler 3 | // (https://github.com/Simple-XX/SimpleCompiler). 4 | // 5 | // test_parser.c for Simple-XX/SimpleCompiler. 6 | 7 | // statement 8 | int global = 1; 9 | 10 | int main() { 11 | !(11 + 45) - 14 * 19 / 19 % 810 + simple(1 + 1, 2) + xx(id(), 2) + 12 | comp[1][id(2)] + iler; 13 | simple = 1; 14 | simple[xx][compiler] = simple + 1; 15 | ; 16 | break; 17 | continue; 18 | return; 19 | return simple + xx; 20 | 21 | // if 22 | if (a && b == true) 23 | simple = xx; 24 | else 25 | simple = compiler; 26 | 27 | if (a && b == true) 28 | simple = xx; 29 | 30 | // while 31 | while (a && b == true) 32 | simple = xx; 33 | 34 | // block 35 | {} 36 | 37 | { 38 | int a[10][10], b; 39 | int a[10][10] = {1}; 40 | simple = xx; 41 | return simple + xx; 42 | } 43 | 44 | { 45 | int a = 1; 46 | const int a1 = 1; 47 | } 48 | 49 | // All test 50 | 51 | { 52 | const int a[3] = {1, 2, 3}; 53 | while (a != 0) { 54 | if (a == 1) { 55 | a = 2; 56 | } else { 57 | a = 3; 58 | } 59 | a = 1; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/test/test_tyck.c: -------------------------------------------------------------------------------- 1 | // This file is a part of Simple-XX/SimpleCompiler 2 | // (https://github.com/Simple-XX/SimpleCompiler). 3 | // 4 | // test_tyck.c for Simple-XX/SimpleCompiler. 5 | 6 | int global = 1; 7 | 8 | int fib(int a) { 9 | if (a == 0) 10 | return 1; 11 | if (a == 1) 12 | return 2; 13 | return fib(a - 1) + fib(a - 2); 14 | } 15 | 16 | int main() { 17 | int a = global + 1; 18 | a = a + fib(a); 19 | return 0; 20 | } -------------------------------------------------------------------------------- /src/typechecker/eval.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file eval.cpp 4 | * @brief eval 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2023-10-26 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2023-10-26Zone.N迁移到 doxygen 14 | *
15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "ast.h" 22 | #include "irgen.h" 23 | #include "typechecker.h" 24 | 25 | ASTPtr FuncDefAST::Eval(TypeCheck &_checker) { 26 | return _checker.EvalFuncDef(*this); 27 | } 28 | 29 | ASTPtr BlockAST::Eval(TypeCheck &_checker) { return _checker.EvalBlock(*this); } 30 | 31 | ASTPtr BinaryAST::Eval(TypeCheck &_checker) { 32 | std::initializer_list opAdd = {Operator::add_op, Operator::sub_op}; 33 | std::initializer_list opMul = {Operator::mul_op, Operator::div_op, 34 | Operator::mod_op}; 35 | std::initializer_list opRel = {Operator::le_op, Operator::ge_op, 36 | Operator::lt_op, Operator::gt_op}; 37 | std::initializer_list opEq = {Operator::equ_op, Operator::nequ_op}; 38 | std::initializer_list opLAnd = {Operator::and_op}; 39 | std::initializer_list opLOr = {Operator::or_op}; 40 | 41 | if (std::find(opLOr.begin(), opLOr.end(), op) != opLOr.end()) { 42 | return _checker.EvalLOrExp(*this); 43 | } else if (std::find(opLAnd.begin(), opLAnd.end(), op) != opLAnd.end()) { 44 | return _checker.EvalLAndExp(*this); 45 | } else if (std::find(opEq.begin(), opEq.end(), op) != opEq.end()) { 46 | return _checker.EvalEqExp(*this); 47 | } else if (std::find(opRel.begin(), opRel.end(), op) != opRel.end()) { 48 | return _checker.EvalRelExp(*this); 49 | } else if (std::find(opAdd.begin(), opAdd.end(), op) != opAdd.end()) { 50 | return _checker.EvalAddExp(*this); 51 | } else if (std::find(opMul.begin(), opMul.end(), op) != opMul.end()) { 52 | return _checker.EvalMulExp(*this); 53 | } else { 54 | return nullptr; 55 | } 56 | } 57 | 58 | ASTPtr IfAST::Eval(TypeCheck &_checker) { return _checker.EvalIfElse(*this); } 59 | 60 | ASTPtr WhileAST::Eval(TypeCheck &_checker) { return _checker.EvalWhile(*this); } 61 | 62 | ASTPtr NumAST::Eval(TypeCheck &_checker) { return _checker.EvalNumber(*this); } 63 | 64 | ASTPtr ProcessedIdAST::Eval(TypeCheck &_checker) { 65 | return _checker.EvalProcessedId(*this); 66 | } 67 | 68 | ASTPtr IdAST::Eval(TypeCheck &_checker) { return _checker.EvalId(*this); } 69 | 70 | ASTPtr UnaryAST::Eval(TypeCheck &_checker) { 71 | return _checker.EvalUnaryExp(*this); 72 | } 73 | 74 | ASTPtr ControlAST::Eval(TypeCheck &_checker) { 75 | return _checker.EvalControl(*this); 76 | } 77 | 78 | ASTPtr AssignAST::Eval(TypeCheck &_checker) { 79 | return _checker.EvalAssign(*this); 80 | } 81 | 82 | ASTPtr StmtAST::Eval(TypeCheck &_checker) { return _checker.EvalStmt(*this); } 83 | 84 | ASTPtr LValAST::Eval(TypeCheck &_checker) { return _checker.EvalLVal(*this); } 85 | 86 | ASTPtr FuncCallAST::Eval(TypeCheck &_checker) { 87 | return _checker.EvalFuncCall(*this); 88 | } 89 | 90 | ASTPtr VarDeclAST::Eval(TypeCheck &_checker) { 91 | return _checker.EvalVarDecl(*this); 92 | } 93 | 94 | ASTPtr VarDefAST::Eval(TypeCheck &_checker) { 95 | return _checker.EvalVarDef(*this); 96 | } 97 | 98 | ASTPtr InitValAST::Eval(TypeCheck &_checker) { 99 | return _checker.EvalInitVal(*this); 100 | } 101 | 102 | ASTPtr CompUnitAST::Eval(TypeCheck &_checker) { 103 | return _checker.EvalCompUnit(*this); 104 | } 105 | 106 | ASTPtr EmptyAST::Eval(TypeCheck &_checker) { return _checker.EvalEmpty(); } -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # CMakeLists.txt for Simple-XX/SimpleCompiler. 6 | 7 | # 设置项目名与版本 8 | project( 9 | test 10 | VERSION 0.0.1 11 | ) 12 | 13 | enable_testing() 14 | include(GoogleTest) 15 | 16 | include_directories( 17 | ${SimpleRenderer_SOURCE_DIR}/src/include 18 | ) 19 | 20 | list(APPEND DEFAULT_TEST_COMPILE_OPTIONS 21 | ${DEFAULT_COMPILE_OPTIONS} 22 | --coverage 23 | ) 24 | 25 | list(APPEND DEFAULT_TEST_LINK_OPTIONS 26 | --coverage 27 | $<$: 28 | -fsanitize=leak 29 | > 30 | -fsanitize=address 31 | -fno-omit-frame-pointer 32 | ) 33 | 34 | link_libraries( 35 | ${DEFAULT_LINK_LIB} 36 | gtest_main 37 | ${glog_LIBRARIES} 38 | ) 39 | 40 | # add_subdirectory(unit_test) 41 | #add_subdirectory(integration_test) 42 | # add_subdirectory(system_test) 43 | 44 | add_coverage_target( 45 | DEPENDS unit_test 46 | SOURCE_DIR ${SimpleRenderer_SOURCE_DIR} 47 | BINARY_DIR ${SimpleRenderer_BINARY_DIR} 48 | EXCLUDE_DIR ${SimpleRenderer_SOURCE_DIR}/3rd/* 49 | ) 50 | -------------------------------------------------------------------------------- /test/system_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # CMakeLists.txt for Simple-XX/SimpleCompiler. 6 | 7 | # 设置项目名与版本 8 | project( 9 | system-test 10 | VERSION 0.0.1 11 | ) 12 | 13 | enable_language(CXX) 14 | 15 | add_executable(system_test 16 | ) 17 | 18 | target_compile_options(system_test PRIVATE 19 | ${DEFAULT_TEST_COMPILE_OPTIONS} 20 | ) 21 | 22 | target_link_options(system_test PRIVATE 23 | ${DEFAULT_TEST_LINK_OPTIONS} 24 | ) 25 | -------------------------------------------------------------------------------- /test/unit_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # This file is a part of Simple-XX/SimpleCompiler 3 | # (https://github.com/Simple-XX/SimpleCompiler). 4 | # 5 | # CMakeLists.txt for Simple-XX/SimpleCompiler. 6 | 7 | # 设置项目名与版本 8 | project( 9 | unit-test 10 | VERSION 0.0.1 11 | ) 12 | 13 | enable_language(CXX) 14 | 15 | add_executable(unit_test 16 | ) 17 | 18 | target_compile_options(unit_test PRIVATE 19 | ${DEFAULT_TEST_COMPILE_OPTIONS} 20 | ) 21 | 22 | target_link_options(unit_test PRIVATE 23 | ${DEFAULT_TEST_LINK_OPTIONS} 24 | ) 25 | 26 | gtest_discover_tests(unit_test) 27 | -------------------------------------------------------------------------------- /test/unit_test/buffer_base_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @file buffer_base_test.cpp 4 | * @brief buffer_base.hpp 测试 5 | * @author Zone.N (Zone.Niuzh@hotmail.com) 6 | * @version 1.0 7 | * @date 2022-09-03 8 | * @copyright MIT LICENSE 9 | * https://github.com/Simple-XX/SimpleCompiler 10 | * @par change log: 11 | * 12 | *
DateAuthorDescription 13 | *
2022-09-03Zone.N创建文件 14 | *
15 | */ 16 | 17 | #include "gtest/gtest.h" 18 | 19 | #include "buffer_base.hpp" 20 | 21 | TEST(buffer_base_t, 空构造) { 22 | SimpleCompiler::buffer_base_t buffer_base0; 23 | EXPECT_EQ(buffer_base0.get_width(), 0); 24 | EXPECT_EQ(buffer_base0.get_height(), 0); 25 | EXPECT_EQ(buffer_base0.length(), 0); 26 | EXPECT_EQ(buffer_base0.data(), nullptr); 27 | } 28 | 29 | TEST(buffer_base_t, 拷贝构造) { 30 | SimpleCompiler::buffer_base_t buffer_base0((uint32_t)100, 200, 0); 31 | EXPECT_EQ(buffer_base0.get_width(), 100); 32 | EXPECT_EQ(buffer_base0.get_height(), 200); 33 | EXPECT_EQ(buffer_base0.BPP, sizeof(uint32_t)); 34 | EXPECT_EQ(buffer_base0.length(), 100 * sizeof(uint32_t) * 200); 35 | EXPECT_NE(buffer_base0.data(), nullptr); 36 | 37 | SimpleCompiler::buffer_base_t buffer_base1; 38 | buffer_base1 = buffer_base0; 39 | EXPECT_EQ(buffer_base1.get_width(), 100); 40 | EXPECT_EQ(buffer_base1.get_height(), 200); 41 | EXPECT_EQ(buffer_base1.BPP, sizeof(uint32_t)); 42 | EXPECT_EQ(buffer_base1.length(), 100 * sizeof(uint32_t) * 200); 43 | EXPECT_NE(buffer_base1.data(), nullptr); 44 | } 45 | 46 | TEST(buffer_base_t, clear) { 47 | SimpleCompiler::buffer_base_t buffer_base0((uint32_t)100, 200, 0); 48 | EXPECT_EQ(buffer_base0.get_width(), 100); 49 | EXPECT_EQ(buffer_base0.get_height(), 200); 50 | EXPECT_EQ(buffer_base0.BPP, sizeof(uint32_t)); 51 | EXPECT_EQ(buffer_base0.length(), 100 * sizeof(uint32_t) * 200); 52 | EXPECT_NE(buffer_base0.data(), nullptr); 53 | EXPECT_EQ(buffer_base0.data()[0], 0); 54 | buffer_base0.clear(std::numeric_limits::lowest()); 55 | EXPECT_EQ(buffer_base0.data()[0], std::numeric_limits::lowest()); 56 | } 57 | 58 | TEST(buffer_base_t, 不同大小的buffer赋值) { 59 | SimpleCompiler::buffer_base_t buffer_base; 60 | EXPECT_TRUE(buffer_base.get_height() == 0); 61 | EXPECT_TRUE(buffer_base.get_width() == 0); 62 | } 63 | -------------------------------------------------------------------------------- /tools/cppcheck-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | * 12 | */build* 13 | 14 | 15 | 16 | * 17 | */3rd/* 18 | 19 | 20 | 21 | missingInclude 22 | */test/* 23 | 24 | 25 | 26 | unmatchedSuppression 27 | * 28 | 29 | 30 | --------------------------------------------------------------------------------