Compare commits
150 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be33eb3942 | |||
| 31d7e91ef1 | |||
| 8b817da3b6 | |||
| d2eafa9dc6 | |||
| c4467b8420 | |||
| 28f65f8775 | |||
| cec96333e7 | |||
| ea553718f0 | |||
| d78b91894e | |||
| 41d060d7e7 | |||
| aa4292a30e | |||
| c6e3bb2e20 | |||
| 2c13ac54df | |||
| 3df858fb85 | |||
| 68eac24152 | |||
| 4f40f0d5e4 | |||
| 902ee6dea3 | |||
| e5cb70732e | |||
| 096177e7e8 | |||
| aa8a1ff8ce | |||
| 676f3ec82c | |||
| b06c4fe3cc | |||
| 85d698acdf | |||
| f6bc40ae4a | |||
| d7ac5fd30b | |||
| f57ba9bd31 | |||
| e850b5c981 | |||
| 0fbfb36262 | |||
| e5bbffe170 | |||
| 5a9f816ccf | |||
| 8054f20375 | |||
| ffb23afaf4 | |||
| 694778e4a0 | |||
| 630e22b73b | |||
| 053c6abf51 | |||
| eeb4c4fc3c | |||
| d88475cc06 | |||
| 4144f7841c | |||
| 27d86d5685 | |||
| ca187c78f1 | |||
| 78e7c800ba | |||
| 4ddad7b456 | |||
| 8c7af571c2 | |||
| d167a8ba96 | |||
| 741171dbba | |||
| 097dbdcc2a | |||
| 35a704a1cb | |||
| a64e1f8330 | |||
| de6f5d510a | |||
| 02a6c684f1 | |||
| 5f915ba8d3 | |||
| 2e5e98868d | |||
| cabd1710ed | |||
| 45c59e0b65 | |||
| 6ebf0c48e3 | |||
| 82dd5f2db0 | |||
| eb969cdbf7 | |||
| 8fcfeda14e | |||
| c99f64708e | |||
| 2d1032c363 | |||
| c46578736a | |||
| b00a42a539 | |||
| 30ac2de73b | |||
| e6511c508c | |||
| ce5414f2eb | |||
| 742bede02f | |||
| 2e331ee016 | |||
| 80714fe7e5 | |||
| 1fceeca011 | |||
| a805814d3f | |||
| 4015acd866 | |||
| 2c4b803058 | |||
| beb0b19026 | |||
| 46f5328183 | |||
| 60cdfd2c33 | |||
| 8cbb9e6987 | |||
| 0fede5f46e | |||
| e79984592e | |||
| 72ef3964ce | |||
| 4e2176b7f0 | |||
| 13de9d713b | |||
| f56b13da2c | |||
| d2eaf2247f | |||
| 51869bf081 | |||
| 3b2f68111e | |||
| 4940b652eb | |||
| 8007825800 | |||
| b705e5d0ad | |||
| 9c2b4db22a | |||
| bc0b1d23e3 | |||
| a52ff33e30 | |||
| 27a87d17ab | |||
| 08a60e6e8a | |||
| c86071416d | |||
| 9d85dc130d | |||
| 2de5ae59f5 | |||
| 681a15cb44 | |||
| b4929be6b8 | |||
| 0e7dec202a | |||
| 088050c903 | |||
| ffee07a03d | |||
| 191cdcef53 | |||
| 34d7eb3c42 | |||
| d1b215861c | |||
| 2a90e165a5 | |||
| c8bf98525d | |||
| 135e874a76 | |||
| 79ee7a657a | |||
| e1cd8c5206 | |||
| 84cff78b86 | |||
| 6c801fbb92 | |||
| c01e6e1db4 | |||
| b753ae0911 | |||
| 09f4ac8de0 | |||
| 07f5d9331b | |||
| 73d74f5e13 | |||
| ce8031b21f | |||
| 07a76d82f4 | |||
| 874a58281f | |||
| 94d3f46fac | |||
| 897ef449fb | |||
| 3aaf3a3991 | |||
| d88fa3b8d3 | |||
| 35c13ee30a | |||
| 098e41d3e5 | |||
| 186602a301 | |||
| 36bff64a91 | |||
| 1ab07a5815 | |||
| ed829bdc21 | |||
| e6a76e7a86 | |||
| 871d031ceb | |||
| 67af0c6bf2 | |||
| fa5611dabd | |||
| 63f6f13883 | |||
| d6941e1d2f | |||
| a3322f0d4c | |||
| 164bab0f13 | |||
| f29fd92fdf | |||
| d1fafa830d | |||
| 9762cf8a2b | |||
| 47b56d52f6 | |||
| 5c24f35c87 | |||
| e22811f2f5 | |||
| 8d97fe896c | |||
| c800b48ca2 | |||
| 1cf26c43f3 | |||
| b57f21556a | |||
| 74f43a1ab7 | |||
| 2b4857001c | |||
| 05c637e594 |
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
.*
|
||||
!.gitignore
|
||||
|
||||
# doxygen generated files
|
||||
docs
|
||||
|
||||
# smcc compiler generated files
|
||||
*.bin
|
||||
.s
|
||||
.asm
|
||||
|
||||
# linux binary files
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.out
|
||||
|
||||
# windows binary files
|
||||
*.obj
|
||||
*.lib
|
||||
*.dll
|
||||
*.exe
|
||||
*.pdb
|
||||
*.dmp
|
||||
|
||||
# developed notes
|
||||
note.md
|
||||
|
||||
# python
|
||||
.venv
|
||||
|
||||
# cbuilder
|
||||
build
|
||||
|
||||
# external
|
||||
external/
|
||||
|
||||
# dot file and svg file
|
||||
*.dot
|
||||
*.svg
|
||||
|
||||
# dll def or other definition file
|
||||
*.def
|
||||
8
LICENSE
Normal file
8
LICENSE
Normal file
@@ -0,0 +1,8 @@
|
||||
The MIT License (MIT)
|
||||
Copyright © 2026 <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
5
Makefile
Normal file
5
Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
build-docs:
|
||||
doxygen Doxyfile
|
||||
|
||||
docs: build-docs
|
||||
python -m http.server -d docs/html
|
||||
35
README.md
Normal file
35
README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Simple C Compiler
|
||||
|
||||
> `scc`
|
||||
|
||||
这是一个简单的C语言裸机环境的自举编译器,可以从C99子集编程语言生成可执行代码。该语言支持基本操作,如算术运算、逻辑运算、条件语句(if/else)、循环语句(while/for)、分支语句(switch/case)、函数调用以及内联汇编调用(asm)。
|
||||
|
||||
## Builder
|
||||
|
||||
该编译器使用`cbuild` 构建,设计思路来源于`rust`的构建工具`cargo`,以默认行为代替掉`make`、`cmake`等构建工具。
|
||||
|
||||
> 由于整个项目除了测试代码,完全没有任何依赖,真正做到`0依赖`,所以无需复杂的依赖管理
|
||||
TODO 未来构建工具本身也会使用c或者lua等可轻松自举的系统重构
|
||||
|
||||
### [cbuild](./tools/cbuild/cbuild.py)
|
||||
|
||||
常用命令:
|
||||
|
||||
- 构建项目: `cbuild build`
|
||||
- 运行程序: `cbuild run`
|
||||
- 运行测试: `cbuild test`
|
||||
- 清理构建产物: `cbuild clean`
|
||||
- 查看依赖树: `cbuild tree`
|
||||
|
||||
## 标准C语言文档链接
|
||||
|
||||
- [open-std](https://www.open-std.org/)
|
||||
- [C - Project status and milestones](https://www.open-std.org/JTC1/SC22/WG14/www/projects#9899)
|
||||
- [ISO/IEC 9899:1999](https://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf)
|
||||
|
||||
## 项目特点
|
||||
|
||||
- **隔离标准库**: 不直接依赖标准库实现,提高可移植性
|
||||
- **轻量化**: 专注于核心功能,减少冗余组件
|
||||
- **模块化**: 采用模块化设计,易于扩展和维护
|
||||
- **自举构建**: 支持通过自身编译器构建项目
|
||||
17
cbuild.toml
Normal file
17
cbuild.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "scc"
|
||||
version = "0.1.0"
|
||||
|
||||
dependencies = [
|
||||
{ name = "argparse", path = "./libs/argparse" },
|
||||
{ name = "lexer", path = "./libs/lexer" },
|
||||
{ name = "pproc", path = "./libs/pproc" },
|
||||
{ name = "parser", path = "./libs/parser" },
|
||||
{ name = "ast", path = "./libs/ast" },
|
||||
{ name = "ast2ir", path = "./libs/ast2ir" },
|
||||
{ name = "hir", path = "./libs/ir/hir" },
|
||||
{ name = "lir", path = "./libs/ir/lir" },
|
||||
{ name = "mir", path = "./libs/ir/mir" },
|
||||
{ name = "ir2mcode", path = "./libs/ir2mcode" },
|
||||
{ name = "sccf2target", path = "./libs/target/sccf2target" },
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
all: ccompiler
|
||||
|
||||
run: ccompiler
|
||||
./ccompiler test.c flat.bin
|
||||
|
||||
ccompiler: frontend ir
|
||||
gcc -g rv32ima_codegen.c -L../../frontend -lfrontend -L../../middleend -lir -o ccompiler
|
||||
|
||||
frontend:
|
||||
make -C ../../frontend
|
||||
|
||||
ir:
|
||||
make -C ../../middleend
|
||||
|
||||
clean:
|
||||
rm -f ccompiler flat.bin
|
||||
make -C ../../frontend clean
|
||||
make -C ../../middleend clean
|
||||
@@ -1,341 +0,0 @@
|
||||
#ifndef __RV32I_GEN_H__
|
||||
#define __RV32I_GEN_H__
|
||||
|
||||
/**
|
||||
31 25 24 20 19 15 14 12 11 7 6 0
|
||||
imm[31:12] rd 0110111 U lui
|
||||
imm[31:12] rd 0010111 U auipc
|
||||
imm[20|10:1|11|19:12] rd 1101111 J jal
|
||||
imm[11:0] rs1 000 rd 1100111 I jalr
|
||||
imm[12|10:5] rs2 rs1 000 imm[4:1|11] 1100011 B beq
|
||||
imm[12|10:5] rs2 rs1 001 imm[4:1|11] 1100011 B bne
|
||||
imm[12|10:5] rs2 rs1 100 imm[4:1|11] 1100011 B blt
|
||||
imm[12|10:5] rs2 rs1 101 imm[4:1|11] 1100011 B bge
|
||||
imm[12|10:5] rs2 rs1 110 imm[4:1|11] 1100011 B bltu
|
||||
imm[12|10:5] rs2 rs1 111 imm[4:1|11] 1100011 B bgeu
|
||||
imm[11:0] rs1 000 rd 0000011 I lb
|
||||
imm[11:0] rs1 001 rd 0000011 I lh
|
||||
imm[11:0] rs1 010 rd 0000011 I lw
|
||||
imm[11:0] rs1 100 rd 0000011 I lbu
|
||||
imm[11:0] rs1 101 rd 0000011 I lhu
|
||||
imm[11:5] rs2 rs1 000 imm[4:0] 0100011 S sb
|
||||
imm[11:5] rs2 rs1 001 imm[4:0] 0100011 S sh
|
||||
imm[11:5] rs2 rs1 010 imm[4:0] 0100011 S sw
|
||||
imm[11:0] rs1 000 rd 0010011 I addi
|
||||
imm[11:0] rs1 010 rd 0010011 I slti
|
||||
imm[11:0] rs1 011 rd 0010011 I sltiu
|
||||
imm[11:0] rs1 100 rd 0010011 I xori
|
||||
imm[11:0] rs1 110 rd 0010011 I ori
|
||||
imm[11:0] rs1 111 rd 0010011 I andi
|
||||
0000000 shamt rs1 001 rd 0010011 I slli
|
||||
0000000 shamt rs1 101 rd 0010011 I srli
|
||||
0100000 shamt rs1 101 rd 0010011 I srai
|
||||
0000000 rs2 rs1 000 rd 0110011 R add
|
||||
0100000 rs2 rs1 000 rd 0110011 R sub
|
||||
0000000 rs2 rs1 001 rd 0110011 R sll
|
||||
0000000 rs2 rs1 010 rd 0110011 R slt
|
||||
0000000 rs2 rs1 011 rd 0110011 R sltu
|
||||
0000000 rs2 rs1 100 rd 0110011 R xor
|
||||
0000000 rs2 rs1 101 rd 0110011 R srl
|
||||
0100000 rs2 rs1 101 rd 0110011 R sra
|
||||
0000000 rs2 rs1 110 rd 0110011 R or
|
||||
0000000 rs2 rs1 111 rd 0110011 R and
|
||||
0000 pred succ 00000 000 00000 0001111 I fence
|
||||
0000 0000 0000 00000 001 00000 0001111 I fence.i
|
||||
000000000000 00000 00 00000 1110011 I ecall
|
||||
000000000000 00000 000 00000 1110011 I ebreak
|
||||
csr rs1 001 rd 1110011 I csrrw
|
||||
csr rs1 010 rd 1110011 I csrrs
|
||||
csr rs1 011 rd 1110011 I csrrc
|
||||
csr zimm 101 rd 1110011 I csrrwi
|
||||
csr zimm 110 rd 1110011 I cssrrsi
|
||||
csr zimm 111 rd 1110011 I csrrci
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
// 寄存器枚举定义
|
||||
typedef enum {
|
||||
REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7,
|
||||
REG_X8, REG_X9, REG_X10, REG_X11, REG_X12, REG_X13, REG_X14, REG_X15,
|
||||
REG_X16, REG_X17, REG_X18, REG_X19, REG_X20, REG_X21, REG_X22, REG_X23,
|
||||
REG_X24, REG_X25, REG_X26, REG_X27, REG_X28, REG_X29, REG_X30, REG_X31,
|
||||
REG_ZERO = REG_X0, REG_RA = REG_X1, REG_SP = REG_X2, REG_GP = REG_X3,
|
||||
REG_TP = REG_X4, REG_T0 = REG_X5, REG_T1 = REG_X6, REG_T2 = REG_X7,
|
||||
REG_S0 = REG_X8, REG_S1 = REG_X9, REG_A0 = REG_X10, REG_A1 = REG_X11,
|
||||
REG_A2 = REG_X12, REG_A3 = REG_X13, REG_A4 = REG_X14, REG_A5 = REG_X15,
|
||||
REG_A6 = REG_X16, REG_A7 = REG_X17, REG_S2 = REG_X18, REG_S3 = REG_X19,
|
||||
REG_S4 = REG_X20, REG_S5 = REG_X21, REG_S6 = REG_X22, REG_S7 = REG_X23,
|
||||
REG_S8 = REG_X24, REG_S9 = REG_X25, REG_S10 = REG_X26, REG_S11 = REG_X27,
|
||||
REG_T3 = REG_X28, REG_T4 = REG_X29, REG_T5 = REG_X30, REG_T6 = REG_X31,
|
||||
} RV32Reg;
|
||||
|
||||
/******************** 立即数处理宏 ********************/
|
||||
#define IMM_12BITS(imm) ((imm) & 0xFFF)
|
||||
#define IMM_20BITS(imm) ((imm) & 0xFFFFF)
|
||||
#define SHAMT_VAL(imm) ((imm) & 0x1F)
|
||||
#define CSR_VAL(csr) ((csr) & 0xFFF)
|
||||
|
||||
// B型立即数编码([12|10:5|4:1|11])
|
||||
#define ENCODE_B_IMM(imm) ( \
|
||||
(((imm) >> 12) & 0x1) << 31 | /* imm[12:12] -> instr[31:31] */ \
|
||||
(((imm) >> 5) & 0x3F) << 25 | /* imm[10:5] -> instr[30:25] */ \
|
||||
(((imm) >> 1) & 0xF) << 8 | /* imm[4:1] -> instr[11:8] */ \
|
||||
(((imm) >> 11) & 0x1) << 7) /* imm[11:11] -> instr[7:7] */
|
||||
|
||||
// J型立即数编码([20|10:1|11|19:12])W
|
||||
#define ENCODE_J_IMM(imm) ( \
|
||||
(((imm) >> 20) & 0x1) << 31 | /* imm[20:20] -> instr[31:31] */ \
|
||||
(((imm) >> 1) & 0x3FF)<< 21 | /* imm[10:1] -> instr[30:21] */ \
|
||||
(((imm) >> 11) & 0x1) << 20 | /* imm[11:11] -> instr[20:20] */ \
|
||||
(((imm) >> 12) & 0xFF) << 12) /* imm[19:12] -> instr[19:12] */
|
||||
/******************** 指令生成宏 ********************/
|
||||
// R型指令宏
|
||||
#define RV32_RTYPE(op, f3, f7, rd, rs1, rs2) (uint32_t)( \
|
||||
(0x33 | ((rd) << 7) | ((f3) << 12) | ((rs1) << 15) | \
|
||||
((rs2) << 20) | ((f7) << 25)) )
|
||||
|
||||
// I型指令宏
|
||||
#define RV32_ITYPE(op, f3, rd, rs1, imm) (uint32_t)( \
|
||||
(op | ((rd) << 7) | ((f3) << 12) | ((rs1) << 15) | \
|
||||
(IMM_12BITS(imm) << 20)) )
|
||||
|
||||
// S型指令宏
|
||||
#define RV32_STYPE(op, f3, rs1, rs2, imm) (uint32_t)( \
|
||||
(op | ((IMM_12BITS(imm) & 0xFE0) << 20) | ((rs1) << 15) | \
|
||||
((rs2) << 20) | ((f3) << 12) | ((IMM_12BITS(imm) & 0x1F) << 7)) )
|
||||
|
||||
// B型指令宏
|
||||
#define RV32_BTYPE(op, f3, rs1, rs2, imm) (uint32_t)( \
|
||||
(op | (ENCODE_B_IMM(imm)) | ((rs1) << 15) | \
|
||||
((rs2) << 20) | ((f3) << 12)) )
|
||||
|
||||
// U型指令宏
|
||||
#define RV32_UTYPE(op, rd, imm) (uint32_t)( \
|
||||
(op | ((rd) << 7) | (IMM_20BITS((imm) >> 12) << 12)) )
|
||||
|
||||
// J型指令宏
|
||||
#define RV32_JTYPE(op, rd, imm) (uint32_t)( \
|
||||
(op | ((rd) << 7) | ENCODE_J_IMM(imm)) )
|
||||
|
||||
/******************** U-type ********************/
|
||||
#define LUI(rd, imm) RV32_UTYPE(0x37, rd, imm)
|
||||
#define AUIPC(rd, imm) RV32_UTYPE(0x17, rd, imm)
|
||||
|
||||
/******************** J-type ********************/
|
||||
#define JAL(rd, imm) RV32_JTYPE(0x6F, rd, imm)
|
||||
|
||||
/******************** I-type ********************/
|
||||
#define JALR(rd, rs1, imm) RV32_ITYPE(0x67, 0x0, rd, rs1, imm)
|
||||
|
||||
// Load instructions
|
||||
#define LB(rd, rs1, imm) RV32_ITYPE(0x03, 0x0, rd, rs1, imm)
|
||||
#define LH(rd, rs1, imm) RV32_ITYPE(0x03, 0x1, rd, rs1, imm)
|
||||
#define LW(rd, rs1, imm) RV32_ITYPE(0x03, 0x2, rd, rs1, imm)
|
||||
#define LBU(rd, rs1, imm) RV32_ITYPE(0x03, 0x4, rd, rs1, imm)
|
||||
#define LHU(rd, rs1, imm) RV32_ITYPE(0x03, 0x5, rd, rs1, imm)
|
||||
|
||||
// Immediate arithmetic
|
||||
#define ADDI(rd, rs1, imm) RV32_ITYPE(0x13, 0x0, rd, rs1, imm)
|
||||
#define SLTI(rd, rs1, imm) RV32_ITYPE(0x13, 0x2, rd, rs1, imm)
|
||||
#define SLTIU(rd, rs1, imm) RV32_ITYPE(0x13, 0x3, rd, rs1, imm)
|
||||
#define XORI(rd, rs1, imm) RV32_ITYPE(0x13, 0x4, rd, rs1, imm)
|
||||
#define ORI(rd, rs1, imm) RV32_ITYPE(0x13, 0x6, rd, rs1, imm)
|
||||
#define ANDI(rd, rs1, imm) RV32_ITYPE(0x13, 0x7, rd, rs1, imm)
|
||||
|
||||
// Shift instructions
|
||||
#define SLLI(rd, rs1, shamt) RV32_ITYPE(0x13, 0x1, rd, rs1, (0x00000000 | (shamt << 20)))
|
||||
#define SRLI(rd, rs1, shamt) RV32_ITYPE(0x13, 0x5, rd, rs1, (0x00000000 | (shamt << 20)))
|
||||
#define SRAI(rd, rs1, shamt) RV32_ITYPE(0x13, 0x5, rd, rs1, (0x40000000 | (shamt << 20)))
|
||||
|
||||
/******************** B-type ********************/
|
||||
#define BEQ(rs1, rs2, imm) RV32_BTYPE(0x63, 0x0, rs1, rs2, imm)
|
||||
#define BNE(rs1, rs2, imm) RV32_BTYPE(0x63, 0x1, rs1, rs2, imm)
|
||||
#define BLT(rs1, rs2, imm) RV32_BTYPE(0x63, 0x4, rs1, rs2, imm)
|
||||
#define BGE(rs1, rs2, imm) RV32_BTYPE(0x63, 0x5, rs1, rs2, imm)
|
||||
#define BLTU(rs1, rs2, imm) RV32_BTYPE(0x63, 0x6, rs1, rs2, imm)
|
||||
#define BGEU(rs1, rs2, imm) RV32_BTYPE(0x63, 0x7, rs1, rs2, imm)
|
||||
|
||||
/******************** S-type ********************/
|
||||
#define SB(rs2, rs1, imm) RV32_STYPE(0x23, 0x0, rs1, rs2, imm)
|
||||
#define SH(rs2, rs1, imm) RV32_STYPE(0x23, 0x1, rs1, rs2, imm)
|
||||
#define SW(rs2, rs1, imm) RV32_STYPE(0x23, 0x2, rs1, rs2, imm)
|
||||
|
||||
/******************** R-type ********************/
|
||||
#define ADD(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x00, rd, rs1, rs2)
|
||||
#define SUB(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x20, rd, rs1, rs2)
|
||||
#define SLL(rd, rs1, rs2) RV32_RTYPE(0x33, 0x1, 0x00, rd, rs1, rs2)
|
||||
#define SLT(rd, rs1, rs2) RV32_RTYPE(0x33, 0x2, 0x00, rd, rs1, rs2)
|
||||
#define SLTU(rd, rs1, rs2) RV32_RTYPE(0x33, 0x3, 0x00, rd, rs1, rs2)
|
||||
#define XOR(rd, rs1, rs2) RV32_RTYPE(0x33, 0x4, 0x00, rd, rs1, rs2)
|
||||
#define SRL(rd, rs1, rs2) RV32_RTYPE(0x33, 0x5, 0x00, rd, rs1, rs2)
|
||||
#define SRA(rd, rs1, rs2) RV32_RTYPE(0x33, 0x5, 0x20, rd, rs1, rs2)
|
||||
#define OR(rd, rs1, rs2) RV32_RTYPE(0x33, 0x6, 0x00, rd, rs1, rs2)
|
||||
#define AND(rd, rs1, rs2) RV32_RTYPE(0x33, 0x7, 0x00, rd, rs1, rs2)
|
||||
|
||||
/******************** I-type (system) ********************/
|
||||
#define FENCE(pred, succ) (uint32_t)( 0x0F | ((pred) << 23) | ((succ) << 27) )
|
||||
#define FENCE_I() (uint32_t)( 0x100F )
|
||||
#define ECALL() (uint32_t)( 0x73 )
|
||||
#define EBREAK() (uint32_t)( 0x100073 )
|
||||
|
||||
// CSR instructions
|
||||
#define CSRRW(rd, csr, rs) RV32_ITYPE(0x73, 0x1, rd, rs, CSR_VAL(csr))
|
||||
#define CSRRS(rd, csr, rs) RV32_ITYPE(0x73, 0x2, rd, rs, CSR_VAL(csr))
|
||||
#define CSRRC(rd, csr, rs) RV32_ITYPE(0x73, 0x3, rd, rs, CSR_VAL(csr))
|
||||
#define CSRRWI(rd, csr, zimm) RV32_ITYPE(0x73, 0x5, rd, 0, (CSR_VAL(csr) | ((zimm) << 15)))
|
||||
#define CSRRSI(rd, csr, zimm) RV32_ITYPE(0x73, 0x6, rd, 0, (CSR_VAL(csr) | ((zimm) << 15)))
|
||||
#define CSRRCI(rd, csr, zimm) RV32_ITYPE(0x73, 0x7, rd, 0, (CSR_VAL(csr) | ((zimm) << 15)))
|
||||
|
||||
|
||||
/* M-Extention */
|
||||
#define MUL(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x01, rd, rs1, rs2)
|
||||
#define DIV(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x05, rd, rs1, rs2)
|
||||
#define REM(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x07, rd, rs1, rs2)
|
||||
|
||||
/******************** Pseudo-instructions ********************/
|
||||
// 伪指令
|
||||
|
||||
// nop (No operation)
|
||||
#define NOP() ADDI(REG_X0, REG_X0, 0) // 无操作
|
||||
|
||||
// neg rd, rs (Two's complement of rs)
|
||||
#define NEG(rd, rs) SUB(rd, REG_ZERO, rs) // 补码
|
||||
|
||||
// negw rd, rs (Two's complement word of rs)
|
||||
#define NEGW(rd, rs) SUBW(rd, REG_ZERO, rs) // 字的补码
|
||||
|
||||
// snez rd, rs (Set if ≠ zero)
|
||||
#define SNEZ(rd, rs) SLTU(rd, REG_X0, rs) // 非0则置位
|
||||
|
||||
// sltz rd, rs (Set if < zero)
|
||||
#define SLTZ(rd, rs) SLT(rd, rs, REG_X0) // 小于0则置位
|
||||
|
||||
// sgtz rd, rs (Set if > zero)
|
||||
#define SGTZ(rd, rs) SLT(rd, REG_X0, rs) // 大于0则置位
|
||||
|
||||
// beqz rs, offset (Branch if = zero)
|
||||
#define BEQZ(rs, offset) BEQ(rs, REG_X0, offset) // 为0则转移
|
||||
|
||||
// bnez rs, offset (Branch if ≠ zero)
|
||||
#define BNEZ(rs, offset) BNE(rs, REG_X0, offset) // 非0则转移
|
||||
|
||||
// blez rs, offset (Branch if ≤ zero)
|
||||
#define BLEZ(rs, offset) BGE(REG_X0, rs, offset) // 小于等于0则转移
|
||||
|
||||
// bgez rs, offset (Branch if ≥ zero)
|
||||
#define BGEZ(rs, offset) BGE(rs, REG_X0, offset) // 大于等于0则转移
|
||||
|
||||
// bltz rs, offset (Branch if < zero)
|
||||
#define BLTZ(rs, offset) BLT(rs, REG_X0, offset) // 小于0则转移
|
||||
|
||||
// bgtz rs, offset (Branch if > zero)
|
||||
#define BGTZ(rs, offset) BLT(REG_X0, rs, offset) // 大于0则转移
|
||||
|
||||
// j offset (Jump)
|
||||
#define J(offset) JAL(REG_X0, offset) // 跳转
|
||||
|
||||
// jr rs (Jump register)
|
||||
#define JR(rs) JALR(REG_X0, rs, 0) // 寄存器跳转
|
||||
|
||||
// ret (Return from subroutine)
|
||||
#define RET() JALR(REG_X0, REG_RA, 0) // 从子过程返回
|
||||
|
||||
// tail offset (Tail call far-away subroutine)
|
||||
#define TAIL_2(offset) AUIPC(REG_X6, offset), JAL(REG_X0, REG_X6, offset) // 尾调用远程子过程, 有2条指令
|
||||
#define TAIL(offset) TAIL_2(offset) // Warning this have 2 instructions
|
||||
|
||||
// csrr csr, rd (Read CSR)
|
||||
#define CSRR(csr, rd) CSRRS(rd, csr, REG_X0) // 读CSR寄存器
|
||||
|
||||
// csrw csr, rs (Write CSR)
|
||||
#define CSR W(csr, rs) CSRRW(csr, REG_X0, rs) // 写CSR寄存器
|
||||
|
||||
// csrs csr, rs (Set bits in CSR)
|
||||
#define CSRS(csr, rs) CSRRS(REG_X0, csr, rs) // CSR寄存器置零位
|
||||
|
||||
// csrrc csr, rs (Clear bits in CSR)
|
||||
#define CSRC(csr, rs) CSRRC(REG_X0, csr, rs) // CSR寄存器清
|
||||
|
||||
// csrci csr, imm (Immediate clear bits in CSR)
|
||||
#define CSRCI(csr, imm) CSRRCI(REG_X0, csr, imm) // 立即数清除CSR
|
||||
|
||||
// csrrwi csr, imm (Write CSR immediate)
|
||||
#define CSRRWI2(csr, imm) CSRRWI(REG_X0, csr, imm) // 立即数写入CSR
|
||||
|
||||
// csrrsi csr, imm (Immediate set bits in CSR)
|
||||
#define CSRRSI2(csr, imm) CSRRSI(REG_X0, csr, imm) // 立即数置位CSR
|
||||
|
||||
// csrrci csr, imm (Immediate clear bits in CSR)
|
||||
#define CSRRCI2(csr, imm) CSRRCI(REG_X0, csr, imm) // 立即数清除CSR
|
||||
|
||||
// // frcsr rd (Read FP control/status register)
|
||||
// #define FRC SR(rd) CSRRS(rd, FCSR, REG_X0) // 读取FP控制/状态寄存器
|
||||
|
||||
// // fscsr rs (Write FP control/status register)
|
||||
// #define FSCSR(rs) CSRRW(REG_X0, FCSR, rs) // 写入FP控制/状态寄存器
|
||||
|
||||
// // frrm rd (Read FP rounding mode)
|
||||
// #define FRRM(rd) CSRRS(rd, FRM, REG_X0) // 读取FP舍入模式
|
||||
|
||||
// // fsrm rs (Write FP rounding mode)
|
||||
// #define FS RM(rs) CSRRW(REG_X0, FRM, rs) // 写入FP舍入模式
|
||||
|
||||
// // frflags rd (Read FP exception flags)
|
||||
// #define FRFLAGS(rd) CSRRS(rd, FFLAGS, REG_X0) // 读取FP例外标志
|
||||
|
||||
// // fsflags rs (Write FP exception flags)
|
||||
// #define FS FLAGS(rs) CSRRW(REG_X0, FFLAGS, rs) // 写入FP例外标志
|
||||
|
||||
|
||||
// Myriad sequences
|
||||
#define LI(rd, num) \
|
||||
LUI(rd, num), \
|
||||
ADDI(rd, rd, num)
|
||||
#define MV(rd, rs) ADDI(rd, rs, 0)
|
||||
#define NOT(rd, rs) XORI(rd, rs, -1)
|
||||
#define SEQZ(rd, rs) SLTIU(rd, rs, 1)
|
||||
|
||||
#define SGT(rd, rs1, rs2) SLT(rd, rs2, rs1)
|
||||
|
||||
// TODO call have error when outof jalr
|
||||
#define CALL(offset) \
|
||||
AUIPC(REG_X1, REG_X0), \
|
||||
JALR(REG_X1, REG_X1, offset)
|
||||
#define CALL_ABS(addr) \
|
||||
AUIPC(REG_X0, addr), \
|
||||
JALR(REG_X1, REG_X0, addr)
|
||||
|
||||
#ifdef RISCV_VM_BUILDIN_ECALL
|
||||
#define ECALL_PNT_INT(num) \
|
||||
ADDI(REG_A0, REG_X0, num), \
|
||||
ADDI(REG_A7, REG_X0, 0x1), \
|
||||
ECALL()
|
||||
|
||||
#define ECALL_PNT_STR(str) \
|
||||
ADDI(REG_A0, REG_X0, str), \
|
||||
ADDI(REG_A7, REG_X0, 0x4), \
|
||||
ECALL()
|
||||
|
||||
#define ECALL_EXIT2() \
|
||||
ADDI(REG_A7, REG_X0, 93), \
|
||||
ECALL()
|
||||
|
||||
#define ECALL_EXIT_ARG(errno) \
|
||||
ADDI(REG_A0, REG_X0, errno), \
|
||||
ECALL_EXIT2()
|
||||
|
||||
#define ECALL_EXIT() \
|
||||
ADDI(REG_A7, REG_X0, 93), \
|
||||
ECALL()
|
||||
|
||||
#define ECALL_SCAN_INT(int) \
|
||||
ADDI(REG_A7, (1025 + 4)), \
|
||||
ECALL()
|
||||
|
||||
#define ECALL_SCAN_STR(str) \
|
||||
ADDI(REG_A0, REG_X0, str), \
|
||||
ADDI(REG_A7, REG_X0, (1025 + 5)), \
|
||||
ECALL()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,458 +0,0 @@
|
||||
#define RISCV_VM_BUILDIN_ECALL
|
||||
#include "rv32gen.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
// 指令编码联合体(自动处理小端序)
|
||||
typedef union rv32code {
|
||||
uint32_t code;
|
||||
uint8_t bytes[4];
|
||||
} rv32code_t;
|
||||
|
||||
#include "../../frontend/frontend.h"
|
||||
#include "../../middleend/ir.h"
|
||||
typedef struct {
|
||||
int code_pos;
|
||||
int to_idx;
|
||||
int cur_idx;
|
||||
int base_offset;
|
||||
enum {
|
||||
JMP_BRANCH,
|
||||
JMP_JUMP,
|
||||
JMP_CALL,
|
||||
} type;
|
||||
} jmp_t;
|
||||
|
||||
static struct {
|
||||
vector_header(codes, rv32code_t);
|
||||
int stack_offset;
|
||||
int stack_base;
|
||||
int tmp_reg;
|
||||
ir_bblock_t* cur_block;
|
||||
ir_func_t* cur_func;
|
||||
ir_prog_t* prog;
|
||||
vector_header(jmp, jmp_t*);
|
||||
vector_header(call, jmp_t*);
|
||||
|
||||
int cur_func_offset;
|
||||
int cur_block_offset;
|
||||
} ctx;
|
||||
|
||||
int write_inst(union rv32code ins, FILE* fp) {
|
||||
return fwrite(&ins, sizeof(union rv32code), 1, fp);
|
||||
}
|
||||
|
||||
#define GENCODE(code) vector_push(ctx.codes, (rv32code_t)(code)); len += 4
|
||||
#define GENCODES(...) do { \
|
||||
rv32code_t codes[] = { \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
for (int i = 0; i < sizeof(codes) / sizeof(codes[0]); i ++) { \
|
||||
GENCODE(codes[i]); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int stack_offset(ir_node_t* ptr) {
|
||||
int offset = ctx.stack_base;
|
||||
for (int i = 0; i < ctx.cur_func->bblocks.size; i ++) {
|
||||
ir_bblock_t* block = vector_at(ctx.cur_func->bblocks, i);
|
||||
for (int i = 0; i < block->instrs.size; i++) {
|
||||
if (vector_at(block->instrs, i) == ptr) {
|
||||
offset += i * 4;
|
||||
assert(offset >= 0 && offset < ctx.stack_offset);
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
offset += block->instrs.size * 4;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static int block_idx(ir_bblock_t* toblock) {
|
||||
for (int i = 0; i < ctx.cur_func->bblocks.size; i ++) {
|
||||
ir_bblock_t* block = vector_at(ctx.cur_func->bblocks, i);
|
||||
if (toblock == block) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static int func_idx(ir_func_t* tofunc) {
|
||||
for (int i = 0; i < ctx.prog->funcs.size; i ++) {
|
||||
ir_func_t* func = vector_at(ctx.prog->funcs, i);
|
||||
if (tofunc == func) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static int system_func(const char* name) {
|
||||
static struct {
|
||||
const char* name;
|
||||
int ecall_num;
|
||||
} defined_func[] = {
|
||||
{"ecall_pnt_int", 1},
|
||||
{"ecall_pnt_char", 11},
|
||||
{"ecall_scan_int", 1025 + 4},
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(defined_func)/sizeof(defined_func[0]); i++) {
|
||||
if (strcmp(name, defined_func[i].name) == 0) {
|
||||
return defined_func[i].ecall_num;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_node_val(ir_node_t* ptr, int reg) {
|
||||
int len = 0;
|
||||
switch (ptr->tag) {
|
||||
case IR_NODE_CONST_INT: {
|
||||
GENCODES(LI(reg, ptr->data.const_int.val));
|
||||
break;
|
||||
}
|
||||
// case IR_NODE_CALL: {
|
||||
// // GENCODE(SW(REG_A0, REG_SP, ctx.stack_offset));
|
||||
// // GENCODE()
|
||||
// // break;
|
||||
// }
|
||||
default: {
|
||||
int offset = stack_offset(ptr);
|
||||
GENCODE(LW(reg, REG_SP, offset));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gen_instr(ir_bblock_t* block, ir_node_t* instr) {
|
||||
int len = 0;
|
||||
int offset;
|
||||
switch (instr->tag) {
|
||||
case IR_NODE_ALLOC: {
|
||||
break;
|
||||
}
|
||||
case IR_NODE_LOAD: {
|
||||
// S1 = *(S0 + imm)
|
||||
offset = stack_offset(instr->data.load.target);
|
||||
GENCODE(LW(REG_T0, REG_SP, offset));
|
||||
break;
|
||||
}
|
||||
case IR_NODE_STORE: {
|
||||
// *(S0 + imm) = S1
|
||||
len += get_node_val(instr->data.store.value, REG_T0);
|
||||
offset = stack_offset(instr->data.store.target);
|
||||
GENCODE(SW(REG_T0, REG_SP, offset));
|
||||
break;
|
||||
}
|
||||
case IR_NODE_RET: {
|
||||
// A0 = S0
|
||||
if (instr->data.ret.ret_val != NULL) {
|
||||
len += get_node_val(instr->data.ret.ret_val, REG_A0);
|
||||
}
|
||||
GENCODE(LW(REG_RA, REG_SP, 0));
|
||||
GENCODE(ADDI(REG_SP, REG_SP, ctx.stack_offset));
|
||||
GENCODE(RET());
|
||||
break;
|
||||
}
|
||||
case IR_NODE_OP: {
|
||||
len += get_node_val(instr->data.op.lhs, REG_T1);
|
||||
len += get_node_val(instr->data.op.rhs, REG_T2);
|
||||
|
||||
switch (instr->data.op.op) {
|
||||
case IR_OP_ADD:
|
||||
GENCODE(ADD(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
case IR_OP_SUB:
|
||||
GENCODE(SUB(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
case IR_OP_MUL:
|
||||
GENCODE(MUL(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
case IR_OP_DIV:
|
||||
GENCODE(DIV(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
case IR_OP_MOD:
|
||||
GENCODE(REM(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
case IR_OP_EQ:
|
||||
GENCODE(XOR(REG_T0, REG_T1, REG_T2));
|
||||
GENCODE(SEQZ(REG_T0, REG_T0));
|
||||
break;
|
||||
case IR_OP_GE:
|
||||
GENCODE(SLT(REG_T0, REG_T1, REG_T2));
|
||||
GENCODE(SEQZ(REG_T0, REG_T0));
|
||||
break;
|
||||
case IR_OP_GT:
|
||||
GENCODE(SGT(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
case IR_OP_LE:
|
||||
GENCODE(SGT(REG_T0, REG_T1, REG_T2));
|
||||
GENCODE(SEQZ(REG_T0, REG_T0));
|
||||
break;
|
||||
case IR_OP_LT:
|
||||
GENCODE(SLT(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
case IR_OP_NEQ:
|
||||
GENCODE(XOR(REG_T0, REG_T1, REG_T2));
|
||||
break;
|
||||
default:
|
||||
error("ERROR gen_instr op in riscv");
|
||||
break;
|
||||
}
|
||||
offset = stack_offset(instr);
|
||||
GENCODE(SW(REG_T0, REG_SP, offset));
|
||||
break;
|
||||
}
|
||||
case IR_NODE_BRANCH: {
|
||||
len += get_node_val(instr->data.branch.cond, REG_T0);
|
||||
int tidx = block_idx(instr->data.branch.true_bblock);
|
||||
int fidx = block_idx(instr->data.branch.false_bblock);
|
||||
int cidx = block_idx(ctx.cur_block);
|
||||
jmp_t* jmp;
|
||||
jmp = xmalloc(sizeof(jmp_t));
|
||||
*jmp = (jmp_t) {
|
||||
.base_offset = 8,
|
||||
.code_pos = ctx.codes.size,
|
||||
.type = JMP_BRANCH,
|
||||
.to_idx = tidx,
|
||||
.cur_idx=cidx,
|
||||
};
|
||||
vector_push(ctx.jmp, jmp);
|
||||
GENCODE(BNEZ(REG_T0, 0));
|
||||
jmp = xmalloc(sizeof(jmp_t));
|
||||
*jmp = (jmp_t) {
|
||||
.base_offset = 4,
|
||||
.code_pos = ctx.codes.size,
|
||||
.type = JMP_JUMP,
|
||||
.to_idx = fidx,
|
||||
.cur_idx=cidx,
|
||||
};
|
||||
vector_push(ctx.jmp, jmp);
|
||||
GENCODE(J(0));
|
||||
break;
|
||||
}
|
||||
case IR_NODE_JUMP: {
|
||||
int idx = block_idx(instr->data.jump.target_bblock);
|
||||
jmp_t* jmp = xmalloc(sizeof(jmp_t));
|
||||
*jmp = (jmp_t) {
|
||||
.base_offset = 4,
|
||||
.code_pos = ctx.codes.size,
|
||||
.type = JMP_JUMP,
|
||||
.to_idx = idx,
|
||||
.cur_idx=block_idx(ctx.cur_block),
|
||||
};
|
||||
vector_push(ctx.jmp, jmp);
|
||||
GENCODE(J(0));
|
||||
break;
|
||||
}
|
||||
case IR_NODE_CALL: {
|
||||
if (instr->data.call.args.size > 8) {
|
||||
error("can't add so much params");
|
||||
}
|
||||
int param_regs[8] = {
|
||||
REG_A0, REG_A1, REG_A2, REG_A3,
|
||||
REG_A4, REG_A5, REG_A6, REG_A7
|
||||
};
|
||||
for (int i = 0; i < instr->data.call.args.size; i++) {
|
||||
ir_node_t* param = vector_at(instr->data.call.args, i);
|
||||
len += get_node_val(param, param_regs[i]);
|
||||
}
|
||||
|
||||
int system_func_idx = system_func(instr->data.call.callee->name);
|
||||
if (system_func_idx != -1) {
|
||||
// ecall
|
||||
GENCODES(
|
||||
ADDI(REG_A7, REG_X0, system_func_idx),
|
||||
ECALL()
|
||||
);
|
||||
goto CALL_END;
|
||||
}
|
||||
|
||||
jmp_t* jmp = xmalloc(sizeof(jmp_t));
|
||||
*jmp = (jmp_t) {
|
||||
.base_offset = ctx.cur_func_offset + ctx.cur_block_offset + len,
|
||||
.code_pos = ctx.codes.size,
|
||||
.type = JMP_CALL,
|
||||
.to_idx = func_idx(instr->data.call.callee),
|
||||
.cur_idx = func_idx(ctx.cur_func),
|
||||
};
|
||||
vector_push(ctx.call, jmp);
|
||||
|
||||
GENCODES(CALL(0));
|
||||
CALL_END:
|
||||
offset = stack_offset(instr);
|
||||
GENCODE(SW(REG_A0, REG_SP, offset));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error("ERROR gen_instr in riscv");
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gen_block(ir_bblock_t* block) {
|
||||
int len = 0;
|
||||
ctx.cur_block = block;
|
||||
for (int i = 0; i < block->instrs.size; i ++) {
|
||||
ctx.cur_block_offset = len;
|
||||
len += gen_instr(block, vector_at(block->instrs, i));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gen_func(ir_func_t* func) {
|
||||
int len = 0;
|
||||
ctx.cur_func = func;
|
||||
ctx.stack_base = 16;
|
||||
ctx.stack_offset = ctx.stack_base;
|
||||
for (int i = 0; i < func->bblocks.size; i++) {
|
||||
ctx.stack_offset += 4 * (*vector_at(func->bblocks, i)).instrs.size;
|
||||
}
|
||||
GENCODE(ADDI(REG_SP, REG_SP, -ctx.stack_offset));
|
||||
GENCODE(SW(REG_RA, REG_SP, 0));
|
||||
|
||||
int param_regs[8] = {
|
||||
REG_A0, REG_A1, REG_A2, REG_A3,
|
||||
REG_A4, REG_A5, REG_A6, REG_A7
|
||||
};
|
||||
if (func->params.size > 8) {
|
||||
error("can't add so much params");
|
||||
}
|
||||
for (int i = 0; i < func->params.size; i++) {
|
||||
int offset = stack_offset(vector_at(func->params, i));
|
||||
GENCODE(SW(param_regs[i], REG_SP, offset));
|
||||
}
|
||||
|
||||
int jmp_cache[func->bblocks.size + 1];
|
||||
|
||||
if (ctx.jmp.data != NULL) vector_free(ctx.jmp);
|
||||
vector_init(ctx.jmp);
|
||||
jmp_cache[0] = 0;
|
||||
for(int i = 0; i < func->bblocks.size; i ++) {
|
||||
ctx.cur_func_offset = len;
|
||||
jmp_cache[i + 1] = jmp_cache[i];
|
||||
int ret = gen_block(vector_at(func->bblocks, i));
|
||||
jmp_cache[i + 1] += ret;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ctx.jmp.size; i++) {
|
||||
jmp_t* jmp = vector_at(ctx.jmp, i);
|
||||
int32_t code = 0;
|
||||
int offset = jmp_cache[jmp->to_idx] - (jmp_cache[jmp->cur_idx + 1] - jmp->base_offset);
|
||||
if (jmp->type == JMP_JUMP) {
|
||||
code = J(offset);
|
||||
} else {
|
||||
code = BNEZ(REG_T0, offset);
|
||||
}
|
||||
ctx.codes.data[jmp->code_pos] = (rv32code_t) {
|
||||
.code = code,
|
||||
};
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gen_code(ir_prog_t* prog) {
|
||||
ctx.prog = prog;
|
||||
|
||||
for (int i = 0; i < prog->extern_funcs.size; i++) {
|
||||
if (system_func(prog->extern_funcs.data[i]->name) == -1) {
|
||||
error("func %s not defined and not a system func", prog->extern_funcs.data[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
int jmp_cache[prog->funcs.size + 1];
|
||||
for(int i = 0; i < prog->funcs.size; i ++) {
|
||||
jmp_cache[i + 1] = jmp_cache[i];
|
||||
int ret = gen_func(vector_at(prog->funcs, i));
|
||||
jmp_cache[i + 1] += ret;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ctx.call.size; i++) {
|
||||
jmp_t* jmp = vector_at(ctx.call, i);
|
||||
int32_t code = 0;
|
||||
// FIXME ERROR
|
||||
int offset = jmp_cache[jmp->to_idx] - (jmp_cache[jmp->cur_idx] + jmp->base_offset);
|
||||
assert(offset > -0xfff && offset < 0xfff);
|
||||
int32_t codes[2] = {
|
||||
CALL(offset)
|
||||
};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ctx.codes.data[jmp->code_pos + i] = (rv32code_t) {
|
||||
.code = codes[i],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Got Main pos;
|
||||
for (int i = 0; i < prog->funcs.size; i++) {
|
||||
if (strcmp(vector_at(prog->funcs, i)->name, "main") == 0) {
|
||||
return jmp_cache[i];
|
||||
}
|
||||
}
|
||||
error("main not found");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// gcc rv32ima_codegen.c -o rv32gen.exe
|
||||
const char* infilename = "test.c";
|
||||
const char* outfilename = "flat.bin";
|
||||
if (argc >= 2) {
|
||||
infilename = argv[1];
|
||||
}
|
||||
if (argc >= 3) {
|
||||
outfilename = argv[2];
|
||||
}
|
||||
FILE* in = fopen(infilename, "r");
|
||||
FILE* out = fopen(outfilename, "wb");
|
||||
if (in == NULL || out == NULL) {
|
||||
printf("Failed to open file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ASTNode* root = frontend(infilename, in, (sread_fn)fread_s);
|
||||
gen_ir_from_ast(root);
|
||||
int main_pos = gen_code(&prog);
|
||||
|
||||
#define CRT_CODE_SIZE 16
|
||||
rv32code_t gcodes[] = {
|
||||
LI(REG_SP, 0x1000),
|
||||
LI(REG_RA, 0x0),
|
||||
CALL(0),
|
||||
// Exit
|
||||
ECALL_EXIT2(),
|
||||
};
|
||||
main_pos += (CRT_CODE_SIZE - 4) * 4;
|
||||
assert(main_pos > -0xfff && main_pos < 0xfff);
|
||||
rv32code_t call_main[2] = {
|
||||
CALL(main_pos)
|
||||
};
|
||||
gcodes[4] = call_main[0];
|
||||
gcodes[5] = call_main[1];
|
||||
|
||||
for (int i = 0; i < CRT_CODE_SIZE; i++) {
|
||||
write_inst((union rv32code) {
|
||||
.code = NOP(),
|
||||
}, out);
|
||||
}
|
||||
fflush(out);
|
||||
assert(CRT_CODE_SIZE >= sizeof(gcodes) / sizeof(gcodes[0]));
|
||||
fseek(out, 0, SEEK_SET);
|
||||
fwrite(gcodes, sizeof(gcodes), 1, out);
|
||||
fflush(out);
|
||||
fseek(out, CRT_CODE_SIZE * 4, SEEK_SET);
|
||||
|
||||
fwrite(ctx.codes.data, sizeof(ctx.codes.data[0]), ctx.codes.size, out);
|
||||
fflush(out);
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
// printf("comiler end out: %s\n", outfilename);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
int main() {
|
||||
return 65536;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
int add(int, int);
|
||||
|
||||
int main(void) {
|
||||
return add(1, 2);
|
||||
}
|
||||
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
VM := ../../rv32-vm
|
||||
CC := ../../ccompiler
|
||||
STD_CC := gcc
|
||||
|
||||
TESTS := $(wildcard *.c)
|
||||
|
||||
# 定义所有测试目标
|
||||
TEST_TARGETS := $(patsubst %.c, %_test, $(TESTS))
|
||||
|
||||
all: $(TEST_TARGETS)
|
||||
|
||||
%_test: %.c
|
||||
@$(STD_CC) -g -o $@ $<
|
||||
@$(CC) $< flat.bin
|
||||
@./$@ ; ret_gcc=$$?
|
||||
@$(VM) flat.bin ; ret_vm=$$?
|
||||
@echo "Testing $@"
|
||||
@if [ $$ret_gcc -eq $$ret_vm ]; then \
|
||||
echo "$@ passed"; \
|
||||
else \
|
||||
echo "$@ failed: GCC returned $$ret_gcc, VM returned $$ret_vm"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -f $(TEST_TARGETS) flat.bin
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -1,6 +0,0 @@
|
||||
int main() {
|
||||
int a, b;
|
||||
a = 1;
|
||||
b = 2;
|
||||
return a + b;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import subprocess
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 配置参数
|
||||
TEST_DIR = Path(".")
|
||||
CC_PATH = Path("../../ccompiler.exe")
|
||||
VM_PATH = Path("../../rv32-vm.exe")
|
||||
WORKSPACE = Path(".") # 测试工作目录
|
||||
|
||||
# 测试用例映射表(示例)
|
||||
TEST_CASE_MAP = {
|
||||
"./01_return.c": 65536,
|
||||
"./02_decl_expr.c": 1,
|
||||
"./03_decl_init.c": 11,
|
||||
"./04_if.c": 1,
|
||||
"./05_else.c": 2,
|
||||
"./06_fcall.c": 3,
|
||||
"./07_while.c": 10,
|
||||
"./08_do_while.c": 128,
|
||||
"./09_for.c": 10,
|
||||
"./10_main.c": 3,
|
||||
"./11_recursive.c": 120,
|
||||
}
|
||||
|
||||
def run_command(cmd, capture_stderr=True):
|
||||
"""执行命令并捕获stderr"""
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
cwd=WORKSPACE,
|
||||
stderr=subprocess.PIPE if capture_stderr else None,
|
||||
text=True,
|
||||
timeout=1,
|
||||
)
|
||||
return result.stderr.strip() if capture_stderr else None
|
||||
|
||||
def run_test(test_file, expected):
|
||||
print(f"\nTesting {test_file}...")
|
||||
|
||||
# 1. 编译生成flat.bin
|
||||
compile_cmd = [str(CC_PATH), str(test_file)]
|
||||
compile_err = run_command(compile_cmd)
|
||||
|
||||
if not (WORKSPACE / "flat.bin").exists():
|
||||
print(f" Compilation failed: {compile_err}")
|
||||
return False
|
||||
|
||||
# 2. 执行虚拟机
|
||||
vm_cmd = [str(VM_PATH), "flat.bin"]
|
||||
|
||||
# 3. 解析返回值(假设最后一行是返回值)
|
||||
try:
|
||||
vm_err = run_command(vm_cmd)
|
||||
actual = int(vm_err.split()[-1])
|
||||
except (ValueError, IndexError) as e:
|
||||
print(f" Invalid VM output: {vm_err}")
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
print(" Timeout expired")
|
||||
return False
|
||||
|
||||
# 4. 验证结果
|
||||
if actual == expected:
|
||||
print(f" PASSED {test_file}")
|
||||
return True
|
||||
else:
|
||||
print(f" FAILED: Expected {expected}, got {actual}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
passed = 0
|
||||
total = 0
|
||||
|
||||
for test_file, expected in TEST_CASE_MAP.items():
|
||||
total += 1
|
||||
if run_test(TEST_DIR / test_file, expected):
|
||||
passed += 1
|
||||
|
||||
# 清理中间文件
|
||||
if (WORKSPACE / "flat.bin").exists():
|
||||
os.remove(WORKSPACE / "flat.bin")
|
||||
|
||||
print(f"\nTest Summary: {passed}/{total} passed")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,47 +0,0 @@
|
||||
# 编译器设置
|
||||
CC = gcc
|
||||
AR = ar
|
||||
CFLAGS = -g -Wall
|
||||
|
||||
# 源文件路径
|
||||
LEXER_DIR = ./lexer
|
||||
PARSER_DIR = ./parser
|
||||
AST_DIR = ./parser/ast
|
||||
SYMTAB_DIR = ./parser/symtab
|
||||
|
||||
# 源文件列表
|
||||
SRCS = \
|
||||
frontend.c \
|
||||
$(LEXER_DIR)/lexer.c \
|
||||
$(LEXER_DIR)/token.c \
|
||||
$(PARSER_DIR)/parser.c \
|
||||
$(AST_DIR)/ast.c \
|
||||
$(AST_DIR)/block.c \
|
||||
$(AST_DIR)/decl.c \
|
||||
$(AST_DIR)/expr.c \
|
||||
$(AST_DIR)/func.c \
|
||||
$(AST_DIR)/program.c \
|
||||
$(AST_DIR)/stmt.c \
|
||||
$(AST_DIR)/term.c \
|
||||
$(SYMTAB_DIR)/hashmap.c \
|
||||
$(SYMTAB_DIR)/scope.c \
|
||||
$(SYMTAB_DIR)/symtab.c \
|
||||
|
||||
# 生成目标文件列表
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
# 最终目标
|
||||
TARGET = libfrontend.a
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET)
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "lexer/lexer.h"
|
||||
#include "parser/symtab/symtab.h"
|
||||
#include "frontend.h"
|
||||
|
||||
struct ASTNode* frontend(const char* file, void* stream, sread_fn sread) {
|
||||
lexer_t lexer;
|
||||
init_lexer(&lexer, file, stream, sread);
|
||||
|
||||
symtab_t symtab;
|
||||
init_symtab(&symtab);
|
||||
|
||||
parser_t parser;
|
||||
init_parser(&parser, &lexer, &symtab);
|
||||
parse_prog(&parser);
|
||||
|
||||
// TODO Free the resourse
|
||||
return parser.root;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef __FRONTEND_H__
|
||||
#define __FRONTEND_H__
|
||||
|
||||
#ifndef error
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#define STD_LIBRARY
|
||||
#define error(...) do { fprintf(stderr, __VA_ARGS__); assert(0); } while (0)
|
||||
#endif
|
||||
#ifndef warn
|
||||
#include <stdio.h>
|
||||
#define STD_LIBRARY
|
||||
#define warn(...) do { fprintf(stdout, __VA_ARGS__); } while (0)
|
||||
#endif
|
||||
|
||||
#define xmalloc(size) malloc(size)
|
||||
|
||||
#ifndef FRONTEND_IMPLEMENTATION
|
||||
#include "parser/parser.h"
|
||||
#include "parser/ast/ast.h"
|
||||
|
||||
typedef int (*sread_fn)(void *dst_buf, int dst_size, int elem_size, int count, void *stream);
|
||||
struct ASTNode* frontend(const char* file, void* stream, sread_fn sread);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,510 +0,0 @@
|
||||
/**
|
||||
* 仿照LCCompiler的词法分析部分
|
||||
*
|
||||
* 如下为LCC的README in 2025.2
|
||||
This hierarchy is the distribution for lcc version 4.2.
|
||||
|
||||
lcc version 3.x is described in the book "A Retargetable C Compiler:
|
||||
Design and Implementation" (Addison-Wesley, 1995, ISBN 0-8053-1670-1).
|
||||
There are significant differences between 3.x and 4.x, most notably in
|
||||
the intermediate code. For details, see
|
||||
https://drh.github.io/lcc/documents/interface4.pdf.
|
||||
|
||||
VERSION 4.2 IS INCOMPATIBLE WITH EARLIER VERSIONS OF LCC. DO NOT
|
||||
UNLOAD THIS DISTRIBUTION ON TOP OF A 3.X DISTRIBUTION.
|
||||
|
||||
LCC is a C89 ("ANSI C") compiler designed to be highly retargetable.
|
||||
|
||||
LOG describes the changes since the last release.
|
||||
|
||||
CPYRIGHT describes the conditions under you can use, copy, modify, and
|
||||
distribute lcc or works derived from lcc.
|
||||
|
||||
doc/install.html is an HTML file that gives a complete description of
|
||||
the distribution and installation instructions.
|
||||
|
||||
Chris Fraser / cwf@aya.yale.edu
|
||||
David Hanson / drh@drhanson.net
|
||||
*/
|
||||
#define FRONTEND_IMPLEMENTATION
|
||||
#include "../frontend.h"
|
||||
#include "token.h"
|
||||
#include "lexer.h"
|
||||
|
||||
static const struct {
|
||||
const char* name;
|
||||
enum CSTD_KEYWORD std_type;
|
||||
tok_type_t tok;
|
||||
} keywords[] = {
|
||||
#define X(name, std_type, tok, ...) { #name, std_type, tok },
|
||||
KEYWORD_TABLE
|
||||
#undef X
|
||||
};
|
||||
|
||||
// by using binary search to find the keyword
|
||||
static inline int keyword_cmp(const char* name, int len) {
|
||||
int low = 0;
|
||||
int high = sizeof(keywords) / sizeof(keywords[0]) - 1;
|
||||
while (low <= high) {
|
||||
int mid = (low + high) / 2;
|
||||
const char *key = keywords[mid].name;
|
||||
int cmp = 0;
|
||||
|
||||
// 自定义字符串比较逻辑
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (name[i] != key[i]) {
|
||||
cmp = (unsigned char)name[i] - (unsigned char)key[i];
|
||||
break;
|
||||
}
|
||||
if (name[i] == '\0') break; // 遇到终止符提前结束
|
||||
}
|
||||
|
||||
if (cmp == 0) {
|
||||
// 完全匹配检查(长度相同)
|
||||
if (key[len] == '\0') return mid;
|
||||
cmp = -1; // 当前关键词比输入长
|
||||
}
|
||||
|
||||
if (cmp < 0) {
|
||||
high = mid - 1;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
return -1; // Not a keyword.
|
||||
}
|
||||
|
||||
void init_lexer(lexer_t* lexer, const char* file_name, void* stream, lexer_sread_fn sread)
|
||||
{
|
||||
lexer->cur_ptr = lexer->end_ptr = (unsigned char*)&(lexer->buffer);
|
||||
lexer->index = 1;
|
||||
lexer->line = 1;
|
||||
|
||||
lexer->stream = stream;
|
||||
lexer->sread = sread;
|
||||
|
||||
for (int i = 0; i < sizeof(lexer->buffer) / sizeof(lexer->buffer[0]); i++) {
|
||||
lexer->buffer[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_buffer(lexer_t* lexer) {
|
||||
int num = lexer->end_ptr - lexer->cur_ptr;
|
||||
for (int i = 0; i < num; i++) {
|
||||
lexer->buffer[i] = lexer->cur_ptr[i];
|
||||
}
|
||||
lexer->cur_ptr = (unsigned char*)lexer->buffer;
|
||||
|
||||
int read_size = LEXER_BUFFER_SIZE - num;
|
||||
// TODO size_t to int maybe lose precision
|
||||
int got_size = lexer->sread(lexer->buffer + num, read_size, 1, read_size, lexer->stream);
|
||||
if (got_size < 0) {
|
||||
error("lexer read error");
|
||||
} else if (got_size < read_size) {
|
||||
lexer->end_ptr += got_size;
|
||||
lexer->end_ptr[0] = '\0'; // EOF
|
||||
lexer->end_ptr++;
|
||||
} else if (got_size == read_size) {
|
||||
lexer->end_ptr += got_size;
|
||||
} else {
|
||||
error("lexer read error imposible got_size > read_size maybe overflow?");
|
||||
}
|
||||
}
|
||||
|
||||
static void goto_newline(lexer_t* lexer) {
|
||||
do {
|
||||
if (lexer->cur_ptr == lexer->end_ptr) {
|
||||
flush_buffer(lexer);
|
||||
lexer->cur_ptr--;
|
||||
}
|
||||
lexer->cur_ptr++;
|
||||
} while (*lexer->cur_ptr != '\n' && *lexer->cur_ptr != '\0');
|
||||
}
|
||||
|
||||
static void goto_block_comment(lexer_t* lexer) {
|
||||
while (1) {
|
||||
if (lexer->end_ptr - lexer->cur_ptr < 2) {
|
||||
flush_buffer(lexer);
|
||||
}
|
||||
|
||||
if (*lexer->cur_ptr == '\0') {
|
||||
break;
|
||||
} else if (lexer->cur_ptr[0] == '*' && lexer->cur_ptr[1] == '/') {
|
||||
lexer->cur_ptr += 2;
|
||||
break;
|
||||
} else {
|
||||
lexer->cur_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO escape character not enough
|
||||
static char got_slash(unsigned char* peek) {
|
||||
switch (*peek) {
|
||||
case '\\': return '\\';
|
||||
case '\'': return '\'';
|
||||
case '\"': return '\"';
|
||||
case '\?': return '\?';
|
||||
case '0': return '\0';
|
||||
|
||||
case 'b': return '\b';
|
||||
case 'f': return '\f';
|
||||
case 'n': return '\n';
|
||||
case 'r': return '\r';
|
||||
case 't': return '\t';
|
||||
case 'v': return '\v';
|
||||
default: error("Unknown escape character");
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_char_literal(lexer_t* lexer, tok_t* token) {
|
||||
char val = 0;
|
||||
unsigned char* peek = lexer->cur_ptr + 1;
|
||||
if (*peek == '\\') {
|
||||
peek++;
|
||||
val = got_slash(peek);
|
||||
peek++;
|
||||
} else {
|
||||
val = *peek++;
|
||||
}
|
||||
|
||||
if (*peek++ != '\'') error("Unclosed character literal");
|
||||
token->val.ch = val;
|
||||
lexer->cur_ptr = peek;
|
||||
token->val.have = 1;
|
||||
token->type = TOKEN_CHAR_LITERAL;
|
||||
}
|
||||
|
||||
static void parse_string_literal(lexer_t* lexer, tok_t* token) {
|
||||
unsigned char* peek = lexer->cur_ptr + 1;
|
||||
// TODO string literal size check
|
||||
char* dest = token->val.str = xmalloc(LEXER_MAX_TOKEN_SIZE + 1);
|
||||
int len = 0;
|
||||
|
||||
while (*peek != '"') {
|
||||
if (peek >= lexer->end_ptr) flush_buffer(lexer);
|
||||
|
||||
if (*peek == '\\') { // 处理转义
|
||||
peek++;
|
||||
*peek = got_slash(peek);
|
||||
}
|
||||
|
||||
if (len >= LEXER_MAX_TOKEN_SIZE) error("String too long");
|
||||
dest[len++] = *peek++;
|
||||
}
|
||||
dest[len] = '\0';
|
||||
lexer->cur_ptr = peek + 1;
|
||||
token->val.have = 1;
|
||||
token->type = TOKEN_STRING_LITERAL;
|
||||
}
|
||||
|
||||
// FIXME it write by AI maybe error
|
||||
static void parse_number(lexer_t* lexer, tok_t* token) {
|
||||
unsigned char* peek = lexer->cur_ptr;
|
||||
int base = 10;
|
||||
int is_float = 0;
|
||||
long long int_val = 0;
|
||||
double float_val = 0.0;
|
||||
double fraction = 1.0;
|
||||
|
||||
// 判断进制
|
||||
if (*peek == '0') {
|
||||
peek++;
|
||||
switch (*peek) {
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
default:
|
||||
base = 8;
|
||||
}
|
||||
}
|
||||
|
||||
// 解析整数部分
|
||||
while (1) {
|
||||
int digit = -1;
|
||||
if (*peek >= '0' && *peek <= '9') {
|
||||
digit = *peek - '0';
|
||||
} else if (base == 16) {
|
||||
if (*peek >= 'a' && *peek <= 'f') digit = *peek - 'a' + 10;
|
||||
else if (*peek >= 'A' && *peek <= 'F') digit = *peek - 'A' + 10;
|
||||
}
|
||||
|
||||
if (digit < 0 || digit >= base) break;
|
||||
|
||||
if (!is_float) {
|
||||
int_val = int_val * base + digit;
|
||||
} else {
|
||||
float_val = float_val * base + digit;
|
||||
fraction *= base;
|
||||
}
|
||||
peek++;
|
||||
}
|
||||
|
||||
// 解析浮点数
|
||||
if (*peek == '.' && base == 10) {
|
||||
is_float = 1;
|
||||
float_val = int_val;
|
||||
peek++;
|
||||
|
||||
while (*peek >= '0' && *peek <= '9') {
|
||||
float_val = float_val * 10.0 + (*peek - '0');
|
||||
fraction *= 10.0;
|
||||
peek++;
|
||||
}
|
||||
float_val /= fraction;
|
||||
}
|
||||
|
||||
// 解析科学计数法
|
||||
if ((*peek == 'e' || *peek == 'E') && base == 10) {
|
||||
is_float = 1;
|
||||
peek++;
|
||||
// int exp_sign = 1;
|
||||
int exponent = 0;
|
||||
|
||||
if (*peek == '+') peek++;
|
||||
else if (*peek == '-') {
|
||||
// exp_sign = -1;
|
||||
peek++;
|
||||
}
|
||||
|
||||
while (*peek >= '0' && *peek <= '9') {
|
||||
exponent = exponent * 10 + (*peek - '0');
|
||||
peek++;
|
||||
}
|
||||
// float_val *= pow(10.0, exp_sign * exponent);
|
||||
}
|
||||
|
||||
// 存储结果
|
||||
lexer->cur_ptr = peek;
|
||||
token->val.have = 1;
|
||||
if (is_float) {
|
||||
token->val.d = float_val;
|
||||
token->type = TOKEN_FLOAT_LITERAL;
|
||||
} else {
|
||||
token->val.ll = int_val;
|
||||
token->type = TOKEN_INT_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define GOT_ONE_TOKEN_BUF_SIZE 64
|
||||
// /zh/c/language/operator_arithmetic.html
|
||||
void get_token(lexer_t* lexer, tok_t* token) {
|
||||
// 需要保证缓冲区始终可读
|
||||
if (lexer->end_ptr - lexer->cur_ptr < GOT_ONE_TOKEN_BUF_SIZE) {
|
||||
flush_buffer(lexer);
|
||||
}
|
||||
register unsigned char* peek = lexer->cur_ptr;
|
||||
|
||||
// 快速跳过空白符
|
||||
while (*peek == ' ' || *peek == '\t') {
|
||||
if (peek == lexer->end_ptr) {
|
||||
break;
|
||||
}
|
||||
peek++;
|
||||
}
|
||||
if (peek != lexer->cur_ptr) {
|
||||
// To TOKEN_FLUSH
|
||||
lexer->cur_ptr = peek;
|
||||
token->type = TOKEN_FLUSH;
|
||||
}
|
||||
|
||||
tok_type_t tok = TOKEN_INIT;
|
||||
tok_val_t constant;
|
||||
constant.have = 0;
|
||||
|
||||
// once step
|
||||
switch (*peek++) {
|
||||
case '=':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_EQ; break;
|
||||
default: peek--, tok = TOKEN_ASSIGN; break;
|
||||
} break;
|
||||
case '+':
|
||||
switch (*peek++) {
|
||||
case '+': tok = TOKEN_ADD_ADD; break;
|
||||
case '=': tok = TOKEN_ASSIGN_ADD; break;
|
||||
default: peek--, tok = TOKEN_ADD; break;
|
||||
} break;
|
||||
case '-':
|
||||
switch (*peek++) {
|
||||
case '-': tok = TOKEN_SUB_SUB; break;
|
||||
case '=': tok = TOKEN_ASSIGN_SUB; break;
|
||||
|
||||
case '>': tok = TOKEN_DEREF; break;
|
||||
default: peek--, tok = TOKEN_SUB; break;
|
||||
} break;
|
||||
case '*':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_ASSIGN_MUL; break;
|
||||
default: peek--, tok = TOKEN_MUL; break;
|
||||
} break;
|
||||
case '/':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_ASSIGN_DIV; break;
|
||||
case '/': {
|
||||
// need get a new line to parse
|
||||
goto_newline(lexer);
|
||||
tok = TOKEN_LINE_COMMENT;
|
||||
goto END;
|
||||
}
|
||||
case '*': {
|
||||
lexer->cur_ptr = peek;
|
||||
goto_block_comment(lexer);
|
||||
tok = TOKEN_BLOCK_COMMENT;
|
||||
goto END;
|
||||
}
|
||||
default: peek--, tok = TOKEN_DIV; break;
|
||||
} break;
|
||||
case '%':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_ASSIGN_MOD; break;
|
||||
default: peek--, tok = TOKEN_MOD; break;
|
||||
} break;
|
||||
case '&':
|
||||
switch (*peek++) {
|
||||
case '&': tok = TOKEN_AND_AND; break;
|
||||
case '=': tok = TOKEN_ASSIGN_AND; break;
|
||||
default: peek--, tok = TOKEN_AND; break;
|
||||
} break;
|
||||
case '|':
|
||||
switch (*peek++) {
|
||||
case '|': tok = TOKEN_OR_OR; break;
|
||||
case '=': tok = TOKEN_ASSIGN_OR; break;
|
||||
default: peek--, tok = TOKEN_OR; break;
|
||||
} break;
|
||||
case '^':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_ASSIGN_XOR; break;
|
||||
default: peek--, tok = TOKEN_XOR; break;
|
||||
} break;
|
||||
case '<':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_LE; break;
|
||||
case '<': tok = (*peek == '=') ? (peek++, TOKEN_ASSIGN_L_SH) : TOKEN_L_SH; break;
|
||||
default: peek--, tok = TOKEN_LT; break;
|
||||
} break;
|
||||
case '>':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_GE; break;
|
||||
case '>': tok = (*peek == '=') ? (peek++, TOKEN_ASSIGN_R_SH) : TOKEN_R_SH; break;
|
||||
default: peek--, tok = TOKEN_GT; break;
|
||||
} break;
|
||||
case '~':
|
||||
tok = TOKEN_BIT_NOT; break;
|
||||
case '!':
|
||||
switch (*peek++) {
|
||||
case '=': tok = TOKEN_NEQ; break;
|
||||
default: peek--, tok = TOKEN_NOT; break;
|
||||
} break;
|
||||
case '[':
|
||||
tok = TOKEN_L_BRACKET; break;
|
||||
case ']':
|
||||
tok = TOKEN_R_BRACKET; break;
|
||||
case '(':
|
||||
tok = TOKEN_L_PAREN; break;
|
||||
case ')':
|
||||
tok = TOKEN_R_PAREN; break;
|
||||
case '{':
|
||||
tok = TOKEN_L_BRACE; break;
|
||||
case '}':
|
||||
tok = TOKEN_R_BRACE; break;
|
||||
case ';':
|
||||
tok = TOKEN_SEMICOLON; break;
|
||||
case ',':
|
||||
tok = TOKEN_COMMA; break;
|
||||
case ':':
|
||||
tok = TOKEN_COLON; break;
|
||||
case '.':
|
||||
if (peek[0] == '.' && peek[1] == '.') {
|
||||
peek += 2;
|
||||
tok = TOKEN_ELLIPSIS;
|
||||
} else {
|
||||
tok = TOKEN_DOT;
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
tok = TOKEN_COND; break;
|
||||
case '\v': case '\r': case '\f': // FIXME it parse as a blank character
|
||||
tok = TOKEN_FLUSH; break;
|
||||
case '\n':
|
||||
// you need to flush a newline or blank
|
||||
lexer->line++;
|
||||
tok = TOKEN_FLUSH; break;
|
||||
case '#':
|
||||
warn("TODO: #define\n");
|
||||
goto_newline(lexer);
|
||||
tok = TOKEN_FLUSH;
|
||||
goto END;
|
||||
case '\0':
|
||||
// EOF
|
||||
tok = TOKEN_EOF;
|
||||
goto END;
|
||||
case '\'':
|
||||
return parse_char_literal(lexer, token);
|
||||
return;
|
||||
case '"':
|
||||
return parse_string_literal(lexer, token);
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
return parse_number(lexer, token);
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
|
||||
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
|
||||
case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
||||
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
|
||||
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':case 'Y': case 'Z':
|
||||
case '_':
|
||||
// TOKEN_IDENT
|
||||
if ((*peek == 'L' && *peek == '\'') || (*peek == 'L' && *peek == '"')) {
|
||||
error("unsupport wide-character char literal by `L` format");
|
||||
}
|
||||
while (1) {
|
||||
if (peek == lexer->end_ptr) {
|
||||
error("unsupport outof 64 length identifier");
|
||||
}
|
||||
if ((*peek >= 'a' && *peek <= 'z') || (*peek >= 'A' && *peek <= 'Z') ||
|
||||
(*peek == '_') || (*peek >= '0' && *peek <= '9')) {
|
||||
peek++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int res = keyword_cmp((const char*)lexer->cur_ptr, peek - (lexer->cur_ptr));
|
||||
if (res == -1) {
|
||||
int strlen = peek - lexer->cur_ptr;
|
||||
unsigned char* str = xmalloc(strlen + 1);
|
||||
constant.have = 1;
|
||||
constant.str = (char*)str;
|
||||
for (int i = 0; i < strlen; i++) {
|
||||
str[i] = lexer->cur_ptr[i];
|
||||
}
|
||||
str[strlen] = '\0';
|
||||
constant.have = 1;
|
||||
constant.str = (char*)str;
|
||||
tok = TOKEN_IDENT; break;
|
||||
} else {
|
||||
tok = keywords[res].tok; break;
|
||||
}
|
||||
default:
|
||||
error("unsupport char in sourse code `%c`", *(lexer->cur_ptr));
|
||||
break;
|
||||
}
|
||||
|
||||
lexer->cur_ptr = peek;
|
||||
END:
|
||||
token->val = constant;
|
||||
token->type = tok;
|
||||
}
|
||||
|
||||
// get_token maybe got invalid (with parser)
|
||||
void get_valid_token(lexer_t* lexer, tok_t* token) {
|
||||
tok_type_t type;
|
||||
do {
|
||||
get_token(lexer, token);
|
||||
type = token->type;
|
||||
} while (type == TOKEN_FLUSH || type == TOKEN_LINE_COMMENT || type == TOKEN_BLOCK_COMMENT);
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef __LEXER_H__
|
||||
#define __LEXER_H__
|
||||
|
||||
#include "token.h"
|
||||
#ifndef LEXER_MAX_TOKEN_SIZE
|
||||
#define LEXER_MAX_TOKEN_SIZE 63
|
||||
#endif
|
||||
#ifndef LEXER_BUFFER_SIZE
|
||||
#define LEXER_BUFFER_SIZE 4095
|
||||
#endif
|
||||
|
||||
typedef int (*lexer_sread_fn)(void *dst_buf, int dst_size,
|
||||
int elem_size, int count, void *stream);
|
||||
|
||||
typedef struct lexer {
|
||||
int line;
|
||||
int index;
|
||||
// const char current_file_name[LEXER_BUFFER_SIZE+1];
|
||||
|
||||
unsigned char* cur_ptr; // 当前扫描的字符,但是还没有开始扫描
|
||||
unsigned char* end_ptr; // 缓冲区最后一个字符的下一个位置
|
||||
char buffer[LEXER_BUFFER_SIZE+1];
|
||||
|
||||
lexer_sread_fn sread;
|
||||
void* stream;
|
||||
} lexer_t;
|
||||
|
||||
void init_lexer(lexer_t* lexer, const char* file_name, void* stream,
|
||||
lexer_sread_fn sread);
|
||||
|
||||
// pure token getter it will included empty token like TOKEN_FLUSH
|
||||
void get_token(lexer_t* lexer, tok_t* token);
|
||||
|
||||
// get_token maybe got invalid (with parser as TOKEN_FLUSH)
|
||||
void get_valid_token(lexer_t* lexer, tok_t* token);
|
||||
|
||||
#endif
|
||||
@@ -1,17 +0,0 @@
|
||||
CC = gcc
|
||||
CFLAGS = -g -Wall
|
||||
SRC = ../lexer.c ../token.c
|
||||
|
||||
all = test_all
|
||||
|
||||
test_all: test
|
||||
./test
|
||||
|
||||
run:
|
||||
$(CC) $(CFLAGS) $(SRC) run.c -o run
|
||||
|
||||
test:
|
||||
$(CC) $(CFLAGS) $(SRC) -o test test.c
|
||||
|
||||
clean:
|
||||
rm -f test run
|
||||
@@ -1,46 +0,0 @@
|
||||
#include "../lexer.h"
|
||||
#include <stdio.h>
|
||||
// gcc -g ../lexer.c ../token.c test_lexer.c -o test_lexer
|
||||
/*
|
||||
tok_tConstant {
|
||||
int have;
|
||||
union {
|
||||
char ch;
|
||||
int i;
|
||||
float f;
|
||||
double d;
|
||||
long long ll;
|
||||
char* str;
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
||||
int g_num;
|
||||
int g_num_arr[3];
|
||||
int main(int argc, char* argv[]) {
|
||||
int num = 0;
|
||||
|
||||
const char* file_name = "test_lexer.c";
|
||||
if (argc == 2) {
|
||||
file_name = argv[1];
|
||||
}
|
||||
FILE* fp = fopen(file_name, "r");
|
||||
if (fp == NULL) {
|
||||
perror("open file failed");
|
||||
return 1;
|
||||
}
|
||||
printf("open file success\n");
|
||||
|
||||
lexer_t lexer;
|
||||
init_lexer(&lexer, "test_lexter.c", fp, (lexer_sread_fn)fread_s);
|
||||
tok_t tok;
|
||||
|
||||
while (1) {
|
||||
get_valid_token(&lexer, &tok);
|
||||
if (tok.type == TOKEN_EOF) {
|
||||
break;
|
||||
}
|
||||
printf("line: %d, column: %d, type: %3d, typename: %s\n",
|
||||
lexer.line, lexer.index, tok.type, get_tok_name(tok.type));
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
// test_lexer.c
|
||||
#include "../../../../libcore/acutest.h"
|
||||
#include "../lexer.h"
|
||||
#include <string.h>
|
||||
|
||||
int test_read(void *dst_buf, int dst_size, int elem_size, int count, void *stream) {
|
||||
if (stream == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int size = dst_size > elem_size * count ? elem_size * count : dst_size;
|
||||
memcpy(dst_buf, stream, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
// 测试辅助函数
|
||||
static inline void test_lexer_string(const char* input, tok_type_t expected_type) {
|
||||
lexer_t lexer;
|
||||
tok_t token;
|
||||
|
||||
init_lexer(&lexer, "test.c", (void*)input, test_read);
|
||||
get_valid_token(&lexer, &token);
|
||||
|
||||
TEST_CHECK(token.type == expected_type);
|
||||
TEST_MSG("Expected: %s", get_tok_name(expected_type));
|
||||
TEST_MSG("Got: %s", get_tok_name(token.type));
|
||||
}
|
||||
|
||||
// 基础运算符测试
|
||||
void test_operators() {
|
||||
TEST_CASE("Arithmetic operators"); {
|
||||
test_lexer_string("+", TOKEN_ADD);
|
||||
test_lexer_string("++", TOKEN_ADD_ADD);
|
||||
test_lexer_string("+=", TOKEN_ASSIGN_ADD);
|
||||
test_lexer_string("-", TOKEN_SUB);
|
||||
test_lexer_string("--", TOKEN_SUB_SUB);
|
||||
test_lexer_string("-=", TOKEN_ASSIGN_SUB);
|
||||
test_lexer_string("*", TOKEN_MUL);
|
||||
test_lexer_string("*=", TOKEN_ASSIGN_MUL);
|
||||
test_lexer_string("/", TOKEN_DIV);
|
||||
test_lexer_string("/=", TOKEN_ASSIGN_DIV);
|
||||
test_lexer_string("%", TOKEN_MOD);
|
||||
test_lexer_string("%=", TOKEN_ASSIGN_MOD);
|
||||
}
|
||||
|
||||
TEST_CASE("Bitwise operators"); {
|
||||
test_lexer_string("&", TOKEN_AND);
|
||||
test_lexer_string("&&", TOKEN_AND_AND);
|
||||
test_lexer_string("&=", TOKEN_ASSIGN_AND);
|
||||
test_lexer_string("|", TOKEN_OR);
|
||||
test_lexer_string("||", TOKEN_OR_OR);
|
||||
test_lexer_string("|=", TOKEN_ASSIGN_OR);
|
||||
test_lexer_string("^", TOKEN_XOR);
|
||||
test_lexer_string("^=", TOKEN_ASSIGN_XOR);
|
||||
test_lexer_string("~", TOKEN_BIT_NOT);
|
||||
test_lexer_string("<<", TOKEN_L_SH);
|
||||
test_lexer_string("<<=", TOKEN_ASSIGN_L_SH);
|
||||
test_lexer_string(">>", TOKEN_R_SH);
|
||||
test_lexer_string(">>=", TOKEN_ASSIGN_R_SH);
|
||||
}
|
||||
|
||||
TEST_CASE("Comparison operators"); {
|
||||
test_lexer_string("==", TOKEN_EQ);
|
||||
test_lexer_string("!=", TOKEN_NEQ);
|
||||
test_lexer_string("<", TOKEN_LT);
|
||||
test_lexer_string("<=", TOKEN_LE);
|
||||
test_lexer_string(">", TOKEN_GT);
|
||||
test_lexer_string(">=", TOKEN_GE);
|
||||
}
|
||||
|
||||
TEST_CASE("Special symbols"); {
|
||||
test_lexer_string("(", TOKEN_L_PAREN);
|
||||
test_lexer_string(")", TOKEN_R_PAREN);
|
||||
test_lexer_string("[", TOKEN_L_BRACKET);
|
||||
test_lexer_string("]", TOKEN_R_BRACKET);
|
||||
test_lexer_string("{", TOKEN_L_BRACE);
|
||||
test_lexer_string("}", TOKEN_R_BRACE);
|
||||
test_lexer_string(";", TOKEN_SEMICOLON);
|
||||
test_lexer_string(",", TOKEN_COMMA);
|
||||
test_lexer_string(":", TOKEN_COLON);
|
||||
test_lexer_string(".", TOKEN_DOT);
|
||||
test_lexer_string("...", TOKEN_ELLIPSIS);
|
||||
test_lexer_string("->", TOKEN_DEREF);
|
||||
test_lexer_string("?", TOKEN_COND);
|
||||
}
|
||||
}
|
||||
|
||||
// 关键字测试
|
||||
void test_keywords() {
|
||||
TEST_CASE("C89 keywords");
|
||||
test_lexer_string("while", TOKEN_WHILE);
|
||||
test_lexer_string("sizeof", TOKEN_SIZEOF);
|
||||
|
||||
// TEST_CASE("C99 keywords");
|
||||
// test_lexer_string("restrict", TOKEN_RESTRICT);
|
||||
// test_lexer_string("_Bool", TOKEN_INT); // 需确认你的类型定义
|
||||
}
|
||||
|
||||
// 字面量测试
|
||||
void test_literals() {
|
||||
TEST_CASE("Integer literals"); {
|
||||
// 十进制
|
||||
test_lexer_string("0", TOKEN_INT_LITERAL);
|
||||
test_lexer_string("123", TOKEN_INT_LITERAL);
|
||||
// test_lexer_string("2147483647", TOKEN_INT_LITERAL);
|
||||
|
||||
// // 十六进制
|
||||
// test_lexer_string("0x0", TOKEN_INT_LITERAL);
|
||||
// test_lexer_string("0x1A3F", TOKEN_INT_LITERAL);
|
||||
// test_lexer_string("0XABCDEF", TOKEN_INT_LITERAL);
|
||||
|
||||
// // 八进制
|
||||
// test_lexer_string("0123", TOKEN_INT_LITERAL);
|
||||
// test_lexer_string("0777", TOKEN_INT_LITERAL);
|
||||
|
||||
// // 边界值测试
|
||||
// test_lexer_string("2147483647", TOKEN_INT_LITERAL); // INT_MAX
|
||||
// test_lexer_string("4294967295", TOKEN_INT_LITERAL); // UINT_MAX
|
||||
}
|
||||
|
||||
TEST_CASE("Character literals"); {
|
||||
test_lexer_string("'a'", TOKEN_CHAR_LITERAL);
|
||||
test_lexer_string("'\\n'", TOKEN_CHAR_LITERAL);
|
||||
test_lexer_string("'\\t'", TOKEN_CHAR_LITERAL);
|
||||
test_lexer_string("'\\\\'", TOKEN_CHAR_LITERAL);
|
||||
test_lexer_string("'\\0'", TOKEN_CHAR_LITERAL);
|
||||
}
|
||||
|
||||
TEST_CASE("String literals"); {
|
||||
test_lexer_string("\"hello\"", TOKEN_STRING_LITERAL);
|
||||
test_lexer_string("\"multi-line\\nstring\"", TOKEN_STRING_LITERAL);
|
||||
test_lexer_string("\"escape\\\"quote\"", TOKEN_STRING_LITERAL);
|
||||
}
|
||||
|
||||
// TEST_CASE("Floating literals");
|
||||
// test_lexer_string("3.14e-5", TOKEN_FLOAT_LITERAL);
|
||||
|
||||
}
|
||||
|
||||
// 边界测试
|
||||
void test_edge_cases() {
|
||||
// TEST_CASE("Long identifiers");
|
||||
// char long_id[LEXER_MAX_TOKEN_SIZE+2] = {0};
|
||||
// memset(long_id, 'a', LEXER_MAX_TOKEN_SIZE+1);
|
||||
// test_lexer_string(long_id, TOKEN_IDENT);
|
||||
|
||||
// TEST_CASE("Buffer boundary");
|
||||
// char boundary[LEXER_BUFFER_SIZE*2] = {0};
|
||||
// memset(boundary, '+', LEXER_BUFFER_SIZE*2-1);
|
||||
// test_lexer_string(boundary, TOKEN_ADD);
|
||||
}
|
||||
|
||||
// 错误处理测试
|
||||
void test_error_handling() {
|
||||
TEST_CASE("Invalid characters");
|
||||
lexer_t lexer;
|
||||
tok_t token;
|
||||
|
||||
init_lexer(&lexer, "test.c", NULL, test_read);
|
||||
get_valid_token(&lexer, &token);
|
||||
|
||||
TEST_CHECK(token.type == TOKEN_EOF); // 应触发错误处理
|
||||
}
|
||||
|
||||
// 测试列表
|
||||
TEST_LIST = {
|
||||
{"operators", test_operators},
|
||||
{"keywords", test_keywords},
|
||||
{"literals", test_literals},
|
||||
{"edge_cases", test_edge_cases},
|
||||
{"error_handling", test_error_handling},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@@ -1,84 +0,0 @@
|
||||
#define FRONTEND_IMPLEMENTATION
|
||||
#include "../frontend.h"
|
||||
#include "token.h"
|
||||
|
||||
#define ROUND_IDX(idx) ((idx) % tokbuf->cap)
|
||||
|
||||
tok_t* pop_tok(tok_buf_t* tokbuf) {
|
||||
if (tokbuf->size == 0) {
|
||||
error("no token to pop");
|
||||
return NULL;
|
||||
}
|
||||
int idx = tokbuf->cur;
|
||||
tokbuf->cur = ROUND_IDX(idx + 1);
|
||||
tokbuf->size -= 1;
|
||||
return tokbuf->buf + idx;
|
||||
}
|
||||
|
||||
void flush_peek_tok(tok_buf_t* tokbuf) {
|
||||
tokbuf->peek = tokbuf->cur;
|
||||
}
|
||||
|
||||
void init_tokbuf(tok_buf_t *tokbuf, void *stream, get_tokbuf_func gettok) {
|
||||
tokbuf->cur = 0;
|
||||
tokbuf->end = 0;
|
||||
tokbuf->peek = 0;
|
||||
tokbuf->size = 0;
|
||||
tokbuf->stream = stream;
|
||||
tokbuf->gettok = gettok;
|
||||
tokbuf->buf = NULL;
|
||||
tokbuf->cap = 0;
|
||||
}
|
||||
|
||||
tok_t *peek_tok(tok_buf_t *tokbuf) {
|
||||
int idx = tokbuf->peek;
|
||||
tokbuf->peek = ROUND_IDX(idx + 1);
|
||||
if (tokbuf->size >= tokbuf->cap) {
|
||||
error("peek too deep, outof array size");
|
||||
}
|
||||
if (idx == tokbuf->end) {
|
||||
if (tokbuf->size == tokbuf->cap) {
|
||||
error("peek_tok buffer overflow");
|
||||
}
|
||||
if (tokbuf->gettok == NULL) {
|
||||
error("peek_tok can not got tok");
|
||||
}
|
||||
tokbuf->gettok(tokbuf->stream, &(tokbuf->buf[idx]));
|
||||
tokbuf->size++;
|
||||
tokbuf->end = tokbuf->peek;
|
||||
}
|
||||
|
||||
return &(tokbuf->buf[idx]);
|
||||
}
|
||||
|
||||
tok_type_t peek_tok_type(tok_buf_t* tokbuf) {
|
||||
return peek_tok(tokbuf)->type;
|
||||
}
|
||||
|
||||
int expect_pop_tok(tok_buf_t* tokbuf, tok_type_t type) {
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_t* tok = peek_tok(tokbuf);
|
||||
if (tok->type != type) {
|
||||
error("expected tok: %s, got %s", get_tok_name(type), get_tok_name(tok->type));
|
||||
} else {
|
||||
pop_tok(tokbuf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 生成字符串映射(根据需求选择#str或#name)
|
||||
static const char* token_strings[] = {
|
||||
// 普通token使用#str
|
||||
#define X(str, tok) [tok] = #str,
|
||||
TOKEN_TABLE
|
||||
#undef X
|
||||
|
||||
// 关键字使用#name
|
||||
#define X(name, std, tok) [tok] = #name,
|
||||
KEYWORD_TABLE
|
||||
#undef X
|
||||
};
|
||||
|
||||
const char* get_tok_name(tok_type_t type) {
|
||||
return token_strings[type];
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
#ifndef __TOKEN_H__
|
||||
#define __TOKEN_H__
|
||||
|
||||
enum CSTD_KEYWORD {
|
||||
CSTD_C89,
|
||||
CSTD_C99,
|
||||
CEXT_ASM,
|
||||
};
|
||||
|
||||
// Using Binary Search To Fast Find Keyword
|
||||
#define KEYWORD_TABLE \
|
||||
X(asm , CEXT_ASM, TOKEN_ASM) \
|
||||
X(break , CSTD_C89, TOKEN_BREAK) \
|
||||
X(case , CSTD_C89, TOKEN_CASE) \
|
||||
X(char , CSTD_C89, TOKEN_CHAR) \
|
||||
X(const , CSTD_C89, TOKEN_CONST) \
|
||||
X(continue , CSTD_C89, TOKEN_CONTINUE) \
|
||||
X(default , CSTD_C89, TOKEN_DEFAULT) \
|
||||
X(do , CSTD_C89, TOKEN_DO) \
|
||||
X(double , CSTD_C89, TOKEN_DOUBLE) \
|
||||
X(else , CSTD_C89, TOKEN_ELSE) \
|
||||
X(enum , CSTD_C89, TOKEN_ENUM) \
|
||||
X(extern , CSTD_C89, TOKEN_EXTERN) \
|
||||
X(float , CSTD_C89, TOKEN_FLOAT) \
|
||||
X(for , CSTD_C89, TOKEN_FOR) \
|
||||
X(goto , CSTD_C89, TOKEN_GOTO) \
|
||||
X(if , CSTD_C89, TOKEN_IF) \
|
||||
X(inline , CSTD_C99, TOKEN_INLINE) \
|
||||
X(int , CSTD_C89, TOKEN_INT) \
|
||||
X(long , CSTD_C89, TOKEN_LONG) \
|
||||
X(register , CSTD_C89, TOKEN_REGISTER) \
|
||||
X(restrict , CSTD_C99, TOKEN_RESTRICT) \
|
||||
X(return , CSTD_C89, TOKEN_RETURN) \
|
||||
X(short , CSTD_C89, TOKEN_SHORT) \
|
||||
X(signed , CSTD_C89, TOKEN_SIGNED) \
|
||||
X(sizeof , CSTD_C89, TOKEN_SIZEOF) \
|
||||
X(static , CSTD_C89, TOKEN_STATIC) \
|
||||
X(struct , CSTD_C89, TOKEN_STRUCT) \
|
||||
X(switch , CSTD_C89, TOKEN_SWITCH) \
|
||||
X(typedef , CSTD_C89, TOKEN_TYPEDEF) \
|
||||
X(union , CSTD_C89, TOKEN_UNION) \
|
||||
X(unsigned , CSTD_C89, TOKEN_UNSIGNED) \
|
||||
X(void , CSTD_C89, TOKEN_VOID) \
|
||||
X(volatile , CSTD_C89, TOKEN_VOLATILE) \
|
||||
X(while , CSTD_C89, TOKEN_WHILE) \
|
||||
// KEYWORD_TABLE
|
||||
|
||||
#define TOKEN_TABLE \
|
||||
X(EOF , TOKEN_EOF) \
|
||||
X(init , TOKEN_INIT) \
|
||||
X(flush , TOKEN_FLUSH) \
|
||||
X("==" , TOKEN_EQ) \
|
||||
X("=" , TOKEN_ASSIGN) \
|
||||
X("++" , TOKEN_ADD_ADD) \
|
||||
X("+=" , TOKEN_ASSIGN_ADD) \
|
||||
X("+" , TOKEN_ADD) \
|
||||
X("--" , TOKEN_SUB_SUB) \
|
||||
X("-=" , TOKEN_ASSIGN_SUB) \
|
||||
X("->" , TOKEN_DEREF) \
|
||||
X("-" , TOKEN_SUB) \
|
||||
X("*=" , TOKEN_ASSIGN_MUL) \
|
||||
X("*" , TOKEN_MUL) \
|
||||
X("/=" , TOKEN_ASSIGN_DIV) \
|
||||
X("/" , TOKEN_DIV) \
|
||||
X("//" , TOKEN_LINE_COMMENT) \
|
||||
X("/* */" , TOKEN_BLOCK_COMMENT) \
|
||||
X("%=" , TOKEN_ASSIGN_MOD) \
|
||||
X("%" , TOKEN_MOD) \
|
||||
X("&&" , TOKEN_AND_AND) \
|
||||
X("&=" , TOKEN_ASSIGN_AND) \
|
||||
X("&" , TOKEN_AND) \
|
||||
X("||" , TOKEN_OR_OR) \
|
||||
X("|=" , TOKEN_ASSIGN_OR) \
|
||||
X("|" , TOKEN_OR) \
|
||||
X("^=" , TOKEN_ASSIGN_XOR) \
|
||||
X("^" , TOKEN_XOR) \
|
||||
X("<<=" , TOKEN_ASSIGN_L_SH) \
|
||||
X("<<" , TOKEN_L_SH) \
|
||||
X("<=" , TOKEN_LE) \
|
||||
X("<" , TOKEN_LT) \
|
||||
X(">>=" , TOKEN_ASSIGN_R_SH) \
|
||||
X(">>" , TOKEN_R_SH) \
|
||||
X(">=" , TOKEN_GE) \
|
||||
X(">" , TOKEN_GT) \
|
||||
X("!" , TOKEN_NOT) \
|
||||
X("!=" , TOKEN_NEQ) \
|
||||
X("~" , TOKEN_BIT_NOT) \
|
||||
X("[" , TOKEN_L_BRACKET) \
|
||||
X("]" , TOKEN_R_BRACKET) \
|
||||
X("(" , TOKEN_L_PAREN) \
|
||||
X(")" , TOKEN_R_PAREN) \
|
||||
X("{" , TOKEN_L_BRACE) \
|
||||
X("}" , TOKEN_R_BRACE) \
|
||||
X(";" , TOKEN_SEMICOLON) \
|
||||
X("," , TOKEN_COMMA) \
|
||||
X(":" , TOKEN_COLON) \
|
||||
X("." , TOKEN_DOT) \
|
||||
X("..." , TOKEN_ELLIPSIS) \
|
||||
X("?" , TOKEN_COND) \
|
||||
X(identifier , TOKEN_IDENT) \
|
||||
X(int_literal , TOKEN_INT_LITERAL) \
|
||||
X(float_literal , TOKEN_FLOAT_LITERAL) \
|
||||
X(char_literal , TOKEN_CHAR_LITERAL) \
|
||||
X(string_literal , TOKEN_STRING_LITERAL) \
|
||||
// END
|
||||
|
||||
// 定义TokenType枚举
|
||||
typedef enum tok_type {
|
||||
// 处理普通token
|
||||
#define X(str, tok) tok,
|
||||
TOKEN_TABLE
|
||||
#undef X
|
||||
|
||||
// 处理关键字(保持原有格式)
|
||||
#define X(name, std, tok) tok,
|
||||
KEYWORD_TABLE
|
||||
#undef X
|
||||
} tok_type_t;
|
||||
|
||||
typedef struct tok_val {
|
||||
int have;
|
||||
union {
|
||||
char ch;
|
||||
int i;
|
||||
float f;
|
||||
double d;
|
||||
long long ll;
|
||||
char* str;
|
||||
};
|
||||
} tok_val_t;
|
||||
|
||||
typedef struct tok {
|
||||
tok_type_t type;
|
||||
tok_val_t val;
|
||||
} tok_t;
|
||||
|
||||
typedef struct tok_buf {
|
||||
int cur;
|
||||
int end;
|
||||
int peek;
|
||||
int size;
|
||||
int cap;
|
||||
tok_t* buf;
|
||||
void* stream;
|
||||
void (*gettok)(void* stream, tok_t* token);
|
||||
} tok_buf_t;
|
||||
|
||||
typedef void(*get_tokbuf_func)(void* stream, tok_t* token);
|
||||
void init_tokbuf(tok_buf_t* tokbuf, void* stream, get_tokbuf_func gettok);
|
||||
tok_t* peek_tok(tok_buf_t* tokbuf);
|
||||
tok_t* pop_tok(tok_buf_t* tokbuf);
|
||||
void flush_peek_tok(tok_buf_t* tokbuf);
|
||||
tok_type_t peek_tok_type(tok_buf_t* tokbuf);
|
||||
int expect_pop_tok(tok_buf_t* tokbuf, tok_type_t type);
|
||||
const char* get_tok_name(tok_type_t type);
|
||||
|
||||
#endif
|
||||
@@ -1,18 +0,0 @@
|
||||
- ast.c 作为抽象语法树的定义
|
||||
|
||||
- block.c 作为块的实现主要用于处理作用域,需要符号表
|
||||
|
||||
- decl.c 作为声明的实现,其中主要携带变量声明,函数声明见 func.c ,需要符号表
|
||||
|
||||
- func.c 作为函数的实现,其中主要携带函数声明,以及函数定义,需要符号表
|
||||
|
||||
- expr.c 作为表达式的实现。需要符号表
|
||||
|
||||
- stmt.c 作为语句的实现。需要表达式类型判断合法性
|
||||
|
||||
- term.c 作为终结符的实现。需要表达式类型判断合法性
|
||||
|
||||
- program.c 作为词法分析(语义分析)入口函数,可以根据parser结构生成AST
|
||||
|
||||
其中stmt参考cppreference
|
||||
其中expr参考AI以及CParser
|
||||
@@ -1,173 +0,0 @@
|
||||
#include "ast.h"
|
||||
#include "../parser.h"
|
||||
struct ASTNode* new_ast_node(void) {
|
||||
struct ASTNode* node = xmalloc(sizeof(struct ASTNode));
|
||||
init_ast_node(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
void init_ast_node(struct ASTNode* node) {
|
||||
node->type = NT_INIT;
|
||||
|
||||
for (int i = 0; i < sizeof(node->children) / sizeof(node->children[0]); i++) {
|
||||
node->children[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// struct ASTNode* find_ast_node(struct ASTNode* node, ast_type_t type) {
|
||||
|
||||
// }
|
||||
|
||||
#include <stdio.h>
|
||||
static void pnt_depth(int depth) {
|
||||
for (int i = 0; i < depth; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
// void pnt_ast(struct ASTNode* node, int depth) {
|
||||
// if (!node) return;
|
||||
// pnt_depth(depth);
|
||||
// switch (node->type) {
|
||||
// case NT_ROOT:
|
||||
// for (int i = 0; i < node->root.child_size; i++) {
|
||||
// pnt_ast(node->root.children[i], depth);
|
||||
// }
|
||||
// return;
|
||||
|
||||
// case NT_ADD : printf("+ \n"); break; // (expr) + (expr)
|
||||
// case NT_SUB : printf("- \n"); break; // (expr) - (expr)
|
||||
// case NT_MUL : printf("* \n"); break; // (expr) * (expr)
|
||||
// case NT_DIV : printf("/ \n"); break; // (expr) / (expr)
|
||||
// case NT_MOD : printf("%%\n"); break; // (expr) % (expr)
|
||||
// case NT_AND : printf("& \n"); break; // (expr) & (expr)
|
||||
// case NT_OR : printf("| \n"); break; // (expr) | (expr)
|
||||
// case NT_XOR : printf("^ \n"); break; // (expr) ^ (expr)
|
||||
// case NT_L_SH : printf("<<\n"); break; // (expr) << (expr)
|
||||
// case NT_R_SH : printf(">>\n"); break; // (expr) >> (expr)
|
||||
// case NT_EQ : printf("==\n"); break; // (expr) == (expr)
|
||||
// case NT_NEQ : printf("!=\n"); break; // (expr) != (expr)
|
||||
// case NT_LE : printf("<=\n"); break; // (expr) <= (expr)
|
||||
// case NT_GE : printf(">=\n"); break; // (expr) >= (expr)
|
||||
// case NT_LT : printf("< \n"); break; // (expr) < (expr)
|
||||
// case NT_GT : printf("> \n"); break; // (expr) > (expr)
|
||||
// case NT_AND_AND : printf("&&\n"); break; // (expr) && (expr)
|
||||
// case NT_OR_OR : printf("||\n"); break; // (expr) || (expr)
|
||||
// case NT_NOT : printf("! \n"); break; // ! (expr)
|
||||
// case NT_BIT_NOT : printf("~ \n"); break; // ~ (expr)
|
||||
// case NT_COMMA : printf(", \n"); break; // expr, expr 逗号运算符
|
||||
// case NT_ASSIGN : printf("= \n"); break; // (expr) = (expr)
|
||||
// // case NT_COND : // (expr) ? (expr) : (expr)
|
||||
|
||||
// case NT_STMT_EMPTY : // ;
|
||||
// printf(";\n");
|
||||
// break;
|
||||
// case NT_STMT_IF : // if (cond) { ... } [else {...}]
|
||||
// printf("if");
|
||||
// pnt_ast(node->if_stmt.cond, depth+1);
|
||||
// pnt_ast(node->if_stmt.if_stmt, depth+1);
|
||||
// if (node->if_stmt.else_stmt) {
|
||||
// pnt_depth(depth);
|
||||
// printf("else");
|
||||
// pnt_ast(node->if_stmt.else_stmt, depth+1);
|
||||
// }
|
||||
// break;
|
||||
// case NT_STMT_WHILE : // while (cond) { ... }
|
||||
// printf("while\n");
|
||||
// pnt_ast(node->while_stmt.cond, depth+1);
|
||||
// pnt_ast(node->while_stmt.body, depth+1);
|
||||
// break;
|
||||
// case NT_STMT_DOWHILE : // do {...} while (cond)
|
||||
// printf("do-while\n");
|
||||
// pnt_ast(node->do_while_stmt.body, depth+1);
|
||||
// pnt_ast(node->do_while_stmt.cond, depth+1);
|
||||
// break;
|
||||
// case NT_STMT_FOR : // for (init; cond; iter) {...}
|
||||
// printf("for\n");
|
||||
// if (node->for_stmt.init)
|
||||
// pnt_ast(node->for_stmt.init, depth+1);
|
||||
// if (node->for_stmt.cond)
|
||||
// pnt_ast(node->for_stmt.cond, depth+1);
|
||||
// if (node->for_stmt.iter)
|
||||
// pnt_ast(node->for_stmt.iter, depth+1);
|
||||
// pnt_ast(node->for_stmt.body, depth+1);
|
||||
// break;
|
||||
// case NT_STMT_SWITCH : // switch (expr) { case ... }
|
||||
// case NT_STMT_BREAK : // break;
|
||||
// case NT_STMT_CONTINUE : // continue;
|
||||
// case NT_STMT_GOTO : // goto label;
|
||||
// case NT_STMT_CASE : // case const_expr:
|
||||
// case NT_STMT_DEFAULT : // default:
|
||||
// case NT_STMT_LABEL : // label:
|
||||
// break;
|
||||
// case NT_STMT_BLOCK : // { ... }
|
||||
// printf("{\n");
|
||||
// for (int i = 0; i < node->block.child_size; i++) {
|
||||
// pnt_ast(node->block.children[i], depth+1);
|
||||
// }
|
||||
// pnt_depth(depth);
|
||||
// printf("}\n");
|
||||
// break;
|
||||
// case NT_STMT_RETURN : // return expr;
|
||||
// printf("return");
|
||||
// if (node->return_stmt.expr_stmt) {
|
||||
// printf(" ");
|
||||
// pnt_ast(node->return_stmt.expr_stmt, depth+1);
|
||||
// } else {
|
||||
// printf("\n");
|
||||
// }
|
||||
// break;
|
||||
// case NT_STMT_EXPR : // expr;
|
||||
// printf("stmt\n");
|
||||
// pnt_ast(node->expr_stmt.expr_stmt, depth);
|
||||
// pnt_depth(depth);
|
||||
// printf(";\n");
|
||||
// break;
|
||||
// case NT_DECL_VAR : // type name; or type name = expr;
|
||||
// printf("decl_val\n");
|
||||
// break;
|
||||
// case NT_DECL_FUNC: // type func_name(param_list);
|
||||
// printf("decl func %s\n", node->func.name->syms.tok.val.str);
|
||||
// break;
|
||||
// case NT_FUNC : // type func_name(param_list) {...}
|
||||
// printf("def func %s\n", node->func.name->syms.tok.val.str);
|
||||
// // pnt_ast(node->child.func.params, depth);
|
||||
// pnt_ast(node->func.body, depth);
|
||||
// // pnt_ast(node->child.func.ret, depth);
|
||||
// break;
|
||||
// case NT_PARAM : // 函数形参
|
||||
// printf("param\n");
|
||||
// case NT_ARG_LIST : // 实参列表(需要与NT_CALL配合)
|
||||
// printf("arg_list\n");
|
||||
// case NT_TERM_CALL : // func (expr)
|
||||
// printf("call\n");
|
||||
// break;
|
||||
// case NT_TERM_IDENT:
|
||||
// printf("%s\n", node->syms.tok.val.str);
|
||||
// break;
|
||||
// case NT_TERM_VAL : // Terminal Symbols like constant, identifier, keyword
|
||||
// tok_t * tok = &node->syms.tok;
|
||||
// switch (tok->type) {
|
||||
// case TOKEN_CHAR_LITERAL:
|
||||
// printf("%c\n", tok->val.ch);
|
||||
// break;
|
||||
// case TOKEN_INT_LITERAL:
|
||||
// printf("%d\n", tok->val.i);
|
||||
// break;
|
||||
// case TOKEN_STRING_LITERAL:
|
||||
// printf("%s\n", tok->val.str);
|
||||
// break;
|
||||
// default:
|
||||
// printf("unknown term val\n");
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
// // 通用子节点递归处理
|
||||
// if (node->type <= NT_ASSIGN) { // 表达式类统一处理子节点
|
||||
// if (node->expr.left) pnt_ast(node->expr.left, depth+1);
|
||||
// if (node->expr.right) pnt_ast(node->expr.right, depth + 1);
|
||||
// }
|
||||
// }
|
||||
@@ -1,189 +0,0 @@
|
||||
#ifndef __AST_H__
|
||||
#define __AST_H__
|
||||
|
||||
#include "../../frontend.h"
|
||||
#include "../../lexer/lexer.h"
|
||||
#include "../../../../libcore/vector.h"
|
||||
#include "../type.h"
|
||||
|
||||
typedef enum {
|
||||
NT_INIT,
|
||||
NT_ROOT, // global scope in root node
|
||||
NT_ADD, // (expr) + (expr)
|
||||
NT_SUB, // (expr) - (expr)
|
||||
NT_MUL, // (expr) * (expr)
|
||||
NT_DIV, // (expr) / (expr)
|
||||
NT_MOD, // (expr) % (expr)
|
||||
NT_AND, // (expr) & (expr)
|
||||
NT_OR, // (expr) | (expr)
|
||||
NT_XOR, // (expr) ^ (expr)
|
||||
NT_L_SH, // (expr) << (expr)
|
||||
NT_R_SH, // (expr) >> (expr)
|
||||
NT_EQ, // (expr) == (expr)
|
||||
NT_NEQ, // (expr) != (expr)
|
||||
NT_LE, // (expr) <= (expr)
|
||||
NT_GE, // (expr) >= (expr)
|
||||
NT_LT, // (expr) < (expr)
|
||||
NT_GT, // (expr) > (expr)
|
||||
NT_AND_AND, // (expr) && (expr)
|
||||
NT_OR_OR, // (expr) || (expr)
|
||||
NT_NOT, // ! (expr)
|
||||
NT_BIT_NOT, // ~ (expr)
|
||||
NT_COND, // (expr) ? (expr) : (expr)
|
||||
NT_COMMA, // expr, expr 逗号运算符
|
||||
NT_ASSIGN, // (expr) = (expr)
|
||||
|
||||
NT_ADDRESS, // &expr (取地址)
|
||||
NT_DEREF, // *expr (解引用)
|
||||
NT_INDEX, // arr[index] (数组访问)
|
||||
NT_MEMBER, // struct.member
|
||||
NT_PTR_MEMBER,// ptr->member
|
||||
|
||||
NT_CAST, // (type)expr 强制类型转换
|
||||
NT_SIZEOF, // sizeof(type|expr)
|
||||
// NT_ALIGNOF, // _Alignof(type) (C11)
|
||||
|
||||
NT_STMT_EMPTY, // ;
|
||||
NT_STMT_IF, // if (cond) { ... } [else {...}]
|
||||
NT_STMT_WHILE, // while (cond) { ... }
|
||||
NT_STMT_DOWHILE, // do {...} while (cond)
|
||||
NT_STMT_FOR, // for (init; cond; iter) {...}
|
||||
NT_STMT_SWITCH, // switch (expr) { case ... }
|
||||
NT_STMT_BREAK, // break;
|
||||
NT_STMT_CONTINUE, // continue;
|
||||
NT_STMT_GOTO, // goto label;
|
||||
NT_STMT_CASE, // case const_expr:
|
||||
NT_STMT_DEFAULT, // default:
|
||||
NT_STMT_LABEL, // label:
|
||||
NT_STMT_BLOCK, // { ... }
|
||||
NT_STMT_RETURN, // return expr;
|
||||
NT_STMT_EXPR, // expr;
|
||||
|
||||
NT_BLOCK,
|
||||
// NT_TYPE_BASE, // 基础类型节点
|
||||
// NT_TYPE_PTR, // 指针类型
|
||||
// NT_TYPE_ARRAY, // 数组类型
|
||||
// NT_TYPE_FUNC, // 函数类型
|
||||
// NT_TYPE_QUAL, // 限定符节点
|
||||
|
||||
NT_DECL_VAR, // type name; or type name = expr;
|
||||
NT_DECL_FUNC, // type func_name(param_list);
|
||||
NT_FUNC, // type func_name(param_list) {...}
|
||||
NT_PARAM, // 函数形参
|
||||
NT_ARG_LIST, // 实参列表(需要与NT_CALL配合)
|
||||
|
||||
NT_TERM_CALL, // func (expr)
|
||||
NT_TERM_VAL,
|
||||
NT_TERM_IDENT,
|
||||
NT_TERM_TYPE,
|
||||
} ast_type_t;
|
||||
|
||||
typedef struct ASTNode {
|
||||
ast_type_t type;
|
||||
|
||||
union {
|
||||
void *children[6];
|
||||
struct {
|
||||
vector_header(children, struct ASTNode*);
|
||||
} root;
|
||||
struct {
|
||||
vector_header(children, struct ASTNode*);
|
||||
} block;
|
||||
struct {
|
||||
struct ASTNode* decl_node;
|
||||
tok_t tok;
|
||||
} syms;
|
||||
struct {
|
||||
vector_header(params, struct ASTNode*);
|
||||
} params;
|
||||
struct {
|
||||
struct ASTNode* name;
|
||||
struct ASTNode* params;
|
||||
struct ASTNode* func_decl;
|
||||
} call;
|
||||
struct {
|
||||
struct ASTNode *type;
|
||||
struct ASTNode *name;
|
||||
struct ASTNode *expr_stmt; // optional
|
||||
void* data;
|
||||
} decl_val;
|
||||
struct {
|
||||
struct ASTNode *ret;
|
||||
struct ASTNode *name;
|
||||
struct ASTNode *params; // array of params
|
||||
struct ASTNode *def;
|
||||
} decl_func;
|
||||
struct {
|
||||
struct ASTNode *decl;
|
||||
struct ASTNode *body; // optional
|
||||
void* data;
|
||||
} func;
|
||||
struct {
|
||||
struct ASTNode *left;
|
||||
struct ASTNode *right;
|
||||
struct ASTNode *optional; // optional
|
||||
} expr;
|
||||
struct {
|
||||
struct ASTNode *cond;
|
||||
struct ASTNode *if_stmt;
|
||||
struct ASTNode *else_stmt; // optional
|
||||
} if_stmt;
|
||||
struct {
|
||||
struct ASTNode *cond;
|
||||
struct ASTNode *body;
|
||||
} switch_stmt;
|
||||
struct {
|
||||
struct ASTNode *cond;
|
||||
struct ASTNode *body;
|
||||
} while_stmt;
|
||||
struct {
|
||||
struct ASTNode *body;
|
||||
struct ASTNode *cond;
|
||||
} do_while_stmt;
|
||||
struct {
|
||||
struct ASTNode *init;
|
||||
struct ASTNode *cond; // optional
|
||||
struct ASTNode *iter; // optional
|
||||
struct ASTNode *body;
|
||||
} for_stmt;
|
||||
struct {
|
||||
struct ASTNode *expr_stmt; // optional
|
||||
} return_stmt;
|
||||
struct {
|
||||
struct ASTNode *label;
|
||||
} goto_stmt;
|
||||
struct {
|
||||
struct ASTNode *label;
|
||||
} label_stmt;
|
||||
struct {
|
||||
struct ASTNode *block;
|
||||
} block_stmt;
|
||||
struct {
|
||||
struct ASTNode *expr_stmt;
|
||||
} expr_stmt;
|
||||
};
|
||||
} ast_node_t;
|
||||
|
||||
struct ASTNode* new_ast_node(void);
|
||||
void init_ast_node(struct ASTNode* node);
|
||||
void pnt_ast(struct ASTNode* node, int depth);
|
||||
|
||||
typedef struct parser parser_t;
|
||||
typedef struct ASTNode* (*parse_func_t) (parser_t*);
|
||||
|
||||
void parse_prog(parser_t* parser);
|
||||
ast_node_t* parse_decl(parser_t* parser);
|
||||
ast_node_t* parse_decl_val(parser_t* parser);
|
||||
|
||||
ast_node_t* parse_block(parser_t* parser);
|
||||
ast_node_t* parse_stmt(parser_t* parser);
|
||||
ast_node_t* parse_expr(parser_t* parser);
|
||||
|
||||
ast_node_t* parse_type(parser_t* parser);
|
||||
|
||||
ast_node_t* new_ast_ident_node(tok_t* tok);
|
||||
ast_node_t* expect_pop_ident(tok_buf_t* tokbuf);
|
||||
|
||||
int peek_decl(tok_buf_t* tokbuf);
|
||||
|
||||
#endif
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
#include "ast.h"
|
||||
#include "../parser.h"
|
||||
#include "../symtab/symtab.h"
|
||||
|
||||
|
||||
#ifndef BLOCK_MAX_NODE
|
||||
#define BLOCK_MAX_NODE (1024)
|
||||
#endif
|
||||
|
||||
ast_node_t* new_ast_node_block() {
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_BLOCK;
|
||||
vector_init(node->block.children);
|
||||
return node;
|
||||
}
|
||||
|
||||
ast_node_t* parse_block(parser_t* parser) {
|
||||
symtab_enter_scope(parser->symtab);
|
||||
tok_buf_t *tokbuf = &parser->tokbuf;
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype;
|
||||
ast_node_t* node = new_ast_node_block();
|
||||
|
||||
expect_pop_tok(tokbuf, TOKEN_L_BRACE);
|
||||
ast_node_t* child = NULL;
|
||||
while (1) {
|
||||
if (peek_decl(tokbuf)) {
|
||||
child = parse_decl(parser);
|
||||
vector_push(node->block.children, child);
|
||||
continue;
|
||||
}
|
||||
|
||||
flush_peek_tok(tokbuf);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
switch (ttype) {
|
||||
case TOKEN_R_BRACE: {
|
||||
pop_tok(tokbuf);
|
||||
goto END;
|
||||
}
|
||||
default: {
|
||||
child = parse_stmt(parser);
|
||||
vector_push(node->block.children, child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
END:
|
||||
symtab_leave_scope(parser->symtab);
|
||||
return node;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#include "../parser.h"
|
||||
#include "ast.h"
|
||||
#include "../symtab/symtab.h"
|
||||
|
||||
/**
|
||||
* 0 false
|
||||
* 1 true
|
||||
*/
|
||||
int peek_decl(tok_buf_t* tokbuf) {
|
||||
flush_peek_tok(tokbuf);
|
||||
switch (peek_tok_type(tokbuf)) {
|
||||
case TOKEN_STATIC:
|
||||
case TOKEN_EXTERN:
|
||||
case TOKEN_REGISTER:
|
||||
case TOKEN_TYPEDEF:
|
||||
error("not impliment");
|
||||
break;
|
||||
default:
|
||||
flush_peek_tok(tokbuf);
|
||||
}
|
||||
|
||||
switch (peek_tok_type(tokbuf)) {
|
||||
case TOKEN_VOID:
|
||||
case TOKEN_CHAR:
|
||||
case TOKEN_SHORT:
|
||||
case TOKEN_INT:
|
||||
case TOKEN_LONG:
|
||||
case TOKEN_FLOAT:
|
||||
case TOKEN_DOUBLE:
|
||||
// FIXME Ptr
|
||||
return 1;
|
||||
default:
|
||||
flush_peek_tok(tokbuf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_node_t* parse_decl_val(parser_t* parser) {
|
||||
tok_buf_t* tokbuf = &parser->tokbuf;
|
||||
tok_type_t ttype;
|
||||
flush_peek_tok(tokbuf);
|
||||
|
||||
ast_node_t* node;
|
||||
ast_node_t* type_node = parse_type(parser);
|
||||
flush_peek_tok(tokbuf);
|
||||
ast_node_t* name_node = new_ast_ident_node(peek_tok(tokbuf));
|
||||
|
||||
node = new_ast_node();
|
||||
node->decl_val.type = type_node;
|
||||
node->decl_val.name = name_node;
|
||||
node->type = NT_DECL_VAR;
|
||||
symtab_add_symbol(parser->symtab, name_node->syms.tok.val.str, node, 0);
|
||||
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype == TOKEN_ASSIGN) {
|
||||
node->decl_val.expr_stmt = parse_stmt(parser);
|
||||
if (node->decl_val.expr_stmt->type != NT_STMT_EXPR) {
|
||||
error("parser_decl_val want stmt_expr");
|
||||
}
|
||||
} else if (ttype == TOKEN_SEMICOLON) {
|
||||
pop_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_SEMICOLON);
|
||||
} else {
|
||||
error("parser_decl_val syntax error");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
ast_node_t* parse_decl(parser_t* parser) {
|
||||
tok_buf_t* tokbuf = &parser->tokbuf;
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype;
|
||||
ast_node_t* node;
|
||||
|
||||
if (peek_decl(tokbuf) == 0) {
|
||||
error("syntax error expect decl_val TYPE");
|
||||
}
|
||||
if (peek_tok_type(tokbuf) != TOKEN_IDENT) {
|
||||
error("syntax error expect decl_val IDENT");
|
||||
}
|
||||
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
switch (ttype) {
|
||||
case TOKEN_L_PAREN: // (
|
||||
return NULL;
|
||||
break;
|
||||
case TOKEN_ASSIGN:
|
||||
case TOKEN_SEMICOLON:
|
||||
node = parse_decl_val(parser);
|
||||
break;
|
||||
default:
|
||||
error("syntax error expect decl_val ASSIGN or SEMICOLON");
|
||||
return NULL;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1,425 +0,0 @@
|
||||
#include "../parser.h"
|
||||
#include "ast.h"
|
||||
#include "../symtab/symtab.h"
|
||||
|
||||
// Copy from `CParse`
|
||||
/**
|
||||
* Operator precedence classes
|
||||
*/
|
||||
enum Precedence {
|
||||
PREC_BOTTOM,
|
||||
PREC_EXPRESSION, /* , left to right */
|
||||
PREC_ASSIGNMENT, /* = += -= *= /= %= <<= >>= &= ^= |= right to left */
|
||||
PREC_CONDITIONAL, /* ?: right to left */
|
||||
PREC_LOGICAL_OR, /* || left to right */
|
||||
PREC_LOGICAL_AND, /* && left to right */
|
||||
PREC_OR, /* | left to right */
|
||||
PREC_XOR, /* ^ left to right */
|
||||
PREC_AND, /* & left to right */
|
||||
PREC_EQUALITY, /* == != left to right */
|
||||
PREC_RELATIONAL, /* < <= > >= left to right */
|
||||
PREC_SHIFT, /* << >> left to right */
|
||||
PREC_ADDITIVE, /* + - left to right */
|
||||
PREC_MULTIPLICATIVE, /* * / % left to right */
|
||||
PREC_CAST, /* (type) right to left */
|
||||
PREC_UNARY, /* ! ~ ++ -- + - * & sizeof right to left */
|
||||
PREC_POSTFIX, /* () [] -> . left to right */
|
||||
PREC_PRIMARY,
|
||||
PREC_TOP
|
||||
};
|
||||
|
||||
enum ParseType {
|
||||
INFIX_PARSER,
|
||||
PREFIX_PARSER,
|
||||
};
|
||||
|
||||
static ast_node_t *parse_subexpression(tok_buf_t* tokbuf, symtab_t *symtab, enum Precedence prec);
|
||||
#define NEXT(prec) parse_subexpression(tokbuf, symtab, prec)
|
||||
|
||||
static ast_node_t* gen_node2(ast_node_t* left, ast_node_t* right,
|
||||
ast_type_t type) {
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = type;
|
||||
node->expr.left = left;
|
||||
node->expr.right = right;
|
||||
return node;
|
||||
// FIXME
|
||||
|
||||
// switch (type) {
|
||||
// case NT_ADD : printf("+ \n"); break; // (expr) + (expr)
|
||||
// case NT_SUB : printf("- \n"); break; // (expr) - (expr)
|
||||
// case NT_MUL : printf("* \n"); break; // (expr) * (expr)
|
||||
// case NT_DIV : printf("/ \n"); break; // (expr) / (expr)
|
||||
// case NT_MOD : printf("%%\n"); break; // (expr) % (expr)
|
||||
// case NT_AND : printf("& \n"); break; // (expr) & (expr)
|
||||
// case NT_OR : printf("| \n"); break; // (expr) | (expr)
|
||||
// case NT_XOR : printf("^ \n"); break; // (expr) ^ (expr)
|
||||
// case NT_L_SH : printf("<<\n"); break; // (expr) << (expr)
|
||||
// case NT_R_SH : printf(">>\n"); break; // (expr) >> (expr)
|
||||
// case NT_EQ : printf("==\n"); break; // (expr) == (expr)
|
||||
// case NT_NEQ : printf("!=\n"); break; // (expr) != (expr)
|
||||
// case NT_LE : printf("<=\n"); break; // (expr) <= (expr)
|
||||
// case NT_GE : printf(">=\n"); break; // (expr) >= (expr)
|
||||
// case NT_LT : printf("< \n"); break; // (expr) < (expr)
|
||||
// case NT_GT : printf("> \n"); break; // (expr) > (expr)
|
||||
// case NT_AND_AND : printf("&&\n"); break; // (expr) && (expr)
|
||||
// case NT_OR_OR : printf("||\n"); break; // (expr) || (expr)
|
||||
// case NT_NOT : printf("! \n"); break; // ! (expr)
|
||||
// case NT_BIT_NOT : printf("~ \n"); break; // ~ (expr)
|
||||
// case NT_COMMA : printf(", \n"); break; // expr, expr 逗号运算符
|
||||
// case NT_ASSIGN : printf("= \n"); break; // (expr) = (expr)
|
||||
// // case NT_COND : // (expr) ? (expr) : (expr)
|
||||
// }
|
||||
}
|
||||
|
||||
static ast_node_t* parse_comma(tok_buf_t* tokbuf, symtab_t *symtab, ast_node_t* left) {
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_COMMA;
|
||||
node->expr.left = left;
|
||||
node->expr.right = NEXT(PREC_EXPRESSION);
|
||||
return node;
|
||||
}
|
||||
|
||||
static ast_node_t* parse_assign(tok_buf_t* tokbuf, symtab_t *symtab, ast_node_t* left) {
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype = peek_tok_type(tokbuf);
|
||||
pop_tok(tokbuf);
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_ASSIGN;
|
||||
// saved left
|
||||
node->expr.left = left;
|
||||
enum Precedence next = PREC_ASSIGNMENT + 1;
|
||||
switch (ttype) {
|
||||
case TOKEN_ASSIGN :
|
||||
left = NEXT(next);
|
||||
break;
|
||||
case TOKEN_ASSIGN_ADD :
|
||||
left = gen_node2(left, NEXT(next), NT_ADD);
|
||||
break;
|
||||
case TOKEN_ASSIGN_SUB :
|
||||
left = gen_node2(left, NEXT(next), NT_SUB);
|
||||
break;
|
||||
case TOKEN_ASSIGN_MUL :
|
||||
left = gen_node2(left, NEXT(next), NT_MUL);
|
||||
break;
|
||||
case TOKEN_ASSIGN_DIV :
|
||||
left = gen_node2(left, NEXT(next), NT_DIV);
|
||||
break;
|
||||
case TOKEN_ASSIGN_MOD :
|
||||
left = gen_node2(left, NEXT(next), NT_MOD);
|
||||
break;
|
||||
case TOKEN_ASSIGN_L_SH :
|
||||
left = gen_node2(left, NEXT(next), NT_L_SH);
|
||||
break;
|
||||
case TOKEN_ASSIGN_R_SH :
|
||||
left = gen_node2(left, NEXT(next), NT_R_SH);
|
||||
break;
|
||||
case TOKEN_ASSIGN_AND :
|
||||
left = gen_node2(left, NEXT(next), NT_AND);
|
||||
break;
|
||||
case TOKEN_ASSIGN_OR :
|
||||
left = gen_node2(left, NEXT(next), NT_OR);
|
||||
break;
|
||||
case TOKEN_ASSIGN_XOR :
|
||||
left = gen_node2(left, NEXT(next), NT_XOR);
|
||||
break;
|
||||
default:
|
||||
error("unsupported operator");
|
||||
break;
|
||||
}
|
||||
node->expr.right = left;
|
||||
return node;
|
||||
}
|
||||
|
||||
static ast_node_t* parse_cmp(tok_buf_t* tokbuf, symtab_t *symtab, ast_node_t* left) {
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype = peek_tok_type(tokbuf);
|
||||
pop_tok(tokbuf);
|
||||
ast_node_t* node = new_ast_node();
|
||||
// saved left
|
||||
node->expr.left = left;
|
||||
switch (ttype) {
|
||||
case TOKEN_EQ:
|
||||
node->type = NT_EQ;
|
||||
node->expr.right = NEXT(PREC_EQUALITY);
|
||||
break;
|
||||
case TOKEN_NEQ:
|
||||
node->type = NT_NEQ;
|
||||
node->expr.right = NEXT(PREC_EQUALITY);
|
||||
break;
|
||||
case TOKEN_LT:
|
||||
node->type = NT_LT;
|
||||
node->expr.right = NEXT(PREC_RELATIONAL);
|
||||
break;
|
||||
case TOKEN_GT:
|
||||
node->type = NT_GT;
|
||||
node->expr.right = NEXT(PREC_RELATIONAL);
|
||||
break;
|
||||
case TOKEN_LE:
|
||||
node->type = NT_LE;
|
||||
node->expr.right = NEXT(PREC_RELATIONAL);
|
||||
break;
|
||||
case TOKEN_GE:
|
||||
node->type = NT_GE;
|
||||
node->expr.right = NEXT(PREC_RELATIONAL);
|
||||
break;
|
||||
default:
|
||||
error("invalid operator");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static ast_node_t* parse_cal(tok_buf_t* tokbuf, symtab_t *symtab, ast_node_t* left) {
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype = peek_tok_type(tokbuf);
|
||||
pop_tok(tokbuf);
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->expr.left = left;
|
||||
switch (ttype) {
|
||||
case TOKEN_OR_OR:
|
||||
node->type = NT_OR_OR;
|
||||
node->expr.right = NEXT(PREC_LOGICAL_OR);
|
||||
break;
|
||||
case TOKEN_AND_AND:
|
||||
node->type = NT_AND_AND;
|
||||
node->expr.right = NEXT(PREC_LOGICAL_AND);
|
||||
break;
|
||||
case TOKEN_OR:
|
||||
node->type = NT_OR;
|
||||
node->expr.right = NEXT(PREC_OR);
|
||||
break;
|
||||
case TOKEN_XOR:
|
||||
node->type = NT_XOR;
|
||||
node->expr.right = NEXT(PREC_XOR);
|
||||
break;
|
||||
case TOKEN_AND:
|
||||
node->type = NT_AND;
|
||||
node->expr.right = NEXT(PREC_AND);
|
||||
break;
|
||||
case TOKEN_L_SH:
|
||||
node->type = NT_L_SH;
|
||||
node->expr.right = NEXT(PREC_SHIFT);
|
||||
break;
|
||||
case TOKEN_R_SH:
|
||||
node->type = NT_R_SH;
|
||||
node->expr.right = NEXT(PREC_SHIFT);
|
||||
break;
|
||||
case TOKEN_ADD:
|
||||
node->type = NT_ADD;
|
||||
node->expr.right = NEXT(PREC_ADDITIVE);
|
||||
break;
|
||||
case TOKEN_SUB:
|
||||
node->type = NT_SUB;
|
||||
node->expr.right = NEXT(PREC_ADDITIVE);
|
||||
break;
|
||||
case TOKEN_MUL:
|
||||
node->type = NT_MUL;
|
||||
node->expr.right = NEXT(PREC_MULTIPLICATIVE);
|
||||
break;
|
||||
case TOKEN_DIV:
|
||||
node->type = NT_DIV;
|
||||
node->expr.right = NEXT(PREC_MULTIPLICATIVE);
|
||||
break;
|
||||
case TOKEN_MOD:
|
||||
node->type = NT_MOD;
|
||||
node->expr.right = NEXT(PREC_MULTIPLICATIVE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static ast_node_t* parse_call(tok_buf_t* tokbuf, symtab_t *symtab, ast_node_t* ident) {
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_TERM_CALL;
|
||||
node->call.name = ident;
|
||||
node->call.params = new_ast_node();
|
||||
vector_init(node->call.params->params.params);
|
||||
pop_tok(tokbuf); // 跳过 '('
|
||||
|
||||
tok_type_t ttype;
|
||||
while (1) {
|
||||
flush_peek_tok(tokbuf);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype == TOKEN_R_PAREN) {
|
||||
break;
|
||||
}
|
||||
ast_node_t* param = NEXT(PREC_EXPRESSION);
|
||||
vector_push(node->call.params->params.params, param);
|
||||
flush_peek_tok(tokbuf);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype == TOKEN_COMMA) pop_tok(tokbuf);
|
||||
}
|
||||
pop_tok(tokbuf); // 跳过 ')'
|
||||
|
||||
const char* name = ident->syms.tok.val.str;
|
||||
ast_node_t* sym = symtab_lookup_symbol(symtab, name);
|
||||
// TODO check func is match
|
||||
if (sym == NULL || sym->type != NT_DECL_FUNC) {
|
||||
error("function not decl %s", name);
|
||||
}
|
||||
node->call.name = ident;
|
||||
node->call.func_decl = sym;
|
||||
return node;
|
||||
}
|
||||
|
||||
static ast_node_t* parse_paren(tok_buf_t* tokbuf, symtab_t *symtab, ast_node_t* left) {
|
||||
flush_peek_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_L_PAREN);
|
||||
left = NEXT(PREC_EXPRESSION);
|
||||
flush_peek_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_R_PAREN);
|
||||
return left;
|
||||
}
|
||||
|
||||
typedef ast_node_t* (*parse_expr_fun_t)(tok_buf_t*, symtab_t* , ast_node_t*);
|
||||
static struct expr_prec_table_t {
|
||||
parse_expr_fun_t parser;
|
||||
enum Precedence prec;
|
||||
enum ParseType ptype;
|
||||
} expr_table [256] = {
|
||||
[TOKEN_COMMA] = {parse_comma, PREC_EXPRESSION, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_ADD] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_SUB] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_MUL] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_DIV] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_MOD] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_L_SH] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_R_SH] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_AND] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_OR] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
[TOKEN_ASSIGN_XOR] = {parse_assign, PREC_ASSIGNMENT, INFIX_PARSER},
|
||||
|
||||
[TOKEN_OR_OR] = {parse_cal, PREC_LOGICAL_OR , INFIX_PARSER},
|
||||
[TOKEN_AND_AND] = {parse_cal, PREC_LOGICAL_AND, INFIX_PARSER},
|
||||
[TOKEN_OR] = {parse_cal, PREC_OR , INFIX_PARSER},
|
||||
[TOKEN_XOR] = {parse_cal, PREC_XOR , INFIX_PARSER},
|
||||
[TOKEN_AND] = {parse_cal, PREC_AND , INFIX_PARSER},
|
||||
|
||||
[TOKEN_EQ] = {parse_cmp, PREC_EQUALITY, INFIX_PARSER},
|
||||
[TOKEN_NEQ] = {parse_cmp, PREC_EQUALITY, INFIX_PARSER},
|
||||
[TOKEN_LT] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER},
|
||||
[TOKEN_LE] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER},
|
||||
[TOKEN_GT] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER},
|
||||
[TOKEN_GE] = {parse_cmp, PREC_RELATIONAL, INFIX_PARSER},
|
||||
|
||||
[TOKEN_L_SH] = {parse_cal, PREC_SHIFT , INFIX_PARSER},
|
||||
[TOKEN_R_SH] = {parse_cal, PREC_SHIFT , INFIX_PARSER},
|
||||
[TOKEN_ADD] = {parse_cal, PREC_ADDITIVE , INFIX_PARSER},
|
||||
[TOKEN_SUB] = {parse_cal, PREC_ADDITIVE , INFIX_PARSER},
|
||||
[TOKEN_MUL] = {parse_cal, PREC_MULTIPLICATIVE , INFIX_PARSER},
|
||||
[TOKEN_DIV] = {parse_cal, PREC_MULTIPLICATIVE , INFIX_PARSER},
|
||||
[TOKEN_MOD] = {parse_cal, PREC_MULTIPLICATIVE , INFIX_PARSER},
|
||||
|
||||
[TOKEN_NOT] = {NULL, PREC_UNARY, PREFIX_PARSER},
|
||||
[TOKEN_BIT_NOT] = {NULL, PREC_UNARY, PREFIX_PARSER},
|
||||
[TOKEN_ADD_ADD] = {NULL, PREC_UNARY, PREFIX_PARSER},
|
||||
[TOKEN_SUB_SUB] = {NULL, PREC_UNARY, PREFIX_PARSER},
|
||||
// + - * & sizeof
|
||||
|
||||
[TOKEN_L_PAREN] = {parse_paren, PREC_POSTFIX, INFIX_PARSER},
|
||||
};
|
||||
|
||||
static ast_node_t *parse_primary_expression(tok_buf_t* tokbuf, symtab_t *symtab) {
|
||||
flush_peek_tok(tokbuf);
|
||||
|
||||
tok_t* tok = peek_tok(tokbuf);
|
||||
ast_node_t *node = new_ast_node();
|
||||
node->type = NT_TERM_VAL;
|
||||
node->syms.tok = *tok;
|
||||
|
||||
switch (tok->type) {
|
||||
case TOKEN_INT_LITERAL:
|
||||
// node->data.data_type = TYPE_INT;
|
||||
break;
|
||||
case TOKEN_FLOAT_LITERAL:
|
||||
warn("float not supported");
|
||||
break;
|
||||
case TOKEN_CHAR_LITERAL:
|
||||
// node->data.data_type = TYPE_CHAR;
|
||||
break;
|
||||
case TOKEN_STRING_LITERAL:
|
||||
// node->data.data_type = TYPE_POINTER;
|
||||
case TOKEN_IDENT:
|
||||
node = expect_pop_ident(tokbuf);
|
||||
tok_type_t ttype = peek_tok_type(tokbuf);
|
||||
if (ttype == TOKEN_L_PAREN) {
|
||||
node = parse_call(tokbuf, symtab, node);
|
||||
} else {
|
||||
void *sym = symtab_lookup_symbol(symtab, tok->val.str);
|
||||
if (sym == NULL) {
|
||||
error("undefined symbol but use %s", tok->val.str);
|
||||
}
|
||||
node->type = NT_TERM_IDENT;
|
||||
node->syms.decl_node = sym;
|
||||
}
|
||||
goto END;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
pop_tok(tokbuf);
|
||||
END:
|
||||
return node;
|
||||
}
|
||||
|
||||
static ast_node_t *parse_subexpression(tok_buf_t* tokbuf, symtab_t *symtab, enum Precedence prec) {
|
||||
tok_type_t ttype;
|
||||
struct expr_prec_table_t* work;
|
||||
ast_node_t* left;
|
||||
|
||||
while (1) {
|
||||
flush_peek_tok(tokbuf);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
work = &expr_table[ttype];
|
||||
// FIXME
|
||||
if (ttype == TOKEN_SEMICOLON || ttype == TOKEN_R_PAREN) {
|
||||
break;
|
||||
}
|
||||
if (work == NULL || work->parser == NULL || work->ptype == PREFIX_PARSER) {
|
||||
if (work->parser != NULL) {
|
||||
left = work->parser(tokbuf, symtab, NULL);
|
||||
} else {
|
||||
left = parse_primary_expression(tokbuf, symtab);
|
||||
}
|
||||
} else if (work->ptype == INFIX_PARSER) {
|
||||
if (work->parser == NULL)
|
||||
break;
|
||||
if (work->prec <= prec)
|
||||
break;
|
||||
left = work->parser(tokbuf, symtab, left);
|
||||
}
|
||||
// assert(left != NULL);
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
ast_node_t* parse_expr(parser_t* parser) {
|
||||
tok_buf_t* tokbuf = &(parser->tokbuf);
|
||||
symtab_t *symtab = parser->symtab;
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype = peek_tok_type(tokbuf);
|
||||
switch (ttype) {
|
||||
case TOKEN_NOT:
|
||||
case TOKEN_AND:
|
||||
case TOKEN_L_PAREN:
|
||||
case TOKEN_MUL:
|
||||
case TOKEN_ADD:
|
||||
case TOKEN_SUB:
|
||||
case TOKEN_BIT_NOT:
|
||||
case TOKEN_AND_AND:
|
||||
case TOKEN_CHAR_LITERAL:
|
||||
case TOKEN_INT_LITERAL:
|
||||
case TOKEN_STRING_LITERAL:
|
||||
case TOKEN_ADD_ADD:
|
||||
case TOKEN_SUB_SUB:
|
||||
case TOKEN_SIZEOF:
|
||||
case TOKEN_IDENT:
|
||||
return NEXT(PREC_EXPRESSION);
|
||||
default:
|
||||
error("Want expr but not got %s", get_tok_name(ttype));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
#include "../parser.h"
|
||||
#include "../symtab/symtab.h"
|
||||
#include "ast.h"
|
||||
|
||||
#ifndef FUNC_PARAM_CACHE_SIZE
|
||||
#define FUNC_PARAM_CACHE_SIZE 32 // 合理初始值,可覆盖99%常见情况
|
||||
#endif
|
||||
|
||||
// TODO 语义分析压入符号表
|
||||
static void parse_params(parser_t* parser, tok_buf_t* cache, ast_node_t* node) {
|
||||
flush_peek_tok(cache);
|
||||
tok_type_t ttype;
|
||||
ast_node_t *params = new_ast_node();
|
||||
node->decl_func.params = params;
|
||||
vector_init(params->params.params);
|
||||
|
||||
int depth = 1;
|
||||
while (depth) {
|
||||
ttype = peek_tok_type(cache);
|
||||
switch (ttype) {
|
||||
case TOKEN_COMMA:
|
||||
break;
|
||||
case TOKEN_ELLIPSIS:
|
||||
ttype = peek_tok_type(cache);
|
||||
if (ttype != TOKEN_R_PAREN) {
|
||||
error("... must be a last parameter list (expect ')')");
|
||||
}
|
||||
// TODO
|
||||
error("not implement");
|
||||
break;
|
||||
case TOKEN_IDENT:
|
||||
// TODO 静态数组
|
||||
flush_peek_tok(cache);
|
||||
ast_node_t* id_node = new_ast_ident_node(peek_tok(cache));
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_DECL_VAR;
|
||||
node->decl_val.name = id_node;
|
||||
// TODO typing sys
|
||||
node->decl_val.type = NULL;
|
||||
node->decl_val.expr_stmt = NULL;
|
||||
node->decl_val.data = NULL;
|
||||
vector_push(params->params.params, node);
|
||||
symtab_add_symbol(parser->symtab, id_node->syms.tok.val.str, node, 0);
|
||||
break;
|
||||
case TOKEN_L_PAREN: {
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
case TOKEN_R_PAREN: {
|
||||
depth--;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
// TODO 使用cache的类型解析
|
||||
// parse_type(parser);
|
||||
// TODO type parse
|
||||
// ttype = peekcachetype(cache);
|
||||
// ttype = peekcachetype(cache);
|
||||
// if (ttype != TOKEN_IDENT) {
|
||||
// node->node_type = NT_DECL_FUNC;
|
||||
// flush_peek_tok(tokbuf);
|
||||
// continue;
|
||||
// }
|
||||
// error("function expected ')' or ','\n");
|
||||
}
|
||||
pop_tok(cache);
|
||||
}
|
||||
}
|
||||
|
||||
ast_type_t check_is_func_decl(tok_buf_t* tokbuf, tok_buf_t* cache) {
|
||||
expect_pop_tok(tokbuf, TOKEN_L_PAREN);
|
||||
int depth = 1;
|
||||
|
||||
while (depth) {
|
||||
tok_t* tok = peek_tok(tokbuf);
|
||||
pop_tok(tokbuf);
|
||||
if (cache->size >= cache->cap - 1) {
|
||||
error("function parameter list too long");
|
||||
}
|
||||
cache->buf[cache->size++] = *tok;
|
||||
switch (tok->type) {
|
||||
case TOKEN_L_PAREN:
|
||||
depth++;
|
||||
break;
|
||||
case TOKEN_R_PAREN:
|
||||
depth--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
cache->end = cache->size;
|
||||
|
||||
switch (peek_tok_type(tokbuf)) {
|
||||
case TOKEN_SEMICOLON:
|
||||
pop_tok(tokbuf);
|
||||
return NT_DECL_FUNC;
|
||||
case TOKEN_L_BRACE:
|
||||
return NT_FUNC;
|
||||
break;
|
||||
default:
|
||||
error("function define or decl need '{' or ';' but you don't got");
|
||||
}
|
||||
}
|
||||
|
||||
static ast_node_t* new_ast_node_funcdecl(ast_node_t* ret, ast_node_t* name) {
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_DECL_FUNC;
|
||||
node->decl_func.ret = ret;
|
||||
node->decl_func.name = name;
|
||||
node->decl_func.def = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
void parse_func(parser_t* parser) {
|
||||
tok_buf_t* tokbuf = &(parser->tokbuf);
|
||||
flush_peek_tok(tokbuf);
|
||||
ast_node_t* ret_node = parse_type(parser);
|
||||
ast_node_t* name_node = expect_pop_ident(tokbuf);
|
||||
const char* func_name = name_node->syms.tok.val.str;
|
||||
ast_node_t* decl = new_ast_node_funcdecl(ret_node, name_node);
|
||||
|
||||
tok_buf_t cache;
|
||||
init_tokbuf(&cache, NULL, NULL);
|
||||
cache.cap = FUNC_PARAM_CACHE_SIZE;
|
||||
tok_t buf[FUNC_PARAM_CACHE_SIZE];
|
||||
cache.buf = buf;
|
||||
|
||||
ast_type_t type = check_is_func_decl(&(parser->tokbuf), &cache);
|
||||
|
||||
ast_node_t* prev = symtab_add_symbol(parser->symtab, func_name, decl, 1);
|
||||
if (prev != NULL) {
|
||||
if (prev->type != NT_DECL_FUNC) {
|
||||
error("the symbol duplicate old is %d, new is func", prev->type);
|
||||
}
|
||||
// TODO check redeclare func is match
|
||||
if (type == NT_FUNC) {
|
||||
// TODO Free decl;
|
||||
free(decl);
|
||||
decl = prev;
|
||||
goto FUNC;
|
||||
}
|
||||
return;
|
||||
}
|
||||
vector_push(parser->root->root.children, decl);
|
||||
if (type == NT_DECL_FUNC) {
|
||||
return;
|
||||
}
|
||||
|
||||
FUNC:
|
||||
// 该data临时用于判断是否重复定义
|
||||
if (decl->decl_func.def != NULL) {
|
||||
error("redefinition of function %s", func_name);
|
||||
}
|
||||
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_FUNC;
|
||||
node->func.decl = decl;
|
||||
node->func.data = NULL;
|
||||
|
||||
decl->decl_func.def = node;
|
||||
symtab_enter_scope(parser->symtab);
|
||||
parse_params(parser, &cache, decl);
|
||||
node->func.body = parse_block(parser);
|
||||
symtab_leave_scope(parser->symtab);
|
||||
|
||||
vector_push(parser->root->root.children, node);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "../parser.h"
|
||||
#include "ast.h"
|
||||
|
||||
#ifndef PROG_MAX_NODE_SIZE
|
||||
#define PROG_MAX_NODE_SIZE (1024 * 4)
|
||||
#endif
|
||||
|
||||
void parse_func(parser_t* parser);
|
||||
|
||||
void parse_prog(parser_t* parser) {
|
||||
/**
|
||||
* Program := (Declaration | Definition)*
|
||||
* same as
|
||||
* Program := Declaration* Definition*
|
||||
*/
|
||||
tok_buf_t *tokbuf = &(parser->tokbuf);
|
||||
parser->root = new_ast_node();
|
||||
ast_node_t* node;
|
||||
parser->root->type = NT_ROOT;
|
||||
vector_init(parser->root->root.children);
|
||||
while (1) {
|
||||
flush_peek_tok(tokbuf);
|
||||
if (peek_tok_type(tokbuf) == TOKEN_EOF) {
|
||||
break;
|
||||
}
|
||||
node = parse_decl(parser);
|
||||
if (node == NULL) {
|
||||
parse_func(parser);
|
||||
} else {
|
||||
vector_push(parser->root->root.children, node);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
#include "../parser.h"
|
||||
#include "ast.h"
|
||||
|
||||
ast_node_t* parse_stmt(parser_t* parser) {
|
||||
tok_buf_t* tokbuf = &parser->tokbuf;
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype = peek_tok_type(tokbuf);
|
||||
ast_node_t* node = new_ast_node();
|
||||
switch (ttype) {
|
||||
case TOKEN_IF: {
|
||||
/**
|
||||
* if (exp) stmt
|
||||
* if (exp) stmt else stmt
|
||||
*/
|
||||
pop_tok(tokbuf);
|
||||
|
||||
expect_pop_tok(tokbuf, TOKEN_L_PAREN);
|
||||
node->if_stmt.cond = parse_expr(parser);
|
||||
flush_peek_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_R_PAREN);
|
||||
|
||||
node->if_stmt.if_stmt = parse_stmt(parser);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype == TOKEN_ELSE) {
|
||||
pop_tok(tokbuf);
|
||||
node->if_stmt.else_stmt = parse_stmt(parser);
|
||||
} else {
|
||||
node->if_stmt.else_stmt = NULL;
|
||||
}
|
||||
node->type = NT_STMT_IF;
|
||||
break;
|
||||
}
|
||||
case TOKEN_SWITCH: {
|
||||
/**
|
||||
* switch (exp) stmt
|
||||
*/
|
||||
pop_tok(tokbuf);
|
||||
|
||||
expect_pop_tok(tokbuf, TOKEN_L_PAREN);
|
||||
node->switch_stmt.cond = parse_expr(parser);
|
||||
expect_pop_tok(tokbuf, TOKEN_R_PAREN);
|
||||
|
||||
node->switch_stmt.body = parse_stmt(parser);
|
||||
node->type = NT_STMT_SWITCH;
|
||||
break;
|
||||
}
|
||||
case TOKEN_WHILE: {
|
||||
/**
|
||||
* while (exp) stmt
|
||||
*/
|
||||
pop_tok(tokbuf);
|
||||
|
||||
expect_pop_tok(tokbuf, TOKEN_L_PAREN);
|
||||
node->while_stmt.cond = parse_expr(parser);
|
||||
expect_pop_tok(tokbuf, TOKEN_R_PAREN);
|
||||
|
||||
node->while_stmt.body = parse_stmt(parser);
|
||||
node->type = NT_STMT_WHILE;
|
||||
break;
|
||||
}
|
||||
case TOKEN_DO: {
|
||||
/**
|
||||
* do stmt while (exp)
|
||||
*/
|
||||
pop_tok(tokbuf);
|
||||
node->do_while_stmt.body = parse_stmt(parser);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype != TOKEN_WHILE) {
|
||||
error("expected while after do");
|
||||
}
|
||||
pop_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_L_PAREN);
|
||||
node->do_while_stmt.cond = parse_expr(parser);
|
||||
expect_pop_tok(tokbuf, TOKEN_R_PAREN);
|
||||
node->type = NT_STMT_DOWHILE;
|
||||
break;
|
||||
}
|
||||
case TOKEN_FOR: {
|
||||
/**
|
||||
* for (init; [cond]; [iter]) stmt
|
||||
*/
|
||||
// node->children.stmt.for_stmt.init
|
||||
pop_tok(tokbuf);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype != TOKEN_L_PAREN) {
|
||||
error("expected ( after for");
|
||||
}
|
||||
pop_tok(tokbuf);
|
||||
|
||||
// init expr or init decl_var
|
||||
// TODO need add this feature
|
||||
if (peek_decl(tokbuf)) {
|
||||
node->for_stmt.init = parse_decl_val(parser);
|
||||
} else {
|
||||
node->for_stmt.init = parse_expr(parser);
|
||||
expect_pop_tok(tokbuf, TOKEN_SEMICOLON);
|
||||
}
|
||||
|
||||
// cond expr or null
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype != TOKEN_SEMICOLON) {
|
||||
node->for_stmt.cond = parse_expr(parser);
|
||||
expect_pop_tok(tokbuf, TOKEN_SEMICOLON);
|
||||
} else {
|
||||
node->for_stmt.cond = NULL;
|
||||
pop_tok(tokbuf);
|
||||
}
|
||||
|
||||
// iter expr or null
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype != TOKEN_R_PAREN) {
|
||||
node->for_stmt.iter = parse_expr(parser);
|
||||
expect_pop_tok(tokbuf, TOKEN_R_PAREN);
|
||||
} else {
|
||||
node->for_stmt.iter = NULL;
|
||||
pop_tok(tokbuf);
|
||||
}
|
||||
|
||||
node->for_stmt.body = parse_stmt(parser);
|
||||
node->type = NT_STMT_FOR;
|
||||
break;
|
||||
}
|
||||
case TOKEN_BREAK: {
|
||||
/**
|
||||
* break ;
|
||||
*/
|
||||
// TODO check 导致外围 for、while 或 do-while 循环或 switch 语句终止。
|
||||
pop_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_SEMICOLON);
|
||||
|
||||
node->type = NT_STMT_BREAK;
|
||||
break;
|
||||
}
|
||||
case TOKEN_CONTINUE: {
|
||||
/**
|
||||
* continue ;
|
||||
*/
|
||||
// TODO check 导致跳过整个 for、 while 或 do-while 循环体的剩余部分。
|
||||
pop_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_SEMICOLON);
|
||||
|
||||
node->type = NT_STMT_CONTINUE;
|
||||
break;
|
||||
}
|
||||
case TOKEN_RETURN: {
|
||||
/**
|
||||
* return [exp] ;
|
||||
*/
|
||||
// TODO 终止当前函数并返回指定值给调用方函数。
|
||||
pop_tok(tokbuf);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype != TOKEN_SEMICOLON) {
|
||||
node->return_stmt.expr_stmt = parse_expr(parser);
|
||||
flush_peek_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_SEMICOLON);
|
||||
} else {
|
||||
node->return_stmt.expr_stmt = NULL;
|
||||
pop_tok(tokbuf);
|
||||
}
|
||||
node->type = NT_STMT_RETURN;
|
||||
break;
|
||||
}
|
||||
case TOKEN_GOTO: {
|
||||
/**
|
||||
* goto label ;
|
||||
*/
|
||||
// TODO check label 将控制无条件转移到所欲位置。
|
||||
//在无法用约定的构造将控制转移到所欲位置时使用。
|
||||
pop_tok(tokbuf);
|
||||
// find symbol table
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype != TOKEN_IDENT) {
|
||||
error("expect identifier after goto");
|
||||
}
|
||||
expect_pop_tok(tokbuf, TOKEN_SEMICOLON);
|
||||
// TODO filling label
|
||||
node->goto_stmt.label = expect_pop_ident(tokbuf);
|
||||
node->type = NT_STMT_GOTO;
|
||||
break;
|
||||
}
|
||||
case TOKEN_SEMICOLON: {
|
||||
/**
|
||||
* ;
|
||||
* empty stmt using by :
|
||||
* while () ;
|
||||
* if () ;
|
||||
* for () ;
|
||||
*/
|
||||
pop_tok(tokbuf);
|
||||
node->type = NT_STMT_EMPTY;
|
||||
break;
|
||||
}
|
||||
case TOKEN_L_BRACE: {
|
||||
/**
|
||||
* stmt_block like: { (decl_var | stmt) ... }
|
||||
*/
|
||||
node->block_stmt.block = parse_block(parser);
|
||||
node->type = NT_STMT_BLOCK;
|
||||
break;
|
||||
}
|
||||
case TOKEN_IDENT: {
|
||||
// TODO label goto
|
||||
if (peek_tok_type(tokbuf) != TOKEN_COLON) {
|
||||
goto EXP;
|
||||
}
|
||||
node->label_stmt.label = expect_pop_ident(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_COLON);
|
||||
node->type = NT_STMT_LABEL;
|
||||
break;
|
||||
}
|
||||
case TOKEN_CASE: {
|
||||
// TODO label switch
|
||||
pop_tok(tokbuf);
|
||||
error("unimplemented switch label");
|
||||
node->label_stmt.label = parse_expr(parser);
|
||||
// TODO 该表达式为const int
|
||||
expect_pop_tok(tokbuf, TOKEN_COLON);
|
||||
node->type = NT_STMT_CASE;
|
||||
break;
|
||||
}
|
||||
case TOKEN_DEFAULT: {
|
||||
// TODO label switch default
|
||||
pop_tok(tokbuf);
|
||||
expect_pop_tok(tokbuf, TOKEN_COLON);
|
||||
node->type = NT_STMT_DEFAULT;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/**
|
||||
* exp ;
|
||||
*/
|
||||
EXP:
|
||||
node->expr_stmt.expr_stmt = parse_expr(parser);
|
||||
flush_peek_tok(tokbuf);
|
||||
ttype = peek_tok_type(tokbuf);
|
||||
if (ttype != TOKEN_SEMICOLON) {
|
||||
error("exp must end with \";\"");
|
||||
}
|
||||
pop_tok(tokbuf);
|
||||
node->type = NT_STMT_EXPR;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "../parser.h"
|
||||
#include "../type.h"
|
||||
#include "ast.h"
|
||||
|
||||
ast_node_t* new_ast_ident_node(tok_t* tok) {
|
||||
if (tok->type != TOKEN_IDENT) {
|
||||
error("syntax error: want identifier but got %d", tok->type);
|
||||
}
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_TERM_IDENT;
|
||||
node->syms.tok = *tok;
|
||||
node->syms.decl_node = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
ast_node_t* expect_pop_ident(tok_buf_t* tokbuf) {
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_t* tok = peek_tok(tokbuf);
|
||||
ast_node_t* node = new_ast_ident_node(tok);
|
||||
pop_tok(tokbuf);
|
||||
return node;
|
||||
}
|
||||
|
||||
ast_node_t* parse_type(parser_t* parser) {
|
||||
tok_buf_t* tokbuf = &parser->tokbuf;
|
||||
flush_peek_tok(tokbuf);
|
||||
tok_type_t ttype = peek_tok_type(tokbuf);
|
||||
data_type_t dtype;
|
||||
switch(ttype) {
|
||||
case TOKEN_VOID: dtype = TYPE_VOID; break;
|
||||
case TOKEN_CHAR: dtype = TYPE_CHAR; break;
|
||||
case TOKEN_SHORT: dtype = TYPE_SHORT; break;
|
||||
case TOKEN_INT: dtype = TYPE_INT; break;
|
||||
case TOKEN_LONG: dtype = TYPE_LONG; break;
|
||||
case TOKEN_FLOAT: dtype = TYPE_FLOAT; break;
|
||||
case TOKEN_DOUBLE: dtype = TYPE_DOUBLE; break;
|
||||
default:
|
||||
error("无效的类型说明符");
|
||||
}
|
||||
|
||||
ast_node_t* node = new_ast_node();
|
||||
node->type = NT_TERM_TYPE;
|
||||
// TODO added by disable warning, will add typing system
|
||||
dtype += 1;
|
||||
pop_tok(tokbuf);
|
||||
|
||||
if (peek_tok_type(tokbuf) == TOKEN_MUL) {
|
||||
pop_tok(tokbuf);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
// #include "../parser.h"
|
||||
// #include "../type.h"
|
||||
|
||||
// enum TypeParseState {
|
||||
// TPS_BASE_TYPE, // 解析基础类型 (int/char等)
|
||||
// TPS_QUALIFIER, // 解析限定符 (const/volatile)
|
||||
// TPS_POINTER, // 解析指针 (*)
|
||||
// TPS_ARRAY, // 解析数组维度 ([n])
|
||||
// TPS_FUNC_PARAMS // 解析函数参数列表
|
||||
// };
|
||||
|
||||
// ast_node_t* parse_type(parser_t* p) {
|
||||
// ast_node_t* type_root = new_ast_node();
|
||||
// ast_node_t* current = type_root;
|
||||
// current->type = NT_TYPE_BASE;
|
||||
|
||||
// enum TypeParseState state = TPS_QUALIFIER;
|
||||
// int pointer_level = 0;
|
||||
|
||||
// while (1) {
|
||||
// tok_type_t t = peektoktype(p);
|
||||
|
||||
// switch (state) {
|
||||
// // 基础类型解析 (int, char等)
|
||||
// case TPS_BASE_TYPE:
|
||||
// if (is_base_type(t)) {
|
||||
// // current->data.data_type = token_to_datatype(t);
|
||||
// pop_tok(p);
|
||||
// state = TPS_POINTER;
|
||||
// } else {
|
||||
// error("Expected type specifier");
|
||||
// }
|
||||
// break;
|
||||
|
||||
// // 类型限定符 (const/volatile)
|
||||
// case TPS_QUALIFIER:
|
||||
// if (t == TOKEN_CONST || t == TOKEN_VOLATILE) {
|
||||
// ast_node_t* qual_node = new_ast_node();
|
||||
// qual_node->type = NT_TYPE_QUAL;
|
||||
// qual_node->data.data_type = t; // 复用data_type字段存储限定符
|
||||
// current->child.decl.type = qual_node;
|
||||
// current = qual_node;
|
||||
// pop_tok(p);
|
||||
// } else {
|
||||
// state = TPS_BASE_TYPE;
|
||||
// }
|
||||
// break;
|
||||
|
||||
// // 指针解析 (*)
|
||||
// case TPS_POINTER:
|
||||
// if (t == TOKEN_MUL) {
|
||||
// ast_node_t* ptr_node = new_ast_node();
|
||||
// ptr_node->type = NT_TYPE_PTR;
|
||||
// current->child.decl.type = ptr_node;
|
||||
// current = ptr_node;
|
||||
// pointer_level++;
|
||||
// pop_tok(p);
|
||||
// } else {
|
||||
// state = TPS_ARRAY;
|
||||
// }
|
||||
// break;
|
||||
|
||||
// // 数组维度 ([n])
|
||||
// case TPS_ARRAY:
|
||||
// if (t == TOKEN_L_BRACKET) {
|
||||
// pop_tok(p); // 吃掉[
|
||||
// ast_node_t* arr_node = new_ast_node();
|
||||
// arr_node->type = NT_TYPE_ARRAY;
|
||||
|
||||
// // 解析数组大小(仅语法检查)
|
||||
// if (peektoktype(p) != TOKEN_R_BRACKET) {
|
||||
// parse_expr(p); // 不计算实际值
|
||||
// }
|
||||
|
||||
// expecttok(p, TOKEN_R_BRACKET);
|
||||
// current->child.decl.type = arr_node;
|
||||
// current = arr_node;
|
||||
// } else {
|
||||
// state = TPS_FUNC_PARAMS;
|
||||
// }
|
||||
// break;
|
||||
|
||||
// // 函数参数列表
|
||||
// case TPS_FUNC_PARAMS:
|
||||
// if (t == TOKEN_L_PAREN) {
|
||||
// ast_node_t* func_node = new_ast_node();
|
||||
// func_node->type = NT_TYPE_FUNC;
|
||||
// current->child.decl.type = func_node;
|
||||
|
||||
// // 解析参数列表(仅结构,不验证类型)
|
||||
// parse_param_list(p, func_node);
|
||||
// current = func_node;
|
||||
// } else {
|
||||
// return type_root; // 类型解析结束
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 判断是否是基础类型
|
||||
// static int is_base_type(tok_type_t t) {
|
||||
// return t >= TOKEN_VOID && t <= TOKEN_DOUBLE;
|
||||
// }
|
||||
|
||||
// // // 转换token到数据类型(简化版)
|
||||
// // static enum DataType token_to_datatype(tok_type_t t) {
|
||||
// // static enum DataType map[] = {
|
||||
// // [TOKEN_VOID] = DT_VOID,
|
||||
// // [TOKEN_CHAR] = DT_CHAR,
|
||||
// // [TOKEN_INT] = DT_INT,
|
||||
// // // ...其他类型映射
|
||||
// // };
|
||||
// // return map[t];
|
||||
// // }
|
||||
|
||||
// // 解析参数列表(轻量级)
|
||||
// static void parse_param_list(parser_t* p, ast_node_t* func) {
|
||||
// expecttok(p, TOKEN_L_PAREN);
|
||||
|
||||
// while (peektoktype(p) != TOKEN_R_PAREN) {
|
||||
// ast_node_t* param = parse_type(p); // 递归解析类型
|
||||
|
||||
// // 允许可选参数名(仅语法检查)
|
||||
// if (peektoktype(p) == TOKEN_IDENT) {
|
||||
// pop_tok(p); // 吃掉参数名
|
||||
// }
|
||||
|
||||
// if (peektoktype(p) == TOKEN_COMMA) {
|
||||
// pop_tok(p);
|
||||
// }
|
||||
// }
|
||||
|
||||
// expecttok(p, TOKEN_R_PAREN);
|
||||
// }
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#include "parser.h"
|
||||
#include "type.h"
|
||||
|
||||
void init_parser(parser_t* parser, lexer_t* lexer, symtab_t* symtab) {
|
||||
parser->cur_node = NULL;
|
||||
parser->root = NULL;
|
||||
|
||||
parser->lexer = lexer;
|
||||
parser->symtab = symtab;
|
||||
init_tokbuf(&parser->tokbuf, lexer, (get_tokbuf_func)get_valid_token);
|
||||
parser->tokbuf.cap = sizeof(parser->TokenBuffer) / sizeof(parser->TokenBuffer[0]);
|
||||
parser->tokbuf.buf = parser->TokenBuffer;
|
||||
}
|
||||
|
||||
void run_parser(parser_t* parser) {
|
||||
parse_prog(parser);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef __PARSER_H__
|
||||
#define __PARSER_H__
|
||||
|
||||
#include "../frontend.h"
|
||||
|
||||
#include "../lexer/lexer.h"
|
||||
typedef struct lexer lexer_t;
|
||||
typedef struct symtab symtab_t;
|
||||
#define PARSER_MAX_TOKEN_QUEUE 16
|
||||
|
||||
typedef struct parser {
|
||||
struct ASTNode* root;
|
||||
struct ASTNode* cur_node;
|
||||
|
||||
lexer_t* lexer;
|
||||
symtab_t* symtab;
|
||||
tok_buf_t tokbuf;
|
||||
tok_t TokenBuffer[PARSER_MAX_TOKEN_QUEUE];
|
||||
int err_level;
|
||||
} parser_t;
|
||||
|
||||
void init_parser(parser_t* parser, lexer_t* lexer, symtab_t* symtab);
|
||||
void run_parser(parser_t* parser);
|
||||
|
||||
#endif
|
||||
@@ -1,53 +0,0 @@
|
||||
// hashmap.c
|
||||
#include "hashmap.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// DJB2哈希算法
|
||||
static unsigned long hash(const char* str) {
|
||||
unsigned long hash = 5381;
|
||||
int c;
|
||||
while ((c = *str++))
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
return hash % HMAP_SIZE;
|
||||
}
|
||||
|
||||
void hmap_init(HashMap* map) {
|
||||
memset(map->buckets, 0, sizeof(map->buckets));
|
||||
}
|
||||
|
||||
void hmap_put(HashMap* map, const char* key, void* value) {
|
||||
unsigned long idx = hash(key);
|
||||
HashMapEntry* entry = malloc(sizeof(HashMapEntry));
|
||||
entry->key = strdup(key);
|
||||
entry->value = value;
|
||||
entry->next = map->buckets[idx];
|
||||
map->buckets[idx] = entry;
|
||||
}
|
||||
|
||||
void* hmap_get(HashMap* map, const char* key) {
|
||||
unsigned long idx = hash(key);
|
||||
HashMapEntry* entry = map->buckets[idx];
|
||||
while (entry) {
|
||||
if (strcmp(entry->key, key) == 0)
|
||||
return entry->value;
|
||||
entry = entry->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hmap_contains(HashMap* map, const char* key) {
|
||||
return hmap_get(map, key) != NULL;
|
||||
}
|
||||
|
||||
void hmap_destroy(HashMap* map) {
|
||||
for (int i = 0; i < HMAP_SIZE; i++) {
|
||||
HashMapEntry* entry = map->buckets[i];
|
||||
while (entry) {
|
||||
HashMapEntry* next = entry->next;
|
||||
free(entry->key);
|
||||
free(entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef HASHMAP_H
|
||||
#define HASHMAP_H
|
||||
|
||||
#define HMAP_SIZE 64
|
||||
|
||||
typedef struct HashMapEntry {
|
||||
char* key;
|
||||
void* value;
|
||||
struct HashMapEntry* next;
|
||||
} HashMapEntry;
|
||||
|
||||
typedef struct {
|
||||
HashMapEntry* buckets[HMAP_SIZE];
|
||||
} HashMap;
|
||||
|
||||
// 初始化哈希表
|
||||
void hmap_init(HashMap* map);
|
||||
|
||||
// 插入键值对
|
||||
void hmap_put(HashMap* map, const char* key, void* value);
|
||||
|
||||
// 查找键值
|
||||
void* hmap_get(HashMap* map, const char* key);
|
||||
|
||||
// 检查键是否存在
|
||||
int hmap_contains(HashMap* map, const char* key);
|
||||
|
||||
// 释放哈希表内存(不释放value)
|
||||
void hmap_destroy(HashMap* map);
|
||||
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
// scope.c
|
||||
#include "scope.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct Scope Scope;
|
||||
|
||||
Scope* scope_create(Scope* parent) {
|
||||
Scope* scope = malloc(sizeof(Scope));
|
||||
hmap_init(&scope->symbols);
|
||||
scope->parent = parent;
|
||||
scope->base_offset = 0;
|
||||
scope->cur_offset = 0;
|
||||
return scope;
|
||||
}
|
||||
|
||||
void scope_destroy(Scope* scope) {
|
||||
hmap_destroy(&scope->symbols);
|
||||
free(scope);
|
||||
}
|
||||
|
||||
void scope_insert(Scope* scope, const char* name, void* symbol) {
|
||||
if (hmap_contains(&scope->symbols, name)) {
|
||||
// 处理重复定义错误
|
||||
fprintf(stderr, "Error: Symbol '%s' already defined\n", name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
hmap_put(&scope->symbols, name, symbol);
|
||||
}
|
||||
|
||||
void* scope_lookup(Scope* scope, const char* name) {
|
||||
void* symbol = NULL;
|
||||
while (scope) {
|
||||
symbol = hmap_get(&scope->symbols, name);
|
||||
if (symbol) break;
|
||||
scope = scope->parent;
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void* scope_lookup_current(Scope* scope, const char* name) {
|
||||
return hmap_get(&scope->symbols, name);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef SCOPE_H
|
||||
#define SCOPE_H
|
||||
|
||||
#include "hashmap.h"
|
||||
|
||||
struct Scope {
|
||||
HashMap symbols; // 当前作用域符号表
|
||||
struct Scope* parent; // 上层作用域
|
||||
int base_offset;
|
||||
int cur_offset;
|
||||
};
|
||||
|
||||
// 创建新作用域(父作用域可为NULL)
|
||||
struct Scope* scope_create(struct Scope* parent);
|
||||
|
||||
// 销毁作用域
|
||||
void scope_destroy(struct Scope* scope);
|
||||
|
||||
// 在当前作用域插入符号
|
||||
void scope_insert(struct Scope* scope, const char* name, void* symbol);
|
||||
|
||||
// 逐级查找符号
|
||||
void* scope_lookup(struct Scope* scope, const char* name);
|
||||
|
||||
// 仅在当前作用域查找
|
||||
void* scope_lookup_current(struct Scope* scope, const char* name);
|
||||
|
||||
#endif
|
||||
@@ -1,49 +0,0 @@
|
||||
// symtab.c
|
||||
#include "../../frontend.h"
|
||||
#include "scope.h"
|
||||
#include "symtab.h"
|
||||
|
||||
typedef symtab_t symtab_t;
|
||||
typedef struct Scope Scope;
|
||||
|
||||
void init_symtab(symtab_t* symtab) {
|
||||
symtab->global_scope = scope_create(NULL);
|
||||
symtab->cur_scope = symtab->global_scope;
|
||||
}
|
||||
|
||||
void del_symtab(symtab_t* symtab) {
|
||||
scope_destroy(symtab->global_scope);
|
||||
}
|
||||
|
||||
void symtab_enter_scope(symtab_t* symtab) {
|
||||
struct Scope* scope = scope_create(symtab->cur_scope);
|
||||
scope->base_offset = symtab->cur_scope->base_offset + symtab->cur_scope->cur_offset;
|
||||
symtab->cur_scope = scope;
|
||||
}
|
||||
|
||||
void symtab_leave_scope(symtab_t* symtab) {
|
||||
Scope * scope = symtab->cur_scope;
|
||||
if (scope == NULL) {
|
||||
error("cannot leave NULL scope or global scope");
|
||||
}
|
||||
symtab->cur_scope = symtab->cur_scope->parent;
|
||||
scope_destroy(scope);
|
||||
}
|
||||
|
||||
void* symtab_add_symbol(symtab_t* symtab, const char* name, void* ast_node, int can_duplicate) {
|
||||
struct Scope* scope = symtab->cur_scope;
|
||||
void* node = scope_lookup_current(scope, name);
|
||||
if (node != NULL) {
|
||||
if (!can_duplicate) {
|
||||
error("duplicate symbol %s", name);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
scope_insert(scope, name, ast_node);
|
||||
return node;
|
||||
}
|
||||
|
||||
void* symtab_lookup_symbol(symtab_t* symtab, const char* name) {
|
||||
return scope_lookup(symtab->cur_scope, name);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// symtab.h
|
||||
#ifndef __SYMTAB_H__
|
||||
#define __SYMTAB_H__
|
||||
|
||||
typedef struct symtab {
|
||||
struct Scope* cur_scope;
|
||||
struct Scope* global_scope;
|
||||
} symtab_t;
|
||||
|
||||
void init_symtab(symtab_t* symtab);
|
||||
void del_symtab(symtab_t* symtab);
|
||||
|
||||
void symtab_enter_scope(symtab_t* symtab);
|
||||
void symtab_leave_scope(symtab_t* symtab);
|
||||
void* symtab_add_symbol(symtab_t* symtab, const char* name, void* ast_node, int can_duplicate);
|
||||
void* symtab_lookup_symbol(symtab_t* symtab, const char* name);
|
||||
|
||||
#endif
|
||||
@@ -1,4 +0,0 @@
|
||||
extern int _print_str(const char* str);
|
||||
int main(void) {
|
||||
_print_str("Hello, world!\n");
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
// int __print_str(char* str);
|
||||
int f(void);
|
||||
|
||||
int main(void) {
|
||||
int a;
|
||||
// f();
|
||||
// a = 1 + 2 * 3 + 4;
|
||||
// __print_str("Hello, world!\n");
|
||||
a = 3 - f() * (3 + 2) % 6;
|
||||
// 测试用例:
|
||||
// if (a) if (2) 3; else b;
|
||||
// 是否正确解析为 if (a) { if (b) c else d }
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "../parser.h"
|
||||
#include "../ast/ast.h"
|
||||
#include "../symtab/symtab.h"
|
||||
#include <stdio.h>
|
||||
|
||||
// gcc -g ../parser.c ../../lexer/lexer.c ../ast/ast.c ../ast/block.c ../ast/decl.c ../ast/expr.c ../ast/func.c ../ast/program.c ../ast/stmt.c ../ast/term.c ../symtab/hashmap.c ../symtab/scope.c ../symtab/symtab.c test_parser.c -o test_parser
|
||||
// gcc -g test_parser.c -L../.. -lfrontend -o test_parser
|
||||
int main(int argc, char** argv) {
|
||||
const char* file_name = "test_file.c";
|
||||
if (argc == 2) {
|
||||
file_name = argv[1];
|
||||
}
|
||||
FILE* fp = fopen(file_name, "r");
|
||||
if (fp == NULL) {
|
||||
perror("open file failed");
|
||||
return 1;
|
||||
}
|
||||
printf("open file success\n");
|
||||
|
||||
struct Lexer lexer;
|
||||
init_lexer(&lexer, file_name, fp, (lexer_sread_fn)fread_s);
|
||||
|
||||
struct SymbolTable symtab;
|
||||
init_symtab(&symtab);
|
||||
|
||||
struct parser parser;
|
||||
init_parser(&parser, &lexer, &symtab);
|
||||
parse_prog(&parser);
|
||||
|
||||
printf("parse_end\n");
|
||||
pnt_ast(parser.root, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef __TYPE_H__
|
||||
#define __TYPE_H__
|
||||
|
||||
#include "../lexer/token.h"
|
||||
|
||||
typedef enum {
|
||||
TYPE_VOID,
|
||||
TYPE_CHAR,
|
||||
TYPE_SHORT,
|
||||
TYPE_INT,
|
||||
TYPE_LONG,
|
||||
TYPE_LONG_LONG,
|
||||
TYPE_FLOAT,
|
||||
TYPE_DOUBLE,
|
||||
TYPE_LONG_DOUBLE,
|
||||
|
||||
// prefix
|
||||
TYPE_SIGNED,
|
||||
TYPE_UNSIGNED,
|
||||
// TYPE_BOOL,
|
||||
// TYPE_COMPLEX,
|
||||
// TYPE_IMAGINARY,
|
||||
|
||||
TYPE_ENUM,
|
||||
TYPE_ARRAY,
|
||||
TYPE_STRUCT,
|
||||
TYPE_UNION,
|
||||
TYPE_FUNCTION,
|
||||
TYPE_POINTER,
|
||||
TYPE_ATOMIC,
|
||||
|
||||
TYPE_TYPEDEF,
|
||||
} data_type_t;
|
||||
|
||||
#endif
|
||||
@@ -1,30 +0,0 @@
|
||||
# 编译器设置
|
||||
CC = gcc
|
||||
AR = ar
|
||||
CFLAGS = -g -Wall
|
||||
|
||||
# 源文件列表
|
||||
SRCS = \
|
||||
ir.c \
|
||||
ir_ast.c \
|
||||
ir_lib.c \
|
||||
ir_type.c
|
||||
|
||||
# 生成目标文件列表
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
# 最终目标
|
||||
TARGET = libir.a
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET)
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -1,159 +0,0 @@
|
||||
// ir_core.h
|
||||
#ifndef IR_CORE_H
|
||||
#define IR_CORE_H
|
||||
|
||||
#include "../../libcore/vector.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// 错误码定义
|
||||
typedef enum {
|
||||
IR_EC_SUCCESS = 0, // 成功
|
||||
IR_EC_MEMORY_ERROR, // 内存分配失败
|
||||
IR_EC_TYPE_MISMATCH, // 类型不匹配
|
||||
IR_EC_INVALID_OPERAND, // 无效操作数
|
||||
IR_EC_DUPLICATE_SYMBOL, // 符号重定义
|
||||
} ir_ecode_t;
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
IR_TYPE_INT32,
|
||||
IR_TYPE_PTR,
|
||||
IR_TYPE_ARRAY,
|
||||
IR_TYPE_FUNC,
|
||||
IR_TYPE_VOID,
|
||||
} tag;
|
||||
union {
|
||||
struct {
|
||||
struct ir_type *base;
|
||||
size_t len;
|
||||
} arr;
|
||||
struct {
|
||||
struct ir_type *ret;
|
||||
struct ir_type **params;
|
||||
size_t param_cnt;
|
||||
} func;
|
||||
};
|
||||
} ir_type_t;
|
||||
|
||||
typedef struct ir_node ir_node_t;
|
||||
|
||||
typedef struct ir_bblock {
|
||||
const char *label;
|
||||
vector_header(instrs, ir_node_t*);
|
||||
// ir_arr_t used_by;
|
||||
} ir_bblock_t; // basic block
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
ir_type_t *type;
|
||||
vector_header(params, ir_node_t*);
|
||||
vector_header(bblocks, ir_bblock_t*);
|
||||
} ir_func_t;
|
||||
|
||||
typedef struct {
|
||||
vector_header(global, ir_node_t*);
|
||||
vector_header(funcs, ir_func_t*);
|
||||
vector_header(extern_funcs, ir_func_t*);
|
||||
} ir_prog_t;
|
||||
|
||||
typedef enum ir_node_tag {
|
||||
IR_NODE_NULL,
|
||||
IR_NODE_CONST_INT,
|
||||
IR_NODE_ALLOC,
|
||||
IR_NODE_LOAD,
|
||||
IR_NODE_STORE,
|
||||
IR_NODE_GET_PTR,
|
||||
IR_NODE_OP,
|
||||
IR_NODE_BRANCH,
|
||||
IR_NODE_JUMP,
|
||||
IR_NODE_CALL,
|
||||
IR_NODE_RET,
|
||||
} ir_node_tag_t;
|
||||
|
||||
struct ir_node {
|
||||
const ir_type_t* type;
|
||||
const char* name;
|
||||
vector_header(used_by, ir_node_t*);
|
||||
ir_node_tag_t tag;
|
||||
union {
|
||||
struct {
|
||||
int32_t val;
|
||||
} const_int;
|
||||
struct {
|
||||
ir_node_t* target;
|
||||
} load;
|
||||
struct {
|
||||
ir_node_t* target;
|
||||
ir_node_t* value;
|
||||
} store;
|
||||
struct {
|
||||
ir_node_t* src_addr;
|
||||
ir_node_t* offset;
|
||||
} get_ptr;
|
||||
struct {
|
||||
enum {
|
||||
/// Not equal to.
|
||||
IR_OP_NEQ,
|
||||
/// Equal to.
|
||||
IR_OP_EQ,
|
||||
/// Greater than.
|
||||
IR_OP_GT,
|
||||
/// Less than.
|
||||
IR_OP_LT,
|
||||
/// Greater than or equal to.
|
||||
IR_OP_GE,
|
||||
/// Less than or equal to.
|
||||
IR_OP_LE,
|
||||
/// Addition.
|
||||
IR_OP_ADD,
|
||||
/// Subtraction.
|
||||
IR_OP_SUB,
|
||||
/// Multiplication.
|
||||
IR_OP_MUL,
|
||||
/// Division.
|
||||
IR_OP_DIV,
|
||||
/// Modulo.
|
||||
IR_OP_MOD,
|
||||
/// Bitwise AND.
|
||||
IR_OP_AND,
|
||||
/// Bitwise OR.
|
||||
IR_OP_OR,
|
||||
/// Bitwise XOR.
|
||||
IR_OP_XOR,
|
||||
/// Bitwise NOT.
|
||||
IR_OP_NOT,
|
||||
/// Shift left logical.
|
||||
IR_OP_SHL,
|
||||
/// Shift right logical.
|
||||
IR_OP_SHR,
|
||||
/// Shift right arithmetic.
|
||||
IR_OP_SAR,
|
||||
} op;
|
||||
ir_node_t* lhs;
|
||||
ir_node_t* rhs;
|
||||
} op;
|
||||
struct {
|
||||
ir_node_t* cond;
|
||||
ir_bblock_t* true_bblock;
|
||||
ir_bblock_t* false_bblock;
|
||||
} branch;
|
||||
struct {
|
||||
ir_bblock_t* target_bblock;
|
||||
} jump;
|
||||
struct {
|
||||
ir_func_t* callee;
|
||||
vector_header(args, ir_node_t*);
|
||||
} call;
|
||||
struct {
|
||||
ir_node_t* ret_val;
|
||||
} ret;
|
||||
} data;
|
||||
};
|
||||
|
||||
extern ir_prog_t prog;
|
||||
struct ASTNode;
|
||||
void gen_ir_from_ast(struct ASTNode* node);
|
||||
|
||||
|
||||
#endif // IR_CORE_H
|
||||
@@ -1,439 +0,0 @@
|
||||
#include "ir.h"
|
||||
#include "ir_lib.h"
|
||||
#include "ir_type.h"
|
||||
#include "../frontend/frontend.h"
|
||||
|
||||
// 上下文结构,记录生成过程中的状态
|
||||
typedef struct {
|
||||
ir_func_t* cur_func; // 当前处理的函数
|
||||
ir_bblock_t* cur_block; // 当前基本块
|
||||
} IRGenContext;
|
||||
IRGenContext ctx;
|
||||
ir_prog_t prog;
|
||||
|
||||
static void emit_instr(ir_bblock_t* block, ir_node_t* node) {
|
||||
if (block == NULL) block = ctx.cur_block;
|
||||
vector_push(block->instrs, node);
|
||||
// return &(vector_at(block->instrs, block->instrs.size - 1));
|
||||
}
|
||||
|
||||
static ir_node_t* emit_br(ir_node_t* cond, ir_bblock_t* trueb, ir_bblock_t* falseb) {
|
||||
ir_node_t* br = new_ir_node(NULL, IR_NODE_BRANCH);
|
||||
emit_instr(NULL, br);
|
||||
br->data.branch.cond = cond;
|
||||
br->data.branch.true_bblock = trueb;
|
||||
br->data.branch.false_bblock = falseb;
|
||||
return br;
|
||||
}
|
||||
|
||||
static ir_node_t* gen_ir_expr(ast_node_t* node);
|
||||
|
||||
static ir_node_t* gen_ir_term(ast_node_t* node) {
|
||||
switch (node->type) {
|
||||
case NT_TERM_VAL: {
|
||||
ir_node_t* ir = new_ir_node(NULL, IR_NODE_CONST_INT);
|
||||
ir->data.const_int.val = node->syms.tok.val.i;
|
||||
return ir;
|
||||
}
|
||||
case NT_TERM_IDENT: {
|
||||
ir_node_t* decl = node->syms.decl_node->decl_val.data;
|
||||
return decl;
|
||||
}
|
||||
case NT_TERM_CALL: {
|
||||
ir_node_t* call = new_ir_node(NULL, IR_NODE_CALL);
|
||||
call->data.call.callee = node->call.func_decl->decl_func.def->func.data;
|
||||
|
||||
for (int i = 0; i < node->call.params->params.params.size; i++) {
|
||||
ast_node_t* param = vector_at(node->call.params->params.params, i);
|
||||
ir_node_t *tmp = gen_ir_expr(param);
|
||||
vector_push(call->data.call.args, tmp);
|
||||
}
|
||||
|
||||
emit_instr(NULL, call);
|
||||
return call;
|
||||
}
|
||||
default: {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ir_node_t* gen_ir_expr(ast_node_t* node) {
|
||||
// term node
|
||||
switch (node->type) {
|
||||
case NT_TERM_VAL:
|
||||
case NT_TERM_IDENT:
|
||||
case NT_TERM_CALL:
|
||||
return gen_ir_term(node);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ir_node_t* lhs = gen_ir_expr(node->expr.left);
|
||||
ir_node_t* rhs = node->expr.right ? gen_ir_expr(node->expr.right) : NULL;
|
||||
if (node->type == NT_COMMA) {
|
||||
return rhs;
|
||||
}
|
||||
|
||||
ir_node_t* instr = NULL;
|
||||
vector_push(lhs->used_by, instr);
|
||||
if (rhs) { vector_push(rhs->used_by, instr); }
|
||||
|
||||
ir_node_t* ret;
|
||||
#define BINOP(operand) do { \
|
||||
instr = new_ir_node(NULL, IR_NODE_OP); \
|
||||
instr->data.op.op = operand; \
|
||||
instr->data.op.lhs = lhs; \
|
||||
instr->data.op.rhs = rhs; \
|
||||
ret = instr; \
|
||||
} while (0)
|
||||
|
||||
switch (node->type) {
|
||||
case NT_ADD: {
|
||||
// (expr) + (expr)
|
||||
BINOP(IR_OP_ADD); break;
|
||||
}
|
||||
case NT_SUB: {
|
||||
// (expr) - (expr)
|
||||
BINOP(IR_OP_SUB); break;
|
||||
}
|
||||
case NT_MUL: {
|
||||
// (expr) * (expr)
|
||||
BINOP(IR_OP_MUL); break;
|
||||
}
|
||||
case NT_DIV: {
|
||||
// (expr) / (expr)
|
||||
BINOP(IR_OP_DIV); break;
|
||||
}
|
||||
case NT_MOD: {
|
||||
// (expr) % (expr)
|
||||
BINOP(IR_OP_MOD); break;
|
||||
}
|
||||
case NT_AND: {
|
||||
// (expr) & (expr)
|
||||
BINOP(IR_OP_AND); break;
|
||||
}
|
||||
case NT_OR: {
|
||||
// (expr) | (expr)
|
||||
BINOP(IR_OP_OR); break;
|
||||
}
|
||||
case NT_XOR: {
|
||||
// (expr) ^ (expr)
|
||||
BINOP(IR_OP_XOR); break;
|
||||
}
|
||||
case NT_BIT_NOT: {
|
||||
// ~ (expr)
|
||||
// TODO
|
||||
// BINOP(IR_OP_NOT);
|
||||
break;
|
||||
}
|
||||
case NT_L_SH: {
|
||||
// (expr) << (expr)
|
||||
BINOP(IR_OP_SHL);
|
||||
break;
|
||||
}
|
||||
case NT_R_SH: {
|
||||
// (expr) >> (expr)
|
||||
BINOP(IR_OP_SHR); // Shift right logical.
|
||||
// TODO
|
||||
// BINOP(IR_OP_SAR); // Shift right arithmetic.
|
||||
break;
|
||||
}
|
||||
case NT_EQ: {
|
||||
// (expr) == (expr)
|
||||
BINOP(IR_OP_EQ); break;
|
||||
}
|
||||
case NT_NEQ: {
|
||||
// (expr) != (expr)
|
||||
BINOP(IR_OP_NEQ); break;
|
||||
}
|
||||
case NT_LE: {
|
||||
// (expr) <= (expr)
|
||||
BINOP(IR_OP_LE); break;
|
||||
}
|
||||
case NT_GE: {
|
||||
// (expr) >= (expr)
|
||||
BINOP(IR_OP_GE); break;
|
||||
}
|
||||
case NT_LT: {
|
||||
// (expr) < (expr)
|
||||
BINOP(IR_OP_LT); break;
|
||||
}
|
||||
case NT_GT: {
|
||||
// (expr) > (expr)
|
||||
BINOP(IR_OP_GE); break;
|
||||
}
|
||||
case NT_AND_AND:// (expr) && (expr)
|
||||
error("unimpliment");
|
||||
break;
|
||||
case NT_OR_OR:// (expr) || (expr)
|
||||
error("unimpliment");
|
||||
break;
|
||||
case NT_NOT: {
|
||||
// ! (expr)
|
||||
instr = new_ir_node(NULL, IR_NODE_OP);
|
||||
instr->data.op.op = IR_OP_EQ,
|
||||
instr->data.op.lhs = &node_zero,
|
||||
instr->data.op.rhs = lhs,
|
||||
ret = instr;
|
||||
break;
|
||||
}
|
||||
case NT_ASSIGN: {
|
||||
// (expr) = (expr)
|
||||
instr = new_ir_node(NULL, IR_NODE_STORE);
|
||||
instr->data.store.target = lhs;
|
||||
instr->data.store.value = rhs;
|
||||
ret = rhs;
|
||||
break;
|
||||
}
|
||||
// case NT_COND: // (expr) ? (expr) : (expr)
|
||||
default: {
|
||||
// TODO self error msg
|
||||
error("Unsupported IR generation for AST node type %d", node->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit_instr(NULL, instr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gen_ir_func(ast_node_t* node, ir_func_t* func) {
|
||||
assert(node->type == NT_FUNC);
|
||||
ir_bblock_t *entry = new_ir_bblock("entry");
|
||||
vector_push(func->bblocks, entry);
|
||||
|
||||
vector_push(prog.funcs, func);
|
||||
IRGenContext prev_ctx = ctx;
|
||||
ctx.cur_func = func;
|
||||
ctx.cur_block = entry;
|
||||
|
||||
ast_node_t* params = node->func.decl->decl_func.params;
|
||||
for (int i = 0; i < params->params.params.size; i ++) {
|
||||
ast_node_t* param = params->params.params.data[i];
|
||||
ir_node_t* decl = new_ir_node(param->decl_val.name->syms.tok.val.str, IR_NODE_ALLOC);
|
||||
emit_instr(entry, decl);
|
||||
vector_push(func->params, decl);
|
||||
// TODO Typing system
|
||||
decl->type = &type_i32;
|
||||
param->decl_val.data = decl;
|
||||
}
|
||||
gen_ir_from_ast(node->func.body);
|
||||
|
||||
ctx = prev_ctx;
|
||||
}
|
||||
|
||||
void gen_ir_jmp(ast_node_t* node) {
|
||||
ir_bblock_t *bblocks[3];
|
||||
for (int i = 0; i < sizeof(bblocks)/sizeof(bblocks[0]); i++) {
|
||||
bblocks[i] = new_ir_bblock(NULL);
|
||||
vector_push(ctx.cur_func->bblocks, bblocks[i]);
|
||||
}
|
||||
|
||||
#define NEW_IR_JMP(name, block) do { \
|
||||
name = new_ir_node(NULL, IR_NODE_JUMP); \
|
||||
name->data.jump.target_bblock = block; \
|
||||
} while (0)
|
||||
|
||||
switch (node->type) {
|
||||
case NT_STMT_IF: {
|
||||
ir_bblock_t* trueb = bblocks[0];
|
||||
ir_bblock_t* falseb = bblocks[1];
|
||||
ir_bblock_t* endb = bblocks[2];
|
||||
ir_node_t* jmp;
|
||||
|
||||
// cond
|
||||
ir_node_t *cond = gen_ir_expr(node->if_stmt.cond);
|
||||
emit_br(cond, trueb, falseb);
|
||||
|
||||
// true block
|
||||
vector_push(ctx.cur_func->bblocks, trueb);
|
||||
ctx.cur_block = trueb;
|
||||
gen_ir_from_ast(node->if_stmt.if_stmt);
|
||||
|
||||
// else block
|
||||
if (node->if_stmt.else_stmt != NULL) {
|
||||
vector_push(ctx.cur_func->bblocks, falseb);
|
||||
ctx.cur_block = falseb;
|
||||
gen_ir_from_ast(node->if_stmt.else_stmt);
|
||||
ir_node_t* jmp;
|
||||
|
||||
ctx.cur_block = endb;
|
||||
vector_push(ctx.cur_func->bblocks, ctx.cur_block);
|
||||
NEW_IR_JMP(jmp, ctx.cur_block);
|
||||
emit_instr(falseb, jmp);
|
||||
} else {
|
||||
ctx.cur_block = falseb;
|
||||
}
|
||||
NEW_IR_JMP(jmp, ctx.cur_block);
|
||||
emit_instr(trueb, jmp);
|
||||
break;
|
||||
}
|
||||
case NT_STMT_WHILE: {
|
||||
ir_bblock_t* entryb = bblocks[0];
|
||||
ir_bblock_t* bodyb = bblocks[1];
|
||||
ir_bblock_t* endb = bblocks[2];
|
||||
|
||||
ir_node_t* entry;
|
||||
NEW_IR_JMP(entry, entryb);
|
||||
emit_instr(NULL, entry);
|
||||
|
||||
// Entry:
|
||||
ctx.cur_block = entryb;
|
||||
ir_node_t *cond = gen_ir_expr(node->while_stmt.cond);
|
||||
emit_br(cond, bodyb, endb);
|
||||
|
||||
// Body:
|
||||
ir_node_t* jmp;
|
||||
ctx.cur_block = bodyb;
|
||||
gen_ir_from_ast(node->while_stmt.body);
|
||||
NEW_IR_JMP(jmp, entryb);
|
||||
emit_instr(NULL, jmp);
|
||||
|
||||
// End:
|
||||
ctx.cur_block = endb;
|
||||
break;
|
||||
}
|
||||
case NT_STMT_DOWHILE: {
|
||||
ir_bblock_t* entryb = bblocks[0];
|
||||
ir_bblock_t* bodyb = bblocks[1];
|
||||
ir_bblock_t* endb = bblocks[2];
|
||||
|
||||
ir_node_t* entry;
|
||||
NEW_IR_JMP(entry, bodyb);
|
||||
emit_instr(NULL, entry);
|
||||
|
||||
// Body:
|
||||
ctx.cur_block = bodyb;
|
||||
gen_ir_from_ast(node->do_while_stmt.body);
|
||||
ir_node_t* jmp;
|
||||
NEW_IR_JMP(jmp, entryb);
|
||||
emit_instr(NULL, jmp);
|
||||
|
||||
// Entry:
|
||||
ctx.cur_block = entryb;
|
||||
ir_node_t *cond = gen_ir_expr(node->do_while_stmt.cond);
|
||||
emit_br(cond, bodyb, endb);
|
||||
|
||||
// End:
|
||||
ctx.cur_block = endb;
|
||||
break;
|
||||
}
|
||||
case NT_STMT_FOR: {
|
||||
ir_bblock_t* entryb = bblocks[0];
|
||||
ir_bblock_t* bodyb = bblocks[1];
|
||||
ir_bblock_t* endb = bblocks[2];
|
||||
|
||||
if (node->for_stmt.init) {
|
||||
gen_ir_from_ast(node->for_stmt.init);
|
||||
}
|
||||
ir_node_t* entry;
|
||||
NEW_IR_JMP(entry, entryb);
|
||||
emit_instr(NULL, entry);
|
||||
|
||||
// Entry:
|
||||
ctx.cur_block = entryb;
|
||||
if (node->for_stmt.cond) {
|
||||
ir_node_t *cond = gen_ir_expr(node->for_stmt.cond);
|
||||
emit_br(cond, bodyb, endb);
|
||||
} else {
|
||||
ir_node_t* jmp;
|
||||
NEW_IR_JMP(jmp, bodyb);
|
||||
}
|
||||
|
||||
// Body:
|
||||
ctx.cur_block = bodyb;
|
||||
gen_ir_from_ast(node->for_stmt.body);
|
||||
if (node->for_stmt.iter) {
|
||||
gen_ir_expr(node->for_stmt.iter);
|
||||
}
|
||||
ir_node_t* jmp;
|
||||
NEW_IR_JMP(jmp, entryb);
|
||||
emit_instr(NULL, jmp);
|
||||
|
||||
// End:
|
||||
ctx.cur_block = endb;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error("ir jmp can't hit here");
|
||||
}
|
||||
}
|
||||
|
||||
void gen_ir_from_ast(ast_node_t* node) {
|
||||
switch (node->type) {
|
||||
case NT_ROOT: {
|
||||
for (int i = 0; i < node->root.children.size; i ++) {
|
||||
gen_ir_from_ast(node->root.children.data[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NT_DECL_FUNC: {
|
||||
ir_func_t* func = new_ir_func(node->decl_func.name->syms.tok.val.str, &type_i32);
|
||||
if (node->decl_func.def == NULL) {
|
||||
ast_node_t* def = new_ast_node();
|
||||
def->func.body = NULL;
|
||||
def->func.decl = node;
|
||||
node->decl_func.def = def;
|
||||
vector_push(prog.extern_funcs, func);
|
||||
}
|
||||
node->decl_func.def->func.data = func;
|
||||
break;
|
||||
}
|
||||
case NT_FUNC: {
|
||||
gen_ir_func(node, node->func.data);
|
||||
break;
|
||||
}
|
||||
case NT_STMT_RETURN: {
|
||||
ir_node_t* ret = NULL;
|
||||
if (node->return_stmt.expr_stmt != NULL) {
|
||||
ret = gen_ir_expr(node->return_stmt.expr_stmt);
|
||||
}
|
||||
ir_node_t* ir = new_ir_node(NULL, IR_NODE_RET);
|
||||
ir->data.ret.ret_val = ret;
|
||||
emit_instr(NULL, ir);
|
||||
|
||||
ir_bblock_t* block = new_ir_bblock(NULL);
|
||||
ctx.cur_block = block;
|
||||
vector_push(ctx.cur_func->bblocks, block);
|
||||
break;
|
||||
}
|
||||
case NT_STMT_BLOCK: {
|
||||
gen_ir_from_ast(node->block_stmt.block);
|
||||
break;
|
||||
}
|
||||
case NT_BLOCK: {
|
||||
for (int i = 0; i < node->block.children.size; i ++) {
|
||||
gen_ir_from_ast(node->block.children.data[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NT_STMT_IF:
|
||||
case NT_STMT_WHILE:
|
||||
case NT_STMT_DOWHILE:
|
||||
case NT_STMT_FOR:
|
||||
gen_ir_jmp(node);
|
||||
break;
|
||||
case NT_DECL_VAR: {
|
||||
ir_node_t* ir = new_ir_node(node->decl_val.name->syms.tok.val.str, IR_NODE_ALLOC);
|
||||
emit_instr(NULL, ir);
|
||||
// TODO Typing system
|
||||
ir->type = &type_i32;
|
||||
node->decl_val.data = ir;
|
||||
if (node->decl_val.expr_stmt != NULL) {
|
||||
gen_ir_from_ast(node->decl_val.expr_stmt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NT_STMT_EXPR: {
|
||||
gen_ir_expr(node->expr_stmt.expr_stmt);
|
||||
break;
|
||||
}
|
||||
case NT_STMT_EMPTY: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO: 错误处理
|
||||
error("unknown node type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "ir.h"
|
||||
|
||||
// FIXME using stdlib.h
|
||||
#include <stdlib.h>
|
||||
|
||||
static int total_alloc = 0;
|
||||
typedef union ir_alloc_item {
|
||||
ir_node_t node;
|
||||
ir_bblock_t bblock;
|
||||
ir_func_t func;
|
||||
ir_prog_t prog;
|
||||
} ir_alloc_item_t;
|
||||
|
||||
ir_alloc_item_t* alloc_item() {
|
||||
return malloc(sizeof(ir_alloc_item_t));
|
||||
}
|
||||
|
||||
void free_item(ir_alloc_item_t* item) {
|
||||
return free(item);
|
||||
}
|
||||
|
||||
ir_node_t* new_ir_node(const char* name, ir_node_tag_t tag) {
|
||||
ir_node_t* node = (ir_node_t*)alloc_item();
|
||||
node->name = name;
|
||||
node->type = NULL;
|
||||
node->tag = tag;
|
||||
switch (tag) {
|
||||
case IR_NODE_ALLOC: {
|
||||
node->type = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_BRANCH: {
|
||||
node->data.branch.cond = NULL;
|
||||
node->data.branch.true_bblock = NULL;
|
||||
node->data.branch.false_bblock = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_CALL: {
|
||||
vector_init(node->data.call.args);
|
||||
node->data.call.callee = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_CONST_INT: {
|
||||
node->data.const_int.val = 0;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_JUMP: {
|
||||
node->data.jump.target_bblock = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_LOAD: {
|
||||
node->data.load.target = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_STORE: {
|
||||
node->data.store.target = NULL;
|
||||
node->data.store.value = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_OP: {
|
||||
node->data.op.op = 0;
|
||||
node->data.op.lhs = NULL;
|
||||
node->data.op.rhs = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_RET: {
|
||||
node->data.ret.ret_val = NULL;
|
||||
break;
|
||||
}
|
||||
case IR_NODE_GET_PTR: {
|
||||
}
|
||||
default: {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
vector_init(node->used_by);
|
||||
return node;
|
||||
}
|
||||
|
||||
void dump_ir_node(ir_node_t* node) {
|
||||
|
||||
}
|
||||
|
||||
void free_irnode() {
|
||||
|
||||
}
|
||||
|
||||
ir_bblock_t* new_ir_bblock(const char* name) {
|
||||
ir_bblock_t* block = (ir_bblock_t*)alloc_item();
|
||||
block->label = name;
|
||||
vector_init(block->instrs);
|
||||
return block;
|
||||
}
|
||||
|
||||
void free_irbblock() {
|
||||
|
||||
}
|
||||
|
||||
ir_func_t* new_ir_func(const char* name, ir_type_t* type) {
|
||||
ir_func_t* func = (ir_func_t*)alloc_item();
|
||||
func->name = name;
|
||||
func->type = type;
|
||||
vector_init(func->params);
|
||||
vector_init(func->bblocks);
|
||||
return func;
|
||||
}
|
||||
|
||||
void free_irfunc() {
|
||||
|
||||
}
|
||||
|
||||
ir_prog_t* new_ir_prog() {
|
||||
ir_prog_t* prog = (ir_prog_t*)alloc_item();
|
||||
vector_init(prog->global);
|
||||
vector_init(prog->funcs);
|
||||
vector_init(prog->extern_funcs);
|
||||
return prog;
|
||||
}
|
||||
|
||||
void free_irprog() {
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef __IR_LIB_H__
|
||||
#define __IR_LIB_H__
|
||||
|
||||
#include "ir.h"
|
||||
ir_node_t* new_ir_node(const char* name, ir_node_tag_t tag);
|
||||
ir_bblock_t* new_ir_bblock(const char* name);
|
||||
ir_func_t* new_ir_func(const char* name, ir_type_t* type);
|
||||
|
||||
#endif
|
||||
@@ -1,12 +0,0 @@
|
||||
#include "ir.h"
|
||||
|
||||
ir_type_t type_i32 = {
|
||||
.tag = IR_TYPE_INT32,
|
||||
};
|
||||
|
||||
ir_node_t node_zero = {
|
||||
.tag = IR_NODE_CONST_INT,
|
||||
.data.const_int = {
|
||||
.val = 0,
|
||||
},
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef __IR_TYPE_H__
|
||||
#define __IR_TYPE_H__
|
||||
|
||||
#include "ir.h"
|
||||
extern ir_type_t type_i32;
|
||||
extern ir_node_t node_zero;
|
||||
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef __REG_ALLOC_H__
|
||||
#define __REG_ALLOC_H__
|
||||
|
||||
typedef struct {
|
||||
|
||||
} reg_alloc_t;
|
||||
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
all: test_ir
|
||||
|
||||
|
||||
test_ir: frontend
|
||||
gcc -g ../ir.c test_ir.c -L../../frontend -lfrontend -o test_ir
|
||||
|
||||
frontend:
|
||||
make -C ../../frontend
|
||||
@@ -1,7 +0,0 @@
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
return add(1, 2);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "../ir.h"
|
||||
#include "../../frontend/frontend.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
const char* file_name = "test_file.c";
|
||||
if (argc == 2) {
|
||||
file_name = argv[1];
|
||||
}
|
||||
FILE* fp = fopen(file_name, "r");
|
||||
if (fp == NULL) {
|
||||
perror("open file failed");
|
||||
return 1;
|
||||
}
|
||||
printf("open file success\n");
|
||||
struct ASTNode* root = frontend("test.c", fp, (sread_fn)fread_s);
|
||||
gen_ir_from_ast(root);
|
||||
return 0;
|
||||
}
|
||||
39
justfile
Normal file
39
justfile
Normal file
@@ -0,0 +1,39 @@
|
||||
list:
|
||||
just --list
|
||||
|
||||
build_docs:
|
||||
doxygen Doxyfile
|
||||
|
||||
docs: build_docs
|
||||
python -m http.server -d docs/html
|
||||
|
||||
count:
|
||||
# you need download `tokei` it can download by cargo
|
||||
tokei libs runtime src -e tests -e libs/mcode
|
||||
|
||||
count-file:
|
||||
# you need download `tokei` it can download by cargo
|
||||
tokei libs runtime src -e tests -e libs/mcode --files
|
||||
|
||||
clean:
|
||||
cbuild clean
|
||||
|
||||
build:
|
||||
cbuild build -cclang --release
|
||||
|
||||
build-install: build
|
||||
cp ./build/release/scc ./scc
|
||||
|
||||
test-scc:
|
||||
# windows: (Get-Content a.txt -Raw) -replace '\x1b\[[0-9;]*[a-zA-Z]', '' | Set-Content clean.txt
|
||||
# linux: sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' a.txt > clean.txt
|
||||
just clean
|
||||
just build-install
|
||||
just clean
|
||||
cbuild build -cscc
|
||||
|
||||
debug-scc:
|
||||
just clean
|
||||
just build-install
|
||||
just clean
|
||||
cbuild build -cscc --dev --record --dry-run
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef __STDCORE_H__
|
||||
#define __STDCORE_H__
|
||||
|
||||
#ifndef __NO_LINK_STDLIB
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#error "__NO_LINK_STDLIB"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,202 +0,0 @@
|
||||
# # vector_gdb.py
|
||||
# import gdb
|
||||
# import re
|
||||
|
||||
# class VectorPrinter:
|
||||
# """解析宏定义的 vector 结构体"""
|
||||
|
||||
# def __init__(self, val):
|
||||
# self.val = val
|
||||
|
||||
# def check_vector_type(self):
|
||||
# """验证是否为合法 vector 结构体"""
|
||||
# try:
|
||||
# # 检查是否包含 size/cap/data 字段
|
||||
# return all(self.val.type.has_key(field)
|
||||
# for field in ['size', 'cap', 'data'])
|
||||
# except gdb.error:
|
||||
# return False
|
||||
|
||||
# def get_array_view(self):
|
||||
# """将 data 字段转换为数组视图"""
|
||||
# if not self.check_vector_type():
|
||||
# return None
|
||||
|
||||
# cap = int(self.val['cap'])
|
||||
# data_ptr = self.val['data']
|
||||
|
||||
# if cap == 0 or data_ptr == 0:
|
||||
# return []
|
||||
|
||||
# # 构造数组类型 (例如 int[cap])
|
||||
# element_type = data_ptr.type.target()
|
||||
# array_type = element_type.array(cap - 1) # C 数组声明语法
|
||||
|
||||
# return data_ptr.cast(array_type.pointer()).dereference()
|
||||
|
||||
# def to_string(self):
|
||||
# if not self.check_vector_type():
|
||||
# return "Not a vector type"
|
||||
|
||||
# size = self.val['size']
|
||||
# cap = self.val['cap']
|
||||
# data = self.get_array_view()
|
||||
|
||||
# return (f"vector(size={size}, cap={cap}, data={data})")
|
||||
|
||||
# class VectorInfoCommand(gdb.Command):
|
||||
# """自定义命令:显示 vector 详细信息"""
|
||||
|
||||
# def __init__(self):
|
||||
# super(VectorInfoCommand, self).__init__("vector_info",
|
||||
# gdb.COMMAND_USER)
|
||||
|
||||
# def invoke(self, arg, from_tty):
|
||||
# val = gdb.parse_and_eval(arg)
|
||||
# printer = VectorPrinter(val)
|
||||
|
||||
# if not printer.check_vector_type():
|
||||
# print(f"'{arg}' is not a vector structure")
|
||||
# return
|
||||
|
||||
# size = int(val['size'])
|
||||
# cap = int(val['cap'])
|
||||
# data = printer.get_array_view()
|
||||
|
||||
# # 输出格式化信息
|
||||
# print(f"Vector {arg}:")
|
||||
# print(f"├─ Size: {size}")
|
||||
# print(f"├─ Capacity: {cap}")
|
||||
# print("└─ Data elements [0..{}]:".format(min(size, cap)-1))
|
||||
|
||||
# for i in range(min(size, cap)):
|
||||
# try:
|
||||
# print(f" [{i}]: {data[i]}")
|
||||
# except gdb.MemoryError:
|
||||
# print(f" [{i}]: <invalid memory>")
|
||||
|
||||
# def register_printers():
|
||||
# """注册自动类型识别"""
|
||||
# def vector_matcher(val):
|
||||
# return VectorPrinter(val).check_vector_type()
|
||||
|
||||
# # 使用 lambda 包装以动态创建 printer
|
||||
# gdb.pretty_printers.append(lambda val:
|
||||
# VectorPrinter(val) if vector_matcher(val) else None)
|
||||
|
||||
# # 注册命令和打印机
|
||||
# VectorInfoCommand()
|
||||
# register_printers()
|
||||
|
||||
# vector_gdb.py
|
||||
import gdb
|
||||
from gdb.printing import PrettyPrinter
|
||||
|
||||
class VectorPrinter:
|
||||
"""兼容新旧注册方式的最终方案"""
|
||||
|
||||
def __init__(self, val: gdb.Value):
|
||||
self.val:gdb.Value = val
|
||||
|
||||
def check_type(self) -> bool:
|
||||
"""类型检查(兼容匿名结构体)"""
|
||||
try:
|
||||
if self.val.type.code != gdb.TYPE_CODE_STRUCT:
|
||||
return False
|
||||
fields = self.val.type.fields()
|
||||
if not fields:
|
||||
return False
|
||||
exp = ['size', 'cap', 'data']
|
||||
for t in fields:
|
||||
if t.name in exp:
|
||||
exp.remove(t.name)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
except gdb.error:
|
||||
return False
|
||||
|
||||
def to_string(self):
|
||||
if not self.check_type():
|
||||
return "Not a vector"
|
||||
|
||||
return "vector({} size={}, cap={})".format(
|
||||
self.val.address,
|
||||
self.val['size'],
|
||||
self.val['cap'],
|
||||
)
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
def children(self):
|
||||
"""生成数组元素(关键改进点)"""
|
||||
if not self.check_type():
|
||||
return []
|
||||
|
||||
size = int(self.val['size'])
|
||||
cap = int(self.val['cap'])
|
||||
data_ptr = self.val['data']
|
||||
|
||||
if cap == 0 or data_ptr == 0:
|
||||
return []
|
||||
|
||||
# 使用 GDB 内置数组转换
|
||||
array = data_ptr.dereference()
|
||||
array = array.cast(data_ptr.type.target().array(cap - 1))
|
||||
|
||||
for i in range(size):
|
||||
# state = "<used>" if i < size else "<unused>"
|
||||
try:
|
||||
value = array[i]
|
||||
yield (f"[{i}] {value.type} {value.address}", value)
|
||||
except gdb.MemoryError:
|
||||
yield (f"[{i}]", "<invalid>")
|
||||
|
||||
# 注册方式一:传统append方法(您之前有效的方式)self
|
||||
def append_printer():
|
||||
gdb.pretty_printers.append(
|
||||
lambda val: VectorPrinter(val) if VectorPrinter(val).check_type() else None
|
||||
)
|
||||
|
||||
# 注册方式二:新版注册方法(备用方案)
|
||||
def register_new_printer():
|
||||
class VectorPrinterLocator(PrettyPrinter):
|
||||
def __init__(self):
|
||||
super().__init__("vector_printer")
|
||||
|
||||
def __call__(self, val):
|
||||
ret = VectorPrinter(val).check_type()
|
||||
print(f"ret {ret}, type {val.type}, {[(i.name, i.type) for i in val.type.fields()]}")
|
||||
return None
|
||||
|
||||
gdb.printing.register_pretty_printer(
|
||||
gdb.current_objfile(),
|
||||
VectorPrinterLocator()
|
||||
)
|
||||
|
||||
# 双重注册保证兼容性
|
||||
append_printer() # 保留您原来有效的方式
|
||||
# register_new_printer() # 添加新版注册
|
||||
|
||||
class VectorInfoCommand(gdb.Command):
|
||||
"""保持原有命令不变"""
|
||||
def __init__(self):
|
||||
super().__init__("vector_info", gdb.COMMAND_USER)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
val = gdb.parse_and_eval(arg)
|
||||
printer = VectorPrinter(val)
|
||||
|
||||
if not printer.check_type():
|
||||
print("Invalid vector")
|
||||
return
|
||||
|
||||
print("=== Vector Details ===")
|
||||
print("Size:", val['size'])
|
||||
print("Capacity:", val['cap'])
|
||||
print("Elements:")
|
||||
for name, value in printer.children():
|
||||
print(f" {name}: {value}")
|
||||
|
||||
VectorInfoCommand()
|
||||
@@ -1,54 +0,0 @@
|
||||
// vector.h
|
||||
#ifndef VECTOR_H
|
||||
#define VECTOR_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define vector_header(name, type) \
|
||||
struct { \
|
||||
size_t size; \
|
||||
size_t cap; \
|
||||
type *data; \
|
||||
} name \
|
||||
|
||||
#define vector_init(vec) \
|
||||
do { \
|
||||
(vec).size = 0, \
|
||||
(vec).cap = 0, \
|
||||
(vec).data = NULL; \
|
||||
} while(0)
|
||||
|
||||
#define vector_push(vec, value) \
|
||||
do { \
|
||||
if (vec.size >= vec.cap) { \
|
||||
int cap = vec.cap ? vec.cap * 2 : 8; \
|
||||
void* data = realloc(vec.data, cap * sizeof(*vec.data)); \
|
||||
if (!data) { \
|
||||
fprintf(stderr, "vector_push: realloc failed\n"); \
|
||||
exit(1); \
|
||||
} \
|
||||
(vec).cap = cap; \
|
||||
(vec).data = data; \
|
||||
} \
|
||||
(vec).data[(vec).size++] = value; \
|
||||
} while(0)
|
||||
|
||||
#define vector_pop(vec) \
|
||||
((vec).data[--(vec).size])
|
||||
|
||||
#define vector_at(vec, idx) \
|
||||
(((vec).data)[idx])
|
||||
|
||||
#define vector_idx(vec, ptr) \
|
||||
((ptr) - (vec).data)
|
||||
|
||||
#define vector_free(vec) \
|
||||
do { \
|
||||
free((vec).data); \
|
||||
(vec).data = NULL; \
|
||||
(vec).size = (vec).cap = 0; \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
65
libs/README.md
Normal file
65
libs/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# lexer
|
||||
|
||||
> (Lexical Analysis) 词法分析
|
||||
|
||||
# pproc
|
||||
|
||||
> (Preprocessor) 预处理器
|
||||
|
||||
# parser
|
||||
|
||||
> (Syntax analysis) 语法分析
|
||||
|
||||
## sema
|
||||
|
||||
> (Semantic Analysis) 语义分析
|
||||
|
||||
# ast
|
||||
|
||||
> (Abstract Syntax Tree) 抽象语法树
|
||||
|
||||
# ast2ir
|
||||
|
||||
> 抽象语法树到中间代码
|
||||
|
||||
# ir
|
||||
|
||||
> (Intermediate Representation) 中间代码标识
|
||||
|
||||
## cfg
|
||||
|
||||
> (Control Flow Graph) 控制流图
|
||||
|
||||
## hir
|
||||
|
||||
> (High-Level Intermediate Representation) 高级中间代码标识
|
||||
|
||||
## lir
|
||||
|
||||
> (Low-Level Intermediate Representation) 低级中间代码标识
|
||||
|
||||
## mir
|
||||
|
||||
> (Machine Intermediate Representation) 机器中间代码标识
|
||||
|
||||
# ir2mcode
|
||||
|
||||
> 中间代码到机器码
|
||||
|
||||
<!-- asm 汇编器 -->
|
||||
|
||||
# mcode
|
||||
|
||||
> (Machine Code) 机器码
|
||||
|
||||
# sccf
|
||||
|
||||
> 统一输出格式
|
||||
|
||||
# target
|
||||
|
||||
> 目标平台支持
|
||||
|
||||
## Windows Portable Executable Format
|
||||
[Windows Portable Executable Format](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#archive-library-file-format)
|
||||
[中文文档](https://learn.microsoft.com/zh-cn/windows/win32/debug/pe-format#archive-library-file-format)
|
||||
10
libs/argparse/cbuild.toml
Normal file
10
libs/argparse/cbuild.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "argparse"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
description = ""
|
||||
|
||||
dependencies = [{ name = "scc_core", path = "../../runtime/scc_core" }]
|
||||
# dependencies = []
|
||||
# features = {}
|
||||
# default_features = []
|
||||
248
libs/argparse/include/argparse.h
Normal file
248
libs/argparse/include/argparse.h
Normal file
@@ -0,0 +1,248 @@
|
||||
#ifndef __SCC_ARGPARSE_H__
|
||||
#define __SCC_ARGPARSE_H__
|
||||
|
||||
#include "optparse.h"
|
||||
#include <scc_core.h>
|
||||
|
||||
// TODO: 实现更多高级功能
|
||||
// 1. 支持子命令嵌套层级限制
|
||||
// 2. 支持选项分组显示(Grouping)
|
||||
// 3. 支持自动补全脚本生成(Bash/Zsh/Fish)
|
||||
// 4. 支持配置文件解析
|
||||
// 5. 支持国际化(i18n)
|
||||
|
||||
/// @brief 命令行参数约束
|
||||
typedef struct scc_argparse_spec scc_argparse_spec_t;
|
||||
/// @brief 命令行参数 eg. someting
|
||||
typedef struct scc_argparse_arg scc_argparse_arg_t;
|
||||
/// @brief 命令行参数选项 eg. -a --long
|
||||
typedef struct scc_argparse_opt scc_argparse_opt_t;
|
||||
/// @brief 命令集合
|
||||
typedef struct scc_argparse_cmd scc_argparse_cmd_t;
|
||||
|
||||
typedef SCC_VEC(scc_argparse_arg_t) scc_argparse_arg_vec_t;
|
||||
typedef SCC_VEC(scc_argparse_opt_t) scc_argparse_opt_vec_t;
|
||||
typedef SCC_VEC(scc_argparse_cmd_t) scc_argparse_cmd_vec_t;
|
||||
|
||||
typedef enum scc_argparse_val_type {
|
||||
SCC_ARGPARSE_VAL_TYPE_UNKNOWN,
|
||||
SCC_ARGPARSE_VAL_TYPE_STRING,
|
||||
SCC_ARGPARSE_VAL_TYPE_BOOL,
|
||||
SCC_ARGPARSE_VAL_TYPE_INT,
|
||||
SCC_ARGPARSE_VAL_TYPE_FLOAT,
|
||||
SCC_ARGPARSE_VAL_TYPE_ENUM,
|
||||
SCC_ARGPARSE_VAL_TYPE_LIST,
|
||||
SCC_ARGPARSE_VAL_TYPE_COUNT,
|
||||
} scc_argparse_val_type_t;
|
||||
|
||||
typedef enum scc_argparse_err {
|
||||
SCC_ARGPARSE_ERR_NONE,
|
||||
SCC_ARGPARSE_ERR_PNT_DEFAULT,
|
||||
SCC_ARGPARSE_ERR_UNKNOWN_ERR,
|
||||
SCC_ARGPARSE_ERR_INVALID_ARG,
|
||||
SCC_ARGPARSE_ERR_INVALID_VALUE,
|
||||
SCC_ARGPARSE_ERR_MISSING_ARG,
|
||||
SCC_ARGPARSE_ERR_MISSING_VALUE,
|
||||
SCC_ARGPARSE_ERR_UNKNOWN_ARG,
|
||||
SCC_ARGPARSE_ERR_UNKNOWN_VALUE,
|
||||
} scc_argparse_err_t;
|
||||
|
||||
typedef SCC_VEC(const char *) scc_argparse_list_t;
|
||||
|
||||
// 约束规范结构体
|
||||
struct scc_argparse_spec {
|
||||
scc_argparse_val_type_t value_type; // 值类型
|
||||
const char *raw_value;
|
||||
|
||||
// 存储位置
|
||||
union {
|
||||
cbool *bool_store; // 布尔值存储
|
||||
int *int_store; // 整数存储
|
||||
float *float_store; // 浮点数存储
|
||||
const char **str_store; // 字符串存储
|
||||
char **str_alloc_store; // 字符串存储(使用alloc,需要free)
|
||||
void **ptr_store; // 通用指针存储
|
||||
scc_argparse_list_t *vec_store; // 新增:指向字符串向量的指针
|
||||
} store;
|
||||
|
||||
// 枚举值约束
|
||||
struct {
|
||||
const char **values; // 枚举值数组
|
||||
int count; // 枚举值数量
|
||||
} choices;
|
||||
|
||||
// 其他约束标志
|
||||
cbool flag_required; // 是否必须
|
||||
cbool flag_store_as_count; // 是否存储为计数(如 -vvv 返回3)
|
||||
cbool flag_allow_empty; // 是否允许空字符串
|
||||
cbool flag_hidden; // 是否隐藏(不显示在帮助)
|
||||
cbool flag_takes_multiple; // 是否接受多个值(如 -I dir1 -I dir2)
|
||||
cbool flag_parse_complex; // 是否进行复杂解析(如 1K, 2M, 3G)
|
||||
};
|
||||
|
||||
struct scc_argparse_arg {
|
||||
scc_argparse_spec_t spec;
|
||||
const char *name;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
struct scc_argparse_opt {
|
||||
scc_argparse_spec_t spec;
|
||||
char short_name;
|
||||
const char *long_name;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
struct scc_argparse_cmd {
|
||||
const char *name;
|
||||
const char *description;
|
||||
scc_argparse_arg_vec_t args;
|
||||
scc_argparse_opt_vec_t opts;
|
||||
scc_argparse_cmd_vec_t subcmds;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SCC_ARGPARSE_LANG_EN,
|
||||
SCC_ARGPARSE_LANG_ZH,
|
||||
} scc_argparse_lang_t;
|
||||
|
||||
typedef struct {
|
||||
const char *prog_name;
|
||||
const char *version;
|
||||
const char *description;
|
||||
const char *epilog;
|
||||
scc_argparse_cmd_t root_cmd;
|
||||
scc_argparse_lang_t lang;
|
||||
cbool need_help;
|
||||
cbool need_version;
|
||||
cbool need_debug;
|
||||
cbool need_error_add_help;
|
||||
cbool need_error_add_usage;
|
||||
} scc_argparse_t;
|
||||
|
||||
typedef SCC_VEC(scc_optparse_opt_t) scc_optparse_opt_vec_t;
|
||||
|
||||
typedef struct {
|
||||
scc_argparse_cmd_t *current_cmd; // 当前正在解析的命令
|
||||
scc_optparse_t optparse; // 底层解析器
|
||||
scc_optparse_opt_vec_t opts; // 当前命令的选项列表
|
||||
scc_optparse_result_t result; // 底层解析结果
|
||||
const void *err_ctx; // 错误上下文(期待的参数或者当前解析的参数)
|
||||
int positional_index; // 当前处理的位置参数索引
|
||||
cbool parsing_done; // 是否已完成解析
|
||||
} scc_argparse_context_t;
|
||||
|
||||
void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
|
||||
const char *description);
|
||||
scc_argparse_cmd_t *scc_argparse_get_root(scc_argparse_t *parser);
|
||||
void scc_argparse_drop(scc_argparse_t *parser);
|
||||
int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv);
|
||||
|
||||
void scc_argparse_cmd_init(scc_argparse_cmd_t *cmd, const char *name,
|
||||
const char *description);
|
||||
void scc_argparse_cmd_drop(scc_argparse_cmd_t *cmd);
|
||||
|
||||
void scc_argparse_cmd_add_arg(scc_argparse_cmd_t *cmd,
|
||||
const scc_argparse_arg_t *arg);
|
||||
void scc_argparse_cmd_add_opt(scc_argparse_cmd_t *cmd,
|
||||
const scc_argparse_opt_t *opt);
|
||||
void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd,
|
||||
const scc_argparse_cmd_t *subcmd);
|
||||
|
||||
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd);
|
||||
void scc_argparse_print_error(scc_argparse_context_t *ctx,
|
||||
scc_argparse_t *parser, scc_argparse_err_t err);
|
||||
|
||||
static inline void scc_argparse_spec_init(scc_argparse_spec_t *spec) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_STRING;
|
||||
spec->raw_value = nullptr;
|
||||
spec->store.ptr_store = nullptr;
|
||||
|
||||
spec->choices.count = 0;
|
||||
spec->choices.values = nullptr;
|
||||
|
||||
spec->flag_required = false;
|
||||
spec->flag_store_as_count = false;
|
||||
spec->flag_allow_empty = false;
|
||||
spec->flag_hidden = false;
|
||||
spec->flag_takes_multiple = false;
|
||||
spec->flag_parse_complex = false;
|
||||
}
|
||||
|
||||
static inline void scc_argparse_opt_init(scc_argparse_opt_t *opt,
|
||||
char short_name, const char *long_name,
|
||||
const char *desc) {
|
||||
opt->short_name = short_name;
|
||||
opt->long_name = long_name;
|
||||
opt->description = desc;
|
||||
scc_argparse_spec_init(&opt->spec);
|
||||
}
|
||||
|
||||
static inline void scc_argparse_arg_init(scc_argparse_arg_t *arg,
|
||||
const char *name, const char *desc) {
|
||||
arg->name = name;
|
||||
arg->description = desc;
|
||||
scc_argparse_spec_init(&arg->spec);
|
||||
}
|
||||
|
||||
static inline void scc_argparse_spec_setup_bool(scc_argparse_spec_t *spec,
|
||||
cbool *store) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_BOOL;
|
||||
spec->store.bool_store = store;
|
||||
}
|
||||
|
||||
static inline void scc_argparse_spec_setup_count(scc_argparse_spec_t *spec,
|
||||
int *store) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_COUNT;
|
||||
spec->flag_store_as_count = true; // 自动设为计数模式
|
||||
spec->store.int_store = store;
|
||||
}
|
||||
|
||||
static inline void scc_argparse_spec_setup_string(scc_argparse_spec_t *spec,
|
||||
const char **store) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_STRING;
|
||||
spec->store.str_store = store;
|
||||
}
|
||||
|
||||
static inline void scc_argparse_spec_setup_int(scc_argparse_spec_t *spec,
|
||||
int *store) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_INT;
|
||||
spec->store.int_store = store;
|
||||
}
|
||||
|
||||
static inline void scc_argparse_spec_setup_float(scc_argparse_spec_t *spec,
|
||||
float *store) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_FLOAT;
|
||||
spec->store.float_store = store;
|
||||
}
|
||||
|
||||
static inline void scc_argparse_spec_setup_choices(scc_argparse_spec_t *spec,
|
||||
const char **values,
|
||||
int count, int *store) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_ENUM;
|
||||
spec->choices.values = values;
|
||||
spec->choices.count = count;
|
||||
spec->store.int_store = store;
|
||||
}
|
||||
|
||||
// 添加设置列表的辅助函数(内联)
|
||||
static inline void scc_argparse_spec_setup_list(scc_argparse_spec_t *spec,
|
||||
scc_argparse_list_t *vec) {
|
||||
spec->value_type = SCC_ARGPARSE_VAL_TYPE_LIST;
|
||||
spec->store.vec_store = vec;
|
||||
// 自动设置 flag_takes_multiple 为 true,因为列表需要多个值
|
||||
spec->flag_takes_multiple = true;
|
||||
}
|
||||
|
||||
#define SCC_ARGPARSE_MACRO_SETTER(attr) \
|
||||
static inline void scc_argparse_spec_set_##attr(scc_argparse_spec_t *spec, \
|
||||
cbool flag) { \
|
||||
spec->flag_##attr = flag; \
|
||||
}
|
||||
SCC_ARGPARSE_MACRO_SETTER(required)
|
||||
SCC_ARGPARSE_MACRO_SETTER(allow_empty)
|
||||
SCC_ARGPARSE_MACRO_SETTER(hidden)
|
||||
SCC_ARGPARSE_MACRO_SETTER(takes_multiple)
|
||||
SCC_ARGPARSE_MACRO_SETTER(parse_complex)
|
||||
|
||||
#endif /* __SCC_ARGPARSE_H__ */
|
||||
59
libs/argparse/include/optparse.h
Normal file
59
libs/argparse/include/optparse.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef __SCC_OPTPARSER_H__
|
||||
#define __SCC_OPTPARSER_H__
|
||||
|
||||
#ifndef nullptr
|
||||
#define nullptr ((void *)0)
|
||||
#endif
|
||||
|
||||
typedef struct scc_optparse_opt {
|
||||
char prefix;
|
||||
char short_name;
|
||||
const char *long_name;
|
||||
void (*handle)(void *user_data);
|
||||
void *user_data;
|
||||
int min_args;
|
||||
int max_args;
|
||||
} scc_optparse_opt_t;
|
||||
|
||||
#define SCC_OPTPARSE_OPT(prefix, short_name, long_name, min_args, max_args) \
|
||||
{prefix, short_name, long_name, 0, 0, min_args, max_args}
|
||||
#define SCC_OPTPARSE_OPT_END() {0}
|
||||
|
||||
typedef enum scc_optparse_error {
|
||||
SCC_OPT_ERROR_NONE,
|
||||
SCC_OPT_ERROR_NOT_FOUND_LONG_ARG,
|
||||
SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG,
|
||||
SCC_OPT_ERROR_NOT_ENOUGH_ARGS,
|
||||
SCC_OPT_ERROR_TOO_MANY_ARGS,
|
||||
} scc_optparse_error_t;
|
||||
|
||||
typedef struct {
|
||||
const scc_optparse_opt_t *opt;
|
||||
int count; // check for min_args <= count <= max_args
|
||||
int arg_pos; // for argv pos
|
||||
int opt_pos; // for short pos
|
||||
} scc_optparse_state_t;
|
||||
|
||||
typedef struct scc_optparse_result {
|
||||
const scc_optparse_opt_t *opt;
|
||||
const char *value;
|
||||
int error;
|
||||
const char *raw_arg;
|
||||
} scc_optparse_result_t;
|
||||
|
||||
typedef struct {
|
||||
int argc;
|
||||
const char **argv;
|
||||
const scc_optparse_opt_t *opts;
|
||||
int handle_positional;
|
||||
int greedy_mode;
|
||||
scc_optparse_state_t current;
|
||||
} scc_optparse_t;
|
||||
|
||||
void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv);
|
||||
void scc_optparse_drop(scc_optparse_t *parser);
|
||||
void scc_optparse_set(scc_optparse_t *parser, const scc_optparse_opt_t *opts);
|
||||
void scc_optparse_reset(scc_optparse_t *parser);
|
||||
int scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res);
|
||||
|
||||
#endif /* __SCC_OPTPARSER_H__ */
|
||||
341
libs/argparse/src/argparse.c
Normal file
341
libs/argparse/src/argparse.c
Normal file
@@ -0,0 +1,341 @@
|
||||
#include <argparse.h>
|
||||
|
||||
void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
|
||||
const char *description) {
|
||||
parser->prog_name = program_name;
|
||||
parser->version = "0.1.0";
|
||||
parser->description = description;
|
||||
parser->epilog = nullptr;
|
||||
|
||||
parser->lang = SCC_ARGPARSE_LANG_EN;
|
||||
parser->need_help = true;
|
||||
parser->need_version = true;
|
||||
parser->need_debug = false;
|
||||
parser->need_error_add_help = false;
|
||||
parser->need_error_add_usage = true;
|
||||
scc_argparse_cmd_init(&parser->root_cmd, parser->prog_name,
|
||||
parser->description);
|
||||
}
|
||||
|
||||
scc_argparse_cmd_t *scc_argparse_get_root(scc_argparse_t *parser) {
|
||||
return &parser->root_cmd;
|
||||
}
|
||||
|
||||
void scc_argparse_drop(scc_argparse_t *parser) {
|
||||
scc_argparse_cmd_drop(&parser->root_cmd);
|
||||
}
|
||||
|
||||
static inline scc_argparse_cmd_t *is_subcommand(scc_argparse_cmd_t *cmd,
|
||||
const char *name) {
|
||||
if (!scc_vec_size(cmd->subcmds)) {
|
||||
return nullptr;
|
||||
}
|
||||
scc_vec_foreach(cmd->subcmds, i) {
|
||||
scc_argparse_cmd_t *subcmd = &scc_vec_at(cmd->subcmds, i);
|
||||
if (scc_strcmp(subcmd->name, name) == 0) {
|
||||
return subcmd;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline void prepare_cmd(scc_optparse_t *optparse,
|
||||
scc_optparse_opt_vec_t *opts,
|
||||
scc_argparse_cmd_t *cmd) {
|
||||
scc_vec_free(*opts);
|
||||
scc_vec_init(*opts);
|
||||
scc_vec_foreach(cmd->opts, i) {
|
||||
scc_argparse_opt_t *opt = &scc_vec_at(cmd->opts, i);
|
||||
int min_args = 0;
|
||||
int max_args = 0;
|
||||
|
||||
// 根据选项类型推断 min_args 和 max_args
|
||||
switch (opt->spec.value_type) {
|
||||
case SCC_ARGPARSE_VAL_TYPE_STRING:
|
||||
case SCC_ARGPARSE_VAL_TYPE_INT:
|
||||
case SCC_ARGPARSE_VAL_TYPE_FLOAT:
|
||||
min_args = 1;
|
||||
max_args = 1;
|
||||
break;
|
||||
case SCC_ARGPARSE_VAL_TYPE_BOOL:
|
||||
min_args = 0;
|
||||
max_args = 0;
|
||||
break;
|
||||
case SCC_ARGPARSE_VAL_TYPE_COUNT:
|
||||
min_args = 0;
|
||||
max_args = 0;
|
||||
break;
|
||||
case SCC_ARGPARSE_VAL_TYPE_LIST: // 列表类型
|
||||
min_args = 1;
|
||||
max_args = 65535; // FIXME maybe INT_MAX ?
|
||||
break;
|
||||
case SCC_ARGPARSE_VAL_TYPE_ENUM:
|
||||
min_args = 1;
|
||||
max_args = 1;
|
||||
break;
|
||||
default:
|
||||
min_args = 0;
|
||||
max_args = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
scc_vec_push(*opts, ((scc_optparse_opt_t){
|
||||
.prefix = '-',
|
||||
.short_name = opt->short_name,
|
||||
.long_name = opt->long_name,
|
||||
.min_args = min_args,
|
||||
.max_args = max_args,
|
||||
.user_data = opt,
|
||||
}));
|
||||
}
|
||||
scc_vec_push(*opts, (scc_optparse_opt_t)SCC_OPTPARSE_OPT_END());
|
||||
scc_optparse_set(optparse, opts->data);
|
||||
}
|
||||
|
||||
static void push_help(scc_argparse_cmd_t *cmd) {
|
||||
if (cmd == nullptr) {
|
||||
return;
|
||||
}
|
||||
scc_vec_push(cmd->opts, ((scc_argparse_opt_t){
|
||||
.short_name = 'h',
|
||||
.long_name = "help",
|
||||
.description = 0,
|
||||
.spec.value_type = SCC_ARGPARSE_VAL_TYPE_BOOL,
|
||||
}));
|
||||
scc_vec_foreach(cmd->subcmds, i) {
|
||||
push_help(&scc_vec_at(cmd->subcmds, i));
|
||||
}
|
||||
}
|
||||
|
||||
static void init_context(scc_argparse_context_t *ctx, scc_argparse_t *parser,
|
||||
int argc, const char **argv) {
|
||||
ctx->current_cmd = scc_argparse_get_root(parser);
|
||||
if (parser->need_help) {
|
||||
push_help(ctx->current_cmd);
|
||||
}
|
||||
scc_optparse_init(&ctx->optparse, argc, argv);
|
||||
scc_vec_init(ctx->opts);
|
||||
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
|
||||
ctx->positional_index = 0;
|
||||
ctx->parsing_done = false;
|
||||
}
|
||||
|
||||
static int transite_error(scc_argparse_context_t *ctx) {
|
||||
scc_argparse_err_t error = SCC_ARGPARSE_ERR_NONE;
|
||||
switch (ctx->result.error) {
|
||||
case SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG:
|
||||
case SCC_OPT_ERROR_NOT_FOUND_LONG_ARG:
|
||||
error = SCC_ARGPARSE_ERR_UNKNOWN_ARG;
|
||||
break;
|
||||
case SCC_OPT_ERROR_NOT_ENOUGH_ARGS:
|
||||
error = SCC_ARGPARSE_ERR_MISSING_VALUE;
|
||||
break;
|
||||
case SCC_OPT_ERROR_TOO_MANY_ARGS:
|
||||
error = SCC_ARGPARSE_ERR_INVALID_ARG;
|
||||
break;
|
||||
default:
|
||||
error = SCC_ARGPARSE_ERR_UNKNOWN_ERR;
|
||||
break;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int check_choices(const scc_argparse_spec_t *spec, const char *value,
|
||||
int *out_index) {
|
||||
if (!spec->choices.values || spec->choices.count == 0) {
|
||||
return SCC_ARGPARSE_ERR_NONE;
|
||||
}
|
||||
|
||||
if (value == nullptr) {
|
||||
return SCC_ARGPARSE_ERR_MISSING_VALUE;
|
||||
}
|
||||
if (out_index)
|
||||
*out_index = -1;
|
||||
|
||||
for (int i = 0; i < spec->choices.count; i += 1) {
|
||||
if (scc_strcmp(value, spec->choices.values[i]) == 0) {
|
||||
if (out_index)
|
||||
*out_index = i;
|
||||
return SCC_ARGPARSE_ERR_NONE;
|
||||
}
|
||||
}
|
||||
return SCC_ARGPARSE_ERR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static int validate_and_cleanup(scc_argparse_context_t *ctx,
|
||||
scc_argparse_t *parser, int errcode) {
|
||||
if (errcode != SCC_ARGPARSE_ERR_NONE) {
|
||||
goto END;
|
||||
}
|
||||
// 检查必需的位置参数
|
||||
scc_vec_foreach(ctx->current_cmd->args, i) {
|
||||
scc_argparse_arg_t *arg = &scc_vec_at(ctx->current_cmd->args, i);
|
||||
if (arg->spec.flag_required) {
|
||||
// 检查是否已存储(非空)
|
||||
if (arg->spec.store.str_store == nullptr ||
|
||||
*arg->spec.store.str_store == nullptr) {
|
||||
errcode = SCC_ARGPARSE_ERR_MISSING_ARG;
|
||||
ctx->err_ctx = arg->name;
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
}
|
||||
scc_vec_foreach(ctx->current_cmd->opts, i) {
|
||||
scc_argparse_opt_t *opt = &scc_vec_at(ctx->current_cmd->opts, i);
|
||||
if (opt->spec.flag_required) {
|
||||
int provided = 0;
|
||||
if (opt->spec.flag_store_as_count) {
|
||||
provided = (opt->spec.store.int_store &&
|
||||
*opt->spec.store.int_store > 0);
|
||||
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
|
||||
provided =
|
||||
(opt->spec.store.bool_store && *opt->spec.store.bool_store);
|
||||
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
|
||||
provided = (opt->spec.store.vec_store &&
|
||||
scc_vec_size(*opt->spec.store.vec_store) > 0);
|
||||
} else {
|
||||
provided = (opt->spec.store.str_store &&
|
||||
*opt->spec.store.str_store != nullptr);
|
||||
}
|
||||
if (!provided) {
|
||||
errcode = SCC_ARGPARSE_ERR_MISSING_ARG;
|
||||
goto END;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
END:
|
||||
if (errcode != SCC_ARGPARSE_ERR_NONE) {
|
||||
scc_argparse_print_error(ctx, parser, errcode);
|
||||
}
|
||||
scc_vec_free(ctx->opts);
|
||||
scc_optparse_drop(&ctx->optparse);
|
||||
return errcode;
|
||||
}
|
||||
|
||||
static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
|
||||
scc_argparse_opt_t *opt = (scc_argparse_opt_t *)ctx->result.opt->user_data;
|
||||
|
||||
if (parser->need_help && scc_strcmp(opt->long_name, "help") == 0) {
|
||||
scc_argparse_print_help(parser, ctx->current_cmd);
|
||||
ctx->parsing_done = true;
|
||||
return SCC_ARGPARSE_ERR_PNT_DEFAULT;
|
||||
}
|
||||
|
||||
if (parser->need_version && scc_strcmp(opt->long_name, "version") == 0) {
|
||||
// TODO default version print
|
||||
}
|
||||
|
||||
if (opt->spec.flag_store_as_count) {
|
||||
(*opt->spec.store.int_store)++;
|
||||
}
|
||||
|
||||
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
|
||||
*opt->spec.store.bool_store = true;
|
||||
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) {
|
||||
return check_choices(&opt->spec, ctx->result.value,
|
||||
opt->spec.store.int_store);
|
||||
}
|
||||
|
||||
if (!ctx->result.value) {
|
||||
return SCC_ARGPARSE_ERR_NONE;
|
||||
}
|
||||
opt->spec.raw_value = ctx->result.value;
|
||||
|
||||
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
|
||||
scc_vec_push(*opt->spec.store.vec_store, ctx->result.value);
|
||||
} else {
|
||||
*opt->spec.store.str_store = ctx->result.value;
|
||||
}
|
||||
|
||||
return SCC_ARGPARSE_ERR_NONE;
|
||||
}
|
||||
|
||||
static int handle_positional_arg(scc_argparse_context_t *ctx,
|
||||
scc_argparse_t *parser) {
|
||||
(void)parser; // TODO
|
||||
scc_argparse_cmd_t *subcmd =
|
||||
is_subcommand(ctx->current_cmd, ctx->result.value);
|
||||
if (subcmd != nullptr) {
|
||||
ctx->current_cmd = subcmd;
|
||||
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
|
||||
return SCC_ARGPARSE_ERR_NONE;
|
||||
}
|
||||
|
||||
if (ctx->positional_index < (int)scc_vec_size(ctx->current_cmd->args)) {
|
||||
scc_argparse_arg_t *arg =
|
||||
&scc_vec_at(ctx->current_cmd->args, ctx->positional_index);
|
||||
*arg->spec.store.str_store = ctx->result.value;
|
||||
arg->spec.raw_value = ctx->result.value;
|
||||
ctx->positional_index++;
|
||||
} else {
|
||||
ctx->parsing_done = true;
|
||||
return SCC_ARGPARSE_ERR_UNKNOWN_ARG;
|
||||
}
|
||||
return SCC_ARGPARSE_ERR_NONE;
|
||||
}
|
||||
|
||||
int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv) {
|
||||
scc_argparse_context_t ctx = {0};
|
||||
init_context(&ctx, parser, argc, argv); // 初始化上下文
|
||||
int errcode = SCC_ARGPARSE_ERR_NONE;
|
||||
|
||||
while (!ctx.parsing_done && errcode == SCC_ARGPARSE_ERR_NONE &&
|
||||
scc_optparse_parse(&ctx.optparse, &ctx.result)) {
|
||||
if (parser->need_debug) {
|
||||
scc_printf("debug:[%c:%s:%d] %s\n",
|
||||
ctx.result.opt ? ctx.result.opt->short_name : '-',
|
||||
ctx.result.opt ? ctx.result.opt->long_name : "--",
|
||||
ctx.result.error,
|
||||
ctx.result.value ? ctx.result.value : "<nullptr>");
|
||||
}
|
||||
|
||||
if (ctx.result.error) {
|
||||
errcode = transite_error(&ctx);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.result.opt != nullptr) {
|
||||
errcode = handle_option(&ctx, parser);
|
||||
} else if (ctx.result.value != nullptr) {
|
||||
errcode = handle_positional_arg(&ctx, parser);
|
||||
} else {
|
||||
UNREACHABLE(); // 不应到达此处
|
||||
}
|
||||
}
|
||||
|
||||
return validate_and_cleanup(&ctx, parser, errcode);
|
||||
}
|
||||
|
||||
void scc_argparse_cmd_init(scc_argparse_cmd_t *cmd, const char *name,
|
||||
const char *description) {
|
||||
cmd->name = name ? name : "cmd_name";
|
||||
cmd->description = description ? description : "cmd_description";
|
||||
scc_vec_init(cmd->args);
|
||||
scc_vec_init(cmd->opts);
|
||||
scc_vec_init(cmd->subcmds);
|
||||
}
|
||||
|
||||
void scc_argparse_cmd_drop(scc_argparse_cmd_t *cmd) {
|
||||
scc_vec_free(cmd->args);
|
||||
scc_vec_free(cmd->opts);
|
||||
scc_vec_foreach(cmd->subcmds, i) {
|
||||
scc_argparse_cmd_drop(&scc_vec_at(cmd->subcmds, i));
|
||||
}
|
||||
scc_vec_free(cmd->subcmds);
|
||||
}
|
||||
|
||||
void scc_argparse_cmd_add_arg(scc_argparse_cmd_t *cmd,
|
||||
const scc_argparse_arg_t *arg) {
|
||||
scc_vec_push(cmd->args, *arg);
|
||||
}
|
||||
|
||||
void scc_argparse_cmd_add_opt(scc_argparse_cmd_t *cmd,
|
||||
const scc_argparse_opt_t *opt) {
|
||||
scc_vec_push(cmd->opts, *opt);
|
||||
}
|
||||
|
||||
void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd,
|
||||
const scc_argparse_cmd_t *subcmd) {
|
||||
scc_vec_push(cmd->subcmds, *subcmd);
|
||||
}
|
||||
266
libs/argparse/src/argparse_print.c
Normal file
266
libs/argparse/src/argparse_print.c
Normal file
@@ -0,0 +1,266 @@
|
||||
#include <argparse.h>
|
||||
|
||||
// 帮助信息处理函数
|
||||
enum {
|
||||
ARGPARSE_USAGE,
|
||||
ARGPARSE_OPTION,
|
||||
ARGPARSE_COMMAND,
|
||||
ARGPARSE_ARGUMENT,
|
||||
|
||||
ARGPARSE_SHOW_ARG,
|
||||
ARGPARSE_SHOW_OPT,
|
||||
ARGPARSE_SHOW_CMD,
|
||||
ARGPARSE_SHOW_HELP_MSG,
|
||||
|
||||
ARGPRASE_USING_HELP_HINT,
|
||||
ARGPARSE_UNKNOWN_ARGUMENT,
|
||||
ARGPARSE_INVALID_VALUE_FOR_OPTION,
|
||||
ARGPARSE_OPTION_MISSING_VALUE,
|
||||
ARGPARSE_DID_YOU_MEAN,
|
||||
};
|
||||
|
||||
static const char *fmt_en[] = {
|
||||
[ARGPARSE_USAGE] = "Usage: %s",
|
||||
[ARGPARSE_OPTION] = " [Options]",
|
||||
[ARGPARSE_COMMAND] = " [Commands]",
|
||||
|
||||
[ARGPARSE_SHOW_ARG] = "Arguments:\n",
|
||||
[ARGPARSE_SHOW_OPT] = "Options:\n",
|
||||
[ARGPARSE_SHOW_CMD] = "Commands:\n",
|
||||
[ARGPARSE_SHOW_HELP_MSG] = "Show this help message and exit",
|
||||
|
||||
[ARGPRASE_USING_HELP_HINT] =
|
||||
"Using '-h' or '--help' to get more information\n",
|
||||
[ARGPARSE_UNKNOWN_ARGUMENT] = "Unknown argument '%s'\n",
|
||||
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "Invalid value for option '%s'\n",
|
||||
[ARGPARSE_OPTION_MISSING_VALUE] = "Option '%s' missing value\n",
|
||||
[ARGPARSE_DID_YOU_MEAN] = "Did you mean: '%s'?\n",
|
||||
};
|
||||
|
||||
static const char *fmt_zh[] = {
|
||||
[ARGPARSE_USAGE] = "用法: %s",
|
||||
[ARGPARSE_OPTION] = " [选项]",
|
||||
[ARGPARSE_COMMAND] = " [命令]",
|
||||
|
||||
[ARGPARSE_SHOW_ARG] = "参数:\n",
|
||||
[ARGPARSE_SHOW_OPT] = "选项:\n",
|
||||
[ARGPARSE_SHOW_CMD] = "命令:\n",
|
||||
[ARGPARSE_SHOW_HELP_MSG] = "显示帮助信息并退出",
|
||||
|
||||
[ARGPRASE_USING_HELP_HINT] = "使用 '-h' 或者 '--help' 获取更多信息\n",
|
||||
[ARGPARSE_UNKNOWN_ARGUMENT] = "未知的参数 '%s'\n",
|
||||
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "无效的选项值 '%s'\n",
|
||||
[ARGPARSE_OPTION_MISSING_VALUE] = "选项 '%s' 缺少值\n",
|
||||
[ARGPARSE_DID_YOU_MEAN] = "您是不是想要输入: '%s'?\n",
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 计算两个字符串的编辑距离(Levenshtein距离)
|
||||
* @param s1 字符串1(非空,以'\0'结尾)
|
||||
* @param s2 字符串2(非空,以'\0'结尾)
|
||||
* @return 编辑距离,内存分配失败返回 -1
|
||||
*/
|
||||
static int scc_levenshtein(const char *s1, const char *s2) {
|
||||
usize len1 = scc_strlen(s1);
|
||||
usize len2 = scc_strlen(s2);
|
||||
|
||||
// 保证 len1 >= len2 以减少内存占用
|
||||
if (len1 < len2) {
|
||||
const char *tmp = s1;
|
||||
s1 = s2;
|
||||
s2 = tmp;
|
||||
usize tlen = len1;
|
||||
len1 = len2;
|
||||
len2 = tlen;
|
||||
}
|
||||
|
||||
// 分配滚动数组(只需 len2+1 个 int)
|
||||
int *dp = (int *)scc_malloc(sizeof(int) * (len2 + 1));
|
||||
if (!dp)
|
||||
return -1;
|
||||
|
||||
// 初始化第0行
|
||||
for (usize j = 0; j <= len2; ++j)
|
||||
dp[j] = (int)j;
|
||||
|
||||
for (usize i = 1; i <= len1; ++i) {
|
||||
int prev = dp[0];
|
||||
dp[0] = (int)i;
|
||||
for (usize j = 1; j <= len2; ++j) {
|
||||
int temp = dp[j];
|
||||
int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
|
||||
dp[j] = scc_min(scc_min(dp[j] + 1, dp[j - 1] + 1), prev + cost);
|
||||
prev = temp;
|
||||
}
|
||||
}
|
||||
|
||||
int dist = dp[len2];
|
||||
scc_free(dp);
|
||||
return dist;
|
||||
}
|
||||
|
||||
static void print_usage(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
|
||||
const char **lines =
|
||||
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
|
||||
scc_printf(lines[ARGPARSE_USAGE],
|
||||
cmd->name ? cmd->name : parser->prog_name);
|
||||
if (scc_vec_size(cmd->opts) > 0)
|
||||
scc_printf(lines[ARGPARSE_OPTION]);
|
||||
scc_vec_foreach(cmd->args, i) {
|
||||
scc_argparse_arg_t *arg = &scc_vec_at(cmd->args, i);
|
||||
scc_printf(arg->spec.flag_required ? " <%s>" : " [%s]", arg->name);
|
||||
}
|
||||
if (scc_vec_size(cmd->subcmds) > 0)
|
||||
scc_printf(lines[ARGPARSE_COMMAND]);
|
||||
scc_printf("\n\n");
|
||||
}
|
||||
|
||||
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
|
||||
const char **lines =
|
||||
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
|
||||
|
||||
print_usage(parser, cmd);
|
||||
|
||||
if (cmd->description)
|
||||
scc_printf("%s\n\n", cmd->description);
|
||||
|
||||
if (scc_vec_size(cmd->args) > 0) {
|
||||
scc_printf(lines[ARGPARSE_SHOW_ARG]);
|
||||
scc_vec_foreach(cmd->args, i) {
|
||||
scc_argparse_arg_t *arg = &scc_vec_at(cmd->args, i);
|
||||
scc_printf(" %-20s %s", arg->name,
|
||||
arg->description ? arg->description : "");
|
||||
// if (!arg->spec.required && arg->spec.flags.has_default)
|
||||
// scc_printf(" (default: %s)", arg->spec.default_value);
|
||||
scc_printf("\n");
|
||||
}
|
||||
scc_printf("\n");
|
||||
}
|
||||
|
||||
if (scc_vec_size(cmd->opts) > 0) {
|
||||
scc_printf(lines[ARGPARSE_SHOW_OPT]);
|
||||
scc_vec_foreach(cmd->opts, i) {
|
||||
scc_argparse_opt_t *opt = &scc_vec_at(cmd->opts, i);
|
||||
if (opt->spec.flag_hidden)
|
||||
continue;
|
||||
if (opt->short_name == 'h')
|
||||
opt->description = lines[ARGPARSE_SHOW_HELP_MSG];
|
||||
scc_str_t buf;
|
||||
scc_str_init(&buf);
|
||||
int pos = 0;
|
||||
if (opt->short_name) {
|
||||
scc_str_append_ch(&buf, '-');
|
||||
scc_str_append_ch(&buf, opt->short_name);
|
||||
if (opt->long_name) {
|
||||
scc_str_append_ch(&buf, ',');
|
||||
scc_str_append_ch(&buf, ' ');
|
||||
}
|
||||
}
|
||||
if (opt->long_name) {
|
||||
scc_str_append_ch(&buf, '-');
|
||||
scc_str_append_ch(&buf, '-');
|
||||
scc_str_append_cstr(&buf, opt->long_name,
|
||||
scc_strlen(opt->long_name));
|
||||
}
|
||||
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) {
|
||||
scc_str_append_ch(&buf, '=');
|
||||
scc_str_append_ch(&buf, '<');
|
||||
for (int j = 0; j < opt->spec.choices.count; j += 1) {
|
||||
if (opt->spec.choices.values[j] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (j != 0) {
|
||||
scc_str_append_ch(&buf, '|');
|
||||
}
|
||||
|
||||
scc_str_append_cstr(
|
||||
&buf, opt->spec.choices.values[j],
|
||||
scc_strlen(opt->spec.choices.values[j]));
|
||||
}
|
||||
scc_str_append_ch(&buf, '>');
|
||||
}
|
||||
scc_printf(" %-25s %s", scc_str_as_cstr(&buf),
|
||||
opt->description ? opt->description : "");
|
||||
// if (opt->spec.default_value)
|
||||
// scc_printf(" (default: %s)", opt->spec.default_value);
|
||||
scc_printf("\n");
|
||||
}
|
||||
scc_printf("\n");
|
||||
}
|
||||
|
||||
if (scc_vec_size(cmd->subcmds) > 0) {
|
||||
scc_printf(lines[ARGPARSE_SHOW_CMD]);
|
||||
scc_vec_foreach(cmd->subcmds, i) {
|
||||
scc_argparse_cmd_t *sub = &scc_vec_at(cmd->subcmds, i);
|
||||
scc_printf(" %-20s %s\n", sub->name,
|
||||
sub->description ? sub->description : "");
|
||||
}
|
||||
scc_printf("\n");
|
||||
}
|
||||
if (parser->epilog)
|
||||
scc_printf("%s\n", parser->epilog);
|
||||
}
|
||||
|
||||
const char *scc_argparse_find_similar_arg(scc_argparse_cmd_t *cmd,
|
||||
const char *arg) {
|
||||
if (arg == nullptr || cmd == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0') {
|
||||
// opt arg
|
||||
scc_vec_foreach(cmd->opts, i) {
|
||||
if (scc_levenshtein(arg + 2, scc_vec_at(cmd->opts, i).long_name) <=
|
||||
2) {
|
||||
return scc_vec_at(cmd->opts, i).long_name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// subcmd arg
|
||||
scc_vec_foreach(cmd->subcmds, i) {
|
||||
if (scc_levenshtein(arg, scc_vec_at(cmd->subcmds, i).name) <= 2) {
|
||||
return scc_vec_at(cmd->subcmds, i).name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scc_argparse_print_error(scc_argparse_context_t *ctx,
|
||||
scc_argparse_t *parser, scc_argparse_err_t err) {
|
||||
const char **lines =
|
||||
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
|
||||
switch (err) {
|
||||
case SCC_ARGPARSE_ERR_INVALID_ARG:
|
||||
case SCC_ARGPARSE_ERR_INVALID_VALUE:
|
||||
scc_printf(lines[ARGPARSE_INVALID_VALUE_FOR_OPTION], ctx->err_ctx);
|
||||
break;
|
||||
case SCC_ARGPARSE_ERR_MISSING_ARG:
|
||||
case SCC_ARGPARSE_ERR_MISSING_VALUE:
|
||||
scc_printf(lines[ARGPARSE_OPTION_MISSING_VALUE], ctx->err_ctx);
|
||||
break;
|
||||
case SCC_ARGPARSE_ERR_UNKNOWN_ARG:
|
||||
case SCC_ARGPARSE_ERR_UNKNOWN_VALUE:
|
||||
scc_printf(lines[ARGPARSE_UNKNOWN_ARGUMENT], ctx->result.raw_arg);
|
||||
const char *similar_arg = scc_argparse_find_similar_arg(
|
||||
ctx->current_cmd, ctx->result.raw_arg);
|
||||
if (similar_arg != 0) {
|
||||
scc_printf(lines[ARGPARSE_DID_YOU_MEAN], similar_arg);
|
||||
}
|
||||
break;
|
||||
// FACK ERROR
|
||||
case SCC_ARGPARSE_ERR_PNT_DEFAULT:
|
||||
return;
|
||||
default:
|
||||
scc_printf("Unknown error: %d\n", err);
|
||||
break;
|
||||
}
|
||||
if (parser->need_help) {
|
||||
scc_printf(lines[ARGPRASE_USING_HELP_HINT]);
|
||||
}
|
||||
if (parser->need_error_add_help) {
|
||||
scc_argparse_print_help(parser, ctx->current_cmd);
|
||||
} else if (parser->need_error_add_usage) {
|
||||
print_usage(parser, ctx->current_cmd);
|
||||
}
|
||||
}
|
||||
112
libs/argparse/src/main.c
Normal file
112
libs/argparse/src/main.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#include <argparse.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int verbose; // -v, --verbose
|
||||
const char *output; // -o, --output
|
||||
const char *config; // --config
|
||||
const char *input; // input argument
|
||||
const char *output_dir; // output_dir argument
|
||||
cbool force; // -f, --force (for subcommand)
|
||||
const char *path; // path argument (for subcommand)
|
||||
} parsed_args_t;
|
||||
|
||||
int main(int argc, const char **argv, const char **envp) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
#endif
|
||||
|
||||
// 初始化解析结果结构体
|
||||
parsed_args_t parsed = {0};
|
||||
parsed.output = "a.out"; // 默认值
|
||||
parsed.output_dir = "."; // 默认值
|
||||
parsed.path = "."; // 默认值
|
||||
|
||||
// 初始化 argparse
|
||||
scc_argparse_t argparse;
|
||||
scc_argparse_init(&argparse, "demo", "一个命令行参数解析演示工具");
|
||||
argparse.need_debug = true;
|
||||
argparse.lang = SCC_ARGPARSE_LANG_ZH; // 中文帮助
|
||||
|
||||
scc_argparse_cmd_t *root = scc_argparse_get_root(&argparse);
|
||||
|
||||
// ---------- 添加选项 ----------
|
||||
// 1. --verbose / -v (计数)
|
||||
scc_argparse_opt_t verbose;
|
||||
scc_argparse_opt_init(&verbose, 'v', "verbose",
|
||||
"增加详细程度(可多次使用)");
|
||||
scc_argparse_spec_setup_count(&verbose.spec, &parsed.verbose);
|
||||
scc_argparse_cmd_add_opt(root, &verbose);
|
||||
|
||||
// 2. --output / -o (字符串)
|
||||
scc_argparse_opt_t output;
|
||||
scc_argparse_opt_init(&output, 'o', "output", "输出文件路径");
|
||||
scc_argparse_spec_setup_string(&output.spec, &parsed.output);
|
||||
scc_argparse_cmd_add_opt(root, &output);
|
||||
|
||||
// 3. --config (字符串)
|
||||
scc_argparse_opt_t config;
|
||||
scc_argparse_opt_init(&config, 0, "config", "配置文件路径");
|
||||
scc_argparse_spec_setup_string(&config.spec, &parsed.config);
|
||||
scc_argparse_cmd_add_opt(root, &config);
|
||||
|
||||
// ---------- 添加位置参数 ----------
|
||||
// 必需参数: input
|
||||
scc_argparse_arg_t input;
|
||||
scc_argparse_arg_init(&input, "input", "输入文件");
|
||||
scc_argparse_spec_setup_string(&input.spec, &parsed.input);
|
||||
scc_argparse_spec_set_required(&input.spec, true);
|
||||
scc_argparse_cmd_add_arg(root, &input);
|
||||
|
||||
// 可选参数: output_dir
|
||||
scc_argparse_arg_t output_dir;
|
||||
scc_argparse_arg_init(&output_dir, "output_dir", "输出目录");
|
||||
scc_argparse_spec_setup_string(&output_dir.spec, &parsed.output_dir);
|
||||
scc_argparse_spec_set_required(&output_dir.spec, false);
|
||||
scc_argparse_cmd_add_arg(root, &output_dir);
|
||||
|
||||
// ---------- 子命令 init ----------
|
||||
scc_argparse_cmd_t init_cmd;
|
||||
scc_argparse_cmd_init(&init_cmd, "init", "初始化仓库");
|
||||
|
||||
// 子命令选项: --force / -f
|
||||
scc_argparse_opt_t force;
|
||||
scc_argparse_opt_init(&force, 'f', "force", "强制覆盖");
|
||||
scc_argparse_spec_setup_bool(&force.spec, &parsed.force);
|
||||
scc_argparse_cmd_add_opt(&init_cmd, &force);
|
||||
|
||||
// 子命令位置参数: path (可选)
|
||||
scc_argparse_arg_t path;
|
||||
scc_argparse_arg_init(&path, "path", "初始化路径");
|
||||
scc_argparse_spec_setup_string(&path.spec, &parsed.path);
|
||||
scc_argparse_spec_set_required(&path.spec, false);
|
||||
scc_argparse_cmd_add_arg(&init_cmd, &path);
|
||||
|
||||
// 将子命令加入根命令
|
||||
scc_argparse_cmd_add_subcmd(root, &init_cmd);
|
||||
|
||||
// 解析参数
|
||||
int ret = scc_argparse_parse(&argparse, argc, argv);
|
||||
|
||||
// 打印解析结果
|
||||
if (ret == 0) {
|
||||
scc_printf("\n解析成功\n");
|
||||
scc_printf("解析到的参数和选项:\n");
|
||||
scc_printf(" verbose: %d\n", parsed.verbose);
|
||||
scc_printf(" output: %s\n", parsed.output);
|
||||
scc_printf(" config: %s\n",
|
||||
parsed.config ? parsed.config : "(未指定)");
|
||||
scc_printf(" input: %s\n", parsed.input);
|
||||
scc_printf(" output_dir: %s\n", parsed.output_dir);
|
||||
scc_ | ||||