Compare commits
131
Commits
main
..
aa8a1ff8ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
No files matched your search
+43
@@ -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
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
build-docs:
|
||||||
|
doxygen Doxyfile
|
||||||
|
|
||||||
|
docs: build-docs
|
||||||
|
python -m http.server -d docs/html
|
||||||
@@ -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
@@ -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,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 / [email protected]
|
|
||||||
David Hanson / [email protected]
|
|
||||||
*/
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "scc_abi"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = []
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
# dependencies = []
|
||||||
|
# features = {}
|
||||||
|
# default_features = []
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* @file scc_abi_type.h
|
||||||
|
* @brief 目标无关的类型布局描述接口
|
||||||
|
* @note 本模块仅定义接口。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SCC_ABI_TYPE_H__
|
||||||
|
#define __SCC_ABI_TYPE_H__
|
||||||
|
|
||||||
|
#include <scc_core.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ABI 基础类型类别枚举。
|
||||||
|
*
|
||||||
|
* 用于快速查询目标预定义的基本类型属性。复杂类型(指针、数组、结构体)
|
||||||
|
* 通过组合与递归计算。
|
||||||
|
*/
|
||||||
|
typedef enum scc_abi_base_type_kind {
|
||||||
|
SCC_ABI_TYPE_VOID,
|
||||||
|
SCC_ABI_TYPE_VA_LIST,
|
||||||
|
SCC_ABI_TYPE_CHAR,
|
||||||
|
SCC_ABI_TYPE_I_CHAR,
|
||||||
|
SCC_ABI_TYPE_U_CHAR,
|
||||||
|
SCC_ABI_TYPE_I_SHORT,
|
||||||
|
SCC_ABI_TYPE_U_SHORT,
|
||||||
|
SCC_ABI_TYPE_I_INT,
|
||||||
|
SCC_ABI_TYPE_U_INT,
|
||||||
|
SCC_ABI_TYPE_I_LONG,
|
||||||
|
SCC_ABI_TYPE_U_LONG,
|
||||||
|
SCC_ABI_TYPE_I_LONG_LONG,
|
||||||
|
SCC_ABI_TYPE_U_LONG_LONG,
|
||||||
|
SCC_ABI_TYPE_PTR,
|
||||||
|
SCC_ABI_TYPE_FLOAT,
|
||||||
|
SCC_ABI_TYPE_DOUBLE,
|
||||||
|
SCC_ABI_TYPE_USIZE,
|
||||||
|
SCC_ABI_TYPE_ISIZE,
|
||||||
|
/* 可扩展:I128, F16, F128, VECTOR, ... */
|
||||||
|
} scc_abi_base_type_kind_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 单个类型的布局信息。
|
||||||
|
*/
|
||||||
|
typedef struct scc_abi_type_layout {
|
||||||
|
int size; /**< 类型占用的字节数 */
|
||||||
|
int alignment; /**< 类型的对齐要求(字节边界) */
|
||||||
|
} scc_abi_type_layout_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scc_abi_base_type_kind_t kind;
|
||||||
|
scc_abi_type_layout_t layout;
|
||||||
|
} scc_abi_base_type_impl_t;
|
||||||
|
#define SCC_ABI_BASE_TYPE_IMPL(type, bytes_size, alians) \
|
||||||
|
[type] = { \
|
||||||
|
.kind = type, \
|
||||||
|
.layout.size = bytes_size, \
|
||||||
|
.layout.alignment = alians, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 单个结构体字段的布局信息。
|
||||||
|
* 由目标布局算法填充,用于 IR 的 getelementptr 常量索引计算。
|
||||||
|
*/
|
||||||
|
typedef struct scc_abi_field_layout {
|
||||||
|
int offset; /**< 字段相对于结构体基址的字节偏移 */
|
||||||
|
int size; /**< 字段自身大小 */
|
||||||
|
int alignment; /**< 字段的对齐要求 */
|
||||||
|
} scc_abi_field_layout_t;
|
||||||
|
|
||||||
|
typedef SCC_VEC(scc_abi_field_layout_t) scc_abi_field_layout_vec_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取基本类型的布局信息。
|
||||||
|
*
|
||||||
|
* 目标必须实现此函数,为每个 scc_abi_base_type_kind_t 返回正确的
|
||||||
|
* size/alignment。
|
||||||
|
*
|
||||||
|
* @param kind 基本类型类别
|
||||||
|
* @param layout 输出参数,存放布局信息
|
||||||
|
* @return 成功返回 0,若 kind 不支持则返回 -1
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
scc_abi_get_base_type_layout(const scc_abi_base_type_impl_t *impls,
|
||||||
|
scc_abi_base_type_kind_t kind,
|
||||||
|
scc_abi_type_layout_t *layout) {
|
||||||
|
if (impls[kind].kind != kind)
|
||||||
|
Panic("invalid base type kind");
|
||||||
|
*layout = impls[kind].layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct scc_abi_type_calc scc_abi_type_calc_t;
|
||||||
|
|
||||||
|
void scc_abi_compute_ast_type_layout(const scc_abi_type_calc_t *ctx, void *type,
|
||||||
|
scc_abi_type_layout_t *layout);
|
||||||
|
|
||||||
|
typedef struct scc_abi_type_calc {
|
||||||
|
void *ctx;
|
||||||
|
const scc_abi_base_type_impl_t *impls;
|
||||||
|
/// @brief 可以是系统内置的结构,比如 struct int128_t
|
||||||
|
void (*compute_type_layout)(const scc_abi_type_calc_t *ctx, void *type,
|
||||||
|
scc_abi_type_layout_t *layout);
|
||||||
|
/// @brief
|
||||||
|
void (*compute_field_layout)(const scc_abi_type_calc_t *ctx, void *type,
|
||||||
|
scc_abi_field_layout_vec_t *field_layouts);
|
||||||
|
} scc_abi_type_calc_t;
|
||||||
|
|
||||||
|
#endif /* __SCC_ABI_TYPE_H__ */
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef __SCC_ABI_DUMMY_H__
|
||||||
|
#define __SCC_ABI_DUMMY_H__
|
||||||
|
|
||||||
|
#endif /* __SCC_ABI_DUMMY_H__ */
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef __SCC_ABI_WIN_X64_PC_H__
|
||||||
|
#define __SCC_ABI_WIN_X64_PC_H__
|
||||||
|
/**
|
||||||
|
* @brief Windows x64 ABI Type
|
||||||
|
* @details
|
||||||
|
* https://learn.microsoft.com/zh-cn/cpp/build/x64-software-conventions?view=msvc-180
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../scc_type_abi.h"
|
||||||
|
|
||||||
|
static const scc_abi_base_type_impl_t scc_abi_base_type_impls[] = {
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* https://learn.microsoft.com/zh-cn/cpp/build/x64-calling-convention?view=msvc-180#varargs
|
||||||
|
* 如果通过 vararg (例如省略号参数) 传递参数,则需遵守常规寄存器参数传递约定.
|
||||||
|
* 该约定规定了将第 5个及后面的参数溢出到堆栈中.
|
||||||
|
* 被调用方负责转储带有其地址的参数.
|
||||||
|
* (仅适用于浮点值)如果被调用方希望在整数寄存器中使用浮点值,则整数寄存器和浮点数寄存器都必须包含该值.
|
||||||
|
*/
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_VA_LIST, 4, 4),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_VOID, 0, 0),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_CHAR, 1, 1),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_I_CHAR, 1, 1),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_U_CHAR, 1, 1),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_I_SHORT, 2, 2),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_U_SHORT, 2, 2),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_I_INT, 4, 4),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_U_INT, 4, 4),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_I_LONG, 4, 4),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_U_LONG, 4, 4),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_I_LONG_LONG, 8, 8),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_U_LONG_LONG, 8, 8),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_PTR, 8, 8),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_FLOAT, 4, 4),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_DOUBLE, 8, 8),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_USIZE, 8, 8),
|
||||||
|
SCC_ABI_BASE_TYPE_IMPL(SCC_ABI_TYPE_ISIZE, 8, 8),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const scc_abi_type_calc_t scc_ast_abi_impl = {
|
||||||
|
.impls = scc_abi_base_type_impls,
|
||||||
|
.ctx = nullptr,
|
||||||
|
.compute_type_layout = scc_abi_compute_ast_type_layout,
|
||||||
|
.compute_field_layout = nullptr,
|
||||||
|
};
|
||||||
|
#ifdef SCC_ABI_IMPLIMENT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SCC_ABI_WIN_X64_PC_H__ */
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#include <scc_ast.h>
|
||||||
|
#include <scc_hir.h>
|
||||||
|
#include <scc_type_abi.h>
|
||||||
|
|
||||||
|
void scc_abi_compute_ast_type_layout(const scc_abi_type_calc_t *ctx, void *type,
|
||||||
|
scc_abi_type_layout_t *layout) {
|
||||||
|
scc_ast_qual_type_t *ast_type = type;
|
||||||
|
scc_abi_base_type_kind_t kind = SCC_ABI_TYPE_VOID;
|
||||||
|
switch (scc_ast_canon_type(ast_type)->builtin.type) {
|
||||||
|
case SCC_AST_BUILTIN_TYPE_VA_LIST:
|
||||||
|
kind = SCC_ABI_TYPE_VA_LIST;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_BOOL:
|
||||||
|
kind = SCC_ABI_TYPE_I_INT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_VOID:
|
||||||
|
kind = SCC_ABI_TYPE_VOID;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_CHAR:
|
||||||
|
kind = SCC_ABI_TYPE_CHAR;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR:
|
||||||
|
kind = SCC_ABI_TYPE_U_CHAR;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_CHAR:
|
||||||
|
kind = SCC_ABI_TYPE_I_CHAR;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SHORT:
|
||||||
|
kind = SCC_ABI_TYPE_I_SHORT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_SHORT:
|
||||||
|
kind = SCC_ABI_TYPE_I_SHORT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT:
|
||||||
|
kind = SCC_ABI_TYPE_U_SHORT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_INT:
|
||||||
|
kind = SCC_ABI_TYPE_I_INT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_INT:
|
||||||
|
kind = SCC_ABI_TYPE_I_INT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_INT:
|
||||||
|
kind = SCC_ABI_TYPE_U_INT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_LONG:
|
||||||
|
kind = SCC_ABI_TYPE_I_LONG;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_LONG:
|
||||||
|
kind = SCC_ABI_TYPE_I_LONG;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG:
|
||||||
|
kind = SCC_ABI_TYPE_U_LONG;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_LONG_LONG:
|
||||||
|
kind = SCC_ABI_TYPE_I_LONG_LONG;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG:
|
||||||
|
kind = SCC_ABI_TYPE_I_LONG_LONG;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG:
|
||||||
|
kind = SCC_ABI_TYPE_U_LONG_LONG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Panic("Unsupported AST type: %d",
|
||||||
|
scc_ast_canon_type(ast_type)->builtin.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scc_abi_get_base_type_layout(ctx->impls, kind, layout);
|
||||||
|
}
|
||||||
@@ -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 = []
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
#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) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_ENUM;
|
||||||
|
spec->choices.values = values;
|
||||||
|
spec->choices.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加设置列表的辅助函数(内联)
|
||||||
|
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__ */
|
||||||
@@ -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__ */
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
#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;
|
||||||
|
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) {
|
||||||
|
if (!spec->choices.values || spec->choices.count == 0) {
|
||||||
|
return SCC_ARGPARSE_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < spec->choices.count; i += 1) {
|
||||||
|
if (scc_strcmp(value, spec->choices.values[i]) == 0) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 &&
|
||||||
|
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);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
#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];
|
||||||
|
char buf[64];
|
||||||
|
int pos = 0;
|
||||||
|
if (opt->short_name) {
|
||||||
|
buf[pos++] = '-';
|
||||||
|
buf[pos++] = opt->short_name;
|
||||||
|
if (opt->long_name) {
|
||||||
|
buf[pos++] = ',';
|
||||||
|
buf[pos++] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt->long_name) {
|
||||||
|
buf[pos++] = '-';
|
||||||
|
buf[pos++] = '-';
|
||||||
|
for (const char *p = opt->long_name; *p; ++p)
|
||||||
|
buf[pos++] = *p;
|
||||||
|
}
|
||||||
|
buf[pos] = '\0';
|
||||||
|
scc_printf(" %-25s %s", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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_printf(" force: %s\n", parsed.force ? "true" : "false");
|
||||||
|
scc_printf(" path: %s\n", parsed.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_argparse_drop(&argparse);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
#include <optparse.h>
|
||||||
|
|
||||||
|
void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv) {
|
||||||
|
parser->argc = argc;
|
||||||
|
parser->argv = argv;
|
||||||
|
parser->opts = 0;
|
||||||
|
scc_optparse_reset(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_optparse_drop(scc_optparse_t *parser) { (void)parser; }
|
||||||
|
|
||||||
|
void scc_optparse_set(scc_optparse_t *parser, const scc_optparse_opt_t *opts) {
|
||||||
|
parser->opts = opts;
|
||||||
|
scc_optparse_reset(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_optparse_reset(scc_optparse_t *parser) {
|
||||||
|
parser->handle_positional = 0;
|
||||||
|
parser->current.opt = 0;
|
||||||
|
parser->current.count = 0;
|
||||||
|
parser->current.arg_pos = 1;
|
||||||
|
parser->current.opt_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *str_chr(const char *s, char c) {
|
||||||
|
while (*s && *s != c)
|
||||||
|
s++;
|
||||||
|
return *s ? s : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scc_optparse_opt_t *
|
||||||
|
scc_optparse_get_long_name(const scc_optparse_opt_t *opts, const char *name,
|
||||||
|
const char end) {
|
||||||
|
if (*name == '\0')
|
||||||
|
return 0;
|
||||||
|
for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) {
|
||||||
|
if (!opt->long_name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; opt->long_name[i]; ++i) {
|
||||||
|
if (name[i] != opt->long_name[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt->long_name[i] == '\0' && (name[i] == '\0' || name[i] == end))
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scc_optparse_opt_t *
|
||||||
|
scc_optparse_get_short_name(const scc_optparse_opt_t *opts,
|
||||||
|
const char short_name) {
|
||||||
|
for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) {
|
||||||
|
if (opt->short_name == short_name) {
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_optparse_parse_long_name(const scc_optparse_opt_t *opts,
|
||||||
|
scc_optparse_result_t *res,
|
||||||
|
const char *arg) {
|
||||||
|
// caller MUST use `--` start arg
|
||||||
|
const scc_optparse_opt_t *ret = 0;
|
||||||
|
const char *chr = str_chr(arg, '=');
|
||||||
|
if (chr) {
|
||||||
|
// --file=a.txt
|
||||||
|
ret = scc_optparse_get_long_name(opts, arg + 2, '=');
|
||||||
|
if (ret == 0) {
|
||||||
|
res->error = SCC_OPT_ERROR_NOT_FOUND_LONG_ARG;
|
||||||
|
} else {
|
||||||
|
res->opt = ret;
|
||||||
|
if (chr[1] == '\0') {
|
||||||
|
// TODO maybe can empty like -file=
|
||||||
|
res->error = SCC_OPT_ERROR_NOT_ENOUGH_ARGS;
|
||||||
|
}
|
||||||
|
res->value = chr + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// --file a.txt
|
||||||
|
ret = scc_optparse_get_long_name(opts, arg + 2, '\0');
|
||||||
|
if (ret == 0) {
|
||||||
|
res->error = SCC_OPT_ERROR_NOT_FOUND_LONG_ARG;
|
||||||
|
} else {
|
||||||
|
res->opt = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_optparse_parse_short_name(const scc_optparse_opt_t *opts,
|
||||||
|
scc_optparse_result_t *res,
|
||||||
|
const char *arg,
|
||||||
|
int arg_start) {
|
||||||
|
// const char *chr =
|
||||||
|
// str_chr(arg + arg_start, '='); // TODO maybe short can have -I=/usr
|
||||||
|
const scc_optparse_opt_t *ret =
|
||||||
|
scc_optparse_get_short_name(opts, arg[arg_start]);
|
||||||
|
if (ret == 0) {
|
||||||
|
res->error = SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG;
|
||||||
|
} else {
|
||||||
|
res->opt = ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_optparse_parse_position_arg(scc_optparse_t *parser,
|
||||||
|
const char *arg,
|
||||||
|
scc_optparse_result_t *res) {
|
||||||
|
parser->current.arg_pos++;
|
||||||
|
res->value = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int scc_optparse_check(scc_optparse_t *parser,
|
||||||
|
scc_optparse_result_t *res) {
|
||||||
|
if (!res->opt || res->error)
|
||||||
|
return 0;
|
||||||
|
if (res->value)
|
||||||
|
parser->current.count += 1;
|
||||||
|
if (parser->current.count < res->opt->max_args && res->opt->max_args > 0)
|
||||||
|
parser->current.opt = res->opt;
|
||||||
|
if (parser->current.count < res->opt->min_args && !res->value)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) {
|
||||||
|
const scc_optparse_opt_t *opts = parser->opts;
|
||||||
|
const char *arg = 0;
|
||||||
|
res->opt = 0;
|
||||||
|
res->value = 0;
|
||||||
|
res->error = SCC_OPT_ERROR_NONE;
|
||||||
|
|
||||||
|
if (parser->current.opt_pos != 0) {
|
||||||
|
arg = parser->argv[parser->current.arg_pos];
|
||||||
|
goto CONTINUE_SHORT_OPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (parser->current.arg_pos < parser->argc) {
|
||||||
|
arg = parser->argv[parser->current.arg_pos];
|
||||||
|
|
||||||
|
if (arg[0] == '\0' || parser->handle_positional) {
|
||||||
|
scc_optparse_parse_position_arg(parser, arg, res);
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const scc_optparse_opt_t *opt = opts; opt && opt->prefix; ++opt) {
|
||||||
|
if (arg[0] != opt->prefix) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser->current.opt = 0;
|
||||||
|
parser->current.count = 0;
|
||||||
|
if (arg[1] == opt->prefix) {
|
||||||
|
// long option like --
|
||||||
|
if (arg[2] == '\0') {
|
||||||
|
// the `--`
|
||||||
|
res->value = arg;
|
||||||
|
parser->handle_positional = 1;
|
||||||
|
parser->current.arg_pos++;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
scc_optparse_parse_long_name(opts, res, arg);
|
||||||
|
parser->current.arg_pos += 1;
|
||||||
|
if (scc_optparse_check(parser, res)) {
|
||||||
|
goto NEXT;
|
||||||
|
}
|
||||||
|
goto RETURN;
|
||||||
|
} else {
|
||||||
|
// short option like -
|
||||||
|
if (arg[1] == '\0') {
|
||||||
|
// the `-`
|
||||||
|
res->value = arg;
|
||||||
|
parser->current.arg_pos++;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
parser->current.opt_pos = 1;
|
||||||
|
CONTINUE_SHORT_OPTION:
|
||||||
|
scc_optparse_parse_short_name(opts, res, arg,
|
||||||
|
parser->current.opt_pos++);
|
||||||
|
int have_next = arg[parser->current.opt_pos];
|
||||||
|
|
||||||
|
parser->current.opt_pos =
|
||||||
|
have_next ? parser->current.opt_pos : 0;
|
||||||
|
parser->current.arg_pos += have_next ? 0 : 1;
|
||||||
|
if (scc_optparse_check(parser, res)) {
|
||||||
|
if (have_next) {
|
||||||
|
res->value = arg + parser->current.opt_pos;
|
||||||
|
parser->current.opt_pos = 0;
|
||||||
|
parser->current.arg_pos += 1;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
goto NEXT;
|
||||||
|
}
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// position arg
|
||||||
|
if (parser->current.opt &&
|
||||||
|
parser->current.count < parser->current.opt->max_args) {
|
||||||
|
res->opt = parser->current.opt;
|
||||||
|
res->value = arg;
|
||||||
|
parser->current.count++;
|
||||||
|
parser->current.arg_pos++;
|
||||||
|
goto RETURN;
|
||||||
|
} else {
|
||||||
|
scc_optparse_parse_position_arg(parser, arg, res);
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
NEXT:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
RETURN:
|
||||||
|
if (res->opt && res->opt->max_args > 0 &&
|
||||||
|
parser->current.count > res->opt->max_args) {
|
||||||
|
res->error = SCC_OPT_ERROR_TOO_MANY_ARGS;
|
||||||
|
}
|
||||||
|
res->raw_arg = arg;
|
||||||
|
return res->opt != 0 || res->value != 0 || res->error != SCC_OPT_ERROR_NONE;
|
||||||
|
}
|
||||||
@@ -0,0 +1,728 @@
|
|||||||
|
#include <optparse.h>
|
||||||
|
#include <utest/acutest.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// 测试用例配置
|
||||||
|
static scc_optparse_opt_t test_opts[] = {
|
||||||
|
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
|
||||||
|
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
|
||||||
|
SCC_OPTPARSE_OPT('-', 'v', "verbose", 0, 0),
|
||||||
|
SCC_OPTPARSE_OPT('-', 'f', "file", 1, 1),
|
||||||
|
SCC_OPTPARSE_OPT('-', 'o', "output", 1, 1),
|
||||||
|
SCC_OPTPARSE_OPT('-', 'l', "list", 0, 10),
|
||||||
|
SCC_OPTPARSE_OPT('-', 'c', "count", 1, 3),
|
||||||
|
SCC_OPTPARSE_OPT_END(),
|
||||||
|
};
|
||||||
|
|
||||||
|
#define INIT_OPTPARSE(argv) \
|
||||||
|
scc_optparse_init(&parser, sizeof(argv) / sizeof(argv[0]), argv); \
|
||||||
|
scc_optparse_set(&parser, test_opts);
|
||||||
|
|
||||||
|
// 测试1: 基础初始化
|
||||||
|
void test_init(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
const char *argv[] = {"program", "-h"};
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
scc_optparse_set(&parser, 0);
|
||||||
|
TEST_CHECK(parser.argc == 2);
|
||||||
|
TEST_CHECK(parser.argv == argv);
|
||||||
|
TEST_CHECK(parser.handle_positional == 0);
|
||||||
|
TEST_CHECK(parser.current.arg_pos == 1);
|
||||||
|
TEST_CHECK(parser.current.opt_pos == 0);
|
||||||
|
TEST_CHECK(parser.opts == 0);
|
||||||
|
TEST_CHECK(parser.current.opt == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试2: 简单短选项
|
||||||
|
void test_simple_short_options(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-h", "-v"};
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析第一个选项 -h
|
||||||
|
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
|
||||||
|
// 解析第二个选项 -v
|
||||||
|
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
|
||||||
|
// 没有更多选项
|
||||||
|
TEST_CHECK(scc_optparse_parse(&parser, &res) == 0);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simple_position_arg(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "a", "b"};
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
// 解析第一个选项
|
||||||
|
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "a") == 0);
|
||||||
|
|
||||||
|
// 解析第二个选项
|
||||||
|
TEST_CHECK(scc_optparse_parse(&parser, &res) != 0);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "b") == 0);
|
||||||
|
|
||||||
|
// 没有更多选项
|
||||||
|
TEST_CHECK(scc_optparse_parse(&parser, &res) == 0);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试3: 带参数的短选项
|
||||||
|
void test_short_options_with_args(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-f", "file.txt", "-o", "output.txt"};
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -f file.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "file.txt") == 0);
|
||||||
|
|
||||||
|
// 解析 -o output.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||||||
|
TEST_CHECK(strcmp(res.value, "output.txt") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试4: 长选项
|
||||||
|
void test_long_options(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "--help", "--file=test.txt", "--output",
|
||||||
|
"out.txt"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 --help
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "help") == 0);
|
||||||
|
|
||||||
|
// 解析 --file=test.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "file") == 0);
|
||||||
|
TEST_CHECK(strcmp(res.value, "test.txt") == 0);
|
||||||
|
|
||||||
|
// 解析 --output out.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "output") == 0);
|
||||||
|
TEST_CHECK(strcmp(res.value, "out.txt") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试5: 混合选项和位置参数
|
||||||
|
void test_mixed_options_positional(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-h", "-f",
|
||||||
|
"input.txt", "positional1", "positional2"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -h
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
|
||||||
|
// 解析 -f input.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||||||
|
|
||||||
|
// 解析第一个位置参数
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "positional1") == 0);
|
||||||
|
|
||||||
|
// 解析第二个位置参数
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "positional2") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试6: 短选项连写
|
||||||
|
void test_combined_short_options(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-vh"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -v
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||||||
|
|
||||||
|
// 解析 -h
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试7: 错误处理 - 未知选项
|
||||||
|
void test_unknown_options(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-x", "--unknown"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -x (未知短选项)
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG);
|
||||||
|
|
||||||
|
// 重置解析器测试长选项
|
||||||
|
const char *argv2[] = {"program", "--unknown"};
|
||||||
|
INIT_OPTPARSE(argv2);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_FOUND_LONG_ARG);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试8: 选项值紧跟在短选项后
|
||||||
|
void test_short_option_with_attached_value(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-finput.txt", "-ooutput.txt"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -finput.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||||||
|
|
||||||
|
// 解析 -ooutput.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||||||
|
TEST_CHECK(strcmp(res.value, "output.txt") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试9: 选项终止符 --
|
||||||
|
void test_option_terminator(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-h", "--", "-f", "file.txt"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -h
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
|
||||||
|
// 解析 -- (应该触发位置参数模式)
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(parser.handle_positional == 1);
|
||||||
|
|
||||||
|
// 解析 -f (作为位置参数)
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "-f") == 0);
|
||||||
|
|
||||||
|
// 解析 file.txt (作为位置参数)
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "file.txt") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试10: 空参数和边界情况
|
||||||
|
void test_edge_cases(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
|
||||||
|
// 测试空参数数组
|
||||||
|
const char *argv1[] = {"program"};
|
||||||
|
INIT_OPTPARSE(argv1);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
|
||||||
|
// 测试空字符串参数
|
||||||
|
const char *argv2[] = {"program", "-f", "", "-o", " "};
|
||||||
|
INIT_OPTPARSE(argv2);
|
||||||
|
|
||||||
|
// 解析 -f ""
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "") == 0);
|
||||||
|
|
||||||
|
// 解析 -o " "
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||||||
|
TEST_CHECK(strcmp(res.value, " ") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试11: 多参数选项
|
||||||
|
void test_multi_argument_option(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-l", "item1", "item2", "item3"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -l
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'l');
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
|
||||||
|
// 由于 -l 可以接受多个参数,后续的参数应该作为 -l 的值
|
||||||
|
// 但根据当前实现,可能需要多次调用
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "item1") == 0);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "item2") == 0);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "item3") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试12: 参数不足的错误
|
||||||
|
void test_insufficient_arguments(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-f"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
// 注意:当前实现可能需要额外检查
|
||||||
|
// TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_ENOUGH_ARGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试13: 长选项带等号但无值
|
||||||
|
void test_long_option_with_equal_no_value(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "--file="};
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NOT_ENOUGH_ARGS);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "file") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试14: 重置和重用解析器
|
||||||
|
void test_parser_reuse(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
|
||||||
|
// 第一次使用
|
||||||
|
const char *argv1[] = {"program", "-h", "-v"};
|
||||||
|
INIT_OPTPARSE(argv1);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||||||
|
|
||||||
|
// 重置并重用
|
||||||
|
const char *argv2[] = {"program", "-f", "test.txt"};
|
||||||
|
INIT_OPTPARSE(argv2);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "test.txt") == 0);
|
||||||
|
}
|
||||||
|
// 测试15: 短选项连写中带参数的情况
|
||||||
|
void test_combined_short_with_arg(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-vfinput.txt"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -v
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
|
||||||
|
// 解析 -finput.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试16: 参数超过最大值的情况
|
||||||
|
void test_too_many_arguments(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-f", "file1", "file2"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -f file1
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "file1") == 0);
|
||||||
|
|
||||||
|
// 尝试给 -f 第二个参数,应该返回错误
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "file2") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试17: 混合短选项和位置参数
|
||||||
|
void test_mixed_short_and_positional(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-v", "-f", "input.txt", "positional"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -v
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||||||
|
|
||||||
|
// 解析 -f input.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||||||
|
|
||||||
|
// 解析位置参数
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "positional") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试18: 复杂的多参数选项
|
||||||
|
void test_complex_multi_argument(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-c", "1", "2", "3", "extra"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 -c 1
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'c');
|
||||||
|
TEST_CHECK(strcmp(res.value, "1") == 0);
|
||||||
|
|
||||||
|
// 解析 -c 2
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'c');
|
||||||
|
TEST_CHECK(strcmp(res.value, "2") == 0);
|
||||||
|
|
||||||
|
// 解析 -c 3
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'c');
|
||||||
|
TEST_CHECK(strcmp(res.value, "3") == 0);
|
||||||
|
|
||||||
|
// 第4个参数应该是位置参数
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "extra") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试19: 长选项带多个参数
|
||||||
|
void test_long_option_multi_args(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "--list", "item1", "item2", "item3"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
// 解析 --list
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||||||
|
TEST_CHECK(res.value == nullptr);
|
||||||
|
|
||||||
|
// 解析 item1
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||||||
|
TEST_CHECK(strcmp(res.value, "item1") == 0);
|
||||||
|
|
||||||
|
// 解析 item2
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||||||
|
TEST_CHECK(strcmp(res.value, "item2") == 0);
|
||||||
|
|
||||||
|
// 解析 item3
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "list") == 0);
|
||||||
|
TEST_CHECK(strcmp(res.value, "item3") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试20: 空长选项名(只有--)
|
||||||
|
void test_empty_long_name(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "--"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(parser.handle_positional == 1);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "--") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试21: 选项前缀非'-'的情况
|
||||||
|
void test_non_dash_prefix(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
|
||||||
|
static scc_optparse_opt_t custom_opts[] = {
|
||||||
|
SCC_OPTPARSE_OPT('/', 'h', "help", 0, 0),
|
||||||
|
SCC_OPTPARSE_OPT_END(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *argv[] = {"program", "/h"};
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
parser.opts = custom_opts;
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt != nullptr);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
TEST_CHECK(res.opt && res.opt->prefix == '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试22: 默认值功能测试
|
||||||
|
// TODO
|
||||||
|
void test_default_value(void) {
|
||||||
|
// scc_optparse_t parser;
|
||||||
|
// scc_optparse_result_t res;
|
||||||
|
|
||||||
|
// static scc_optparse_opt_t default_opts[] = {
|
||||||
|
// SCC_OPTPARSE_OPT('-', 'o', "output", 0, 1, "default.txt", nullptr),
|
||||||
|
// SCC_OPTPARSE_OPT_END(),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // 测试1: 不提供选项,应该有默认值
|
||||||
|
// const char *argv1[] = {"program"};
|
||||||
|
// INIT_OPTPARSE(argv1);
|
||||||
|
// parser.opts = default_opts;
|
||||||
|
|
||||||
|
// scc_optparse_parse(&parser, &res);
|
||||||
|
// // 注意:这个测试需要修改解析器以支持默认值
|
||||||
|
// // 当前实现不支持,所以只是演示
|
||||||
|
|
||||||
|
// // 测试2: 提供选项但没有值,应该使用默认值
|
||||||
|
// const char *argv2[] = {"program", "-o"};
|
||||||
|
// INIT_OPTPARSE(argv2);
|
||||||
|
// parser.opts = default_opts;
|
||||||
|
|
||||||
|
// scc_optparse_parse(&parser, &res);
|
||||||
|
// TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
// TEST_CHECK(res.opt && res.opt->short_name == 'o');
|
||||||
|
// // 这里可以检查默认值,但当前实现不支持
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试23: 回调函数测试
|
||||||
|
static int callback_called = 0;
|
||||||
|
static void test_callback(void *value) {
|
||||||
|
(void)value;
|
||||||
|
callback_called = 1;
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
void test_callback_function(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
|
||||||
|
static scc_optparse_opt_t callback_opts[] = {
|
||||||
|
SCC_OPTPARSE_OPT('-', 'h', "help", 0, 0),
|
||||||
|
SCC_OPTPARSE_OPT_END(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *argv[] = {"program", "--help"};
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
parser.opts = callback_opts;
|
||||||
|
|
||||||
|
callback_called = 0;
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && strcmp(res.opt->long_name, "help") == 0);
|
||||||
|
|
||||||
|
// // 调用回调函数
|
||||||
|
// if (res.opt->invoke) {
|
||||||
|
// res.opt->invoke(nullptr);
|
||||||
|
// TEST_CHECK(callback_called == 1);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试24: 复杂的短选项组合
|
||||||
|
void test_complex_short_combination(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-vhfinput.txt"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
scc_optparse_set(&parser, test_opts);
|
||||||
|
|
||||||
|
// 解析 -v
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||||||
|
|
||||||
|
// 解析 -h
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
|
||||||
|
// 解析 -finput.txt
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.error == SCC_OPT_ERROR_NONE);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
TEST_CHECK(strcmp(res.value, "input.txt") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试25: 边界情况 - 单个横杠
|
||||||
|
void test_single_dash(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-", "--", "arg"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
scc_optparse_set(&parser, test_opts);
|
||||||
|
|
||||||
|
// 解析单个横杠(通常表示标准输入)
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt == nullptr);
|
||||||
|
TEST_CHECK(strcmp(res.value, "-") == 0);
|
||||||
|
|
||||||
|
// 解析 --
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(parser.handle_positional == 1);
|
||||||
|
|
||||||
|
// 解析 arg
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(strcmp(res.value, "arg") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试26: 重置解析器状态
|
||||||
|
void test_parser_state_reset(void) {
|
||||||
|
scc_optparse_t parser;
|
||||||
|
scc_optparse_result_t res;
|
||||||
|
const char *argv[] = {"program", "-f", "file1", "--", "arg1", "arg2"};
|
||||||
|
|
||||||
|
INIT_OPTPARSE(argv);
|
||||||
|
scc_optparse_set(&parser, test_opts);
|
||||||
|
|
||||||
|
// 解析 -f file1
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'f');
|
||||||
|
|
||||||
|
// 解析 --
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(parser.handle_positional == 1);
|
||||||
|
|
||||||
|
// 解析 arg1
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(strcmp(res.value, "arg1") == 0);
|
||||||
|
|
||||||
|
// 解析 arg2
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(strcmp(res.value, "arg2") == 0);
|
||||||
|
|
||||||
|
// 重置解析器
|
||||||
|
const char *argv2[] = {"program", "-v", "-h"};
|
||||||
|
INIT_OPTPARSE(argv2);
|
||||||
|
|
||||||
|
// 应该能正确解析新的参数
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'v');
|
||||||
|
|
||||||
|
scc_optparse_parse(&parser, &res);
|
||||||
|
TEST_CHECK(res.opt && res.opt->short_name == 'h');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新测试列表
|
||||||
|
TEST_LIST = {
|
||||||
|
{"test_init", test_init},
|
||||||
|
{"test_simple_short_options", test_simple_short_options},
|
||||||
|
{"test_simple_position_arg", test_simple_position_arg},
|
||||||
|
{"test_short_options_with_args", test_short_options_with_args},
|
||||||
|
{"test_long_options", test_long_options},
|
||||||
|
{"test_mixed_options_positional", test_mixed_options_positional},
|
||||||
|
{"test_combined_short_options", test_combined_short_options},
|
||||||
|
{"test_unknown_options", test_unknown_options},
|
||||||
|
{"test_short_option_with_attached_value",
|
||||||
|
test_short_option_with_attached_value},
|
||||||
|
{"test_option_terminator", test_option_terminator},
|
||||||
|
{"test_edge_cases", test_edge_cases},
|
||||||
|
{"test_multi_argument_option", test_multi_argument_option},
|
||||||
|
{"test_insufficient_arguments", test_insufficient_arguments},
|
||||||
|
{"test_long_option_with_equal_no_value",
|
||||||
|
test_long_option_with_equal_no_value},
|
||||||
|
{"test_parser_reuse", test_parser_reuse},
|
||||||
|
{"test_combined_short_with_arg", test_combined_short_with_arg},
|
||||||
|
{"test_too_many_arguments", test_too_many_arguments},
|
||||||
|
{"test_mixed_short_and_positional", test_mixed_short_and_positional},
|
||||||
|
{"test_complex_multi_argument", test_complex_multi_argument},
|
||||||
|
{"test_long_option_multi_args", test_long_option_multi_args},
|
||||||
|
{"test_empty_long_name", test_empty_long_name},
|
||||||
|
{"test_non_dash_prefix", test_non_dash_prefix},
|
||||||
|
{"test_default_value", test_default_value},
|
||||||
|
{"test_callback_function", test_callback_function},
|
||||||
|
{"test_complex_short_combination", test_complex_short_combination},
|
||||||
|
{"test_single_dash", test_single_dash},
|
||||||
|
{"test_parser_state_reset", test_parser_state_reset},
|
||||||
|
{nullptr, nullptr},
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "scc_ast"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = []
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
{ name = "tree_dump", path = "../tree_dump" },
|
||||||
|
{ name = "scc_pos", path = "../sstream" },
|
||||||
|
]
|
||||||
|
# features = {}
|
||||||
|
# default_features = []
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef __SCC_AST_H__
|
||||||
|
#define __SCC_AST_H__
|
||||||
|
|
||||||
|
#include "scc_ast_def.h"
|
||||||
|
|
||||||
|
typedef struct scc_ast_ctx {
|
||||||
|
scc_ast_canon_type_vec_t canonical_type_pool;
|
||||||
|
scc_ast_canon_type_t *builtin_types[SCC_AST_BUILTIN_TYPE_COUNT];
|
||||||
|
scc_ast_node_vec_t all_nodes;
|
||||||
|
} scc_ast_ctx_t;
|
||||||
|
|
||||||
|
void scc_ast_ctx_init(scc_ast_ctx_t *ctx);
|
||||||
|
void scc_ast_ctx_drop(scc_ast_ctx_t *ctx);
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *scc_ast_ctx_get_builtin_type(scc_ast_ctx_t *ctx,
|
||||||
|
scc_ast_builtin_type_t kind);
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *scc_ast_ctx_alloc_type(scc_ast_ctx_t *ctx);
|
||||||
|
|
||||||
|
static inline void *scc_ast_ctx_alloc_node(scc_ast_ctx_t *ctx, usize size) {
|
||||||
|
void *ptr = scc_malloc(size);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
Panic("Out of memory");
|
||||||
|
}
|
||||||
|
scc_memset(ptr, 0, size);
|
||||||
|
scc_vec_push(ctx->all_nodes, (scc_ast_node_t *)ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SCC_AST_ALLOC(ctx, type) \
|
||||||
|
((type *)scc_ast_ctx_alloc_node(ctx, sizeof(type)))
|
||||||
|
#define SCC_AST_ALLOC_QUAL_TYPE(ctx) SCC_AST_ALLOC(ctx, scc_ast_qual_type_t)
|
||||||
|
#define SCC_AST_ALLOC_DECL(ctx) SCC_AST_ALLOC(ctx, scc_ast_decl_t)
|
||||||
|
#define SCC_AST_ALLOC_EXPR(ctx) SCC_AST_ALLOC(ctx, scc_ast_expr_t)
|
||||||
|
#define SCC_AST_ALLOC_STMT(ctx) SCC_AST_ALLOC(ctx, scc_ast_stmt_t)
|
||||||
|
|
||||||
|
// have defined cannoical_type type
|
||||||
|
static inline void
|
||||||
|
scc_ast_type_builtin_init(scc_ast_qual_type_t *type, scc_ast_ctx_t *ctx,
|
||||||
|
scc_ast_builtin_type_t builtin_type, scc_pos_t loc) {
|
||||||
|
Assert(type != nullptr);
|
||||||
|
type->base.loc = loc;
|
||||||
|
type->base.type = SCC_AST_TYPE_BUILTIN;
|
||||||
|
type->type = scc_ast_ctx_get_builtin_type(ctx, builtin_type);
|
||||||
|
type->quals = (scc_ast_decl_specifier_t){0}; // FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __SCC_AST_H__*/
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
#ifndef __SCC_AST_DEF_H__
|
||||||
|
#define __SCC_AST_DEF_H__
|
||||||
|
|
||||||
|
#include <scc_core.h>
|
||||||
|
#include <scc_pos.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AST 节点类型枚举
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SCC_AST_UNKNOWN,
|
||||||
|
// 声明
|
||||||
|
scc_ast_decl_t_BEGIN, // 声明开始 sema 函数作用域开始
|
||||||
|
SCC_AST_DECL_LIST, // 声明列表
|
||||||
|
SCC_AST_DECL_VAR, // 变量声明
|
||||||
|
SCC_AST_DECL_FUNC, // 函数声明
|
||||||
|
SCC_AST_DECL_PARAM, // 参数声明
|
||||||
|
SCC_AST_DECL_STRUCT, // 结构体声明
|
||||||
|
SCC_AST_DECL_UNION, // 联合声明
|
||||||
|
SCC_AST_DECL_ENUM, // 枚举声明
|
||||||
|
SCC_AST_DECL_TYPEDEF, // typedef 声明
|
||||||
|
scc_ast_decl_t_END, // 声明结束 sema 函数作用域结束
|
||||||
|
|
||||||
|
// 语句
|
||||||
|
scc_ast_stmt_t_BEGIN, // 语句开始 sema 作用域开始
|
||||||
|
SCC_AST_STMT_COMPOUND, // 复合语句 { ... }
|
||||||
|
SCC_AST_STMT_EXPR, // 表达式语句
|
||||||
|
SCC_AST_STMT_IF, // if 语句
|
||||||
|
SCC_AST_STMT_WHILE, // while 语句
|
||||||
|
SCC_AST_STMT_DO_WHILE, // do-while 语句
|
||||||
|
SCC_AST_STMT_FOR, // for 语句
|
||||||
|
SCC_AST_STMT_SWITCH, // switch 语句
|
||||||
|
SCC_AST_STMT_CASE, // case 语句
|
||||||
|
SCC_AST_STMT_DEFAULT, // default 语句
|
||||||
|
SCC_AST_STMT_BREAK, // break 语句
|
||||||
|
SCC_AST_STMT_CONTINUE, // continue 语句
|
||||||
|
SCC_AST_STMT_RETURN, // return 语句
|
||||||
|
SCC_AST_STMT_GOTO, // goto 语句
|
||||||
|
SCC_AST_STMT_LABEL, // 标签语句
|
||||||
|
scc_ast_stmt_t_END, // 结束语句 sema 作用域结束
|
||||||
|
|
||||||
|
// 表达式
|
||||||
|
scc_ast_expr_t_BEGIN, // 表达式开始
|
||||||
|
SCC_AST_EXPR_BINARY, // 二元运算
|
||||||
|
SCC_AST_EXPR_UNARY, // 一元运算
|
||||||
|
SCC_AST_EXPR_COND, // 条件表达式 ?:
|
||||||
|
SCC_AST_EXPR_CALL, // 函数调用
|
||||||
|
SCC_AST_EXPR_ARRAY_SUBSCRIPT, // 数组下标
|
||||||
|
SCC_AST_EXPR_MEMBER, // 成员访问 .
|
||||||
|
SCC_AST_EXPR_PTR_MEMBER, // 指针成员访问 ->
|
||||||
|
SCC_AST_EXPR_CAST, // 类型转换
|
||||||
|
SCC_AST_EXPR_SIZE_OF, // sizeof
|
||||||
|
SCC_AST_EXPR_ALIGN_OF, // _Alignof
|
||||||
|
SCC_AST_EXPR_COMPOUND, // 复合字面量
|
||||||
|
SCC_AST_EXPR_LVALUE, // 右值
|
||||||
|
SCC_AST_EXPR_BUILTIN, // 内置表达式
|
||||||
|
// 字面量
|
||||||
|
SCC_AST_EXPR_INT_LITERAL, // 整数字面量
|
||||||
|
SCC_AST_EXPR_FLOAT_LITERAL, // 浮点字面量
|
||||||
|
SCC_AST_EXPR_CHAR_LITERAL, // 字符字面量
|
||||||
|
SCC_AST_EXPR_STRING_LITERAL, // 字符串字面量
|
||||||
|
// 标识符
|
||||||
|
SCC_AST_EXPR_IDENTIFIER, // 标识符
|
||||||
|
scc_ast_expr_t_END, // 表达式结束
|
||||||
|
|
||||||
|
// 类型
|
||||||
|
scc_ast_qual_type_t_BEGIN, // 类型开始
|
||||||
|
SCC_AST_TYPE_BUILTIN, // 内置类型
|
||||||
|
SCC_AST_TYPE_POINTER, // 指针类型
|
||||||
|
SCC_AST_TYPE_ARRAY, // 数组类型
|
||||||
|
SCC_AST_TYPE_FUNCTION, // 函数类型
|
||||||
|
SCC_AST_TYPE_STRUCT, // 结构体类型
|
||||||
|
SCC_AST_TYPE_UNION, // 联合类型
|
||||||
|
SCC_AST_TYPE_ENUM, // 枚举类型
|
||||||
|
SCC_AST_TYPE_TYPEDEF, // typedef 类型
|
||||||
|
scc_ast_qual_type_t_END, // 类型结束
|
||||||
|
|
||||||
|
// 其他
|
||||||
|
scc_ast_translation_unit_t_BEGIN,
|
||||||
|
SCC_AST_TRANSLATION_UNIT, // 翻译单元(根节点)
|
||||||
|
scc_ast_translation_unit_t_END,
|
||||||
|
} scc_ast_node_kind_t;
|
||||||
|
|
||||||
|
typedef struct scc_ast_node {
|
||||||
|
scc_ast_node_kind_t type;
|
||||||
|
scc_pos_t loc;
|
||||||
|
} scc_ast_node_t;
|
||||||
|
typedef SCC_VEC(scc_ast_node_t *) scc_ast_node_vec_t;
|
||||||
|
|
||||||
|
#define SCC_AST_CAST_TO(kind, expr) \
|
||||||
|
((kind *)(Assert(((scc_ast_node_t *)expr)->type > kind##_BEGIN && \
|
||||||
|
((scc_ast_node_t *)expr)->type < kind##_END), \
|
||||||
|
(expr)))
|
||||||
|
#define SCC_AST_IS_A(kind, expr) \
|
||||||
|
((expr) && (((scc_ast_node_t *)expr)->type > kind##_BEGIN && \
|
||||||
|
((scc_ast_node_t *)expr)->type < kind##_END))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 内置类型枚举
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SCC_AST_BUILTIN_TYPE_UNKNOWN,
|
||||||
|
SCC_AST_BUILTIN_TYPE_VA_LIST,
|
||||||
|
SCC_AST_BUILTIN_TYPE_VOID,
|
||||||
|
SCC_AST_BUILTIN_TYPE_BOOL,
|
||||||
|
SCC_AST_BUILTIN_TYPE_CHAR,
|
||||||
|
SCC_AST_BUILTIN_TYPE_SHORT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_INT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_LONG,
|
||||||
|
SCC_AST_BUILTIN_TYPE_LONG_LONG,
|
||||||
|
|
||||||
|
SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR,
|
||||||
|
SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_UNSIGNED_INT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG,
|
||||||
|
SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG,
|
||||||
|
|
||||||
|
SCC_AST_BUILTIN_TYPE_SIGNED_CHAR,
|
||||||
|
SCC_AST_BUILTIN_TYPE_SIGNED_SHORT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_SIGNED_INT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_SIGNED_LONG,
|
||||||
|
SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG,
|
||||||
|
|
||||||
|
SCC_AST_BUILTIN_TYPE_FLOAT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_DOUBLE,
|
||||||
|
SCC_AST_BUILTIN_TYPE_LONG_DOUBLE,
|
||||||
|
SCC_AST_BUILTIN_TYPE_COMPLEX_FLOAT,
|
||||||
|
SCC_AST_BUILTIN_TYPE_COMPLEX_DOUBLE,
|
||||||
|
SCC_AST_BUILTIN_TYPE_COMPLEX_LONG_DOUBLE,
|
||||||
|
|
||||||
|
SCC_AST_BUILTIN_TYPE_COUNT,
|
||||||
|
} scc_ast_builtin_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 限定符
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
// storage-class-specifier
|
||||||
|
cbool is_typedef;
|
||||||
|
cbool is_extern;
|
||||||
|
cbool is_static;
|
||||||
|
cbool is_auto;
|
||||||
|
cbool is_register;
|
||||||
|
// type-qualifier
|
||||||
|
cbool is_const;
|
||||||
|
cbool is_volatile;
|
||||||
|
cbool is_restrict;
|
||||||
|
cbool is_atomic;
|
||||||
|
// function-specifier
|
||||||
|
cbool is_inline;
|
||||||
|
} scc_ast_decl_specifier_t;
|
||||||
|
|
||||||
|
typedef union scc_ast_canonical_type scc_ast_canon_type_t;
|
||||||
|
typedef struct scc_ast_qual_type scc_ast_qual_type_t;
|
||||||
|
typedef struct scc_ast_expr scc_ast_expr_t;
|
||||||
|
typedef struct scc_ast_stmt scc_ast_stmt_t;
|
||||||
|
typedef struct scc_ast_decl scc_ast_decl_t;
|
||||||
|
|
||||||
|
typedef SCC_VEC(scc_ast_canon_type_t *) scc_ast_canon_type_vec_t;
|
||||||
|
typedef SCC_VEC(scc_ast_qual_type_t *) scc_ast_qual_type_vec_t;
|
||||||
|
typedef SCC_VEC(scc_ast_expr_t *) scc_ast_expr_vec_t;
|
||||||
|
typedef SCC_VEC(scc_ast_stmt_t *) scc_ast_stmt_vec_t;
|
||||||
|
typedef SCC_VEC(scc_ast_decl_t *) scc_ast_decl_vec_t;
|
||||||
|
|
||||||
|
// 通过指针实现泛型 only stmt or decl
|
||||||
|
typedef SCC_VEC(scc_ast_node_t *) scc_ast_block_item_vec_t;
|
||||||
|
|
||||||
|
// 规范类型 Canonical Type
|
||||||
|
union scc_ast_canonical_type {
|
||||||
|
struct {
|
||||||
|
scc_ast_builtin_type_t type;
|
||||||
|
} builtin;
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *pointee;
|
||||||
|
} pointer;
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *element;
|
||||||
|
scc_ast_expr_t *size; // 可为 nullptr <=> 不定长数组
|
||||||
|
} array;
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *return_type;
|
||||||
|
scc_ast_decl_vec_t params; // va_list <=> ...
|
||||||
|
} function;
|
||||||
|
struct {
|
||||||
|
const char *name;
|
||||||
|
scc_ast_decl_t *decl; // can be nullptr
|
||||||
|
} record;
|
||||||
|
struct {
|
||||||
|
const char *name;
|
||||||
|
/// @brief 指向typedef的声明(可以间接找到typedef的指向的类型)
|
||||||
|
scc_ast_decl_t *decl;
|
||||||
|
} typedef_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 类型表示
|
||||||
|
*/
|
||||||
|
struct scc_ast_qual_type {
|
||||||
|
scc_ast_node_t base;
|
||||||
|
scc_ast_decl_specifier_t quals;
|
||||||
|
scc_ast_canon_type_t *type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const scc_ast_canon_type_t *
|
||||||
|
scc_ast_canon_type(const scc_ast_qual_type_t *type) {
|
||||||
|
return type->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline scc_ast_canon_type_t *
|
||||||
|
scc_ast_mut_canon_type(scc_ast_qual_type_t *type) {
|
||||||
|
return type->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AST 操作符枚举
|
||||||
|
* 这个枚举定义了所有在AST中使用的操作符,与词法token分离
|
||||||
|
*/
|
||||||
|
typedef enum scc_ast_expr_op {
|
||||||
|
/* 无操作符 */
|
||||||
|
SCC_AST_OP_NONE = 0,
|
||||||
|
/* 赋值操作符 */
|
||||||
|
SCC_AST_OP_ASSIGN, // =
|
||||||
|
SCC_AST_OP_ASSIGN_ADD, // +=
|
||||||
|
SCC_AST_OP_ASSIGN_SUB, // -=
|
||||||
|
SCC_AST_OP_ASSIGN_MUL, // *=
|
||||||
|
SCC_AST_OP_ASSIGN_DIV, // /=
|
||||||
|
SCC_AST_OP_ASSIGN_MOD, // %=
|
||||||
|
SCC_AST_OP_ASSIGN_AND, // &=
|
||||||
|
SCC_AST_OP_ASSIGN_XOR, // ^=
|
||||||
|
SCC_AST_OP_ASSIGN_OR, // |=
|
||||||
|
SCC_AST_OP_ASSIGN_LSHIFT, // <<=
|
||||||
|
SCC_AST_OP_ASSIGN_RSHIFT, // >>=
|
||||||
|
/* 条件操作符 */
|
||||||
|
SCC_AST_OP_CONDITIONAL, // ?:
|
||||||
|
/* 逗号操作符 */
|
||||||
|
SCC_AST_OP_COMMA, // ,
|
||||||
|
/* 逻辑操作符 */
|
||||||
|
SCC_AST_OP_LOGICAL_OR, // ||
|
||||||
|
SCC_AST_OP_LOGICAL_AND, // &&
|
||||||
|
/* 位操作符 */
|
||||||
|
SCC_AST_OP_BITWISE_OR, // |
|
||||||
|
SCC_AST_OP_BITWISE_XOR, // ^
|
||||||
|
SCC_AST_OP_BITWISE_AND, // &
|
||||||
|
/* 相等性操作符 */
|
||||||
|
SCC_AST_OP_EQUAL, // ==
|
||||||
|
SCC_AST_OP_NOT_EQUAL, // !=
|
||||||
|
/* 关系操作符 */
|
||||||
|
SCC_AST_OP_LESS, // <
|
||||||
|
SCC_AST_OP_GREATER, // >
|
||||||
|
SCC_AST_OP_LESS_EQUAL, // <=
|
||||||
|
SCC_AST_OP_GREATER_EQUAL, // >=
|
||||||
|
/* 移位操作符 */
|
||||||
|
SCC_AST_OP_LEFT_SHIFT, // <<
|
||||||
|
SCC_AST_OP_RIGHT_SHIFT, // >>
|
||||||
|
/* 算术操作符 */
|
||||||
|
SCC_AST_OP_ADD, // +
|
||||||
|
SCC_AST_OP_SUB, // -
|
||||||
|
SCC_AST_OP_MUL, // *
|
||||||
|
SCC_AST_OP_DIV, // /
|
||||||
|
SCC_AST_OP_MOD, // %
|
||||||
|
/* 一元操作符 */
|
||||||
|
SCC_AST_OP_UNARY_PLUS, // + (一元)
|
||||||
|
SCC_AST_OP_UNARY_MINUS, // - (一元)
|
||||||
|
SCC_AST_OP_ADDRESS_OF, // &
|
||||||
|
SCC_AST_OP_INDIRECTION, // *
|
||||||
|
SCC_AST_OP_BITWISE_NOT, // ~
|
||||||
|
SCC_AST_OP_LOGICAL_NOT, // !
|
||||||
|
SCC_AST_OP_PREFIX_INCREMENT, // ++ (前缀)
|
||||||
|
SCC_AST_OP_PREFIX_DECREMENT, // -- (前缀)
|
||||||
|
SCC_AST_OP_POSTFIX_INCREMENT, // ++ (后缀)
|
||||||
|
SCC_AST_OP_POSTFIX_DECREMENT, // -- (后缀)
|
||||||
|
} scc_ast_expr_op_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCC_AST_EXPR_BUILTIN_VA_START,
|
||||||
|
SCC_AST_EXPR_BUILTIN_VA_END,
|
||||||
|
SCC_AST_EXPR_BUILTIN_VA_COPY,
|
||||||
|
SCC_AST_EXPR_BUILTIN_VA_ARG,
|
||||||
|
} scc_ast_builtin_expr_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 表达式节点
|
||||||
|
*/
|
||||||
|
struct scc_ast_expr {
|
||||||
|
scc_ast_node_t base;
|
||||||
|
union {
|
||||||
|
// 二元运算
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_op_t op;
|
||||||
|
scc_ast_expr_t *lhs;
|
||||||
|
scc_ast_expr_t *rhs;
|
||||||
|
} binary;
|
||||||
|
// 一元运算
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_op_t op;
|
||||||
|
scc_ast_expr_t *operand;
|
||||||
|
} unary;
|
||||||
|
// 条件表达式
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *cond;
|
||||||
|
scc_ast_expr_t *then_expr;
|
||||||
|
scc_ast_expr_t *else_expr;
|
||||||
|
} cond;
|
||||||
|
// 函数调用
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *callee;
|
||||||
|
scc_ast_expr_vec_t args;
|
||||||
|
} call;
|
||||||
|
// 数组下标
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *array;
|
||||||
|
scc_ast_expr_t *index;
|
||||||
|
} subscript;
|
||||||
|
// 成员访问 指针成员访问
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *base;
|
||||||
|
const char *name;
|
||||||
|
usize _target_idx; ///< fill by sema
|
||||||
|
} member;
|
||||||
|
// cast 类型转换
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *type;
|
||||||
|
scc_ast_expr_t *expr;
|
||||||
|
} cast;
|
||||||
|
// sizeof / _Alignof / ...
|
||||||
|
union {
|
||||||
|
scc_ast_qual_type_t *type;
|
||||||
|
scc_ast_expr_t *expr;
|
||||||
|
} attr_of;
|
||||||
|
// 复合字面量
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *base;
|
||||||
|
/// @brief 赋值语句的左值
|
||||||
|
scc_ast_expr_vec_t lhs_exprs;
|
||||||
|
/// @brief 赋值语句的右值
|
||||||
|
scc_ast_expr_vec_t rhs_exprs;
|
||||||
|
} compound;
|
||||||
|
// 字面量
|
||||||
|
struct {
|
||||||
|
const char *lexme;
|
||||||
|
cbool owned;
|
||||||
|
} literal;
|
||||||
|
// 标识符
|
||||||
|
struct {
|
||||||
|
const char *name;
|
||||||
|
scc_ast_decl_t *_target; ///< fill by sema
|
||||||
|
} identifier;
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *type;
|
||||||
|
} lvalue;
|
||||||
|
// 内置表达式
|
||||||
|
struct {
|
||||||
|
scc_ast_builtin_expr_type_t type;
|
||||||
|
scc_ast_expr_vec_t args;
|
||||||
|
} builtin;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 语句节点
|
||||||
|
*/
|
||||||
|
struct scc_ast_stmt {
|
||||||
|
scc_ast_node_t base;
|
||||||
|
union {
|
||||||
|
// 复合语句
|
||||||
|
struct {
|
||||||
|
scc_ast_block_item_vec_t block_items; // decl or stmt
|
||||||
|
} compound;
|
||||||
|
// 表达式语句
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *expr;
|
||||||
|
} expr;
|
||||||
|
// if 语句
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *cond;
|
||||||
|
scc_ast_stmt_t *then_stmt;
|
||||||
|
scc_ast_stmt_t *opt_else_stmt; // stmt or nullptr
|
||||||
|
} if_stmt;
|
||||||
|
// while do-while 语句
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *cond;
|
||||||
|
scc_ast_stmt_t *body;
|
||||||
|
} while_stmt;
|
||||||
|
// for 语句
|
||||||
|
struct {
|
||||||
|
scc_ast_node_t *init; // expr or decl or nullptr
|
||||||
|
scc_ast_expr_t *cond; // 可为 nullptr
|
||||||
|
scc_ast_expr_t *incr; // 可为 nullptr
|
||||||
|
scc_ast_stmt_t *body;
|
||||||
|
} for_stmt;
|
||||||
|
// switch 语句
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *cond;
|
||||||
|
scc_ast_stmt_t *body;
|
||||||
|
} switch_stmt;
|
||||||
|
// case 语句
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *expr;
|
||||||
|
scc_ast_stmt_t *stmt;
|
||||||
|
} case_stmt;
|
||||||
|
// default 语句
|
||||||
|
struct {
|
||||||
|
scc_ast_stmt_t *stmt;
|
||||||
|
} default_stmt;
|
||||||
|
// break/continue
|
||||||
|
struct {
|
||||||
|
scc_ast_stmt_t *_target; // fill by sema
|
||||||
|
} jump;
|
||||||
|
// return 语句
|
||||||
|
struct {
|
||||||
|
scc_ast_expr_t *expr; // 可为 nullptr
|
||||||
|
} return_stmt;
|
||||||
|
// goto 语句
|
||||||
|
struct {
|
||||||
|
const char *label;
|
||||||
|
scc_ast_stmt_t *_target; // fill by sema
|
||||||
|
} goto_stmt;
|
||||||
|
// 标签语句
|
||||||
|
struct {
|
||||||
|
const char *label;
|
||||||
|
scc_ast_stmt_t *stmt;
|
||||||
|
} label_stmt;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 声明节点
|
||||||
|
*/
|
||||||
|
struct scc_ast_decl {
|
||||||
|
scc_ast_node_t base;
|
||||||
|
const char *name;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
scc_ast_decl_vec_t vars;
|
||||||
|
} list;
|
||||||
|
// 变量声明
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *type;
|
||||||
|
scc_ast_expr_t *init; // 可为 nullptr
|
||||||
|
} var;
|
||||||
|
// 函数声明
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *type; // 函数类型
|
||||||
|
scc_ast_stmt_t *body; // 可为 nullptr 表示只有声明
|
||||||
|
} func;
|
||||||
|
// 参数声明
|
||||||
|
struct {
|
||||||
|
scc_ast_qual_type_t *type;
|
||||||
|
int param_idx;
|
||||||
|
} param;
|
||||||
|
// 结构体/联合/枚举声明
|
||||||
|
struct {
|
||||||
|
/// @brief 结构体/联合/枚举的字段
|
||||||
|
scc_ast_decl_vec_t fields;
|
||||||
|
} record;
|
||||||
|
struct {
|
||||||
|
/// @brief 被 typedef 的类型
|
||||||
|
scc_ast_qual_type_t *type;
|
||||||
|
} typedef_decl;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 翻译单元节点(根节点)
|
||||||
|
*/
|
||||||
|
typedef struct scc_ast_translation_unit {
|
||||||
|
scc_ast_node_t base;
|
||||||
|
scc_ast_decl_vec_t declarations;
|
||||||
|
} scc_ast_translation_unit_t;
|
||||||
|
|
||||||
|
#endif /* __SCC_AST_DEF_H__ */
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @file ast_dump.h
|
||||||
|
* @brief AST dump 工具,支持多种输出格式(插件化设计)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SCC_AST_DUMP_H__
|
||||||
|
#define __SCC_AST_DUMP_H__
|
||||||
|
|
||||||
|
#include "scc_ast_def.h"
|
||||||
|
#include <scc_tree_dump.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 以指定格式 dump AST
|
||||||
|
*
|
||||||
|
* @param node AST 节点(可以是任意类型的节点)
|
||||||
|
* @param ctx dump 上下文
|
||||||
|
*/
|
||||||
|
void scc_ast_dump_node(scc_tree_dump_t *ctx, const scc_ast_node_t *node);
|
||||||
|
|
||||||
|
#endif /* __SCC_AST_DUMP_H__ */
|
||||||
@@ -0,0 +1,505 @@
|
|||||||
|
#ifndef __SCC_AST_UTILS_H__
|
||||||
|
#define __SCC_AST_UTILS_H__
|
||||||
|
|
||||||
|
#include "scc_ast_def.h"
|
||||||
|
|
||||||
|
// decls can be nullptr but maybe warning
|
||||||
|
static inline void
|
||||||
|
scc_ast_translation_unit_init(scc_ast_translation_unit_t *translation_unit,
|
||||||
|
scc_ast_decl_vec_t *decls, scc_pos_t loc) {
|
||||||
|
Assert(translation_unit != nullptr);
|
||||||
|
translation_unit->base.type = SCC_AST_TRANSLATION_UNIT;
|
||||||
|
translation_unit->base.loc = loc;
|
||||||
|
if (decls == nullptr) {
|
||||||
|
scc_vec_init(translation_unit->declarations);
|
||||||
|
} else {
|
||||||
|
translation_unit->declarations = *decls;
|
||||||
|
scc_vec_init(*decls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_decl_list_init(scc_ast_decl_t *decl,
|
||||||
|
scc_ast_decl_vec_t *list_move,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(decl != nullptr && list_move != nullptr);
|
||||||
|
decl->base.loc = loc;
|
||||||
|
decl->base.type = SCC_AST_DECL_LIST;
|
||||||
|
decl->name = nullptr;
|
||||||
|
decl->list.vars = *list_move;
|
||||||
|
scc_vec_init(*list_move);
|
||||||
|
}
|
||||||
|
|
||||||
|
// name and var_init can be nullptr
|
||||||
|
static inline void scc_ast_decl_unsafe_val_init(scc_ast_decl_t *decl,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_expr_t *var_init,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(decl != nullptr && type != nullptr);
|
||||||
|
decl->base.loc = loc;
|
||||||
|
decl->base.type = SCC_AST_DECL_VAR;
|
||||||
|
decl->name = name;
|
||||||
|
decl->var.type = type;
|
||||||
|
decl->var.init = var_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
// var_init can be nullptr
|
||||||
|
static inline void scc_ast_decl_val_init(scc_ast_decl_t *decl,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_expr_t *var_init,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(decl != nullptr && name != nullptr && type != nullptr);
|
||||||
|
decl->base.loc = loc;
|
||||||
|
decl->base.type = SCC_AST_DECL_VAR;
|
||||||
|
decl->name = name;
|
||||||
|
decl->var.type = type;
|
||||||
|
decl->var.init = var_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
// body can be nullptr
|
||||||
|
static inline void scc_ast_decl_func_init(scc_ast_decl_t *decl,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_stmt_t *body, scc_pos_t loc) {
|
||||||
|
Assert(decl != nullptr && name != nullptr && type != nullptr);
|
||||||
|
decl->base.loc = loc;
|
||||||
|
decl->base.type = SCC_AST_DECL_FUNC;
|
||||||
|
decl->name = name;
|
||||||
|
decl->func.type = type;
|
||||||
|
Assert(type->base.type == SCC_AST_TYPE_FUNCTION);
|
||||||
|
decl->func.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
// name can be nullptr
|
||||||
|
static inline void scc_ast_decl_param_init(scc_ast_decl_t *decl,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
const char *name, int idx,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(decl != nullptr && type != nullptr);
|
||||||
|
decl->base.loc = loc;
|
||||||
|
decl->base.type = SCC_AST_DECL_PARAM;
|
||||||
|
decl->name = name;
|
||||||
|
decl->param.type = type;
|
||||||
|
decl->param.param_idx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _scc_ast_decl_record_init(scc_ast_decl_t *decl,
|
||||||
|
scc_ast_node_kind_t type,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_decl_vec_t *fields_move,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(decl != nullptr);
|
||||||
|
if (name == nullptr) {
|
||||||
|
name = "<anonymous>";
|
||||||
|
}
|
||||||
|
decl->base.loc = loc;
|
||||||
|
decl->base.type = type;
|
||||||
|
decl->name = name;
|
||||||
|
if (fields_move == nullptr) {
|
||||||
|
scc_vec_init(decl->record.fields);
|
||||||
|
} else {
|
||||||
|
decl->record.fields = *fields_move;
|
||||||
|
scc_vec_init(*fields_move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// name and fields can be nullptr
|
||||||
|
static inline void scc_ast_decl_struct_init(scc_ast_decl_t *decl,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_decl_vec_t *fields_move,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
_scc_ast_decl_record_init(decl, SCC_AST_DECL_STRUCT, name, fields_move,
|
||||||
|
loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// name and fields can be nullptr
|
||||||
|
static inline void scc_ast_decl_union_init(scc_ast_decl_t *decl,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_decl_vec_t *fields_move,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
_scc_ast_decl_record_init(decl, SCC_AST_DECL_UNION, name, fields_move, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// name and fields can be nullptr
|
||||||
|
static inline void scc_ast_decl_enum_init(scc_ast_decl_t *decl,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_decl_vec_t *fields_move,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
_scc_ast_decl_record_init(decl, SCC_AST_DECL_ENUM, name, fields_move, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_decl_typedef_init(scc_ast_decl_t *decl,
|
||||||
|
const char *name,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(decl != nullptr && name != nullptr && type != nullptr);
|
||||||
|
decl->base.loc = loc;
|
||||||
|
decl->base.type = SCC_AST_DECL_TYPEDEF;
|
||||||
|
decl->name = name;
|
||||||
|
decl->typedef_decl.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// items can be nullptr
|
||||||
|
static inline void scc_ast_stmt_compound_init(
|
||||||
|
scc_ast_stmt_t *stmt, scc_ast_block_item_vec_t *items_move, scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_COMPOUND;
|
||||||
|
if (items_move == nullptr) {
|
||||||
|
scc_vec_init(stmt->compound.block_items);
|
||||||
|
} else {
|
||||||
|
stmt->compound.block_items = *items_move;
|
||||||
|
scc_vec_init(*items_move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// expr can be nullptr
|
||||||
|
static inline void scc_ast_stmt_expr_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_expr_t *expr, scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_EXPR;
|
||||||
|
stmt->expr.expr = expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// opt_else can be nullptr
|
||||||
|
static inline void scc_ast_stmt_if_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_expr_t *cond,
|
||||||
|
scc_ast_stmt_t *then,
|
||||||
|
scc_ast_stmt_t *opt_else,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && cond != nullptr && then != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_IF;
|
||||||
|
stmt->if_stmt.cond = cond;
|
||||||
|
stmt->if_stmt.then_stmt = then;
|
||||||
|
stmt->if_stmt.opt_else_stmt = opt_else;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_while_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_expr_t *cond,
|
||||||
|
scc_ast_stmt_t *body,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && cond != nullptr && body != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_WHILE;
|
||||||
|
stmt->while_stmt.cond = cond;
|
||||||
|
stmt->while_stmt.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_do_while_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_expr_t *cond,
|
||||||
|
scc_ast_stmt_t *body,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && cond != nullptr && body != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_DO_WHILE;
|
||||||
|
stmt->while_stmt.cond = cond;
|
||||||
|
stmt->while_stmt.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
static inline void scc_ast_stmt_for_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_node_t *init,
|
||||||
|
scc_ast_expr_t *cond,
|
||||||
|
scc_ast_expr_t *incr,
|
||||||
|
scc_ast_stmt_t *body, scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && body != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_FOR;
|
||||||
|
stmt->for_stmt.init = init;
|
||||||
|
stmt->for_stmt.cond = cond;
|
||||||
|
stmt->for_stmt.incr = incr;
|
||||||
|
stmt->for_stmt.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_switch_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_expr_t *cond,
|
||||||
|
scc_ast_stmt_t *body,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && cond != nullptr && body != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_SWITCH;
|
||||||
|
stmt->switch_stmt.cond = cond;
|
||||||
|
stmt->switch_stmt.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_case_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_expr_t *cond,
|
||||||
|
scc_ast_stmt_t *body, scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && cond != nullptr && body != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_CASE;
|
||||||
|
stmt->case_stmt.expr = cond;
|
||||||
|
stmt->case_stmt.stmt = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_default_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_stmt_t *body,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && body != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_DEFAULT;
|
||||||
|
stmt->default_stmt.stmt = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_break_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_BREAK;
|
||||||
|
stmt->jump._target = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_continue_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_CONTINUE;
|
||||||
|
stmt->jump._target = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expr can be nullptr
|
||||||
|
static inline void scc_ast_stmt_return_init(scc_ast_stmt_t *stmt,
|
||||||
|
scc_ast_expr_t *expr,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_RETURN;
|
||||||
|
stmt->return_stmt.expr = expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_goto_init(scc_ast_stmt_t *stmt,
|
||||||
|
const char *label, scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr && label != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_GOTO;
|
||||||
|
stmt->goto_stmt.label = label;
|
||||||
|
stmt->goto_stmt._target = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_stmt_label_init(scc_ast_stmt_t *stmt,
|
||||||
|
const char *label,
|
||||||
|
scc_ast_stmt_t *body,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(stmt != nullptr);
|
||||||
|
stmt->base.loc = loc;
|
||||||
|
stmt->base.type = SCC_AST_STMT_LABEL;
|
||||||
|
stmt->label_stmt.label = label;
|
||||||
|
stmt->label_stmt.stmt = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_binary_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_op_t op,
|
||||||
|
scc_ast_expr_t *lhs,
|
||||||
|
scc_ast_expr_t *rhs,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && lhs != nullptr && rhs != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_BINARY;
|
||||||
|
expr->binary.op = op;
|
||||||
|
expr->binary.lhs = lhs;
|
||||||
|
expr->binary.rhs = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_unary_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_op_t op,
|
||||||
|
scc_ast_expr_t *operand,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && operand != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_UNARY;
|
||||||
|
expr->unary.op = op;
|
||||||
|
expr->unary.operand = operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_cond_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_t *cond,
|
||||||
|
scc_ast_expr_t *then_expr,
|
||||||
|
scc_ast_expr_t *else_expr,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && cond != nullptr && then_expr != nullptr &&
|
||||||
|
else_expr != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_COND;
|
||||||
|
expr->cond.cond = cond;
|
||||||
|
expr->cond.then_expr = then_expr;
|
||||||
|
expr->cond.else_expr = else_expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// args can be nullptr
|
||||||
|
static inline void scc_ast_expr_call_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_t *callee,
|
||||||
|
scc_ast_expr_vec_t *args,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && callee != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_CALL;
|
||||||
|
expr->call.callee = callee;
|
||||||
|
if (args == nullptr) {
|
||||||
|
scc_vec_init(expr->call.args);
|
||||||
|
} else {
|
||||||
|
expr->call.args = *args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCC_AST_EXPR_ARRAY_SUBSCRIPT, // 数组下标
|
||||||
|
static inline void scc_ast_expr_array_subscript_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_t *array,
|
||||||
|
scc_ast_expr_t *index,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && array != nullptr && index != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_ARRAY_SUBSCRIPT;
|
||||||
|
expr->subscript.array = array;
|
||||||
|
expr->subscript.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _scc_ast_expr_member_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_node_kind_t type,
|
||||||
|
scc_ast_expr_t *object,
|
||||||
|
const char *member,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && object != nullptr && member != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = type;
|
||||||
|
expr->member.base = object;
|
||||||
|
expr->member.name = member;
|
||||||
|
expr->member._target_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_member_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_t *object,
|
||||||
|
const char *member, scc_pos_t loc) {
|
||||||
|
_scc_ast_expr_member_init(expr, SCC_AST_EXPR_MEMBER, object, member, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_ptr_member_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_t *object,
|
||||||
|
const char *member,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
_scc_ast_expr_member_init(expr, SCC_AST_EXPR_PTR_MEMBER, object, member,
|
||||||
|
loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_cast_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
scc_ast_expr_t *operand,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && type != nullptr && operand != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_CAST;
|
||||||
|
expr->cast.type = type;
|
||||||
|
expr->cast.expr = operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
// type and target_expr can be nullptr but it only one of them can be nullptr
|
||||||
|
static inline void scc_ast_expr_sizeof_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
scc_ast_expr_t *target_expr,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_SIZE_OF;
|
||||||
|
expr->attr_of.type = type;
|
||||||
|
expr->attr_of.expr = target_expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// type and target_expr can be nullptr but it only one of them can be nullptr
|
||||||
|
static inline void scc_ast_expr_alignof_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
scc_ast_expr_t *target_expr,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type =
|
||||||
|
SCC_AST_EXPR_SIZE_OF; // 注意:这里使用了 SIZE_OF,可能需要改为 ALIGN_OF
|
||||||
|
expr->attr_of.type = type;
|
||||||
|
expr->attr_of.expr = target_expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lhs_exprs and rhs_exprs can be nullptr
|
||||||
|
static inline void scc_ast_expr_compound_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_expr_t *base,
|
||||||
|
scc_ast_expr_vec_t *lhs_exprs,
|
||||||
|
scc_ast_expr_vec_t *rhs_exprs,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && base != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_COMPOUND;
|
||||||
|
expr->compound.base = base;
|
||||||
|
|
||||||
|
if (lhs_exprs == nullptr) {
|
||||||
|
scc_vec_init(expr->compound.lhs_exprs);
|
||||||
|
} else {
|
||||||
|
expr->compound.lhs_exprs = *lhs_exprs;
|
||||||
|
scc_vec_init(*lhs_exprs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhs_exprs == nullptr) {
|
||||||
|
scc_vec_init(expr->compound.rhs_exprs);
|
||||||
|
} else {
|
||||||
|
expr->compound.rhs_exprs = *rhs_exprs;
|
||||||
|
scc_vec_init(*rhs_exprs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_literal_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_node_kind_t type,
|
||||||
|
const char *value, cbool owned,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && value != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = type;
|
||||||
|
expr->literal.lexme = value;
|
||||||
|
expr->literal.owned = owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_literal_int_init(scc_ast_expr_t *expr,
|
||||||
|
const char *value, cbool owned,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
scc_ast_expr_literal_init(expr, SCC_AST_EXPR_INT_LITERAL, value, owned,
|
||||||
|
loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_literal_float_init(scc_ast_expr_t *expr,
|
||||||
|
const char *value,
|
||||||
|
cbool owned, scc_pos_t loc) {
|
||||||
|
scc_ast_expr_literal_init(expr, SCC_AST_EXPR_FLOAT_LITERAL, value, owned,
|
||||||
|
loc);
|
||||||
|
}
|
||||||
|
static inline void scc_ast_expr_literal_char_init(scc_ast_expr_t *expr,
|
||||||
|
const char *value,
|
||||||
|
cbool owned, scc_pos_t loc) {
|
||||||
|
scc_ast_expr_literal_init(expr, SCC_AST_EXPR_CHAR_LITERAL, value, owned,
|
||||||
|
loc);
|
||||||
|
}
|
||||||
|
static inline void scc_ast_expr_literal_string_init(scc_ast_expr_t *expr,
|
||||||
|
const char *value,
|
||||||
|
cbool owned,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
scc_ast_expr_literal_init(expr, SCC_AST_EXPR_STRING_LITERAL, value, owned,
|
||||||
|
loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_identifier_init(scc_ast_expr_t *expr,
|
||||||
|
const char *name,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && name != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_IDENTIFIER;
|
||||||
|
expr->identifier.name = name;
|
||||||
|
expr->identifier._target = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_ast_expr_lvalue_init(scc_ast_expr_t *expr,
|
||||||
|
scc_ast_qual_type_t *type,
|
||||||
|
scc_pos_t loc) {
|
||||||
|
Assert(expr != nullptr && type != nullptr);
|
||||||
|
expr->base.loc = loc;
|
||||||
|
expr->base.type = SCC_AST_EXPR_LVALUE;
|
||||||
|
expr->lvalue.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __SCC_AST_UTILS_H__ */
|
||||||
@@ -0,0 +1,587 @@
|
|||||||
|
/**
|
||||||
|
* @file ast_dump.c
|
||||||
|
* @brief AST dump 实现
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <scc_ast_dump.h>
|
||||||
|
#include <scc_tree_dump.h>
|
||||||
|
|
||||||
|
static const char *node_type_names[] = {
|
||||||
|
[SCC_AST_UNKNOWN] = "Unknown",
|
||||||
|
[scc_ast_decl_t_BEGIN] = "ERROR",
|
||||||
|
[SCC_AST_DECL_LIST] = "DeclList",
|
||||||
|
[SCC_AST_DECL_VAR] = "VarDecl",
|
||||||
|
[SCC_AST_DECL_FUNC] = "FuncDecl",
|
||||||
|
[SCC_AST_DECL_PARAM] = "ParamDecl",
|
||||||
|
[SCC_AST_DECL_STRUCT] = "StructDecl",
|
||||||
|
[SCC_AST_DECL_UNION] = "UnionDecl",
|
||||||
|
[SCC_AST_DECL_ENUM] = "EnumDecl",
|
||||||
|
[SCC_AST_DECL_TYPEDEF] = "TypedefDecl",
|
||||||
|
[scc_ast_decl_t_END] = "ERROR",
|
||||||
|
[scc_ast_stmt_t_BEGIN] = "ERROR",
|
||||||
|
[SCC_AST_STMT_COMPOUND] = "CompoundStmt",
|
||||||
|
[SCC_AST_STMT_EXPR] = "ExprStmt",
|
||||||
|
[SCC_AST_STMT_IF] = "IfStmt",
|
||||||
|
[SCC_AST_STMT_WHILE] = "WhileStmt",
|
||||||
|
[SCC_AST_STMT_DO_WHILE] = "DoWhileStmt",
|
||||||
|
[SCC_AST_STMT_FOR] = "ForStmt",
|
||||||
|
[SCC_AST_STMT_SWITCH] = "SwitchStmt",
|
||||||
|
[SCC_AST_STMT_CASE] = "CaseStmt",
|
||||||
|
[SCC_AST_STMT_DEFAULT] = "DefaultStmt",
|
||||||
|
[SCC_AST_STMT_BREAK] = "BreakStmt",
|
||||||
|
[SCC_AST_STMT_CONTINUE] = "ContinueStmt",
|
||||||
|
[SCC_AST_STMT_RETURN] = "ReturnStmt",
|
||||||
|
[SCC_AST_STMT_GOTO] = "GotoStmt",
|
||||||
|
[SCC_AST_STMT_LABEL] = "LabelStmt",
|
||||||
|
[scc_ast_stmt_t_END] = "ERROR",
|
||||||
|
[scc_ast_expr_t_BEGIN] = "ERROR",
|
||||||
|
[SCC_AST_EXPR_BINARY] = "BinaryExpr",
|
||||||
|
[SCC_AST_EXPR_UNARY] = "UnaryExpr",
|
||||||
|
[SCC_AST_EXPR_COND] = "ConditionalExpr",
|
||||||
|
[SCC_AST_EXPR_CALL] = "CallExpr",
|
||||||
|
[SCC_AST_EXPR_ARRAY_SUBSCRIPT] = "ArraySubscriptExpr",
|
||||||
|
[SCC_AST_EXPR_MEMBER] = "MemberExpr",
|
||||||
|
[SCC_AST_EXPR_PTR_MEMBER] = "PtrMemberExpr",
|
||||||
|
[SCC_AST_EXPR_CAST] = "CastExpr",
|
||||||
|
[SCC_AST_EXPR_SIZE_OF] = "SizeofExpr",
|
||||||
|
[SCC_AST_EXPR_ALIGN_OF] = "AlignofExpr",
|
||||||
|
[SCC_AST_EXPR_COMPOUND] = "CompoundExpr",
|
||||||
|
[SCC_AST_EXPR_LVALUE] = "LvalueExpr",
|
||||||
|
[SCC_AST_EXPR_INT_LITERAL] = "IntLiteralExpr",
|
||||||
|
[SCC_AST_EXPR_FLOAT_LITERAL] = "FloatLiteralExpr",
|
||||||
|
[SCC_AST_EXPR_CHAR_LITERAL] = "CharLiteralExpr",
|
||||||
|
[SCC_AST_EXPR_STRING_LITERAL] = "StringLiteralExpr",
|
||||||
|
[SCC_AST_EXPR_IDENTIFIER] = "IdentifierExpr",
|
||||||
|
[scc_ast_expr_t_END] = "ERROR",
|
||||||
|
[scc_ast_qual_type_t_BEGIN] = "ERROR",
|
||||||
|
[SCC_AST_TYPE_BUILTIN] = "BuiltinType",
|
||||||
|
[SCC_AST_TYPE_POINTER] = "PointerType",
|
||||||
|
[SCC_AST_TYPE_ARRAY] = "ArrayType",
|
||||||
|
[SCC_AST_TYPE_FUNCTION] = "FunctionType",
|
||||||
|
[SCC_AST_TYPE_STRUCT] = "StructType",
|
||||||
|
[SCC_AST_TYPE_UNION] = "UnionType",
|
||||||
|
[SCC_AST_TYPE_ENUM] = "EnumType",
|
||||||
|
[SCC_AST_TYPE_TYPEDEF] = "TypedefType",
|
||||||
|
[scc_ast_qual_type_t_END] = "ERROR",
|
||||||
|
[scc_ast_translation_unit_t_BEGIN] = "ERROR",
|
||||||
|
[SCC_AST_TRANSLATION_UNIT] = "TranslationUnit",
|
||||||
|
[scc_ast_translation_unit_t_END] = "ERROR",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *get_node_type_str(scc_ast_node_kind_t type) {
|
||||||
|
return node_type_names[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *builtin_type_names[] = {
|
||||||
|
[SCC_AST_BUILTIN_TYPE_UNKNOWN] = "<unknown type>",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_VA_LIST] = "...",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_VOID] = "void",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_BOOL] = "bool",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_CHAR] = "char",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_SHORT] = "short",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_INT] = "int",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_LONG] = "long",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_LONG_LONG] = "long long",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR] = "unsigned char",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT] = "unsigned short",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_UNSIGNED_INT] = "unsigned int",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG] = "unsigned long",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG] = "unsigned long long",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_SIGNED_CHAR] = "signed char",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_SIGNED_SHORT] = "signed short",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_SIGNED_INT] = "signed int",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_SIGNED_LONG] = "signed long",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG] = "signed long long",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_FLOAT] = "float",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_DOUBLE] = "double",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_LONG_DOUBLE] = "long double",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_COMPLEX_FLOAT] = "complex float",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_COMPLEX_DOUBLE] = "complex double",
|
||||||
|
[SCC_AST_BUILTIN_TYPE_COMPLEX_LONG_DOUBLE] = "complex long double",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *get_builtin_type_str(scc_ast_builtin_type_t type) {
|
||||||
|
return builtin_type_names[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_op_str(scc_ast_expr_op_t op) {
|
||||||
|
switch (op) {
|
||||||
|
case SCC_AST_OP_ASSIGN:
|
||||||
|
return "=";
|
||||||
|
case SCC_AST_OP_ASSIGN_ADD:
|
||||||
|
return "+=";
|
||||||
|
case SCC_AST_OP_ASSIGN_SUB:
|
||||||
|
return "-=";
|
||||||
|
case SCC_AST_OP_ASSIGN_MUL:
|
||||||
|
return "*=";
|
||||||
|
case SCC_AST_OP_ASSIGN_DIV:
|
||||||
|
return "/=";
|
||||||
|
case SCC_AST_OP_ASSIGN_MOD:
|
||||||
|
return "%=";
|
||||||
|
case SCC_AST_OP_ASSIGN_AND:
|
||||||
|
return "&=";
|
||||||
|
case SCC_AST_OP_ASSIGN_XOR:
|
||||||
|
return "^=";
|
||||||
|
case SCC_AST_OP_ASSIGN_OR:
|
||||||
|
return "|=";
|
||||||
|
case SCC_AST_OP_ASSIGN_LSHIFT:
|
||||||
|
return "<<=";
|
||||||
|
case SCC_AST_OP_ASSIGN_RSHIFT:
|
||||||
|
return ">>=";
|
||||||
|
case SCC_AST_OP_CONDITIONAL:
|
||||||
|
return "? :";
|
||||||
|
case SCC_AST_OP_LOGICAL_OR:
|
||||||
|
return "||";
|
||||||
|
case SCC_AST_OP_LOGICAL_AND:
|
||||||
|
return "&&";
|
||||||
|
case SCC_AST_OP_BITWISE_OR:
|
||||||
|
return "|";
|
||||||
|
case SCC_AST_OP_BITWISE_XOR:
|
||||||
|
return "^";
|
||||||
|
case SCC_AST_OP_BITWISE_AND:
|
||||||
|
return "&";
|
||||||
|
case SCC_AST_OP_EQUAL:
|
||||||
|
return "==";
|
||||||
|
case SCC_AST_OP_NOT_EQUAL:
|
||||||
|
return "!=";
|
||||||
|
case SCC_AST_OP_LESS:
|
||||||
|
return "<";
|
||||||
|
case SCC_AST_OP_GREATER:
|
||||||
|
return ">";
|
||||||
|
case SCC_AST_OP_LESS_EQUAL:
|
||||||
|
return "<=";
|
||||||
|
case SCC_AST_OP_GREATER_EQUAL:
|
||||||
|
return ">=";
|
||||||
|
case SCC_AST_OP_LEFT_SHIFT:
|
||||||
|
return "<<";
|
||||||
|
case SCC_AST_OP_RIGHT_SHIFT:
|
||||||
|
return ">>";
|
||||||
|
case SCC_AST_OP_ADD:
|
||||||
|
return "+";
|
||||||
|
case SCC_AST_OP_SUB:
|
||||||
|
return "-";
|
||||||
|
case SCC_AST_OP_MUL:
|
||||||
|
return "*";
|
||||||
|
case SCC_AST_OP_DIV:
|
||||||
|
return "/";
|
||||||
|
case SCC_AST_OP_MOD:
|
||||||
|
return "%";
|
||||||
|
case SCC_AST_OP_UNARY_PLUS:
|
||||||
|
return "+";
|
||||||
|
case SCC_AST_OP_UNARY_MINUS:
|
||||||
|
return "-";
|
||||||
|
case SCC_AST_OP_ADDRESS_OF:
|
||||||
|
return "&";
|
||||||
|
case SCC_AST_OP_INDIRECTION:
|
||||||
|
return "*";
|
||||||
|
case SCC_AST_OP_BITWISE_NOT:
|
||||||
|
return "~";
|
||||||
|
case SCC_AST_OP_LOGICAL_NOT:
|
||||||
|
return "!";
|
||||||
|
case SCC_AST_OP_PREFIX_INCREMENT:
|
||||||
|
return "++";
|
||||||
|
case SCC_AST_OP_PREFIX_DECREMENT:
|
||||||
|
return "--";
|
||||||
|
case SCC_AST_OP_POSTFIX_INCREMENT:
|
||||||
|
return "++";
|
||||||
|
case SCC_AST_OP_POSTFIX_DECREMENT:
|
||||||
|
return "--";
|
||||||
|
default:
|
||||||
|
return "<op>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dump_child_node(scc_tree_dump_t *td, scc_ast_node_t *child,
|
||||||
|
cbool is_last) {
|
||||||
|
if (!child)
|
||||||
|
return;
|
||||||
|
scc_tree_dump_push(td, is_last);
|
||||||
|
scc_ast_dump_node(td, child);
|
||||||
|
scc_tree_dump_pop(td);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dump_quals(scc_tree_dump_t *td,
|
||||||
|
scc_ast_decl_specifier_t quals) {
|
||||||
|
if (quals.is_atomic)
|
||||||
|
scc_tree_dump_value(td, " atomic");
|
||||||
|
if (quals.is_restrict)
|
||||||
|
scc_tree_dump_value(td, " restrict");
|
||||||
|
if (quals.is_volatile)
|
||||||
|
scc_tree_dump_value(td, " volatile");
|
||||||
|
if (quals.is_const)
|
||||||
|
scc_tree_dump_value(td, " const");
|
||||||
|
if (quals.is_inline)
|
||||||
|
scc_tree_dump_value(td, " inline");
|
||||||
|
if (quals.is_extern)
|
||||||
|
scc_tree_dump_value(td, " extern");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_type_impl(scc_tree_dump_t *td,
|
||||||
|
const scc_ast_qual_type_t *type) {
|
||||||
|
if (!type)
|
||||||
|
return;
|
||||||
|
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_node(td, "%s", get_node_type_str(type->base.type));
|
||||||
|
dump_quals(td, type->quals);
|
||||||
|
|
||||||
|
switch (type->base.type) {
|
||||||
|
case SCC_AST_TYPE_BUILTIN:
|
||||||
|
scc_tree_dump_value(
|
||||||
|
td, " '%s'",
|
||||||
|
get_builtin_type_str(scc_ast_canon_type(type)->builtin.type));
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_POINTER:
|
||||||
|
scc_tree_dump_value(td, " pointer");
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_ARRAY:
|
||||||
|
scc_tree_dump_value(td, " array");
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_FUNCTION:
|
||||||
|
scc_tree_dump_value(td, " function");
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_STRUCT:
|
||||||
|
if (scc_ast_canon_type(type)->record.name)
|
||||||
|
scc_tree_dump_value(td, " 'struct %s'",
|
||||||
|
scc_ast_canon_type(type)->record.name);
|
||||||
|
else
|
||||||
|
scc_tree_dump_value(td, " <anonymous struct>");
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_UNION:
|
||||||
|
if (scc_ast_canon_type(type)->record.name)
|
||||||
|
scc_tree_dump_value(td, " 'union %s'",
|
||||||
|
scc_ast_canon_type(type)->record.name);
|
||||||
|
else
|
||||||
|
scc_tree_dump_value(td, " <anonymous union>");
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_ENUM:
|
||||||
|
if (scc_ast_canon_type(type)->record.name)
|
||||||
|
scc_tree_dump_value(td, " 'enum %s'",
|
||||||
|
scc_ast_canon_type(type)->record.name);
|
||||||
|
else
|
||||||
|
scc_tree_dump_value(td, " anonymous enum");
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_TYPEDEF:
|
||||||
|
scc_tree_dump_value(td, " '%s'",
|
||||||
|
scc_ast_canon_type(type)->typedef_type.name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type->base.type) {
|
||||||
|
case SCC_AST_TYPE_POINTER:
|
||||||
|
dump_child_node(
|
||||||
|
td, (scc_ast_node_t *)scc_ast_canon_type(type)->pointer.pointee,
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_ARRAY:
|
||||||
|
dump_child_node(
|
||||||
|
td, (scc_ast_node_t *)scc_ast_canon_type(type)->array.element,
|
||||||
|
scc_ast_canon_type(type)->array.size == nullptr);
|
||||||
|
if (scc_ast_canon_type(type)->array.size)
|
||||||
|
dump_child_node(
|
||||||
|
td, (scc_ast_node_t *)scc_ast_canon_type(type)->array.size,
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_TYPE_FUNCTION:
|
||||||
|
scc_tree_dump_push(td, false);
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_node(td, "%s", "ReturnType");
|
||||||
|
dump_child_node(
|
||||||
|
td,
|
||||||
|
(scc_ast_node_t *)scc_ast_canon_type(type)->function.return_type,
|
||||||
|
true);
|
||||||
|
scc_tree_dump_pop(td);
|
||||||
|
|
||||||
|
scc_tree_dump_push(td, true);
|
||||||
|
if (scc_vec_size(scc_ast_canon_type(type)->function.params) != 0) {
|
||||||
|
scc_vec_foreach(scc_ast_canon_type(type)->function.params, i) {
|
||||||
|
scc_ast_decl_t *param =
|
||||||
|
scc_vec_at(scc_ast_canon_type(type)->function.params, i);
|
||||||
|
dump_type_impl(td, param->param.type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_value(td, "()");
|
||||||
|
}
|
||||||
|
scc_tree_dump_pop(td);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_expr_impl(scc_tree_dump_t *td, const scc_ast_expr_t *expr) {
|
||||||
|
if (!expr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_node(td, "%s", get_node_type_str(expr->base.type));
|
||||||
|
|
||||||
|
switch (expr->base.type) {
|
||||||
|
case SCC_AST_EXPR_BINARY:
|
||||||
|
scc_tree_dump_value(td, " '%s'", get_op_str(expr->binary.op));
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_UNARY:
|
||||||
|
scc_tree_dump_value(td, " '%s'", get_op_str(expr->unary.op));
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_INT_LITERAL:
|
||||||
|
case SCC_AST_EXPR_FLOAT_LITERAL:
|
||||||
|
case SCC_AST_EXPR_CHAR_LITERAL:
|
||||||
|
case SCC_AST_EXPR_STRING_LITERAL:
|
||||||
|
scc_tree_dump_value(td, " %s", expr->literal.lexme);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_IDENTIFIER:
|
||||||
|
if (expr->identifier.name)
|
||||||
|
scc_tree_dump_value(td, " '%s'", expr->identifier.name);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_SIZE_OF:
|
||||||
|
case SCC_AST_EXPR_ALIGN_OF:
|
||||||
|
scc_tree_dump_value(
|
||||||
|
td, " '%s'",
|
||||||
|
(expr->base.type == SCC_AST_EXPR_SIZE_OF) ? "sizeof" : "alignof");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (expr->base.type) {
|
||||||
|
case SCC_AST_EXPR_BINARY:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->binary.lhs, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->binary.rhs, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_UNARY:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->unary.operand, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_COND:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->cond.cond, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->cond.then_expr, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->cond.else_expr, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_CALL:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->call.callee, false);
|
||||||
|
for (size_t i = 0; i < expr->call.args.size; i++) {
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->call.args.data[i],
|
||||||
|
i == expr->call.args.size - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_ARRAY_SUBSCRIPT:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->subscript.array, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->subscript.index, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_MEMBER:
|
||||||
|
case SCC_AST_EXPR_PTR_MEMBER:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->member.base, false);
|
||||||
|
scc_tree_dump_push(td, true);
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_node(
|
||||||
|
td, "%s",
|
||||||
|
expr->base.type == SCC_AST_EXPR_MEMBER ? "Member" : "PtrMember");
|
||||||
|
scc_tree_dump_value(td, " %s", expr->member.name);
|
||||||
|
scc_tree_dump_pop(td);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_CAST:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->cast.type, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->cast.expr, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_SIZE_OF:
|
||||||
|
case SCC_AST_EXPR_ALIGN_OF:
|
||||||
|
if (expr->attr_of.expr)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->attr_of.expr, true);
|
||||||
|
else if (expr->attr_of.type)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->attr_of.type, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_LVALUE:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->lvalue.type, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_EXPR_COMPOUND:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)expr->compound.base, false);
|
||||||
|
{
|
||||||
|
usize size = scc_vec_size(expr->compound.lhs_exprs);
|
||||||
|
for (usize i = 0; i < size; i++) {
|
||||||
|
dump_child_node(
|
||||||
|
td,
|
||||||
|
(scc_ast_node_t *)scc_vec_at(expr->compound.lhs_exprs, i),
|
||||||
|
false);
|
||||||
|
dump_child_node(
|
||||||
|
td,
|
||||||
|
(scc_ast_node_t *)scc_vec_at(expr->compound.rhs_exprs, i),
|
||||||
|
i + 1 == size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_stmt_impl(scc_tree_dump_t *td, const scc_ast_stmt_t *stmt) {
|
||||||
|
if (!stmt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_node(td, "%s", get_node_type_str(stmt->base.type));
|
||||||
|
|
||||||
|
switch (stmt->base.type) {
|
||||||
|
case SCC_AST_STMT_RETURN:
|
||||||
|
if (!stmt->return_stmt.expr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->return_stmt.expr, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_GOTO:
|
||||||
|
if (stmt->goto_stmt.label)
|
||||||
|
scc_tree_dump_value(td, " Label: %s", stmt->goto_stmt.label);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_LABEL:
|
||||||
|
if (stmt->label_stmt.label)
|
||||||
|
scc_tree_dump_value(td, " %s", stmt->label_stmt.label);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->label_stmt.stmt, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_BREAK:
|
||||||
|
case SCC_AST_STMT_CONTINUE:
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_DEFAULT:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->default_stmt.stmt, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_COMPOUND:
|
||||||
|
scc_vec_foreach(stmt->compound.block_items, i) {
|
||||||
|
dump_child_node(
|
||||||
|
td, (scc_ast_node_t *)scc_vec_at(stmt->compound.block_items, i),
|
||||||
|
i + 1 == stmt->compound.block_items.size);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_EXPR:
|
||||||
|
if (stmt->expr.expr)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->expr.expr, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_IF:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->if_stmt.cond, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->if_stmt.then_stmt,
|
||||||
|
!stmt->if_stmt.opt_else_stmt);
|
||||||
|
if (stmt->if_stmt.opt_else_stmt)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->if_stmt.opt_else_stmt,
|
||||||
|
true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_WHILE:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->while_stmt.cond, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->while_stmt.body, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_DO_WHILE:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->while_stmt.body, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->while_stmt.cond, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_SWITCH:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->switch_stmt.cond, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->switch_stmt.body, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_FOR:
|
||||||
|
if (stmt->for_stmt.init)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->for_stmt.init, false);
|
||||||
|
if (stmt->for_stmt.cond)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->for_stmt.cond, false);
|
||||||
|
if (stmt->for_stmt.incr)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->for_stmt.incr, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->for_stmt.body, true);
|
||||||
|
return;
|
||||||
|
case SCC_AST_STMT_CASE:
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->case_stmt.expr, false);
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)stmt->case_stmt.stmt, true);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_decl_impl(scc_tree_dump_t *td, const scc_ast_decl_t *decl) {
|
||||||
|
if (!decl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_node(td, "%s", get_node_type_str(decl->base.type));
|
||||||
|
if (decl->name)
|
||||||
|
scc_tree_dump_value(td, " '%s'", decl->name);
|
||||||
|
|
||||||
|
switch (decl->base.type) {
|
||||||
|
case SCC_AST_DECL_LIST:
|
||||||
|
scc_vec_foreach(decl->list.vars, i) {
|
||||||
|
dump_child_node(td,
|
||||||
|
(scc_ast_node_t *)scc_vec_at(decl->list.vars, i),
|
||||||
|
i + 1 == scc_vec_size(decl->list.vars));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_AST_DECL_VAR: {
|
||||||
|
if (decl->var.type) {
|
||||||
|
scc_ast_qual_type_t *type = decl->var.type;
|
||||||
|
dump_child_node(td, &type->base, decl->var.init == nullptr);
|
||||||
|
// if ((type->base.type == SCC_AST_TYPE_STRUCT ||
|
||||||
|
// type->base.type == SCC_AST_TYPE_ENUM ||
|
||||||
|
// type->base.type == SCC_AST_TYPE_UNION) &&
|
||||||
|
// scc_ast_canon_type(type)->record.decl) {
|
||||||
|
// dump_child_node(
|
||||||
|
// td, (scc_ast_node_t
|
||||||
|
// *)scc_ast_canon_type(type)->record.decl, true);
|
||||||
|
// }
|
||||||
|
if (decl->var.init)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)decl->var.init, true);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SCC_AST_DECL_FUNC:
|
||||||
|
if (decl->func.type) {
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)decl->func.type,
|
||||||
|
decl->func.body == nullptr);
|
||||||
|
if (decl->func.body)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)decl->func.body, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_AST_DECL_PARAM:
|
||||||
|
if (decl->param.type)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)decl->param.type, true);
|
||||||
|
break;
|
||||||
|
case SCC_AST_DECL_STRUCT:
|
||||||
|
case SCC_AST_DECL_UNION:
|
||||||
|
case SCC_AST_DECL_ENUM:
|
||||||
|
scc_vec_foreach(decl->record.fields, i) {
|
||||||
|
dump_child_node(
|
||||||
|
td, (scc_ast_node_t *)scc_vec_at(decl->record.fields, i),
|
||||||
|
i + 1 == scc_vec_size(decl->record.fields));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_AST_DECL_TYPEDEF:
|
||||||
|
if (decl->typedef_decl.type)
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)decl->typedef_decl.type,
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_unit_impl(scc_tree_dump_t *td,
|
||||||
|
const scc_ast_translation_unit_t *unit) {
|
||||||
|
if (!unit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
scc_tree_dump_begin_line(td);
|
||||||
|
scc_tree_dump_node(td, "%s", get_node_type_str(unit->base.type));
|
||||||
|
scc_vec_foreach(unit->declarations, i) {
|
||||||
|
dump_child_node(td, (scc_ast_node_t *)scc_vec_at(unit->declarations, i),
|
||||||
|
i + 1 == scc_vec_size(unit->declarations));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_ast_dump_node(scc_tree_dump_t *td, const scc_ast_node_t *node) {
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
if (SCC_AST_IS_A(scc_ast_expr_t, node))
|
||||||
|
dump_expr_impl(td, SCC_AST_CAST_TO(scc_ast_expr_t, node));
|
||||||
|
else if (SCC_AST_IS_A(scc_ast_stmt_t, node))
|
||||||
|
dump_stmt_impl(td, SCC_AST_CAST_TO(scc_ast_stmt_t, node));
|
||||||
|
else if (SCC_AST_IS_A(scc_ast_decl_t, node))
|
||||||
|
dump_decl_impl(td, SCC_AST_CAST_TO(scc_ast_decl_t, node));
|
||||||
|
else if (SCC_AST_IS_A(scc_ast_qual_type_t, node))
|
||||||
|
dump_type_impl(td, SCC_AST_CAST_TO(scc_ast_qual_type_t, node));
|
||||||
|
else if (SCC_AST_IS_A(scc_ast_translation_unit_t, node))
|
||||||
|
dump_unit_impl(td, SCC_AST_CAST_TO(scc_ast_translation_unit_t, node));
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#include <scc_ast.h>
|
||||||
|
|
||||||
|
static scc_ast_canon_type_t *alloc_canonical_type(scc_ast_ctx_t *ctx) {
|
||||||
|
scc_ast_canon_type_t *type = scc_malloc(sizeof(scc_ast_canon_type_t));
|
||||||
|
if (type == nullptr) {
|
||||||
|
Panic("alloc_canonical_type: malloc failed");
|
||||||
|
}
|
||||||
|
scc_vec_push(ctx->canonical_type_pool, type);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_ast_ctx_init(scc_ast_ctx_t *ctx) {
|
||||||
|
scc_vec_init(ctx->canonical_type_pool);
|
||||||
|
scc_vec_init(ctx->all_nodes);
|
||||||
|
|
||||||
|
// 创建全部内置类型
|
||||||
|
for (int i = 0; i < SCC_AST_BUILTIN_TYPE_COUNT; i += 1) {
|
||||||
|
scc_ast_canon_type_t *t = alloc_canonical_type(ctx);
|
||||||
|
t->builtin.type = (scc_ast_builtin_type_t)i; // 直接按顺序对应
|
||||||
|
ctx->builtin_types[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_ast_ctx_drop(scc_ast_ctx_t *ctx) {
|
||||||
|
// 释放所有规范类型
|
||||||
|
scc_vec_foreach(ctx->canonical_type_pool, i) {
|
||||||
|
scc_free(ctx->canonical_type_pool.data[i]);
|
||||||
|
}
|
||||||
|
scc_vec_foreach(ctx->all_nodes, i) { scc_free(ctx->all_nodes.data[i]); }
|
||||||
|
scc_vec_free(ctx->canonical_type_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *
|
||||||
|
scc_ast_ctx_get_builtin_type(scc_ast_ctx_t *ctx, scc_ast_builtin_type_t kind) {
|
||||||
|
assert(kind < SCC_AST_BUILTIN_TYPE_COUNT &&
|
||||||
|
kind > SCC_AST_BUILTIN_TYPE_UNKNOWN);
|
||||||
|
return ctx->builtin_types[kind];
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *scc_ast_ctx_alloc_type(scc_ast_ctx_t *ctx) {
|
||||||
|
return alloc_canonical_type(ctx);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void test_example() {
|
||||||
|
printf("Test passed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test_example();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "scc_ast2ir"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = []
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
{ name = "scc_ast", path = "../ast" },
|
||||||
|
{ name = "scc_hir", path = "../ir/hir" },
|
||||||
|
{ name = "scc_abi", path = "../abi" },
|
||||||
|
]
|
||||||
|
# features = {}
|
||||||
|
# default_features = []
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef __SCC_AST2IR_H__
|
||||||
|
#define __SCC_AST2IR_H__
|
||||||
|
|
||||||
|
#include <scc_ast.h>
|
||||||
|
#include <scc_hir_builder.h>
|
||||||
|
#include <scc_type_abi.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scc_hir_builder_t builder;
|
||||||
|
scc_hashtable_t ast2ir_cache; ///< ast node to ir ref cache
|
||||||
|
scc_hashtable_t break_cache; ///< break cache
|
||||||
|
scc_hashtable_t continue_cache; ///< continue cache
|
||||||
|
scc_hashtable_t symtab; ///< symbol to ir_ref
|
||||||
|
// scc_strpool_t strpool; ///< string pool
|
||||||
|
const scc_abi_type_calc_t *abi;
|
||||||
|
cbool hint_using_value; // 转换时尽可能使用value而不是alloc
|
||||||
|
|
||||||
|
scc_ast_ctx_t *ast_ctx;
|
||||||
|
} scc_ast2ir_ctx_t;
|
||||||
|
|
||||||
|
void scc_ast2ir_ctx_init(scc_ast2ir_ctx_t *ctx, const scc_abi_type_calc_t *abi,
|
||||||
|
scc_ast_ctx_t *ast_ctx, scc_hir_cprog_t *cprog);
|
||||||
|
void scc_ast2ir_ctx_drop(scc_ast2ir_ctx_t *ctx);
|
||||||
|
|
||||||
|
void scc_ast2ir_run(scc_ast2ir_ctx_t *ctx,
|
||||||
|
const scc_ast_translation_unit_t *tu);
|
||||||
|
void scc_ast2ir_decl(scc_ast2ir_ctx_t *ctx, const scc_ast_decl_t *decl,
|
||||||
|
cbool is_global);
|
||||||
|
scc_hir_value_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx,
|
||||||
|
const scc_ast_expr_t *expr,
|
||||||
|
cbool is_lvalue);
|
||||||
|
void scc_ast2ir_stmt(scc_ast2ir_ctx_t *ctx, const scc_ast_stmt_t *stmt);
|
||||||
|
scc_hir_type_ref_t scc_ast2ir_type(scc_ast2ir_ctx_t *ctx,
|
||||||
|
const scc_ast_qual_type_t *ast_type);
|
||||||
|
|
||||||
|
#endif /* __SCC_AST2IR_H__ */
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,14 @@
|
|||||||
|
#include <scc_ast2ir.h>
|
||||||
|
#include <utest/acutest.h>
|
||||||
|
|
||||||
|
void test_type(void) {}
|
||||||
|
|
||||||
|
void test_expr(void) {}
|
||||||
|
|
||||||
|
void test_stmt(void) {}
|
||||||
|
|
||||||
|
void test_decl(void) {}
|
||||||
|
|
||||||
|
TEST_LIST = {
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "scc_cfg"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = []
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
{ name = "scc_core", path = "../../../runtime/scc_core" },
|
||||||
|
{ name = "scc_utils", path = "../../../runtime/scc_utils" },
|
||||||
|
]
|
||||||
|
# features = {}
|
||||||
|
# default_features = []
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
#ifndef __SCC_CFG_H__
|
||||||
|
#define __SCC_CFG_H__
|
||||||
|
|
||||||
|
#include <scc_core.h>
|
||||||
|
#include <scc_hashtable.h>
|
||||||
|
|
||||||
|
#define SCC_CFG_ID_nullptr (0)
|
||||||
|
typedef usize scc_cfg_id_t;
|
||||||
|
typedef scc_cfg_id_t scc_cfg_bblock_id_t;
|
||||||
|
typedef scc_cfg_id_t scc_cfg_func_id_t;
|
||||||
|
typedef scc_cfg_id_t scc_cfg_symbol_id_t;
|
||||||
|
|
||||||
|
typedef SCC_VEC(scc_cfg_bblock_id_t) scc_cfg_bblock_id_vec_t;
|
||||||
|
typedef SCC_VEC(void *) scc_cfg_value_vec_t;
|
||||||
|
typedef struct scc_cfg_module scc_cfg_module_t;
|
||||||
|
typedef struct scc_cfg_func scc_cfg_func_t;
|
||||||
|
typedef struct scc_cfg_bblock scc_cfg_bblock_t;
|
||||||
|
typedef struct scc_cfg_symbol scc_cfg_symbol_t;
|
||||||
|
|
||||||
|
struct scc_cfg_bblock {
|
||||||
|
scc_cfg_bblock_id_t id;
|
||||||
|
const char *name;
|
||||||
|
scc_cfg_bblock_id_vec_t preds; // 前驱
|
||||||
|
scc_cfg_bblock_id_vec_t succs; // 后继
|
||||||
|
scc_cfg_value_vec_t values; // using cast
|
||||||
|
void *meta;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scc_cfg_func {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
scc_cfg_bblock_id_t next_bblock_id;
|
||||||
|
scc_cfg_bblock_id_vec_t bblocks;
|
||||||
|
scc_hashtable_t bblock_map; // id -> index
|
||||||
|
|
||||||
|
scc_cfg_bblock_id_t entry_bblock_id; // maybe it will always 0
|
||||||
|
void *meta;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCC_CFG_SYMBOL_KIND_FUNC,
|
||||||
|
SCC_CFG_SYMBOL_KIND_DATA,
|
||||||
|
SCC_CFG_SYMBOL_KIND_EXTERN,
|
||||||
|
} scc_cfg_symbol_kind_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCC_CFG_SYMBOL_LINK_GLOABL,
|
||||||
|
SCC_CFG_SYMBOL_LINK_LOCAL,
|
||||||
|
} scc_cfg_symbol_linkage_t;
|
||||||
|
|
||||||
|
struct scc_cfg_symbol {
|
||||||
|
const char *name;
|
||||||
|
scc_cfg_symbol_kind_t kind;
|
||||||
|
scc_cfg_symbol_linkage_t linkage;
|
||||||
|
void *meta;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SCC_VEC(scc_cfg_bblock_t) scc_cfg_bblock_vec_t;
|
||||||
|
typedef SCC_VEC(scc_cfg_func_t) scc_cfg_func_vec_t;
|
||||||
|
typedef SCC_VEC(scc_cfg_symbol_t) scc_cfg_symbol_vec_t;
|
||||||
|
struct scc_cfg_module {
|
||||||
|
scc_cfg_bblock_vec_t bblocks;
|
||||||
|
scc_cfg_func_vec_t funcs; /* 所有函数定义(按添加顺序) */
|
||||||
|
scc_cfg_symbol_vec_t symbols; /* 全局符号表 */
|
||||||
|
scc_hashtable_t symbol_map; /* 名称 -> 索引 */
|
||||||
|
};
|
||||||
|
|
||||||
|
void scc_cfg_module_init(scc_cfg_module_t *module);
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param module
|
||||||
|
* @warning 由于void*存在可能导致严重内存泄漏
|
||||||
|
*/
|
||||||
|
void scc_cfg_module_drop(scc_cfg_module_t *module);
|
||||||
|
scc_cfg_func_id_t scc_cfg_module_add_func(scc_cfg_module_t *module,
|
||||||
|
const scc_cfg_func_t *func);
|
||||||
|
scc_cfg_func_t *scc_cfg_module_unsafe_get_func(const scc_cfg_module_t *module,
|
||||||
|
scc_cfg_func_id_t id);
|
||||||
|
scc_cfg_bblock_id_t scc_cfg_module_add_bblock(scc_cfg_module_t *module,
|
||||||
|
const scc_cfg_bblock_t *bblock);
|
||||||
|
scc_cfg_bblock_t *
|
||||||
|
scc_cfg_module_unsafe_get_bblock(const scc_cfg_module_t *module,
|
||||||
|
scc_cfg_bblock_id_t id);
|
||||||
|
scc_cfg_symbol_id_t scc_cfg_module_add_symbol(scc_cfg_module_t *module,
|
||||||
|
const scc_cfg_symbol_t *symbol);
|
||||||
|
scc_cfg_symbol_t *
|
||||||
|
scc_cfg_module_unsafe_get_symbol(const scc_cfg_module_t *module,
|
||||||
|
scc_cfg_symbol_id_t id);
|
||||||
|
scc_cfg_symbol_id_t scc_cfg_module_lookup_symbol(const scc_cfg_module_t *module,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
static inline const scc_cfg_symbol_t *
|
||||||
|
scc_cfg_module_unsafe_lookup_symbol(const scc_cfg_module_t *module,
|
||||||
|
const char *name) {
|
||||||
|
scc_cfg_id_t idx = scc_cfg_module_lookup_symbol(module, name);
|
||||||
|
if (idx == 0) {
|
||||||
|
Panic("Can't find symbol %s", name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &scc_vec_at(module->symbols, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scc_cfg_bblock_id_t scc_cfg_func_add_bblock(scc_cfg_func_t *func,
|
||||||
|
// const scc_cfg_bblock_t *bblock);
|
||||||
|
// scc_cfg_bblock_t *scc_cfg_func_unsafe_get_bblock(scc_cfg_func_t *func,
|
||||||
|
// scc_cfg_bblock_id_t id);
|
||||||
|
// scc_cfg_bblock_t *scc_cfg_func_unsafe_entry_bblock(scc_cfg_func_t *func);
|
||||||
|
|
||||||
|
void scc_cfg_bblock_add_pred(scc_cfg_bblock_t *bb, scc_cfg_bblock_id_t pred_id);
|
||||||
|
void scc_cfg_bblock_remove_pred(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t pred_id);
|
||||||
|
void scc_cfg_bblock_clear_pred(scc_cfg_bblock_t *bb);
|
||||||
|
void scc_cfg_bblock_add_succ(scc_cfg_bblock_t *bb, scc_cfg_bblock_id_t succ_id);
|
||||||
|
void scc_cfg_bblock_remove_succ(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t succ_id);
|
||||||
|
void scc_cfg_bblock_clear_succs(scc_cfg_bblock_t *bb);
|
||||||
|
|
||||||
|
#endif /* __SCC_CFG_H__ */
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
#include <scc_cfg.h>
|
||||||
|
|
||||||
|
void scc_cfg_module_init(scc_cfg_module_t *module) {
|
||||||
|
scc_vec_init(module->bblocks);
|
||||||
|
scc_vec_init(module->funcs);
|
||||||
|
scc_vec_init(module->symbols);
|
||||||
|
scc_hashtable_cstr_init(&module->symbol_map);
|
||||||
|
|
||||||
|
// 0 for null
|
||||||
|
scc_vec_push(module->bblocks, (scc_cfg_bblock_t){0});
|
||||||
|
scc_vec_push(module->funcs, (scc_cfg_func_t){0});
|
||||||
|
scc_vec_push(module->symbols, (scc_cfg_symbol_t){0});
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_cfg_module_drop(scc_cfg_module_t *module) {
|
||||||
|
scc_vec_free(module->bblocks);
|
||||||
|
scc_vec_free(module->funcs);
|
||||||
|
scc_vec_free(module->symbols);
|
||||||
|
scc_hashtable_drop(&module->symbol_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_cfg_func_id_t scc_cfg_module_add_func(scc_cfg_module_t *module,
|
||||||
|
const scc_cfg_func_t *func) {
|
||||||
|
scc_cfg_func_id_t id = scc_vec_size(module->funcs);
|
||||||
|
scc_vec_push(module->funcs, *func);
|
||||||
|
Assert(id != SCC_CFG_ID_nullptr);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_cfg_func_t *scc_cfg_module_unsafe_get_func(const scc_cfg_module_t *module,
|
||||||
|
scc_cfg_func_id_t id) {
|
||||||
|
if (id == SCC_CFG_ID_nullptr) {
|
||||||
|
Panic("nullptr func id");
|
||||||
|
}
|
||||||
|
if (id >= scc_vec_size(module->funcs)) {
|
||||||
|
Panic("invalid func id");
|
||||||
|
}
|
||||||
|
return &scc_vec_at(module->funcs, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_cfg_bblock_id_t scc_cfg_module_add_bblock(scc_cfg_module_t *module,
|
||||||
|
const scc_cfg_bblock_t *bblock) {
|
||||||
|
scc_cfg_bblock_id_t id = scc_vec_size(module->bblocks);
|
||||||
|
scc_cfg_bblock_t bblock_ = *bblock;
|
||||||
|
bblock_.id = id;
|
||||||
|
scc_vec_push(module->bblocks, bblock_);
|
||||||
|
Assert(id != SCC_CFG_ID_nullptr);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_cfg_bblock_t *
|
||||||
|
scc_cfg_module_unsafe_get_bblock(const scc_cfg_module_t *module,
|
||||||
|
scc_cfg_bblock_id_t id) {
|
||||||
|
if (id == SCC_CFG_ID_nullptr) {
|
||||||
|
Panic("nullptr bblocks id");
|
||||||
|
}
|
||||||
|
if (id >= scc_vec_size(module->bblocks)) {
|
||||||
|
Panic("invalid bblocks id");
|
||||||
|
}
|
||||||
|
return &scc_vec_at(module->bblocks, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_cfg_symbol_id_t scc_cfg_module_add_symbol(scc_cfg_module_t *module,
|
||||||
|
const scc_cfg_symbol_t *symbol) {
|
||||||
|
scc_cfg_func_id_t id = scc_vec_size(module->symbols);
|
||||||
|
scc_vec_push(module->symbols, *symbol);
|
||||||
|
Assert(id != SCC_CFG_ID_nullptr);
|
||||||
|
scc_hashtable_set(&module->symbol_map, symbol->name, (void *)(usize)id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_cfg_symbol_t *
|
||||||
|
scc_cfg_module_unsafe_get_symbol(const scc_cfg_module_t *module,
|
||||||
|
scc_cfg_symbol_id_t id) {
|
||||||
|
if (id == SCC_CFG_ID_nullptr) {
|
||||||
|
Panic("nullptr symbol id");
|
||||||
|
}
|
||||||
|
if (id >= scc_vec_size(module->bblocks)) {
|
||||||
|
Panic("invalid symbol id");
|
||||||
|
}
|
||||||
|
return &scc_vec_at(module->symbols, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_cfg_symbol_id_t scc_cfg_module_lookup_symbol(const scc_cfg_module_t *module,
|
||||||
|
const char *name) {
|
||||||
|
return (scc_cfg_symbol_id_t)(usize)scc_hashtable_get(&module->symbol_map,
|
||||||
|
name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_add_pred(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t pred_id) {}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_remove_pred(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t pred_id) {}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_clear_pred(scc_cfg_bblock_t *bb) {}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_add_succ(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t succ_id) {}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_remove_succ(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t succ_id) {}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_clear_succs(scc_cfg_bblock_t *bb) {}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "scc_hir"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = []
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
{ name = "scc_cfg", path = "../cfg" },
|
||||||
|
{ name = "scc_utils", path = "../../../runtime/scc_utils" },
|
||||||
|
{ name = "tree_dump", path = "../../tree_dump" },
|
||||||
|
]
|
||||||
|
# dependencies = []
|
||||||
|
# features = {}
|
||||||
|
# default_features = []
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __SCC_HIR_H__
|
||||||
|
#define __SCC_HIR_H__
|
||||||
|
|
||||||
|
#include "scc_hir_def.h"
|
||||||
|
#include "scc_hir_prog.h"
|
||||||
|
|
||||||
|
void scc_hir_type_init(scc_hir_type_t *in, scc_hir_type_tag_t tag);
|
||||||
|
void scc_hir_bblock_init(scc_hir_bblock_t *in, const char *label);
|
||||||
|
void scc_hir_func_init(scc_hir_func_t *in, const char *name);
|
||||||
|
// node name can be nullptr ptr
|
||||||
|
void scc_hir_value_init(scc_hir_value_t *in, const char *name,
|
||||||
|
scc_hir_value_tag_t tag);
|
||||||
|
|
||||||
|
#endif /* __SCC_HIR_H__ */
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
#ifndef __SCC_HIR_BUILDER_H__
|
||||||
|
#define __SCC_HIR_BUILDER_H__
|
||||||
|
|
||||||
|
#include "scc_hir.h"
|
||||||
|
#include "scc_hir_prog.h"
|
||||||
|
|
||||||
|
typedef struct scc_hir_builder scc_hir_builder_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IR 构建器上下文
|
||||||
|
*
|
||||||
|
* 负责管理 IR 构建过程中的所有状态:
|
||||||
|
* - 类型统一化(type uniquing)
|
||||||
|
* - 符号命名分配
|
||||||
|
* - 内存管理
|
||||||
|
* - 当前构建位置(函数、基本块)
|
||||||
|
*/
|
||||||
|
struct scc_hir_builder {
|
||||||
|
scc_hir_cprog_t *cprog;
|
||||||
|
scc_hir_func_ref_t current_func; ///< 当前正在构建的函数
|
||||||
|
scc_hir_bblock_ref_t current_bblock; ///< 当前基本块
|
||||||
|
|
||||||
|
scc_hashtable_t type_uniquing; // 类型哈希表:hash -> type_ref
|
||||||
|
scc_hashtable_t const_pool; // 常量哈希表:hash -> node_ref
|
||||||
|
scc_hashtable_t func_decl_set; // 函数声明集合:name -> func_ref
|
||||||
|
#ifndef SCC_NO_DEBUG
|
||||||
|
int borrow_depth;
|
||||||
|
const char *dbg_file;
|
||||||
|
int dbg_line;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef SCC_NO_DEBUG
|
||||||
|
#define SCC_HIR_BUILDER_BEGIN_BORROW(builder, ptr_var, ptr_expr) \
|
||||||
|
do { \
|
||||||
|
(builder)->borrow_depth++; \
|
||||||
|
(builder)->dbg_file = __FILE__; \
|
||||||
|
(builder)->dbg_line = __LINE__; \
|
||||||
|
ptr_var = (ptr_expr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SCC_HIR_BUILDER_END_BORROW(builder) \
|
||||||
|
do { \
|
||||||
|
(builder)->borrow_depth--; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define SCC_HIR_BUILDER_CHECK_NO_BORROW(builder) \
|
||||||
|
do { \
|
||||||
|
if ((builder)->borrow_depth != 0) { \
|
||||||
|
Panic("IR Builder: attempt to reallocate while borrowed at %s:%d", \
|
||||||
|
(builder)->dbg_file, (builder)->dbg_line); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define SCC_HIR_BUILDER_BEGIN_BORROW(builder, ptr_var, ptr_expr) \
|
||||||
|
ptr_var = (ptr_expr)
|
||||||
|
#define SCC_HIR_BUILDER_END_BORROW(builder) ((void)0)
|
||||||
|
#define SCC_HIR_BUILDER_CHECK_NO_BORROW(builder) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化 IR 构建器
|
||||||
|
*/
|
||||||
|
void scc_hir_builder_init(scc_hir_builder_t *builder, scc_hir_cprog_t *cprog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 销毁 IR 构建器及其所有资源
|
||||||
|
*/
|
||||||
|
void scc_hir_builder_drop(scc_hir_builder_t *builder);
|
||||||
|
|
||||||
|
scc_hir_func_ref_t scc_hir_builder_func(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type_ref,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
scc_hir_type_ref_t scc_hir_builder_type(scc_hir_builder_t *builder,
|
||||||
|
const scc_hir_type_t *type_desc);
|
||||||
|
|
||||||
|
void scc_hir_builder_add_instr(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t instr);
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_global_alloca(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type,
|
||||||
|
scc_hir_value_ref_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建alloca指令(在当前基本块中)
|
||||||
|
* @param type 分配的类型
|
||||||
|
* @param name 变量名(可为nullptr)
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_alloca(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
|
#define SCC_HIR_BUILDER_TYPE_FUNC(scc_type) \
|
||||||
|
[[maybe_unused]] static inline scc_hir_type_ref_t \
|
||||||
|
scc_hir_builder_type_##scc_type(scc_hir_builder_t *builder) { \
|
||||||
|
scc_hir_type_t type_desc; \
|
||||||
|
scc_hir_type_init(&type_desc, SCC_HIR_TYPE_##scc_type); \
|
||||||
|
return scc_hir_builder_type(builder, &type_desc); \
|
||||||
|
}
|
||||||
|
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(unknown)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(void)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(i8)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(i16)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(i32)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(i64)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(i128)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(u8)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(u16)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(u32)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(u64)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(u128)
|
||||||
|
// SCC_HIR_BUILDER_TYPE_FUNC(f8)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(f16)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(f32)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(f64)
|
||||||
|
SCC_HIR_BUILDER_TYPE_FUNC(f128)
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_builtin_memcpy(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t dest,
|
||||||
|
scc_hir_value_ref_t src,
|
||||||
|
scc_hir_value_ref_t len);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
static inline scc_hir_value_ref_t
|
||||||
|
scc_hir_builder_integer(scc_hir_builder_t *builder, scc_hir_type_ref_t type,
|
||||||
|
scc_ap_t *val) {
|
||||||
|
scc_hir_value_t value;
|
||||||
|
scc_hir_value_init(&value, nullptr, SCC_HIR_VALUE_TAG_INTEGER);
|
||||||
|
// FIXME assign
|
||||||
|
value.data.integer = *val;
|
||||||
|
value.type = type;
|
||||||
|
return scc_hir_module_add_value(&builder->cprog->module, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_const_string(scc_hir_builder_t *builder,
|
||||||
|
const char *str, usize len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 开始构建函数
|
||||||
|
* @param func_ref 函数引用
|
||||||
|
* @param param_names 参数名列表(可为nullptr)
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
void scc_hir_builder_begin_func(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_func_ref_t func_ref);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结束当前函数的构建
|
||||||
|
*/
|
||||||
|
void scc_hir_builder_end_func(scc_hir_builder_t *builder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取当前正在构建的函数
|
||||||
|
*/
|
||||||
|
scc_hir_func_ref_t scc_hir_builder_current_func(scc_hir_builder_t *builder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建一个新的基本块,并自动添加到当前函数中,但不改变当前块。
|
||||||
|
* @param builder IR构建器
|
||||||
|
* @param label 基本块标签(可为 nullptr,自动生成)
|
||||||
|
* @return 新基本块的引用
|
||||||
|
*/
|
||||||
|
scc_hir_bblock_ref_t scc_hir_builder_bblock(scc_hir_builder_t *builder,
|
||||||
|
const char *label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 开始构建新的基本块
|
||||||
|
* @param label 基本块标签(可为nullptr,自动生成)
|
||||||
|
* @return 基本块引用
|
||||||
|
*/
|
||||||
|
scc_hir_bblock_ref_t scc_hir_builder_begin_bblock(scc_hir_builder_t *builder,
|
||||||
|
const char *label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 结束当前基本块的构建
|
||||||
|
*/
|
||||||
|
void scc_hir_builder_end_bblock(scc_hir_builder_t *builder);
|
||||||
|
|
||||||
|
scc_hir_func_ref_t scc_hir_builder_current_bblock(scc_hir_builder_t *builder);
|
||||||
|
/**
|
||||||
|
* @brief 设置当前基本块
|
||||||
|
*/
|
||||||
|
void scc_hir_builder_set_current_bblock(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_bblock_ref_t bblock);
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_func_arg_ref(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type,
|
||||||
|
const char *name,
|
||||||
|
usize arg_idx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建load指令
|
||||||
|
* @param ptr 指针操作数
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_load(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建store指令
|
||||||
|
* @param ptr 目标指针
|
||||||
|
* @param value 要存储的值
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_store(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t ptr,
|
||||||
|
scc_hir_value_ref_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建getptr指令(指针运算)
|
||||||
|
* @param ptr 基础指针
|
||||||
|
* @param index 索引值
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_get_elem_ptr(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t ptr,
|
||||||
|
scc_hir_value_ref_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建二元运算指令
|
||||||
|
* @param op 操作符
|
||||||
|
* @param lhs 左操作数
|
||||||
|
* @param rhs 右操作数
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_binop(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_op_type_t op,
|
||||||
|
scc_hir_value_ref_t lhs,
|
||||||
|
scc_hir_value_ref_t rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建比较指令
|
||||||
|
* @param op 比较操作符
|
||||||
|
* @param lhs 左操作数
|
||||||
|
* @param rhs 右操作数
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_cmp(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_op_type_t op,
|
||||||
|
scc_hir_value_ref_t lhs,
|
||||||
|
scc_hir_value_ref_t rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建跳转指令(无条件)
|
||||||
|
* @param target 目标基本块
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_jump(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_bblock_ref_t target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建条件分支指令
|
||||||
|
* @param cond 条件值
|
||||||
|
* @param true_target 条件为真时的目标
|
||||||
|
* @param false_target 条件为假时的目标
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_branch(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t cond,
|
||||||
|
scc_hir_bblock_ref_t true_target,
|
||||||
|
scc_hir_bblock_ref_t false_target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建函数调用指令
|
||||||
|
* @param callee 被调用函数
|
||||||
|
* @param args 参数列表
|
||||||
|
* @param arg_count 参数数量
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_call(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_func_ref_t callee,
|
||||||
|
const scc_hir_value_ref_t *args,
|
||||||
|
usize arg_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建返回指令(带返回值)
|
||||||
|
* @param value 返回值
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_ret(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建返回指令(void返回)
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_ret_void(scc_hir_builder_t *builder);
|
||||||
|
|
||||||
|
#endif /* __SCC_HIR_BUILDER_H__ */
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
#ifndef __SCC_HIR_DEF_H__
|
||||||
|
#define __SCC_HIR_DEF_H__
|
||||||
|
|
||||||
|
#include <scc_ap.h>
|
||||||
|
#include <scc_cfg.h>
|
||||||
|
#include <scc_core.h>
|
||||||
|
|
||||||
|
#define SCC_HIR_REF_nullptr SCC_CFG_ID_nullptr
|
||||||
|
|
||||||
|
typedef SCC_VEC(u8) scc_hir_buffer_t;
|
||||||
|
|
||||||
|
typedef scc_cfg_bblock_t scc_hir_bblock_t;
|
||||||
|
typedef scc_cfg_bblock_id_t scc_hir_bblock_ref_t;
|
||||||
|
typedef SCC_VEC(scc_hir_bblock_ref_t) scc_hir_bblock_ref_vec_t;
|
||||||
|
|
||||||
|
typedef scc_cfg_func_t scc_hir_func_t;
|
||||||
|
typedef scc_cfg_func_id_t scc_hir_func_ref_t;
|
||||||
|
typedef SCC_VEC(scc_hir_func_ref_t) scc_hir_func_ref_vec_t;
|
||||||
|
|
||||||
|
typedef struct scc_hir_value scc_hir_value_t;
|
||||||
|
typedef scc_cfg_id_t scc_hir_value_ref_t;
|
||||||
|
typedef SCC_VEC(scc_hir_value_ref_t) scc_hir_value_ref_vec_t;
|
||||||
|
|
||||||
|
typedef struct scc_hir_type scc_hir_type_t;
|
||||||
|
typedef scc_cfg_id_t scc_hir_type_ref_t;
|
||||||
|
typedef SCC_VEC(scc_hir_type_ref_t) scc_hir_type_ref_vec_t;
|
||||||
|
|
||||||
|
typedef enum scc_hir_type_tag {
|
||||||
|
SCC_HIR_TYPE_unknown,
|
||||||
|
SCC_HIR_TYPE_void,
|
||||||
|
SCC_HIR_TYPE_i8,
|
||||||
|
SCC_HIR_TYPE_i16,
|
||||||
|
SCC_HIR_TYPE_i32,
|
||||||
|
SCC_HIR_TYPE_i64,
|
||||||
|
SCC_HIR_TYPE_i128,
|
||||||
|
SCC_HIR_TYPE_u8,
|
||||||
|
SCC_HIR_TYPE_u16,
|
||||||
|
SCC_HIR_TYPE_u32,
|
||||||
|
SCC_HIR_TYPE_u64,
|
||||||
|
SCC_HIR_TYPE_u128,
|
||||||
|
SCC_HIR_TYPE_f16,
|
||||||
|
SCC_HIR_TYPE_f32,
|
||||||
|
SCC_HIR_TYPE_f64,
|
||||||
|
SCC_HIR_TYPE_f128,
|
||||||
|
SCC_HIR_TYPE_PTR,
|
||||||
|
SCC_HIR_TYPE_ARRAY,
|
||||||
|
SCC_HIR_TYPE_FUNC,
|
||||||
|
SCC_HIR_TYPE_STRUCT,
|
||||||
|
SCC_HIR_TYPE_UNION,
|
||||||
|
SCC_HIR_TYPE_VECTOR, // TODO SIMD
|
||||||
|
} scc_hir_type_tag_t;
|
||||||
|
|
||||||
|
struct scc_hir_type {
|
||||||
|
scc_hir_type_tag_t tag;
|
||||||
|
const char *name; // For Debug
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
scc_hir_type_ref_t base;
|
||||||
|
usize len; // TODO usize is target dependent
|
||||||
|
} array;
|
||||||
|
struct {
|
||||||
|
scc_hir_type_ref_t base;
|
||||||
|
} pointer;
|
||||||
|
struct {
|
||||||
|
scc_hir_type_ref_vec_t fields;
|
||||||
|
} aggregate;
|
||||||
|
struct {
|
||||||
|
scc_hir_type_ref_vec_t params;
|
||||||
|
scc_hir_type_ref_t ret_type;
|
||||||
|
} function;
|
||||||
|
} data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum scc_hir_value_tag {
|
||||||
|
SCC_HIR_VALUE_TAG_NULLPTR,
|
||||||
|
SCC_HIR_VALUE_TAG_BUILTIN,
|
||||||
|
SCC_HIR_VALUE_TAG_INTEGER,
|
||||||
|
SCC_HIR_VALUE_TAG_DECIMAL,
|
||||||
|
SCC_HIR_VALUE_TAG_ARRAY,
|
||||||
|
SCC_HIR_VALUE_TAG_AGGREGATE, ///< 聚合值
|
||||||
|
SCC_HIR_VALUE_TAG_CONV, ///< 类型转换
|
||||||
|
SCC_HIR_VALUE_TAG_FUNC_ARG_REF, ///< 函数参数引用
|
||||||
|
SCC_HIR_VALUE_TAG_BLOCK_ARG_REF, ///< 基本块参数引用
|
||||||
|
SCC_HIR_VALUE_TAG_ALLOC, ///< 分配内存
|
||||||
|
SCC_HIR_VALUE_TAG_GLOBAL_ALLOC, ///< 全局分配
|
||||||
|
SCC_HIR_VALUE_TAG_LOAD, ///< 加载数据
|
||||||
|
SCC_HIR_VALUE_TAG_STORE, ///< 存储数据
|
||||||
|
SCC_HIR_VALUE_TAG_GET_ELEM_PTR, ///< 获取元素指针
|
||||||
|
SCC_HIR_VALUE_TAG_OP, ///< 二元运算
|
||||||
|
SCC_HIR_VALUE_TAG_BRANCH, ///< 有条件分支
|
||||||
|
SCC_HIR_VALUE_TAG_JUMP, ///< 无条件跳转
|
||||||
|
SCC_HIR_VALUE_TAG_CALL, ///< 调用函数
|
||||||
|
SCC_HIR_VALUE_TAG_RET, ///< 函数返回
|
||||||
|
} scc_hir_value_tag_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/// Empty op for init or nop
|
||||||
|
SCC_HIR_OP_EMPTY,
|
||||||
|
/// Not equal to.
|
||||||
|
SCC_HIR_OP_NEQ,
|
||||||
|
/// Equal to.
|
||||||
|
SCC_HIR_OP_EQ,
|
||||||
|
/// Greater than.
|
||||||
|
SCC_HIR_OP_GT,
|
||||||
|
/// Less than.
|
||||||
|
SCC_HIR_OP_LT,
|
||||||
|
/// Greater than or equal to.
|
||||||
|
SCC_HIR_OP_GE,
|
||||||
|
/// Less than or equal to.
|
||||||
|
SCC_HIR_OP_LE,
|
||||||
|
/// Addition.
|
||||||
|
SCC_HIR_OP_ADD,
|
||||||
|
/// Subtraction.
|
||||||
|
SCC_HIR_OP_SUB,
|
||||||
|
/// Multiplication.
|
||||||
|
SCC_HIR_OP_MUL,
|
||||||
|
/// Division.
|
||||||
|
SCC_HIR_OP_DIV,
|
||||||
|
/// Modulo.
|
||||||
|
SCC_HIR_OP_MOD,
|
||||||
|
/// Bitwise AND.
|
||||||
|
SCC_HIR_OP_AND,
|
||||||
|
/// Bitwise OR.
|
||||||
|
SCC_HIR_OP_OR,
|
||||||
|
/// Bitwise XOR.
|
||||||
|
SCC_HIR_OP_XOR,
|
||||||
|
/// Bitwise NOT.
|
||||||
|
SCC_HIR_OP_NOT,
|
||||||
|
/// Shift left logical.
|
||||||
|
SCC_HIR_OP_SHL,
|
||||||
|
/// Shift right logical.
|
||||||
|
SCC_HIR_OP_SHR,
|
||||||
|
/// Shift right arithmetic.
|
||||||
|
SCC_HIR_OP_SAR,
|
||||||
|
} scc_hir_op_type_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCC_HIR_BUILTIN_TAG_MEMCPY,
|
||||||
|
SCC_HIR_BUILTIN_TAG_MEMSET,
|
||||||
|
SCC_HIR_BUILTIN_TAG_VA_START,
|
||||||
|
SCC_HIR_BUILTIN_TAG_VA_ARG,
|
||||||
|
SCC_HIR_BUILTIN_TAG_VA_END,
|
||||||
|
SCC_HIR_BUILTIN_TAG_VA_COPY,
|
||||||
|
} scc_hir_builtin_tag_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scc_hir_builtin_tag_t tag;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t dest;
|
||||||
|
scc_hir_value_ref_t src;
|
||||||
|
scc_hir_value_ref_t size;
|
||||||
|
} memcpy;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t dest;
|
||||||
|
scc_hir_value_ref_t value;
|
||||||
|
scc_hir_value_ref_t size;
|
||||||
|
} memset;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t ap; // va_list 的地址(i8* 或 struct*)
|
||||||
|
scc_hir_value_ref_t last; // 最后一个固定参数的引用(用于 va_start)
|
||||||
|
} va_start;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t ap; // va_list 的地址
|
||||||
|
scc_hir_type_ref_t type; // 要提取的参数的类型
|
||||||
|
} va_arg;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t ap; // va_list 的地址
|
||||||
|
} va_end;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t dest; // 目标 va_list 地址
|
||||||
|
scc_hir_value_ref_t src; // 源 va_list 地址
|
||||||
|
} va_copy;
|
||||||
|
} func;
|
||||||
|
} scc_hir_builtin_t;
|
||||||
|
|
||||||
|
struct scc_hir_value {
|
||||||
|
scc_hir_type_ref_t type;
|
||||||
|
const char *name;
|
||||||
|
scc_hir_value_ref_vec_t used_by;
|
||||||
|
scc_hir_value_tag_t tag;
|
||||||
|
union {
|
||||||
|
scc_hir_builtin_t builtin;
|
||||||
|
scc_ap_t integer;
|
||||||
|
void *decimal;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t base_type;
|
||||||
|
scc_hir_buffer_t fields;
|
||||||
|
} const_array;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_vec_t fields;
|
||||||
|
} aggregate;
|
||||||
|
struct {
|
||||||
|
usize idx;
|
||||||
|
} arg_ref;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t value;
|
||||||
|
} global_alloc;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t operand;
|
||||||
|
scc_hir_type_ref_t target_type; // 目标类型
|
||||||
|
enum { CONV_SEXT, CONV_ZEXT, CONV_TRUNC } conv_type;
|
||||||
|
} conv;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t target;
|
||||||
|
} load;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t target;
|
||||||
|
scc_hir_value_ref_t value;
|
||||||
|
} store;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t src_addr;
|
||||||
|
scc_hir_value_ref_t index;
|
||||||
|
} get_elem_ptr;
|
||||||
|
struct {
|
||||||
|
scc_hir_op_type_t op;
|
||||||
|
scc_hir_value_ref_t lhs;
|
||||||
|
scc_hir_value_ref_t rhs;
|
||||||
|
} op;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t cond;
|
||||||
|
scc_hir_bblock_ref_t true_bblock;
|
||||||
|
scc_hir_bblock_ref_t false_bblock;
|
||||||
|
} branch;
|
||||||
|
struct {
|
||||||
|
scc_hir_bblock_ref_t target_bblock;
|
||||||
|
} jump;
|
||||||
|
struct {
|
||||||
|
scc_hir_func_ref_t callee; // TODO function pointer call
|
||||||
|
scc_hir_value_ref_vec_t args;
|
||||||
|
} call;
|
||||||
|
struct {
|
||||||
|
scc_hir_value_ref_t ret_val;
|
||||||
|
} ret;
|
||||||
|
} data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct scc_hir_bblock_meta {
|
||||||
|
// scc_hir_value_ref_vec_t instrs;
|
||||||
|
// ir_arr_t used_by;
|
||||||
|
} scc_hir_bblock_meta_t;
|
||||||
|
|
||||||
|
typedef struct scc_hir_func_meta {
|
||||||
|
scc_hir_type_ref_t type;
|
||||||
|
scc_hir_value_ref_vec_t params;
|
||||||
|
} scc_hir_func_meta_t;
|
||||||
|
|
||||||
|
#define SCC_HIR_BBLOCK_VALUES(bblock) \
|
||||||
|
((scc_hir_value_ref_vec_t *)&((bblock)->values))
|
||||||
|
|
||||||
|
#define SCC_HIR_BBLOCK_META(bblock) ((scc_hir_bblock_meta_t *)((bblock)->meta))
|
||||||
|
#define SCC_HIR_FUNC_META(func) ((scc_hir_func_meta_t *)((func)->meta))
|
||||||
|
|
||||||
|
#endif /* __SCC_HIR_DEF_H__ */
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef __SCC_HIR_DUMP_H__
|
||||||
|
#define __SCC_HIR_DUMP_H__
|
||||||
|
|
||||||
|
#include "scc_hir_prog.h"
|
||||||
|
#include <scc_tree_dump.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scc_hir_cprog_t *cprog;
|
||||||
|
scc_tree_dump_t *dump_ctx;
|
||||||
|
} scc_hir_dump_t;
|
||||||
|
|
||||||
|
void scc_hir_dump_init(scc_hir_dump_t *ctx, scc_tree_dump_t *tree_dump,
|
||||||
|
scc_hir_cprog_t *cprog);
|
||||||
|
void scc_hir_dump_value(scc_hir_dump_t *ctx, scc_hir_value_ref_t node_ref);
|
||||||
|
void scc_hir_dump_type(scc_hir_dump_t *ctx, scc_hir_type_ref_t type_ref);
|
||||||
|
void scc_hir_dump_bblock(scc_hir_dump_t *ctx, scc_hir_bblock_ref_t bblock_ref);
|
||||||
|
void scc_hir_dump_func(scc_hir_dump_t *ctx, scc_hir_func_ref_t func_ref);
|
||||||
|
void scc_hir_dump_cprog(scc_hir_dump_t *ctx);
|
||||||
|
void scc_hir_dump_cprog_linear(scc_hir_dump_t *ctx);
|
||||||
|
|
||||||
|
#endif /* __SCC_HIR_DUMP_H__ */
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef __SCC_HIR_MODULE_H__
|
||||||
|
#define __SCC_HIR_MODULE_H__
|
||||||
|
|
||||||
|
#include "scc_hir_def.h"
|
||||||
|
#include <scc_cfg.h>
|
||||||
|
#include <scc_hashtable.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scc_cfg_module_t cfg_module;
|
||||||
|
|
||||||
|
scc_cfg_id_t value_uid;
|
||||||
|
scc_cfg_id_t type_uid;
|
||||||
|
SCC_VEC(scc_hir_value_t) values;
|
||||||
|
SCC_VEC(scc_hir_type_t) types;
|
||||||
|
// UID -> ref index
|
||||||
|
scc_hashtable_t uid2value;
|
||||||
|
scc_hashtable_t uid2type;
|
||||||
|
|
||||||
|
SCC_VEC(scc_hir_bblock_meta_t *) bblock_meta;
|
||||||
|
SCC_VEC(scc_hir_func_meta_t *) funcs_meta;
|
||||||
|
} scc_hir_module_t;
|
||||||
|
|
||||||
|
void scc_hir_module_init(scc_hir_module_t *ctx);
|
||||||
|
void scc_hir_module_drop(scc_hir_module_t *ctx);
|
||||||
|
scc_hir_type_ref_t scc_hir_module_add_type(scc_hir_module_t *ctx,
|
||||||
|
const scc_hir_type_t *type);
|
||||||
|
scc_hir_value_ref_t scc_hir_module_add_value(scc_hir_module_t *ctx,
|
||||||
|
const scc_hir_value_t *node);
|
||||||
|
scc_hir_bblock_ref_t scc_hir_module_add_bblock(scc_hir_module_t *ctx,
|
||||||
|
const scc_hir_bblock_t *bblock);
|
||||||
|
scc_hir_func_ref_t scc_hir_module_add_func(scc_hir_module_t *ctx,
|
||||||
|
const scc_hir_func_t *func);
|
||||||
|
scc_hir_type_t *scc_hir_module_get_type(scc_hir_module_t *ctx,
|
||||||
|
scc_hir_type_ref_t ref);
|
||||||
|
scc_hir_value_t *scc_hir_module_get_value(scc_hir_module_t *ctx,
|
||||||
|
scc_hir_value_ref_t ref);
|
||||||
|
scc_hir_bblock_t *scc_hir_module_get_bblock(scc_hir_module_t *ctx,
|
||||||
|
scc_hir_bblock_ref_t ref);
|
||||||
|
scc_hir_func_t *scc_hir_module_get_func(scc_hir_module_t *ctx,
|
||||||
|
scc_hir_func_ref_t ref);
|
||||||
|
|
||||||
|
static inline scc_hir_type_t *
|
||||||
|
scc_hir_module_get_type_by_value(scc_hir_module_t *ctx,
|
||||||
|
scc_hir_value_ref_t ref) {
|
||||||
|
scc_hir_value_t *value = scc_hir_module_get_value(ctx, ref);
|
||||||
|
Assert(value != nullptr);
|
||||||
|
return scc_hir_module_get_type(ctx, value->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __SCC_HIR_MODULE_H__ */
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef __SCC_HIR_PROG_H__
|
||||||
|
#define __SCC_HIR_PROG_H__
|
||||||
|
|
||||||
|
#include "scc_hir_def.h"
|
||||||
|
#include "scc_hir_module.h"
|
||||||
|
|
||||||
|
typedef struct scc_hir_cprog {
|
||||||
|
scc_hir_module_t module;
|
||||||
|
scc_hir_value_ref_vec_t global_vals; /* 全局变量 */
|
||||||
|
scc_hir_func_ref_vec_t func_defs; /* 所有函数定义 */
|
||||||
|
scc_hir_func_ref_vec_t func_decls; /* 所有函数包括定义的声明 */
|
||||||
|
} scc_hir_cprog_t;
|
||||||
|
|
||||||
|
void scc_hir_cprog_init(scc_hir_cprog_t *in);
|
||||||
|
void scc_hir_cprog_drop(scc_hir_cprog_t *in);
|
||||||
|
|
||||||
|
#endif /* __SCC_HIR_PROG_H__ */
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#include <scc_hir.h>
|
||||||
|
|
||||||
|
void scc_hir_type_init(scc_hir_type_t *in, scc_hir_type_tag_t tag) {
|
||||||
|
Assert(in != nullptr);
|
||||||
|
in->tag = tag;
|
||||||
|
in->name = nullptr;
|
||||||
|
switch (tag) {
|
||||||
|
case SCC_HIR_TYPE_unknown:
|
||||||
|
case SCC_HIR_TYPE_void:
|
||||||
|
case SCC_HIR_TYPE_i8:
|
||||||
|
case SCC_HIR_TYPE_i16:
|
||||||
|
case SCC_HIR_TYPE_i32:
|
||||||
|
case SCC_HIR_TYPE_i64:
|
||||||
|
case SCC_HIR_TYPE_i128:
|
||||||
|
case SCC_HIR_TYPE_u8:
|
||||||
|
case SCC_HIR_TYPE_u16:
|
||||||
|
case SCC_HIR_TYPE_u32:
|
||||||
|
case SCC_HIR_TYPE_u64:
|
||||||
|
case SCC_HIR_TYPE_u128:
|
||||||
|
case SCC_HIR_TYPE_f16:
|
||||||
|
case SCC_HIR_TYPE_f32:
|
||||||
|
case SCC_HIR_TYPE_f64:
|
||||||
|
case SCC_HIR_TYPE_f128:
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_ARRAY:
|
||||||
|
in->data.array.base = 0;
|
||||||
|
in->data.array.len = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_PTR:
|
||||||
|
in->data.pointer.base = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_FUNC:
|
||||||
|
scc_vec_init(in->data.function.params);
|
||||||
|
in->data.function.ret_type = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_UNION:
|
||||||
|
case SCC_HIR_TYPE_STRUCT:
|
||||||
|
scc_vec_init(in->data.aggregate.fields);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_bblock_init(scc_hir_bblock_t *in, const char *label) {
|
||||||
|
Assert(in != nullptr);
|
||||||
|
Assert(label != nullptr);
|
||||||
|
in->name = label;
|
||||||
|
scc_vec_init(in->values);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_func_init(scc_hir_func_t *in, const char *name) {
|
||||||
|
Assert(in != nullptr);
|
||||||
|
Assert(name != nullptr);
|
||||||
|
in->name = name;
|
||||||
|
scc_hir_func_meta_t *meta = SCC_HIR_FUNC_META(in);
|
||||||
|
meta->type = SCC_HIR_REF_nullptr;
|
||||||
|
scc_vec_init(in->bblocks);
|
||||||
|
scc_vec_init(meta->params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_value_init(scc_hir_value_t *in, const char *name,
|
||||||
|
scc_hir_value_tag_t tag) {
|
||||||
|
Assert(in != nullptr);
|
||||||
|
in->name = name;
|
||||||
|
in->tag = tag;
|
||||||
|
scc_vec_init(in->used_by);
|
||||||
|
in->type = 0;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case SCC_HIR_VALUE_TAG_NULLPTR:
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_BUILTIN:
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_INTEGER:
|
||||||
|
scc_ap_init(&in->data.integer);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_ALLOC:
|
||||||
|
// TODO();
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_LOAD:
|
||||||
|
in->data.load.target = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_STORE:
|
||||||
|
in->data.store.target = 0;
|
||||||
|
in->data.store.value = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_GET_ELEM_PTR:
|
||||||
|
in->data.get_elem_ptr.src_addr = 0;
|
||||||
|
in->data.get_elem_ptr.index = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_OP:
|
||||||
|
in->data.op.op = SCC_HIR_OP_EMPTY;
|
||||||
|
in->data.op.lhs = 0;
|
||||||
|
in->data.op.rhs = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_BRANCH:
|
||||||
|
in->data.branch.cond = 0;
|
||||||
|
in->data.branch.true_bblock = 0;
|
||||||
|
in->data.branch.false_bblock = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_JUMP:
|
||||||
|
in->data.jump.target_bblock = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_CALL:
|
||||||
|
scc_vec_init(in->data.call.args);
|
||||||
|
in->data.call.callee = 0;
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_RET:
|
||||||
|
in->data.ret.ret_val = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,705 @@
|
|||||||
|
#include <scc_hir_builder.h>
|
||||||
|
#include <scc_hir_def.h>
|
||||||
|
#include <scc_hir_prog.h>
|
||||||
|
|
||||||
|
#define GET_MODULE(builder) (&(builder->cprog->module))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 哈希混合函数(类似Rust的hash combine)
|
||||||
|
*
|
||||||
|
* Rust的默认哈希实现(SipHash 1-3)使用旋转和异或,
|
||||||
|
* 这里使用简单的FNV-1a变体,类似于Rust的`#[derive(Hash)]`
|
||||||
|
*/
|
||||||
|
static inline u32 scc_hash_mix(u32 seed, u32 value) {
|
||||||
|
// FNV-1a风格混合
|
||||||
|
return (seed ^ value) * 16777619u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 hash_type(const void *_key, void *userdata) {
|
||||||
|
scc_hir_module_t *module = userdata;
|
||||||
|
const scc_hir_type_t *key =
|
||||||
|
_key == SCC_HIR_REF_nullptr
|
||||||
|
? &scc_vec_at(module->types, 0)
|
||||||
|
: scc_hir_module_get_type(module, (usize)_key);
|
||||||
|
// 初始哈希:tag
|
||||||
|
u32 hash = (u32)key->tag;
|
||||||
|
|
||||||
|
switch (key->tag) {
|
||||||
|
case SCC_HIR_TYPE_void:
|
||||||
|
case SCC_HIR_TYPE_u8:
|
||||||
|
case SCC_HIR_TYPE_u16:
|
||||||
|
case SCC_HIR_TYPE_u32:
|
||||||
|
case SCC_HIR_TYPE_u64:
|
||||||
|
case SCC_HIR_TYPE_u128:
|
||||||
|
case SCC_HIR_TYPE_i8:
|
||||||
|
case SCC_HIR_TYPE_i16:
|
||||||
|
case SCC_HIR_TYPE_i32:
|
||||||
|
case SCC_HIR_TYPE_i64:
|
||||||
|
case SCC_HIR_TYPE_i128:
|
||||||
|
case SCC_HIR_TYPE_f16:
|
||||||
|
case SCC_HIR_TYPE_f32:
|
||||||
|
case SCC_HIR_TYPE_f64:
|
||||||
|
case SCC_HIR_TYPE_f128:
|
||||||
|
// 基本类型,只有tag
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCC_HIR_TYPE_PTR:
|
||||||
|
hash = scc_hash_mix(hash, (u32)key->data.array.base);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCC_HIR_TYPE_ARRAY:
|
||||||
|
hash = scc_hash_mix(hash, (u32)key->data.array.base);
|
||||||
|
hash = scc_hash_mix(hash, (u32)key->data.array.len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCC_HIR_TYPE_FUNC:
|
||||||
|
// 注意:这里需要递归哈希参数类型
|
||||||
|
hash = scc_hash_mix(hash, (u32)key->data.function.ret_type);
|
||||||
|
for (usize i = 0; i < key->data.function.params.size; i++) {
|
||||||
|
hash = scc_hash_mix(hash, (u32)key->data.function.params.data[i]);
|
||||||
|
}
|
||||||
|
hash = scc_hash_mix(hash, (u32)key->data.function.params.size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCC_HIR_TYPE_STRUCT:
|
||||||
|
case SCC_HIR_TYPE_UNION:
|
||||||
|
return 0;
|
||||||
|
case SCC_HIR_TYPE_VECTOR:
|
||||||
|
default:
|
||||||
|
Panic("Invalid type tag %d", key->tag);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmp_type(const void *_key1, const void *_key2, void *userdata) {
|
||||||
|
scc_hir_module_t *module = userdata;
|
||||||
|
const scc_hir_type_t *key1 = _key1 == SCC_HIR_REF_nullptr
|
||||||
|
? &scc_vec_at(module->types, 0)
|
||||||
|
: scc_hir_module_get_type(module,
|
||||||
|
(usize)_key1),
|
||||||
|
*key2 = _key2 == SCC_HIR_REF_nullptr
|
||||||
|
? &scc_vec_at(module->types, 0)
|
||||||
|
: scc_hir_module_get_type(module,
|
||||||
|
(usize)_key2);
|
||||||
|
Assert(key1 != nullptr && key2 != nullptr);
|
||||||
|
if (key1->tag == SCC_HIR_TYPE_unknown ||
|
||||||
|
key2->tag == SCC_HIR_TYPE_unknown) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// tag不同
|
||||||
|
if (key1->tag != key2->tag) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (key1->tag) {
|
||||||
|
case SCC_HIR_TYPE_void:
|
||||||
|
case SCC_HIR_TYPE_u8:
|
||||||
|
case SCC_HIR_TYPE_u16:
|
||||||
|
case SCC_HIR_TYPE_u32:
|
||||||
|
case SCC_HIR_TYPE_u64:
|
||||||
|
case SCC_HIR_TYPE_u128:
|
||||||
|
case SCC_HIR_TYPE_i8:
|
||||||
|
case SCC_HIR_TYPE_i16:
|
||||||
|
case SCC_HIR_TYPE_i32:
|
||||||
|
case SCC_HIR_TYPE_i64:
|
||||||
|
case SCC_HIR_TYPE_i128:
|
||||||
|
case SCC_HIR_TYPE_f16:
|
||||||
|
case SCC_HIR_TYPE_f32:
|
||||||
|
case SCC_HIR_TYPE_f64:
|
||||||
|
case SCC_HIR_TYPE_f128:
|
||||||
|
return 0; // 基本类型,tag相同即可
|
||||||
|
case SCC_HIR_TYPE_PTR:
|
||||||
|
return key1->data.pointer.base != key2->data.pointer.base;
|
||||||
|
case SCC_HIR_TYPE_ARRAY:
|
||||||
|
return (key1->data.array.base != key2->data.array.base) ||
|
||||||
|
(key1->data.array.len != key2->data.array.len);
|
||||||
|
case SCC_HIR_TYPE_FUNC: {
|
||||||
|
if (key1->data.function.ret_type != key2->data.function.ret_type) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (key1->data.function.params.size !=
|
||||||
|
key2->data.function.params.size) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
scc_vec_foreach(key1->data.function.params, i) {
|
||||||
|
if (key1->data.function.params.data[i] !=
|
||||||
|
key2->data.function.params.data[i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case SCC_HIR_TYPE_STRUCT:
|
||||||
|
case SCC_HIR_TYPE_UNION: {
|
||||||
|
if (key1->data.aggregate.fields.size !=
|
||||||
|
key2->data.aggregate.fields.size) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
scc_vec_foreach(key1->data.aggregate.fields, i) {
|
||||||
|
if (key1->data.aggregate.fields.data[i] !=
|
||||||
|
key2->data.aggregate.fields.data[i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Panic("Unknown key type %d", key1->tag);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_builder_init(scc_hir_builder_t *builder, scc_hir_cprog_t *cprog) {
|
||||||
|
builder->current_bblock = SCC_HIR_REF_nullptr;
|
||||||
|
builder->current_func = SCC_HIR_REF_nullptr;
|
||||||
|
builder->cprog = cprog;
|
||||||
|
|
||||||
|
scc_hashtable_init(&builder->type_uniquing, hash_type, cmp_type,
|
||||||
|
GET_MODULE(builder));
|
||||||
|
// scc_hashtable_init(&builder->const_pool, /* 常量哈希函数 */,
|
||||||
|
// /* 常量比较函数 */);
|
||||||
|
scc_hashtable_cstr_init(&builder->func_decl_set);
|
||||||
|
#ifndef SCC_NO_DEBUG
|
||||||
|
builder->borrow_depth = 0;
|
||||||
|
builder->dbg_file = nullptr;
|
||||||
|
builder->dbg_line = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_builder_drop(scc_hir_builder_t *builder) {
|
||||||
|
scc_hashtable_drop(&builder->type_uniquing);
|
||||||
|
// scc_hashtable_drop(&builder->const_pool);
|
||||||
|
scc_hashtable_drop(&builder->func_decl_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_func_ref_t scc_hir_builder_func(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type_ref,
|
||||||
|
const char *name) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
// 检查是否已声明
|
||||||
|
void *found = scc_hashtable_get(&builder->func_decl_set, (void *)name);
|
||||||
|
if (found) {
|
||||||
|
return (scc_hir_func_ref_t)(usize)found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新函数
|
||||||
|
scc_hir_func_t func;
|
||||||
|
scc_hir_func_meta_t *meta = scc_malloc(sizeof(scc_hir_func_meta_t));
|
||||||
|
Assert(meta != nullptr);
|
||||||
|
func.meta = meta;
|
||||||
|
scc_hir_func_init(&func, name);
|
||||||
|
meta->type = type_ref;
|
||||||
|
|
||||||
|
scc_hir_func_ref_t func_ref =
|
||||||
|
scc_hir_module_add_func(GET_MODULE(builder), &func);
|
||||||
|
scc_hashtable_set(&builder->func_decl_set, (void *)name,
|
||||||
|
(void *)(usize)func_ref);
|
||||||
|
|
||||||
|
scc_vec_push(builder->cprog->func_decls, func_ref);
|
||||||
|
return func_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_bblock_ref_t scc_hir_builder_bblock(scc_hir_builder_t *builder,
|
||||||
|
const char *label) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_bblock_t bblock = {0};
|
||||||
|
if (label) {
|
||||||
|
bblock.name = label;
|
||||||
|
}
|
||||||
|
// scc_hir_bblock_meta_t bblock_meta = {0};
|
||||||
|
// FIXME 当前没有bblock meta
|
||||||
|
bblock.meta = nullptr;
|
||||||
|
scc_vec_init(*SCC_HIR_BBLOCK_VALUES(&bblock));
|
||||||
|
|
||||||
|
scc_hir_bblock_ref_t bblock_ref =
|
||||||
|
scc_hir_module_add_bblock(GET_MODULE(builder), &bblock);
|
||||||
|
|
||||||
|
// 将基本块添加到当前函数
|
||||||
|
scc_hir_func_t *current_func = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, current_func,
|
||||||
|
scc_hir_module_get_func(GET_MODULE(builder), builder->current_func));
|
||||||
|
if (current_func) {
|
||||||
|
// FIXME hack cfg
|
||||||
|
scc_vec_push(current_func->bblocks, bblock_ref);
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder);
|
||||||
|
return bblock_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_type_ref_t scc_hir_builder_type(scc_hir_builder_t *builder,
|
||||||
|
const scc_hir_type_t *type_desc) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
|
||||||
|
Assert(type_desc->tag != SCC_HIR_TYPE_unknown);
|
||||||
|
|
||||||
|
scc_vec_at(GET_MODULE(builder)->types, 0) = *type_desc;
|
||||||
|
// 先查哈希表
|
||||||
|
void *found = scc_hashtable_get(&builder->type_uniquing, (void *)(usize)0);
|
||||||
|
if (found) {
|
||||||
|
return (scc_hir_type_ref_t)(usize)found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不存在,添加新类型
|
||||||
|
scc_hir_type_ref_t new_ref =
|
||||||
|
scc_hir_module_add_type(GET_MODULE(builder), type_desc);
|
||||||
|
scc_hashtable_set(&builder->type_uniquing, (void *)(usize)new_ref,
|
||||||
|
(void *)(usize)new_ref);
|
||||||
|
return new_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_const_string(scc_hir_builder_t *builder,
|
||||||
|
const char *str, usize len) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_type_ref_t u8_type = scc_hir_builder_type_u8(builder);
|
||||||
|
scc_hir_type_t array_type = {
|
||||||
|
.tag = SCC_HIR_TYPE_ARRAY,
|
||||||
|
.data.array.base = u8_type,
|
||||||
|
.data.array.len = len - 1, // 包含 nullptr 结尾
|
||||||
|
};
|
||||||
|
scc_hir_type_ref_t array_type_ref =
|
||||||
|
scc_hir_builder_type(builder, &array_type);
|
||||||
|
|
||||||
|
// 5. 创建聚合节点
|
||||||
|
scc_hir_value_t const_array_value = {
|
||||||
|
.tag = SCC_HIR_VALUE_TAG_ARRAY,
|
||||||
|
.type = array_type_ref,
|
||||||
|
.data.const_array.base_type = u8_type,
|
||||||
|
};
|
||||||
|
char *buff = scc_malloc(len - 1);
|
||||||
|
Assert(buff);
|
||||||
|
// FIXME content to real string
|
||||||
|
for (usize i = 1; i < len - 1; i++) {
|
||||||
|
buff[i - 1] = str[i];
|
||||||
|
}
|
||||||
|
buff[len - 2] = '\0';
|
||||||
|
scc_vec_unsafe_from_buffer(const_array_value.data.const_array.fields,
|
||||||
|
(u8 *)buff, len - 1);
|
||||||
|
scc_hir_value_ref_t const_array_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &const_array_value);
|
||||||
|
Assert(const_array_ref != SCC_HIR_REF_nullptr);
|
||||||
|
|
||||||
|
// 3. 创建全局变量节点,类型为指针,初始值指向常量数组
|
||||||
|
scc_hir_value_ref_t global_value_ref =
|
||||||
|
scc_hir_builder_global_alloca(builder, array_type_ref, const_array_ref);
|
||||||
|
// scc_hashtable_insert(builder);
|
||||||
|
|
||||||
|
scc_hir_value_ref_t pointer_to_global_value = scc_hir_module_add_value(
|
||||||
|
GET_MODULE(builder),
|
||||||
|
&(scc_hir_value_t){
|
||||||
|
.tag = SCC_HIR_VALUE_TAG_GET_ELEM_PTR,
|
||||||
|
.data.get_elem_ptr.src_addr = global_value_ref,
|
||||||
|
.data.get_elem_ptr.index = SCC_HIR_VALUE_TAG_NULLPTR,
|
||||||
|
});
|
||||||
|
scc_hir_builder_add_instr(builder, pointer_to_global_value);
|
||||||
|
return pointer_to_global_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_builder_begin_func(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_func_ref_t func_ref) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
builder->current_func = func_ref;
|
||||||
|
|
||||||
|
// 借用 func_ptr 和 func_type 以获取参数类型列表
|
||||||
|
scc_hir_func_t *func_ptr = nullptr;
|
||||||
|
scc_hir_type_t *func_type = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, func_ptr,
|
||||||
|
scc_hir_module_get_func(GET_MODULE(builder), func_ref));
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, func_type,
|
||||||
|
scc_hir_module_get_type(GET_MODULE(builder),
|
||||||
|
SCC_HIR_FUNC_META(func_ptr)->type));
|
||||||
|
|
||||||
|
if (func_type == nullptr || func_type->tag != SCC_HIR_TYPE_FUNC) {
|
||||||
|
LOG_ERROR("Invalid function type");
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_type
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_ptr
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (func_ptr == nullptr) {
|
||||||
|
LOG_ERROR("Invalid function reference");
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_type
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_ptr
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (scc_vec_size(func_ptr->bblocks) != 0 ||
|
||||||
|
scc_vec_size(SCC_HIR_FUNC_META(func_ptr)->params) != 0) {
|
||||||
|
LOG_FATAL("Multiple function definitions");
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_type
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_ptr
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放借用,因为下面要调用 add_value(可能 realloc)
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_type
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_builder_end_func(scc_hir_builder_t *builder) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_func_t *func_ptr = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, func_ptr,
|
||||||
|
scc_hir_module_get_func(GET_MODULE(builder), builder->current_func));
|
||||||
|
if (func_ptr == nullptr) {
|
||||||
|
LOG_FATAL("Invalid function reference");
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (scc_vec_size(func_ptr->bblocks) == 0) {
|
||||||
|
scc_vec_push(builder->cprog->func_decls, builder->current_func);
|
||||||
|
} else {
|
||||||
|
scc_vec_push(builder->cprog->func_defs, builder->current_func);
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder);
|
||||||
|
builder->current_func = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_func_ref_t scc_hir_builder_current_func(scc_hir_builder_t *builder) {
|
||||||
|
// 只读操作,无需检查借用
|
||||||
|
return builder->current_func;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_bblock_ref_t scc_hir_builder_begin_bblock(scc_hir_builder_t *builder,
|
||||||
|
const char *label) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
builder->current_bblock = scc_hir_builder_bblock(builder, label);
|
||||||
|
return builder->current_bblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_builder_end_bblock(scc_hir_builder_t *builder) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
builder->current_bblock = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_builder_set_current_bblock(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_bblock_ref_t bblock) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
builder->current_bblock = bblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_func_ref_t scc_hir_builder_current_bblock(scc_hir_builder_t *builder) {
|
||||||
|
return builder->current_bblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_builder_add_instr(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t instr) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_bblock_t *current_bblock = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, current_bblock,
|
||||||
|
scc_hir_module_get_bblock(GET_MODULE(builder),
|
||||||
|
builder->current_bblock));
|
||||||
|
if (current_bblock) {
|
||||||
|
scc_vec_push(*SCC_HIR_BBLOCK_VALUES(current_bblock), instr);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Current basic block is not set");
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_global_alloca(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type,
|
||||||
|
scc_hir_value_ref_t value) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
char *name = scc_malloc(32);
|
||||||
|
scc_hir_value_ref_t global_value_ref = scc_hir_module_add_value(
|
||||||
|
GET_MODULE(builder), &(scc_hir_value_t){
|
||||||
|
.name = name,
|
||||||
|
.tag = SCC_HIR_VALUE_TAG_GLOBAL_ALLOC,
|
||||||
|
.type = type,
|
||||||
|
.data.global_alloc.value = value,
|
||||||
|
});
|
||||||
|
scc_snprintf(name, 32, "$G%u", global_value_ref);
|
||||||
|
scc_vec_push(builder->cprog->global_vals, global_value_ref);
|
||||||
|
return global_value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_alloca(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type,
|
||||||
|
const char *name) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t alloc_node = {0};
|
||||||
|
alloc_node.tag = SCC_HIR_VALUE_TAG_ALLOC;
|
||||||
|
alloc_node.type = scc_hir_module_add_type(
|
||||||
|
GET_MODULE(builder),
|
||||||
|
&(scc_hir_type_t){.tag = SCC_HIR_TYPE_PTR, .data.pointer.base = type});
|
||||||
|
alloc_node.name = name;
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &alloc_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_func_arg_ref(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_type_ref_t type,
|
||||||
|
const char *name,
|
||||||
|
usize arg_idx) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t value = {0};
|
||||||
|
value.tag = SCC_HIR_VALUE_TAG_FUNC_ARG_REF;
|
||||||
|
value.type = type;
|
||||||
|
value.name = name;
|
||||||
|
value.data.arg_ref.idx = arg_idx;
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &value);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_load(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t target) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t load_node = {0};
|
||||||
|
load_node.tag = SCC_HIR_VALUE_TAG_LOAD;
|
||||||
|
load_node.data.load.target = target;
|
||||||
|
|
||||||
|
// 借用 ptr_node 和 ptr_type 获取类型信息
|
||||||
|
scc_hir_value_t *ptr_node = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, ptr_node,
|
||||||
|
scc_hir_module_get_value(GET_MODULE(builder), target));
|
||||||
|
if (ptr_node) {
|
||||||
|
scc_hir_type_t *ptr_type = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, ptr_type,
|
||||||
|
scc_hir_module_get_type(GET_MODULE(builder), ptr_node->type));
|
||||||
|
if (ptr_type && ptr_type->tag == SCC_HIR_TYPE_PTR) {
|
||||||
|
load_node.type = ptr_type->data.pointer.base;
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // ptr_type
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // ptr_node
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &load_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_store(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t target,
|
||||||
|
scc_hir_value_ref_t value) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
Assert(target != SCC_HIR_REF_nullptr && value != SCC_HIR_REF_nullptr);
|
||||||
|
scc_hir_value_t store_node = {0};
|
||||||
|
store_node.tag = SCC_HIR_VALUE_TAG_STORE;
|
||||||
|
store_node.data.store.target = target;
|
||||||
|
store_node.data.store.value = value;
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &store_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_get_elem_ptr(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t target,
|
||||||
|
scc_hir_value_ref_t index) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t get_ptr_node = {0};
|
||||||
|
get_ptr_node.tag = SCC_HIR_VALUE_TAG_GET_ELEM_PTR;
|
||||||
|
get_ptr_node.data.get_elem_ptr.src_addr = target;
|
||||||
|
get_ptr_node.data.get_elem_ptr.index = index;
|
||||||
|
|
||||||
|
// 借用类型信息
|
||||||
|
scc_hir_type_t *type_ref = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, type_ref,
|
||||||
|
scc_hir_module_get_type_by_value(GET_MODULE(builder), target));
|
||||||
|
Assert(type_ref != nullptr);
|
||||||
|
scc_hir_type_t type = *type_ref; // 拷贝一份,避免后续借用
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // type_ref
|
||||||
|
|
||||||
|
if (type.tag == SCC_HIR_TYPE_PTR) {
|
||||||
|
scc_hir_type_t *base_type = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, base_type,
|
||||||
|
scc_hir_module_get_type(GET_MODULE(builder),
|
||||||
|
type_ref->data.pointer.base));
|
||||||
|
if (base_type->tag == SCC_HIR_TYPE_ARRAY) {
|
||||||
|
scc_hir_type_t type = (scc_hir_type_t){
|
||||||
|
.tag = SCC_HIR_TYPE_PTR,
|
||||||
|
.data.pointer.base = base_type->data.array.base,
|
||||||
|
};
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // base_type
|
||||||
|
get_ptr_node.type = scc_hir_builder_type(builder, &type);
|
||||||
|
} else {
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // base_type
|
||||||
|
get_ptr_node.type = scc_hir_builder_type(builder, &type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
get_ptr_node.type = scc_hir_builder_type(builder, &type);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &get_ptr_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_binop(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_op_type_t op,
|
||||||
|
scc_hir_value_ref_t lhs,
|
||||||
|
scc_hir_value_ref_t rhs) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t binop_node = {0};
|
||||||
|
binop_node.tag = SCC_HIR_VALUE_TAG_OP;
|
||||||
|
binop_node.data.op.op = op;
|
||||||
|
binop_node.data.op.lhs = lhs;
|
||||||
|
binop_node.data.op.rhs = rhs;
|
||||||
|
|
||||||
|
// 借用 lhs_node 获取类型
|
||||||
|
scc_hir_value_t *lhs_node = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, lhs_node, scc_hir_module_get_value(GET_MODULE(builder), lhs));
|
||||||
|
if (lhs_node) {
|
||||||
|
binop_node.type = lhs_node->type;
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // lhs_node
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &binop_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_cmp(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_op_type_t op,
|
||||||
|
scc_hir_value_ref_t lhs,
|
||||||
|
scc_hir_value_ref_t rhs) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t cmp_node = {0};
|
||||||
|
cmp_node.tag = SCC_HIR_VALUE_TAG_OP;
|
||||||
|
cmp_node.data.op.op = op;
|
||||||
|
cmp_node.data.op.lhs = lhs;
|
||||||
|
cmp_node.data.op.rhs = rhs;
|
||||||
|
cmp_node.type = 0; // FIXME
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &cmp_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_jump(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_bblock_ref_t target) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t jump_node = {0};
|
||||||
|
jump_node.tag = SCC_HIR_VALUE_TAG_JUMP;
|
||||||
|
jump_node.data.jump.target_bblock = target;
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &jump_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_branch(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t cond,
|
||||||
|
scc_hir_bblock_ref_t true_target,
|
||||||
|
scc_hir_bblock_ref_t false_target) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t branch_node = {0};
|
||||||
|
branch_node.tag = SCC_HIR_VALUE_TAG_BRANCH;
|
||||||
|
branch_node.data.branch.cond = cond;
|
||||||
|
branch_node.data.branch.true_bblock = true_target;
|
||||||
|
branch_node.data.branch.false_bblock = false_target;
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &branch_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_call(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_func_ref_t callee,
|
||||||
|
const scc_hir_value_ref_t *args,
|
||||||
|
usize arg_count) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t call_node = {0};
|
||||||
|
call_node.tag = SCC_HIR_VALUE_TAG_CALL;
|
||||||
|
call_node.data.call.callee = callee;
|
||||||
|
|
||||||
|
scc_vec_init(call_node.data.call.args);
|
||||||
|
for (usize i = 0; i < arg_count; i++) {
|
||||||
|
scc_vec_push(call_node.data.call.args, args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 借用 callee_func 和 func_type 获取返回类型
|
||||||
|
scc_hir_func_t *callee_func = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, callee_func,
|
||||||
|
scc_hir_module_get_func(GET_MODULE(builder), callee));
|
||||||
|
if (callee_func) {
|
||||||
|
scc_hir_type_t *func_type = nullptr;
|
||||||
|
SCC_HIR_BUILDER_BEGIN_BORROW(
|
||||||
|
builder, func_type,
|
||||||
|
scc_hir_module_get_type(GET_MODULE(builder),
|
||||||
|
SCC_HIR_FUNC_META(callee_func)->type));
|
||||||
|
if (func_type && func_type->tag == SCC_HIR_TYPE_FUNC) {
|
||||||
|
call_node.type = func_type->data.function.ret_type;
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // func_type
|
||||||
|
}
|
||||||
|
SCC_HIR_BUILDER_END_BORROW(builder); // callee_func
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &call_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_ret(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t value) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t ret_node = {0};
|
||||||
|
ret_node.tag = SCC_HIR_VALUE_TAG_RET;
|
||||||
|
ret_node.data.ret.ret_val = value;
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &ret_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_ret_void(scc_hir_builder_t *builder) {
|
||||||
|
SCC_HIR_BUILDER_CHECK_NO_BORROW(builder);
|
||||||
|
scc_hir_value_t ret_node = {0};
|
||||||
|
ret_node.tag = SCC_HIR_VALUE_TAG_RET;
|
||||||
|
|
||||||
|
scc_hir_value_t ret_val_node;
|
||||||
|
scc_hir_value_init(&ret_val_node, nullptr, SCC_HIR_VALUE_TAG_NULLPTR);
|
||||||
|
ret_val_node.type = scc_hir_builder_type_void(builder);
|
||||||
|
ret_node.data.ret.ret_val =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &ret_val_node);
|
||||||
|
|
||||||
|
scc_hir_value_ref_t value_ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &ret_node);
|
||||||
|
scc_hir_builder_add_instr(builder, value_ref);
|
||||||
|
return value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_builtin_memcpy(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t dest,
|
||||||
|
scc_hir_value_ref_t src,
|
||||||
|
scc_hir_value_ref_t len) {
|
||||||
|
Assert(builder && src && dest && len);
|
||||||
|
scc_hir_value_t value;
|
||||||
|
scc_hir_value_init(&value, nullptr, SCC_HIR_VALUE_TAG_BUILTIN);
|
||||||
|
value.type = scc_hir_builder_type_void(builder); // memcpy 返回 void*
|
||||||
|
value.data.builtin.tag = SCC_HIR_BUILTIN_TAG_MEMCPY;
|
||||||
|
value.data.builtin.func.memcpy.dest = dest;
|
||||||
|
value.data.builtin.func.memcpy.src = src;
|
||||||
|
value.data.builtin.func.memcpy.size = len;
|
||||||
|
scc_hir_value_ref_t ref =
|
||||||
|
scc_hir_module_add_value(GET_MODULE(builder), &value);
|
||||||
|
scc_hir_builder_add_instr(builder, ref);
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
@@ -0,0 +1,719 @@
|
|||||||
|
#include <scc_hir_dump.h>
|
||||||
|
#include <scc_hir_prog.h>
|
||||||
|
#include <scc_tree_dump.h>
|
||||||
|
|
||||||
|
#define GET_MODULE(ctx) (&(ctx->cprog->module))
|
||||||
|
|
||||||
|
static const char *get_node_type_str(scc_hir_value_tag_t tag) {
|
||||||
|
static const char *node_types[] = {
|
||||||
|
[SCC_HIR_VALUE_TAG_NULLPTR] = "NullPtr",
|
||||||
|
[SCC_HIR_VALUE_TAG_BUILTIN] = "Builtin",
|
||||||
|
[SCC_HIR_VALUE_TAG_INTEGER] = "ConstInt",
|
||||||
|
[SCC_HIR_VALUE_TAG_DECIMAL] = "ConstFloat",
|
||||||
|
[SCC_HIR_VALUE_TAG_ARRAY] = "ConstArray",
|
||||||
|
[SCC_HIR_VALUE_TAG_AGGREGATE] = "Aggregate",
|
||||||
|
[SCC_HIR_VALUE_TAG_CONV] = "Convert",
|
||||||
|
[SCC_HIR_VALUE_TAG_FUNC_ARG_REF] = "FuncArgRef",
|
||||||
|
[SCC_HIR_VALUE_TAG_BLOCK_ARG_REF] = "BlockArgRef",
|
||||||
|
[SCC_HIR_VALUE_TAG_ALLOC] = "Alloc",
|
||||||
|
[SCC_HIR_VALUE_TAG_GLOBAL_ALLOC] = "GlobalAlloc",
|
||||||
|
[SCC_HIR_VALUE_TAG_LOAD] = "Load",
|
||||||
|
[SCC_HIR_VALUE_TAG_STORE] = "Store",
|
||||||
|
[SCC_HIR_VALUE_TAG_GET_ELEM_PTR] = "GetElemPtr",
|
||||||
|
[SCC_HIR_VALUE_TAG_OP] = "Op",
|
||||||
|
[SCC_HIR_VALUE_TAG_BRANCH] = "Branch",
|
||||||
|
[SCC_HIR_VALUE_TAG_JUMP] = "Jump",
|
||||||
|
[SCC_HIR_VALUE_TAG_CALL] = "Call",
|
||||||
|
[SCC_HIR_VALUE_TAG_RET] = "Ret",
|
||||||
|
};
|
||||||
|
if (tag >= 0 && (usize)tag < sizeof(node_types) / sizeof(node_types[0]) &&
|
||||||
|
node_types[tag])
|
||||||
|
return node_types[tag];
|
||||||
|
return "UnknownIRNode";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_op_str(scc_hir_op_type_t op) {
|
||||||
|
static const char *ops[] = {
|
||||||
|
[SCC_HIR_OP_EMPTY] = "empty", [SCC_HIR_OP_NEQ] = "!=",
|
||||||
|
[SCC_HIR_OP_EQ] = "==", [SCC_HIR_OP_GT] = ">",
|
||||||
|
[SCC_HIR_OP_LT] = "<", [SCC_HIR_OP_GE] = ">=",
|
||||||
|
[SCC_HIR_OP_LE] = "<=", [SCC_HIR_OP_ADD] = "+",
|
||||||
|
[SCC_HIR_OP_SUB] = "-", [SCC_HIR_OP_MUL] = "*",
|
||||||
|
[SCC_HIR_OP_DIV] = "/", [SCC_HIR_OP_MOD] = "%",
|
||||||
|
[SCC_HIR_OP_AND] = "&", [SCC_HIR_OP_OR] = "|",
|
||||||
|
[SCC_HIR_OP_XOR] = "^", [SCC_HIR_OP_NOT] = "~",
|
||||||
|
[SCC_HIR_OP_SHL] = "<<", [SCC_HIR_OP_SHR] = ">>",
|
||||||
|
[SCC_HIR_OP_SAR] = ">>a",
|
||||||
|
};
|
||||||
|
if (op >= 0 && (usize)op < sizeof(ops) / sizeof(ops[0]) && ops[op])
|
||||||
|
return ops[op];
|
||||||
|
return "<unknown_op>";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_type_tag_str(scc_hir_type_tag_t tag) {
|
||||||
|
static const char *type_tags[] = {
|
||||||
|
[SCC_HIR_TYPE_void] = "void", [SCC_HIR_TYPE_u8] = "u8",
|
||||||
|
[SCC_HIR_TYPE_u16] = "u16", [SCC_HIR_TYPE_u32] = "u32",
|
||||||
|
[SCC_HIR_TYPE_u64] = "u64", [SCC_HIR_TYPE_u128] = "u128",
|
||||||
|
[SCC_HIR_TYPE_i8] = "i8", [SCC_HIR_TYPE_i16] = "i16",
|
||||||
|
[SCC_HIR_TYPE_i32] = "i32", [SCC_HIR_TYPE_i64] = "i64",
|
||||||
|
[SCC_HIR_TYPE_i128] = "i128", [SCC_HIR_TYPE_f16] = "f16",
|
||||||
|
[SCC_HIR_TYPE_f32] = "f32", [SCC_HIR_TYPE_f64] = "f64",
|
||||||
|
[SCC_HIR_TYPE_f128] = "f128", [SCC_HIR_TYPE_PTR] = "ptr",
|
||||||
|
[SCC_HIR_TYPE_ARRAY] = "array", [SCC_HIR_TYPE_FUNC] = "func",
|
||||||
|
[SCC_HIR_TYPE_STRUCT] = "struct", [SCC_HIR_TYPE_VECTOR] = "vector",
|
||||||
|
};
|
||||||
|
if (tag >= 0 && (usize)tag < sizeof(type_tags) / sizeof(type_tags[0]) &&
|
||||||
|
type_tags[tag])
|
||||||
|
return type_tags[tag];
|
||||||
|
return "<unknown_type>";
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dump_child_node_ref(scc_hir_dump_t *ctx,
|
||||||
|
scc_hir_value_ref_t child,
|
||||||
|
cbool is_last) {
|
||||||
|
if (!child)
|
||||||
|
return;
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, is_last);
|
||||||
|
scc_hir_dump_value(ctx, child);
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_integer_node(scc_hir_dump_t *ctx,
|
||||||
|
const scc_hir_value_t *value) {
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, true);
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
// FIXME hack it
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "%d", value->data.integer.data.digit);
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_op_node(scc_hir_dump_t *ctx, const scc_hir_value_t *value) {
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, false);
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "op: ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "%s", get_op_str(value->data.op.op));
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
|
||||||
|
if (value->data.op.lhs)
|
||||||
|
dump_child_node_ref(ctx, value->data.op.lhs,
|
||||||
|
value->data.op.rhs ? false : true);
|
||||||
|
if (value->data.op.rhs)
|
||||||
|
dump_child_node_ref(ctx, value->data.op.rhs, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_load_node(scc_hir_dump_t *ctx, const scc_hir_value_t *value) {
|
||||||
|
if (value->data.load.target)
|
||||||
|
dump_child_node_ref(ctx, value->data.load.target, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_store_node(scc_hir_dump_t *ctx, const scc_hir_value_t *value) {
|
||||||
|
if (value->data.store.target)
|
||||||
|
dump_child_node_ref(ctx, value->data.store.target, false);
|
||||||
|
if (value->data.store.value)
|
||||||
|
dump_child_node_ref(ctx, value->data.store.value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_get_elem_ptr_node(scc_hir_dump_t *ctx,
|
||||||
|
const scc_hir_value_t *value) {
|
||||||
|
if (value->data.get_elem_ptr.src_addr)
|
||||||
|
dump_child_node_ref(ctx, value->data.get_elem_ptr.src_addr, false);
|
||||||
|
if (value->data.get_elem_ptr.index)
|
||||||
|
dump_child_node_ref(ctx, value->data.get_elem_ptr.index, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_branch_node(scc_hir_dump_t *ctx,
|
||||||
|
const scc_hir_value_t *value) {
|
||||||
|
if (value->data.branch.cond)
|
||||||
|
dump_child_node_ref(ctx, value->data.branch.cond, false);
|
||||||
|
|
||||||
|
if (value->data.branch.true_bblock) {
|
||||||
|
scc_hir_bblock_t *true_bblock = scc_hir_module_get_bblock(
|
||||||
|
GET_MODULE(ctx), value->data.branch.true_bblock);
|
||||||
|
if (true_bblock) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "TrueBlock: ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "'%s'",
|
||||||
|
true_bblock->name ? true_bblock->name
|
||||||
|
: "<unnamed>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value->data.branch.false_bblock) {
|
||||||
|
scc_hir_bblock_t *false_bblock = scc_hir_module_get_bblock(
|
||||||
|
GET_MODULE(ctx), value->data.branch.false_bblock);
|
||||||
|
if (false_bblock) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "FalseBlock: ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "'%s'",
|
||||||
|
false_bblock->name ? false_bblock->name
|
||||||
|
: "<unnamed>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_jump_node(scc_hir_dump_t *ctx, const scc_hir_value_t *value) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
if (value->data.jump.target_bblock) {
|
||||||
|
scc_hir_bblock_t *target = scc_hir_module_get_bblock(
|
||||||
|
GET_MODULE(ctx), value->data.jump.target_bblock);
|
||||||
|
if (target)
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "to '%s'",
|
||||||
|
target->name ? target->name : "<unnamed>");
|
||||||
|
else
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "to invalid block");
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "to nullptr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_call_node(scc_hir_dump_t *ctx, const scc_hir_value_t *value) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
if (value->data.call.callee) {
|
||||||
|
scc_hir_func_t *callee =
|
||||||
|
scc_hir_module_get_func(GET_MODULE(ctx), value->data.call.callee);
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "func='%s'",
|
||||||
|
callee ? (callee->name ? callee->name : "<unnamed>")
|
||||||
|
: "<invalid>");
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "func=nullptr");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usize i = 0; i < scc_vec_size(value->data.call.args); i++) {
|
||||||
|
cbool is_last = (i + 1 == scc_vec_size(value->data.call.args));
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, is_last);
|
||||||
|
scc_hir_value_ref_t arg = scc_vec_at(value->data.call.args, i);
|
||||||
|
dump_child_node_ref(ctx, arg, is_last);
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_ret_node(scc_hir_dump_t *ctx, const scc_hir_value_t *value) {
|
||||||
|
if (value->data.ret.ret_val)
|
||||||
|
dump_child_node_ref(ctx, value->data.ret.ret_val, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_init(scc_hir_dump_t *ctx, scc_tree_dump_t *td,
|
||||||
|
scc_hir_cprog_t *cprog) {
|
||||||
|
ctx->dump_ctx = td;
|
||||||
|
ctx->cprog = cprog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_value(scc_hir_dump_t *ctx, scc_hir_value_ref_t value_ref) {
|
||||||
|
scc_hir_value_t *value =
|
||||||
|
scc_hir_module_get_value(GET_MODULE(ctx), value_ref);
|
||||||
|
if (!value) {
|
||||||
|
LOG_ERROR("Invalid value ref");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "%s", get_node_type_str(value->tag));
|
||||||
|
if (value->name && value->name[0])
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, " [%s]", value->name);
|
||||||
|
if (value->type) {
|
||||||
|
scc_hir_type_t *type =
|
||||||
|
scc_hir_module_get_type(GET_MODULE(ctx), value->type);
|
||||||
|
if (type) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, " : ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "%s",
|
||||||
|
get_type_tag_str(type->tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value->tag) {
|
||||||
|
case SCC_HIR_VALUE_TAG_INTEGER:
|
||||||
|
dump_integer_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_ALLOC:
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_LOAD:
|
||||||
|
dump_load_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_STORE:
|
||||||
|
dump_store_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_GET_ELEM_PTR:
|
||||||
|
dump_get_elem_ptr_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_OP:
|
||||||
|
dump_op_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_BRANCH:
|
||||||
|
dump_branch_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_JUMP:
|
||||||
|
dump_jump_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_CALL:
|
||||||
|
dump_call_node(ctx, value);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_RET:
|
||||||
|
dump_ret_node(ctx, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "unknown");
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, " tag(%d)", value->tag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_type(scc_hir_dump_t *ctx, scc_hir_type_ref_t type_ref) {
|
||||||
|
if (!ctx || !type_ref) {
|
||||||
|
LOG_ERROR("invalid parameter");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scc_hir_type_t *type = scc_hir_module_get_type(GET_MODULE(ctx), type_ref);
|
||||||
|
if (!type) {
|
||||||
|
LOG_ERROR("invalid type ref");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "Type: ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "%s", get_type_tag_str(type->tag));
|
||||||
|
|
||||||
|
switch (type->tag) {
|
||||||
|
case SCC_HIR_TYPE_PTR:
|
||||||
|
if (type->data.pointer.base) {
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, true);
|
||||||
|
scc_hir_dump_type(ctx, type->data.pointer.base);
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_ARRAY:
|
||||||
|
if (type->data.array.len > 0) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "Array Length: ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "%zu", type->data.array.len);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "\n");
|
||||||
|
}
|
||||||
|
if (type->data.array.base) {
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, true);
|
||||||
|
scc_hir_dump_type(ctx, type->data.array.base);
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_FUNC:
|
||||||
|
for (usize i = 0; i < scc_vec_size(type->data.function.params); i++) {
|
||||||
|
cbool is_last = (i + 1 == scc_vec_size(type->data.function.params));
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, is_last);
|
||||||
|
scc_hir_dump_type(ctx, scc_vec_at(type->data.function.params, i));
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
if (type->data.function.ret_type) {
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, true);
|
||||||
|
scc_hir_dump_type(ctx, type->data.function.ret_type);
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_bblock(scc_hir_dump_t *ctx, scc_hir_bblock_ref_t bblock_ref) {
|
||||||
|
if (!ctx || !bblock_ref) {
|
||||||
|
LOG_ERROR("invalid parameter");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scc_hir_bblock_t *bblock =
|
||||||
|
scc_hir_module_get_bblock(GET_MODULE(ctx), bblock_ref);
|
||||||
|
if (!bblock) {
|
||||||
|
LOG_ERROR("invalid bblock ref");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "BasicBlock: ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "'%s'",
|
||||||
|
bblock->name ? bblock->name : "<unnamed>");
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "\n");
|
||||||
|
|
||||||
|
scc_hir_value_ref_vec_t *values = SCC_HIR_BBLOCK_VALUES(bblock);
|
||||||
|
scc_vec_foreach(*values, i) {
|
||||||
|
cbool is_last = (i + 1 == scc_vec_size(*values));
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, is_last);
|
||||||
|
scc_hir_dump_value(ctx, scc_vec_at(*values, i));
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_func(scc_hir_dump_t *ctx, scc_hir_func_ref_t func_ref) {
|
||||||
|
scc_hir_func_t *func = scc_hir_module_get_func(GET_MODULE(ctx), func_ref);
|
||||||
|
if (!ctx || !func) {
|
||||||
|
LOG_ERROR("invalid parameter");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "Function: ");
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "'%s'",
|
||||||
|
func->name ? func->name : "<unnamed>");
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "\n");
|
||||||
|
|
||||||
|
if (SCC_HIR_FUNC_META(func)->type) {
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, false);
|
||||||
|
scc_hir_dump_type(ctx, SCC_HIR_FUNC_META(func)->type);
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
for (usize i = 0; i < scc_vec_size(SCC_HIR_FUNC_META(func)->params); i++) {
|
||||||
|
cbool is_last =
|
||||||
|
(i + 1 == scc_vec_size(SCC_HIR_FUNC_META(func)->params));
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, is_last);
|
||||||
|
scc_hir_dump_value(ctx, scc_vec_at(SCC_HIR_FUNC_META(func)->params, i));
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
for (usize i = 0; i < scc_vec_size(func->bblocks); i++) {
|
||||||
|
cbool is_last = (i + 1 == scc_vec_size(func->bblocks));
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, is_last);
|
||||||
|
scc_hir_dump_bblock(ctx, scc_vec_at(func->bblocks, i));
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_cprog(scc_hir_dump_t *ctx) {
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "Func defs:\n");
|
||||||
|
scc_vec_foreach(ctx->cprog->func_defs, i) {
|
||||||
|
cbool is_last = (i + 1 == scc_vec_size(ctx->cprog->func_defs));
|
||||||
|
scc_tree_dump_push(ctx->dump_ctx, is_last);
|
||||||
|
scc_hir_dump_func(ctx, scc_vec_at(ctx->cprog->func_defs, i));
|
||||||
|
scc_tree_dump_pop(ctx->dump_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- 线性输出(保留原逻辑,改用新 API)-----
|
||||||
|
void scc_hir_dump_type_linear(scc_hir_dump_t *ctx,
|
||||||
|
scc_hir_type_ref_t type_ref) {
|
||||||
|
scc_hir_type_t *type = scc_hir_module_get_type(GET_MODULE(ctx), type_ref);
|
||||||
|
if (!ctx || !type) {
|
||||||
|
LOG_ERROR("invalid parameter");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (type->tag) {
|
||||||
|
case SCC_HIR_TYPE_unknown:
|
||||||
|
case SCC_HIR_TYPE_void:
|
||||||
|
case SCC_HIR_TYPE_i8:
|
||||||
|
case SCC_HIR_TYPE_i16:
|
||||||
|
case SCC_HIR_TYPE_i32:
|
||||||
|
case SCC_HIR_TYPE_i64:
|
||||||
|
case SCC_HIR_TYPE_i128:
|
||||||
|
case SCC_HIR_TYPE_u8:
|
||||||
|
case SCC_HIR_TYPE_u16:
|
||||||
|
case SCC_HIR_TYPE_u32:
|
||||||
|
case SCC_HIR_TYPE_u64:
|
||||||
|
case SCC_HIR_TYPE_u128:
|
||||||
|
case SCC_HIR_TYPE_f16:
|
||||||
|
case SCC_HIR_TYPE_f32:
|
||||||
|
case SCC_HIR_TYPE_f64:
|
||||||
|
case SCC_HIR_TYPE_f128:
|
||||||
|
scc_tree_dump_value(ctx->dump_ctx, "%s", get_type_tag_str(type->tag));
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_ARRAY:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "[");
|
||||||
|
scc_hir_dump_type_linear(ctx, type->data.array.base);
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, ", %zu]", type->data.array.len);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_PTR:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "*");
|
||||||
|
scc_hir_dump_type_linear(ctx, type->data.pointer.base);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_FUNC:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "(");
|
||||||
|
for (usize i = 0; i < scc_vec_size(type->data.function.params); i++) {
|
||||||
|
if (i > 0)
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ", ");
|
||||||
|
scc_hir_dump_type_linear(ctx,
|
||||||
|
scc_vec_at(type->data.function.params, i));
|
||||||
|
}
|
||||||
|
if (type->data.function.ret_type) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ") -> ");
|
||||||
|
scc_hir_dump_type_linear(ctx, type->data.function.ret_type);
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ")");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_HIR_TYPE_STRUCT:
|
||||||
|
case SCC_HIR_TYPE_UNION:
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "%s {",
|
||||||
|
type->tag == SCC_HIR_TYPE_STRUCT ? "struct"
|
||||||
|
: "union");
|
||||||
|
scc_vec_foreach(type->data.aggregate.fields, i) {
|
||||||
|
scc_hir_dump_type_linear(
|
||||||
|
ctx, scc_vec_at(type->data.aggregate.fields, i));
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ";");
|
||||||
|
}
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR("invalid type tag");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void format_ref_or_value(scc_hir_dump_t *ctx,
|
||||||
|
scc_hir_value_ref_t value_ref) {
|
||||||
|
scc_hir_value_t *value =
|
||||||
|
scc_hir_module_get_value(GET_MODULE(ctx), value_ref);
|
||||||
|
if (!value) {
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "%%%u", value_ref);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// FIXME
|
||||||
|
// if (value->tag == SCC_HIR_VALUE_TAG_INTEGER) {
|
||||||
|
// scc_tree_dump_append_fmt(ctx->dump_ctx, "%d",
|
||||||
|
// value->data.const_int.int32);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
if (value->name && value->name[0] != '\0') {
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "%%%u[%s]", value_ref, value->name);
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_node(ctx->dump_ctx, "%%%u", value_ref);
|
||||||
|
}
|
||||||
|
if (value->type != SCC_HIR_REF_nullptr) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ":");
|
||||||
|
scc_hir_dump_type_linear(ctx, value->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_value_linear(scc_hir_dump_t *ctx,
|
||||||
|
scc_hir_value_ref_t value_ref) {
|
||||||
|
scc_hir_value_t *value =
|
||||||
|
scc_hir_module_get_value(GET_MODULE(ctx), value_ref);
|
||||||
|
if (!value) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "<invalid value>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cbool needs_equals = (value->tag != SCC_HIR_VALUE_TAG_BRANCH &&
|
||||||
|
value->tag != SCC_HIR_VALUE_TAG_JUMP &&
|
||||||
|
value->tag != SCC_HIR_VALUE_TAG_RET &&
|
||||||
|
value->tag != SCC_HIR_VALUE_TAG_STORE);
|
||||||
|
|
||||||
|
if (needs_equals) {
|
||||||
|
format_ref_or_value(ctx, value_ref);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, " = ");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value->tag) {
|
||||||
|
case SCC_HIR_VALUE_TAG_BUILTIN: {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "@scc::");
|
||||||
|
switch (value->data.builtin.tag) {
|
||||||
|
case SCC_HIR_BUILTIN_TAG_MEMCPY:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "memcpy");
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "(");
|
||||||
|
format_ref_or_value(ctx, value->data.builtin.func.memcpy.dest);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ", ");
|
||||||
|
format_ref_or_value(ctx, value->data.builtin.func.memcpy.src);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ", ");
|
||||||
|
format_ref_or_value(ctx, value->data.builtin.func.memcpy.size);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ")");
|
||||||
|
break;
|
||||||
|
case SCC_HIR_BUILTIN_TAG_MEMSET:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "memset");
|
||||||
|
break;
|
||||||
|
case SCC_HIR_BUILTIN_TAG_VA_ARG:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "va_arg");
|
||||||
|
break;
|
||||||
|
case SCC_HIR_BUILTIN_TAG_VA_END:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "va_end");
|
||||||
|
break;
|
||||||
|
case SCC_HIR_BUILTIN_TAG_VA_COPY:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "va_copy");
|
||||||
|
break;
|
||||||
|
case SCC_HIR_BUILTIN_TAG_VA_START:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "va_start");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Panic("Unknown builtin tag");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCC_HIR_VALUE_TAG_INTEGER:
|
||||||
|
// 值已经在 format 中输出,这里不需要再做
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_AGGREGATE:
|
||||||
|
// 聚合类型:递归输出每个元素(每个占一行)
|
||||||
|
scc_vec_foreach(value->data.aggregate.fields, i) {
|
||||||
|
scc_hir_dump_value_linear(
|
||||||
|
ctx, scc_vec_at(value->data.aggregate.fields, i));
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SCC_HIR_VALUE_TAG_ALLOC:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "alloc");
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_LOAD:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "load ");
|
||||||
|
format_ref_or_value(ctx, value->data.load.target);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_STORE:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "store ");
|
||||||
|
format_ref_or_value(ctx, value->data.store.value);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, " -> ");
|
||||||
|
format_ref_or_value(ctx, value->data.store.target);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_GET_ELEM_PTR:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "getelemptr ");
|
||||||
|
format_ref_or_value(ctx, value->data.get_elem_ptr.src_addr);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ", ");
|
||||||
|
format_ref_or_value(ctx, value->data.get_elem_ptr.index);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_OP:
|
||||||
|
format_ref_or_value(ctx, value->data.op.lhs);
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, " %s ",
|
||||||
|
get_op_str(value->data.op.op));
|
||||||
|
format_ref_or_value(ctx, value->data.op.rhs);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_BRANCH:
|
||||||
|
if (value->data.branch.cond) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "br ");
|
||||||
|
format_ref_or_value(ctx, value->data.branch.cond);
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx,
|
||||||
|
", label %%L%u, label %%L%u",
|
||||||
|
value->data.branch.true_bblock,
|
||||||
|
value->data.branch.false_bblock);
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "br label %%L%u",
|
||||||
|
value->data.branch.true_bblock);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_JUMP:
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "jmp label %%L%u",
|
||||||
|
value->data.jump.target_bblock);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_CALL: {
|
||||||
|
scc_hir_func_t *func =
|
||||||
|
scc_hir_module_get_func(GET_MODULE(ctx), value->data.call.callee);
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "call @%s(",
|
||||||
|
func ? (func->name ? func->name : "<unnamed>")
|
||||||
|
: "<invalid>");
|
||||||
|
for (usize i = 0; i < scc_vec_size(value->data.call.args); i++) {
|
||||||
|
if (i > 0)
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ", ");
|
||||||
|
format_ref_or_value(ctx, scc_vec_at(value->data.call.args, i));
|
||||||
|
}
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SCC_HIR_VALUE_TAG_RET:
|
||||||
|
if (value->data.ret.ret_val != 0) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "ret ");
|
||||||
|
format_ref_or_value(ctx, value->data.ret.ret_val);
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "ret void");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_FUNC_ARG_REF:
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "arg[%zu]",
|
||||||
|
value->data.arg_ref.idx);
|
||||||
|
break;
|
||||||
|
case SCC_HIR_VALUE_TAG_GLOBAL_ALLOC:
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "global %s", value->name);
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_hir_dump_value_linear(ctx, value->data.global_alloc.value);
|
||||||
|
return;
|
||||||
|
case SCC_HIR_VALUE_TAG_ARRAY:
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "const_array ");
|
||||||
|
scc_hir_dump_type_linear(ctx, value->data.const_array.base_type);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, " [");
|
||||||
|
scc_vec_foreach(value->data.const_array.fields, i) {
|
||||||
|
u8 ch = scc_vec_at(value->data.const_array.fields, i);
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, " `%c`, ", ch ? ch : ' ');
|
||||||
|
}
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, " ]");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "<%s value %u>",
|
||||||
|
get_node_type_str(value->tag), value_ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_bblock_linear(scc_hir_dump_t *ctx,
|
||||||
|
scc_hir_bblock_ref_t bblock_ref) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_hir_bblock_t *bblock =
|
||||||
|
scc_hir_module_get_bblock(GET_MODULE(ctx), bblock_ref);
|
||||||
|
if (!bblock) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "<invalid block>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bblock->name && bblock->name[0] != '\0')
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "%%L%d %s:", bblock_ref,
|
||||||
|
bblock->name);
|
||||||
|
else
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "%%L%d <unnamed>:", bblock_ref);
|
||||||
|
|
||||||
|
scc_hir_value_ref_vec_t *values = SCC_HIR_BBLOCK_VALUES(bblock);
|
||||||
|
for (usize i = 0; i < scc_vec_size(*values); i++) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, " ");
|
||||||
|
scc_hir_dump_value_linear(ctx, scc_vec_at(*values, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_func_linear(scc_hir_dump_t *ctx, scc_hir_func_ref_t func_ref,
|
||||||
|
int is_decl) {
|
||||||
|
scc_hir_func_t *func = scc_hir_module_get_func(GET_MODULE(ctx), func_ref);
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
if (!func) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "<invalid function>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "func @%s",
|
||||||
|
(func->name && func->name[0]) ? func->name
|
||||||
|
: "<unnamed>");
|
||||||
|
|
||||||
|
if (scc_vec_size(SCC_HIR_FUNC_META(func)->params) > 0) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "(");
|
||||||
|
for (usize i = 0; i < scc_vec_size(SCC_HIR_FUNC_META(func)->params);
|
||||||
|
i++) {
|
||||||
|
if (i > 0)
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ", ");
|
||||||
|
scc_hir_value_ref_t param_ref =
|
||||||
|
scc_vec_at(SCC_HIR_FUNC_META(func)->params, i);
|
||||||
|
scc_hir_value_t *param_node =
|
||||||
|
scc_hir_module_get_value(GET_MODULE(ctx), param_ref);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "%");
|
||||||
|
if (param_node && param_node->name && param_node->name[0] != '\0')
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "%u[%s]", param_ref,
|
||||||
|
param_node->name);
|
||||||
|
else
|
||||||
|
scc_tree_dump_append_fmt(ctx->dump_ctx, "%u", param_ref);
|
||||||
|
}
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ")");
|
||||||
|
} else {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "()");
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ": ");
|
||||||
|
scc_hir_dump_type_linear(ctx, SCC_HIR_FUNC_META(func)->type);
|
||||||
|
|
||||||
|
if (is_decl) {
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, ";");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, " {");
|
||||||
|
for (usize i = 0; i < scc_vec_size(func->bblocks); i++) {
|
||||||
|
scc_hir_dump_bblock_linear(ctx, scc_vec_at(func->bblocks, i));
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "");
|
||||||
|
}
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_tree_dump_append(ctx->dump_ctx, "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_hir_dump_cprog_linear(scc_hir_dump_t *ctx) {
|
||||||
|
for (usize i = 0; i < scc_vec_size(ctx->cprog->global_vals); i++) {
|
||||||
|
scc_tree_dump_begin_line(ctx->dump_ctx);
|
||||||
|
scc_hir_dump_value_linear(ctx, scc_vec_at(ctx->cprog->global_vals, i));
|
||||||
|
}
|
||||||
|
for (usize i = 0; i < scc_vec_size(ctx->cprog->func_decls); i++) {
|
||||||
|
scc_hir_func_ref_t func_decl = scc_vec_at(ctx->cprog->func_decls, i);
|
||||||
|
scc_hir_func_t *func =
|
||||||
|
scc_hir_module_get_func(GET_MODULE(ctx), func_decl);
|
||||||
|
if (func && scc_vec_size(func->bblocks) == 0)
|
||||||
|
scc_hir_dump_func_linear(ctx, func_decl, 1);
|
||||||
|
}
|
||||||
|
for (usize i = 0; i < scc_vec_size(ctx->cprog->func_defs); i++) {
|
||||||
|
scc_hir_dump_func_linear(ctx, scc_vec_at(ctx->cprog->func_defs, i), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 289 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user