Compare commits
148
Commits
main
..
8b817da3b6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b817da3b6 | ||
|
|
d2eafa9dc6 | ||
|
|
c4467b8420 | ||
|
|
28f65f8775 | ||
|
|
cec96333e7 | ||
|
|
ea553718f0 | ||
|
|
d78b91894e | ||
|
|
41d060d7e7 | ||
|
|
aa4292a30e | ||
|
|
c6e3bb2e20 | ||
|
|
2c13ac54df | ||
|
|
3df858fb85 | ||
|
|
68eac24152 | ||
|
|
4f40f0d5e4 | ||
|
|
902ee6dea3 | ||
|
|
e5cb70732e | ||
|
|
096177e7e8 | ||
|
|
aa8a1ff8ce | ||
|
|
676f3ec82c | ||
|
|
b06c4fe3cc | ||
|
|
85d698acdf | ||
|
|
f6bc40ae4a | ||
|
|
d7ac5fd30b | ||
|
|
f57ba9bd31 | ||
|
|
e850b5c981 | ||
|
|
0fbfb36262 | ||
|
|
e5bbffe170 | ||
|
|
5a9f816ccf | ||
|
|
8054f20375 | ||
|
|
ffb23afaf4 | ||
|
|
694778e4a0 | ||
|
|
630e22b73b | ||
|
|
053c6abf51 | ||
|
|
eeb4c4fc3c | ||
|
|
d88475cc06 | ||
|
|
4144f7841c | ||
|
|
27d86d5685 | ||
|
|
ca187c78f1 | ||
|
|
78e7c800ba | ||
|
|
4ddad7b456 | ||
|
|
8c7af571c2 | ||
|
|
d167a8ba96 | ||
|
|
741171dbba | ||
|
|
097dbdcc2a | ||
|
|
35a704a1cb | ||
|
|
a64e1f8330 | ||
|
|
de6f5d510a | ||
|
|
02a6c684f1 | ||
|
|
5f915ba8d3 | ||
|
|
2e5e98868d | ||
|
|
cabd1710ed | ||
|
|
45c59e0b65 | ||
|
|
6ebf0c48e3 | ||
|
|
82dd5f2db0 | ||
|
|
eb969cdbf7 | ||
|
|
8fcfeda14e | ||
|
|
c99f64708e | ||
|
|
2d1032c363 | ||
|
|
c46578736a | ||
|
|
b00a42a539 | ||
|
|
30ac2de73b | ||
|
|
e6511c508c | ||
|
|
ce5414f2eb | ||
|
|
742bede02f | ||
|
|
2e331ee016 | ||
|
|
80714fe7e5 | ||
|
|
1fceeca011 | ||
|
|
a805814d3f | ||
|
|
4015acd866 | ||
|
|
2c4b803058 | ||
|
|
beb0b19026 | ||
|
|
46f5328183 | ||
|
|
60cdfd2c33 | ||
|
|
8cbb9e6987 | ||
|
|
0fede5f46e | ||
|
|
e79984592e | ||
|
|
72ef3964ce | ||
|
|
4e2176b7f0 | ||
|
|
13de9d713b | ||
|
|
f56b13da2c | ||
|
|
d2eaf2247f | ||
|
|
51869bf081 | ||
|
|
3b2f68111e | ||
|
|
4940b652eb | ||
|
|
8007825800 | ||
|
|
b705e5d0ad | ||
|
|
9c2b4db22a | ||
|
|
bc0b1d23e3 | ||
|
|
a52ff33e30 | ||
|
|
27a87d17ab | ||
|
|
08a60e6e8a | ||
|
|
c86071416d | ||
|
|
9d85dc130d | ||
|
|
2de5ae59f5 | ||
|
|
681a15cb44 | ||
|
|
b4929be6b8 | ||
|
|
0e7dec202a | ||
|
|
088050c903 | ||
|
|
ffee07a03d | ||
|
|
191cdcef53 | ||
|
|
34d7eb3c42 | ||
|
|
d1b215861c | ||
|
|
2a90e165a5 | ||
|
|
c8bf98525d | ||
|
|
135e874a76 | ||
|
|
79ee7a657a | ||
|
|
e1cd8c5206 | ||
|
|
84cff78b86 | ||
|
|
6c801fbb92 | ||
|
|
c01e6e1db4 | ||
|
|
b753ae0911 | ||
|
|
09f4ac8de0 | ||
|
|
07f5d9331b | ||
|
|
73d74f5e13 | ||
|
|
ce8031b21f | ||
|
|
07a76d82f4 | ||
|
|
874a58281f | ||
|
|
94d3f46fac | ||
|
|
897ef449fb | ||
|
|
3aaf3a3991 | ||
|
|
d88fa3b8d3 | ||
|
|
35c13ee30a | ||
|
|
098e41d3e5 | ||
|
|
186602a301 | ||
|
|
36bff64a91 | ||
|
|
1ab07a5815 | ||
|
|
ed829bdc21 | ||
|
|
e6a76e7a86 | ||
|
|
871d031ceb | ||
|
|
67af0c6bf2 | ||
|
|
fa5611dabd | ||
|
|
63f6f13883 | ||
|
|
d6941e1d2f | ||
|
|
a3322f0d4c | ||
|
|
164bab0f13 | ||
|
|
f29fd92fdf | ||
|
|
d1fafa830d | ||
|
|
9762cf8a2b | ||
|
|
47b56d52f6 | ||
|
|
5c24f35c87 | ||
|
|
e22811f2f5 | ||
|
|
8d97fe896c | ||
|
|
c800b48ca2 | ||
|
|
1cf26c43f3 | ||
|
|
b57f21556a | ||
|
|
74f43a1ab7 | ||
|
|
2b4857001c | ||
|
|
05c637e594 |
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,3 +0,0 @@
|
|||||||
int main() {
|
|
||||||
return 65536;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
int add(int, int);
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
return add(1, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int add(int a, int b) {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
VM := ../../rv32-vm
|
|
||||||
CC := ../../ccompiler
|
|
||||||
STD_CC := gcc
|
|
||||||
|
|
||||||
TESTS := $(wildcard *.c)
|
|
||||||
|
|
||||||
# 定义所有测试目标
|
|
||||||
TEST_TARGETS := $(patsubst %.c, %_test, $(TESTS))
|
|
||||||
|
|
||||||
all: $(TEST_TARGETS)
|
|
||||||
|
|
||||||
%_test: %.c
|
|
||||||
@$(STD_CC) -g -o $@ $<
|
|
||||||
@$(CC) $< flat.bin
|
|
||||||
@./$@ ; ret_gcc=$$?
|
|
||||||
@$(VM) flat.bin ; ret_vm=$$?
|
|
||||||
@echo "Testing $@"
|
|
||||||
@if [ $$ret_gcc -eq $$ret_vm ]; then \
|
|
||||||
echo "$@ passed"; \
|
|
||||||
else \
|
|
||||||
echo "$@ failed: GCC returned $$ret_gcc, VM returned $$ret_vm"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(TEST_TARGETS) flat.bin
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
int main() {
|
|
||||||
int a, b;
|
|
||||||
a = 1;
|
|
||||||
b = 2;
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# 配置参数
|
|
||||||
TEST_DIR = Path(".")
|
|
||||||
CC_PATH = Path("../../ccompiler.exe")
|
|
||||||
VM_PATH = Path("../../rv32-vm.exe")
|
|
||||||
WORKSPACE = Path(".") # 测试工作目录
|
|
||||||
|
|
||||||
# 测试用例映射表(示例)
|
|
||||||
TEST_CASE_MAP = {
|
|
||||||
"./01_return.c": 65536,
|
|
||||||
"./02_decl_expr.c": 1,
|
|
||||||
"./03_decl_init.c": 11,
|
|
||||||
"./04_if.c": 1,
|
|
||||||
"./05_else.c": 2,
|
|
||||||
"./06_fcall.c": 3,
|
|
||||||
"./07_while.c": 10,
|
|
||||||
"./08_do_while.c": 128,
|
|
||||||
"./09_for.c": 10,
|
|
||||||
"./10_main.c": 3,
|
|
||||||
"./11_recursive.c": 120,
|
|
||||||
}
|
|
||||||
|
|
||||||
def run_command(cmd, capture_stderr=True):
|
|
||||||
"""执行命令并捕获stderr"""
|
|
||||||
result = subprocess.run(
|
|
||||||
cmd,
|
|
||||||
cwd=WORKSPACE,
|
|
||||||
stderr=subprocess.PIPE if capture_stderr else None,
|
|
||||||
text=True,
|
|
||||||
timeout=1,
|
|
||||||
)
|
|
||||||
return result.stderr.strip() if capture_stderr else None
|
|
||||||
|
|
||||||
def run_test(test_file, expected):
|
|
||||||
print(f"\nTesting {test_file}...")
|
|
||||||
|
|
||||||
# 1. 编译生成flat.bin
|
|
||||||
compile_cmd = [str(CC_PATH), str(test_file)]
|
|
||||||
compile_err = run_command(compile_cmd)
|
|
||||||
|
|
||||||
if not (WORKSPACE / "flat.bin").exists():
|
|
||||||
print(f" Compilation failed: {compile_err}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 2. 执行虚拟机
|
|
||||||
vm_cmd = [str(VM_PATH), "flat.bin"]
|
|
||||||
|
|
||||||
# 3. 解析返回值(假设最后一行是返回值)
|
|
||||||
try:
|
|
||||||
vm_err = run_command(vm_cmd)
|
|
||||||
actual = int(vm_err.split()[-1])
|
|
||||||
except (ValueError, IndexError) as e:
|
|
||||||
print(f" Invalid VM output: {vm_err}")
|
|
||||||
return False
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print(" Timeout expired")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 4. 验证结果
|
|
||||||
if actual == expected:
|
|
||||||
print(f" PASSED {test_file}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f" FAILED: Expected {expected}, got {actual}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
passed = 0
|
|
||||||
total = 0
|
|
||||||
|
|
||||||
for test_file, expected in TEST_CASE_MAP.items():
|
|
||||||
total += 1
|
|
||||||
if run_test(TEST_DIR / test_file, expected):
|
|
||||||
passed += 1
|
|
||||||
|
|
||||||
# 清理中间文件
|
|
||||||
if (WORKSPACE / "flat.bin").exists():
|
|
||||||
os.remove(WORKSPACE / "flat.bin")
|
|
||||||
|
|
||||||
print(f"\nTest Summary: {passed}/{total} passed")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# 编译器设置
|
|
||||||
CC = gcc
|
|
||||||
AR = ar
|
|
||||||
CFLAGS = -g -Wall
|
|
||||||
|
|
||||||
# 源文件路径
|
|
||||||
LEXER_DIR = ./lexer
|
|
||||||
PARSER_DIR = ./parser
|
|
||||||
AST_DIR = ./parser/ast
|
|
||||||
SYMTAB_DIR = ./parser/symtab
|
|
||||||
|
|
||||||
# 源文件列表
|
|
||||||
SRCS = \
|
|
||||||
frontend.c \
|
|
||||||
$(LEXER_DIR)/lexer.c \
|
|
||||||
$(LEXER_DIR)/token.c \
|
|
||||||
$(PARSER_DIR)/parser.c \
|
|
||||||
$(AST_DIR)/ast.c \
|
|
||||||
$(AST_DIR)/block.c \
|
|
||||||
$(AST_DIR)/decl.c \
|
|
||||||
$(AST_DIR)/expr.c \
|
|
||||||
$(AST_DIR)/func.c \
|
|
||||||
$(AST_DIR)/program.c \
|
|
||||||
$(AST_DIR)/stmt.c \
|
|
||||||
$(AST_DIR)/term.c \
|
|
||||||
$(SYMTAB_DIR)/hashmap.c \
|
|
||||||
$(SYMTAB_DIR)/scope.c \
|
|
||||||
$(SYMTAB_DIR)/symtab.c \
|
|
||||||
|
|
||||||
# 生成目标文件列表
|
|
||||||
OBJS = $(SRCS:.c=.o)
|
|
||||||
|
|
||||||
# 最终目标
|
|
||||||
TARGET = libfrontend.a
|
|
||||||
|
|
||||||
all: $(TARGET)
|
|
||||||
|
|
||||||
$(TARGET): $(OBJS)
|
|
||||||
$(AR) rcs $@ $^
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(OBJS) $(TARGET)
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#include "lexer/lexer.h"
|
|
||||||
#include "parser/symtab/symtab.h"
|
|
||||||
#include "frontend.h"
|
|
||||||
|
|
||||||
struct ASTNode* frontend(const char* file, void* stream, sread_fn sread) {
|
|
||||||
lexer_t lexer;
|
|
||||||
init_lexer(&lexer, file, stream, sread);
|
|
||||||
|
|
||||||
symtab_t symtab;
|
|
||||||
init_symtab(&symtab);
|
|
||||||
|
|
||||||
parser_t parser;
|
|
||||||
init_parser(&parser, &lexer, &symtab);
|
|
||||||
parse_prog(&parser);
|
|
||||||
|
|
||||||
// TODO Free the resourse
|
|
||||||
return parser.root;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#ifndef __FRONTEND_H__
|
|
||||||
#define __FRONTEND_H__
|
|
||||||
|
|
||||||
#ifndef error
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#define STD_LIBRARY
|
|
||||||
#define error(...) do { fprintf(stderr, __VA_ARGS__); assert(0); } while (0)
|
|
||||||
#endif
|
|
||||||
#ifndef warn
|
|
||||||
#include <stdio.h>
|
|
||||||
#define STD_LIBRARY
|
|
||||||
#define warn(...) do { fprintf(stdout, __VA_ARGS__); } while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define xmalloc(size) malloc(size)
|
|
||||||
|
|
||||||
#ifndef FRONTEND_IMPLEMENTATION
|
|
||||||
#include "parser/parser.h"
|
|
||||||
#include "parser/ast/ast.h"
|
|
||||||
|
|
||||||
typedef int (*sread_fn)(void *dst_buf, int dst_size, int elem_size, int count, void *stream);
|
|
||||||
struct ASTNode* frontend(const char* file, void* stream, sread_fn sread);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,510 +0,0 @@
|
|||||||
/**
|
|
||||||
* 仿照LCCompiler的词法分析部分
|
|
||||||
*
|
|
||||||
* 如下为LCC的README in 2025.2
|
|
||||||
This hierarchy is the distribution for lcc version 4.2.
|
|
||||||
|
|
||||||
lcc version 3.x is described in the book "A Retargetable C Compiler:
|
|
||||||
Design and Implementation" (Addison-Wesley, 1995, ISBN 0-8053-1670-1).
|
|
||||||
There are significant differences between 3.x and 4.x, most notably in
|
|
||||||
the intermediate code. For details, see
|
|
||||||
https://drh.github.io/lcc/documents/interface4.pdf.
|
|
||||||
|
|
||||||
VERSION 4.2 IS INCOMPATIBLE WITH EARLIER VERSIONS OF LCC. DO NOT
|
|
||||||
UNLOAD THIS DISTRIBUTION ON TOP OF A 3.X DISTRIBUTION.
|
|
||||||
|
|
||||||
LCC is a C89 ("ANSI C") compiler designed to be highly retargetable.
|
|
||||||
|
|
||||||
LOG describes the changes since the last release.
|
|
||||||
|
|
||||||
CPYRIGHT describes the conditions under you can use, copy, modify, and
|
|
||||||
distribute lcc or works derived from lcc.
|
|
||||||
|
|
||||||
doc/install.html is an HTML file that gives a complete description of
|
|
||||||
the distribution and installation instructions.
|
|
||||||
|
|
||||||
Chris Fraser / [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,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,248 @@
|
|||||||
|
#ifndef __SCC_ARGPARSE_H__
|
||||||
|
#define __SCC_ARGPARSE_H__
|
||||||
|
|
||||||
|
#include "optparse.h"
|
||||||
|
#include <scc_core.h>
|
||||||
|
|
||||||
|
// TODO: 实现更多高级功能
|
||||||
|
// 1. 支持子命令嵌套层级限制
|
||||||
|
// 2. 支持选项分组显示(Grouping)
|
||||||
|
// 3. 支持自动补全脚本生成(Bash/Zsh/Fish)
|
||||||
|
// 4. 支持配置文件解析
|
||||||
|
// 5. 支持国际化(i18n)
|
||||||
|
|
||||||
|
/// @brief 命令行参数约束
|
||||||
|
typedef struct scc_argparse_spec scc_argparse_spec_t;
|
||||||
|
/// @brief 命令行参数 eg. someting
|
||||||
|
typedef struct scc_argparse_arg scc_argparse_arg_t;
|
||||||
|
/// @brief 命令行参数选项 eg. -a --long
|
||||||
|
typedef struct scc_argparse_opt scc_argparse_opt_t;
|
||||||
|
/// @brief 命令集合
|
||||||
|
typedef struct scc_argparse_cmd scc_argparse_cmd_t;
|
||||||
|
|
||||||
|
typedef SCC_VEC(scc_argparse_arg_t) scc_argparse_arg_vec_t;
|
||||||
|
typedef SCC_VEC(scc_argparse_opt_t) scc_argparse_opt_vec_t;
|
||||||
|
typedef SCC_VEC(scc_argparse_cmd_t) scc_argparse_cmd_vec_t;
|
||||||
|
|
||||||
|
typedef enum scc_argparse_val_type {
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_UNKNOWN,
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_STRING,
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_BOOL,
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_INT,
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_FLOAT,
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_ENUM,
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_LIST,
|
||||||
|
SCC_ARGPARSE_VAL_TYPE_COUNT,
|
||||||
|
} scc_argparse_val_type_t;
|
||||||
|
|
||||||
|
typedef enum scc_argparse_err {
|
||||||
|
SCC_ARGPARSE_ERR_NONE,
|
||||||
|
SCC_ARGPARSE_ERR_PNT_DEFAULT,
|
||||||
|
SCC_ARGPARSE_ERR_UNKNOWN_ERR,
|
||||||
|
SCC_ARGPARSE_ERR_INVALID_ARG,
|
||||||
|
SCC_ARGPARSE_ERR_INVALID_VALUE,
|
||||||
|
SCC_ARGPARSE_ERR_MISSING_ARG,
|
||||||
|
SCC_ARGPARSE_ERR_MISSING_VALUE,
|
||||||
|
SCC_ARGPARSE_ERR_UNKNOWN_ARG,
|
||||||
|
SCC_ARGPARSE_ERR_UNKNOWN_VALUE,
|
||||||
|
} scc_argparse_err_t;
|
||||||
|
|
||||||
|
typedef SCC_VEC(const char *) scc_argparse_list_t;
|
||||||
|
|
||||||
|
// 约束规范结构体
|
||||||
|
struct scc_argparse_spec {
|
||||||
|
scc_argparse_val_type_t value_type; // 值类型
|
||||||
|
const char *raw_value;
|
||||||
|
|
||||||
|
// 存储位置
|
||||||
|
union {
|
||||||
|
cbool *bool_store; // 布尔值存储
|
||||||
|
int *int_store; // 整数存储
|
||||||
|
float *float_store; // 浮点数存储
|
||||||
|
const char **str_store; // 字符串存储
|
||||||
|
char **str_alloc_store; // 字符串存储(使用alloc,需要free)
|
||||||
|
void **ptr_store; // 通用指针存储
|
||||||
|
scc_argparse_list_t *vec_store; // 新增:指向字符串向量的指针
|
||||||
|
} store;
|
||||||
|
|
||||||
|
// 枚举值约束
|
||||||
|
struct {
|
||||||
|
const char **values; // 枚举值数组
|
||||||
|
int count; // 枚举值数量
|
||||||
|
} choices;
|
||||||
|
|
||||||
|
// 其他约束标志
|
||||||
|
cbool flag_required; // 是否必须
|
||||||
|
cbool flag_store_as_count; // 是否存储为计数(如 -vvv 返回3)
|
||||||
|
cbool flag_allow_empty; // 是否允许空字符串
|
||||||
|
cbool flag_hidden; // 是否隐藏(不显示在帮助)
|
||||||
|
cbool flag_takes_multiple; // 是否接受多个值(如 -I dir1 -I dir2)
|
||||||
|
cbool flag_parse_complex; // 是否进行复杂解析(如 1K, 2M, 3G)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scc_argparse_arg {
|
||||||
|
scc_argparse_spec_t spec;
|
||||||
|
const char *name;
|
||||||
|
const char *description;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scc_argparse_opt {
|
||||||
|
scc_argparse_spec_t spec;
|
||||||
|
char short_name;
|
||||||
|
const char *long_name;
|
||||||
|
const char *description;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct scc_argparse_cmd {
|
||||||
|
const char *name;
|
||||||
|
const char *description;
|
||||||
|
scc_argparse_arg_vec_t args;
|
||||||
|
scc_argparse_opt_vec_t opts;
|
||||||
|
scc_argparse_cmd_vec_t subcmds;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCC_ARGPARSE_LANG_EN,
|
||||||
|
SCC_ARGPARSE_LANG_ZH,
|
||||||
|
} scc_argparse_lang_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *prog_name;
|
||||||
|
const char *version;
|
||||||
|
const char *description;
|
||||||
|
const char *epilog;
|
||||||
|
scc_argparse_cmd_t root_cmd;
|
||||||
|
scc_argparse_lang_t lang;
|
||||||
|
cbool need_help;
|
||||||
|
cbool need_version;
|
||||||
|
cbool need_debug;
|
||||||
|
cbool need_error_add_help;
|
||||||
|
cbool need_error_add_usage;
|
||||||
|
} scc_argparse_t;
|
||||||
|
|
||||||
|
typedef SCC_VEC(scc_optparse_opt_t) scc_optparse_opt_vec_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scc_argparse_cmd_t *current_cmd; // 当前正在解析的命令
|
||||||
|
scc_optparse_t optparse; // 底层解析器
|
||||||
|
scc_optparse_opt_vec_t opts; // 当前命令的选项列表
|
||||||
|
scc_optparse_result_t result; // 底层解析结果
|
||||||
|
const void *err_ctx; // 错误上下文(期待的参数或者当前解析的参数)
|
||||||
|
int positional_index; // 当前处理的位置参数索引
|
||||||
|
cbool parsing_done; // 是否已完成解析
|
||||||
|
} scc_argparse_context_t;
|
||||||
|
|
||||||
|
void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
|
||||||
|
const char *description);
|
||||||
|
scc_argparse_cmd_t *scc_argparse_get_root(scc_argparse_t *parser);
|
||||||
|
void scc_argparse_drop(scc_argparse_t *parser);
|
||||||
|
int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv);
|
||||||
|
|
||||||
|
void scc_argparse_cmd_init(scc_argparse_cmd_t *cmd, const char *name,
|
||||||
|
const char *description);
|
||||||
|
void scc_argparse_cmd_drop(scc_argparse_cmd_t *cmd);
|
||||||
|
|
||||||
|
void scc_argparse_cmd_add_arg(scc_argparse_cmd_t *cmd,
|
||||||
|
const scc_argparse_arg_t *arg);
|
||||||
|
void scc_argparse_cmd_add_opt(scc_argparse_cmd_t *cmd,
|
||||||
|
const scc_argparse_opt_t *opt);
|
||||||
|
void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd,
|
||||||
|
const scc_argparse_cmd_t *subcmd);
|
||||||
|
|
||||||
|
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd);
|
||||||
|
void scc_argparse_print_error(scc_argparse_context_t *ctx,
|
||||||
|
scc_argparse_t *parser, scc_argparse_err_t err);
|
||||||
|
|
||||||
|
static inline void scc_argparse_spec_init(scc_argparse_spec_t *spec) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_STRING;
|
||||||
|
spec->raw_value = nullptr;
|
||||||
|
spec->store.ptr_store = nullptr;
|
||||||
|
|
||||||
|
spec->choices.count = 0;
|
||||||
|
spec->choices.values = nullptr;
|
||||||
|
|
||||||
|
spec->flag_required = false;
|
||||||
|
spec->flag_store_as_count = false;
|
||||||
|
spec->flag_allow_empty = false;
|
||||||
|
spec->flag_hidden = false;
|
||||||
|
spec->flag_takes_multiple = false;
|
||||||
|
spec->flag_parse_complex = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_opt_init(scc_argparse_opt_t *opt,
|
||||||
|
char short_name, const char *long_name,
|
||||||
|
const char *desc) {
|
||||||
|
opt->short_name = short_name;
|
||||||
|
opt->long_name = long_name;
|
||||||
|
opt->description = desc;
|
||||||
|
scc_argparse_spec_init(&opt->spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_arg_init(scc_argparse_arg_t *arg,
|
||||||
|
const char *name, const char *desc) {
|
||||||
|
arg->name = name;
|
||||||
|
arg->description = desc;
|
||||||
|
scc_argparse_spec_init(&arg->spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_spec_setup_bool(scc_argparse_spec_t *spec,
|
||||||
|
cbool *store) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_BOOL;
|
||||||
|
spec->store.bool_store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_spec_setup_count(scc_argparse_spec_t *spec,
|
||||||
|
int *store) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_COUNT;
|
||||||
|
spec->flag_store_as_count = true; // 自动设为计数模式
|
||||||
|
spec->store.int_store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_spec_setup_string(scc_argparse_spec_t *spec,
|
||||||
|
const char **store) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_STRING;
|
||||||
|
spec->store.str_store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_spec_setup_int(scc_argparse_spec_t *spec,
|
||||||
|
int *store) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_INT;
|
||||||
|
spec->store.int_store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_spec_setup_float(scc_argparse_spec_t *spec,
|
||||||
|
float *store) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_FLOAT;
|
||||||
|
spec->store.float_store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void scc_argparse_spec_setup_choices(scc_argparse_spec_t *spec,
|
||||||
|
const char **values,
|
||||||
|
int count, int *store) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_ENUM;
|
||||||
|
spec->choices.values = values;
|
||||||
|
spec->choices.count = count;
|
||||||
|
spec->store.int_store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加设置列表的辅助函数(内联)
|
||||||
|
static inline void scc_argparse_spec_setup_list(scc_argparse_spec_t *spec,
|
||||||
|
scc_argparse_list_t *vec) {
|
||||||
|
spec->value_type = SCC_ARGPARSE_VAL_TYPE_LIST;
|
||||||
|
spec->store.vec_store = vec;
|
||||||
|
// 自动设置 flag_takes_multiple 为 true,因为列表需要多个值
|
||||||
|
spec->flag_takes_multiple = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SCC_ARGPARSE_MACRO_SETTER(attr) \
|
||||||
|
static inline void scc_argparse_spec_set_##attr(scc_argparse_spec_t *spec, \
|
||||||
|
cbool flag) { \
|
||||||
|
spec->flag_##attr = flag; \
|
||||||
|
}
|
||||||
|
SCC_ARGPARSE_MACRO_SETTER(required)
|
||||||
|
SCC_ARGPARSE_MACRO_SETTER(allow_empty)
|
||||||
|
SCC_ARGPARSE_MACRO_SETTER(hidden)
|
||||||
|
SCC_ARGPARSE_MACRO_SETTER(takes_multiple)
|
||||||
|
SCC_ARGPARSE_MACRO_SETTER(parse_complex)
|
||||||
|
|
||||||
|
#endif /* __SCC_ARGPARSE_H__ */
|
||||||
@@ -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,341 @@
|
|||||||
|
#include <argparse.h>
|
||||||
|
|
||||||
|
void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
|
||||||
|
const char *description) {
|
||||||
|
parser->prog_name = program_name;
|
||||||
|
parser->version = "0.1.0";
|
||||||
|
parser->description = description;
|
||||||
|
parser->epilog = nullptr;
|
||||||
|
|
||||||
|
parser->lang = SCC_ARGPARSE_LANG_EN;
|
||||||
|
parser->need_help = true;
|
||||||
|
parser->need_version = true;
|
||||||
|
parser->need_debug = false;
|
||||||
|
parser->need_error_add_help = false;
|
||||||
|
parser->need_error_add_usage = true;
|
||||||
|
scc_argparse_cmd_init(&parser->root_cmd, parser->prog_name,
|
||||||
|
parser->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_argparse_cmd_t *scc_argparse_get_root(scc_argparse_t *parser) {
|
||||||
|
return &parser->root_cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_drop(scc_argparse_t *parser) {
|
||||||
|
scc_argparse_cmd_drop(&parser->root_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline scc_argparse_cmd_t *is_subcommand(scc_argparse_cmd_t *cmd,
|
||||||
|
const char *name) {
|
||||||
|
if (!scc_vec_size(cmd->subcmds)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
scc_vec_foreach(cmd->subcmds, i) {
|
||||||
|
scc_argparse_cmd_t *subcmd = &scc_vec_at(cmd->subcmds, i);
|
||||||
|
if (scc_strcmp(subcmd->name, name) == 0) {
|
||||||
|
return subcmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void prepare_cmd(scc_optparse_t *optparse,
|
||||||
|
scc_optparse_opt_vec_t *opts,
|
||||||
|
scc_argparse_cmd_t *cmd) {
|
||||||
|
scc_vec_free(*opts);
|
||||||
|
scc_vec_init(*opts);
|
||||||
|
scc_vec_foreach(cmd->opts, i) {
|
||||||
|
scc_argparse_opt_t *opt = &scc_vec_at(cmd->opts, i);
|
||||||
|
int min_args = 0;
|
||||||
|
int max_args = 0;
|
||||||
|
|
||||||
|
// 根据选项类型推断 min_args 和 max_args
|
||||||
|
switch (opt->spec.value_type) {
|
||||||
|
case SCC_ARGPARSE_VAL_TYPE_STRING:
|
||||||
|
case SCC_ARGPARSE_VAL_TYPE_INT:
|
||||||
|
case SCC_ARGPARSE_VAL_TYPE_FLOAT:
|
||||||
|
min_args = 1;
|
||||||
|
max_args = 1;
|
||||||
|
break;
|
||||||
|
case SCC_ARGPARSE_VAL_TYPE_BOOL:
|
||||||
|
min_args = 0;
|
||||||
|
max_args = 0;
|
||||||
|
break;
|
||||||
|
case SCC_ARGPARSE_VAL_TYPE_COUNT:
|
||||||
|
min_args = 0;
|
||||||
|
max_args = 0;
|
||||||
|
break;
|
||||||
|
case SCC_ARGPARSE_VAL_TYPE_LIST: // 列表类型
|
||||||
|
min_args = 1;
|
||||||
|
max_args = 65535; // FIXME maybe INT_MAX ?
|
||||||
|
break;
|
||||||
|
case SCC_ARGPARSE_VAL_TYPE_ENUM:
|
||||||
|
min_args = 1;
|
||||||
|
max_args = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
min_args = 0;
|
||||||
|
max_args = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_vec_push(*opts, ((scc_optparse_opt_t){
|
||||||
|
.prefix = '-',
|
||||||
|
.short_name = opt->short_name,
|
||||||
|
.long_name = opt->long_name,
|
||||||
|
.min_args = min_args,
|
||||||
|
.max_args = max_args,
|
||||||
|
.user_data = opt,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
scc_vec_push(*opts, (scc_optparse_opt_t)SCC_OPTPARSE_OPT_END());
|
||||||
|
scc_optparse_set(optparse, opts->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void push_help(scc_argparse_cmd_t *cmd) {
|
||||||
|
if (cmd == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scc_vec_push(cmd->opts, ((scc_argparse_opt_t){
|
||||||
|
.short_name = 'h',
|
||||||
|
.long_name = "help",
|
||||||
|
.description = 0,
|
||||||
|
.spec.value_type = SCC_ARGPARSE_VAL_TYPE_BOOL,
|
||||||
|
}));
|
||||||
|
scc_vec_foreach(cmd->subcmds, i) {
|
||||||
|
push_help(&scc_vec_at(cmd->subcmds, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_context(scc_argparse_context_t *ctx, scc_argparse_t *parser,
|
||||||
|
int argc, const char **argv) {
|
||||||
|
ctx->current_cmd = scc_argparse_get_root(parser);
|
||||||
|
if (parser->need_help) {
|
||||||
|
push_help(ctx->current_cmd);
|
||||||
|
}
|
||||||
|
scc_optparse_init(&ctx->optparse, argc, argv);
|
||||||
|
scc_vec_init(ctx->opts);
|
||||||
|
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
|
||||||
|
ctx->positional_index = 0;
|
||||||
|
ctx->parsing_done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int transite_error(scc_argparse_context_t *ctx) {
|
||||||
|
scc_argparse_err_t error = SCC_ARGPARSE_ERR_NONE;
|
||||||
|
switch (ctx->result.error) {
|
||||||
|
case SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG:
|
||||||
|
case SCC_OPT_ERROR_NOT_FOUND_LONG_ARG:
|
||||||
|
error = SCC_ARGPARSE_ERR_UNKNOWN_ARG;
|
||||||
|
break;
|
||||||
|
case SCC_OPT_ERROR_NOT_ENOUGH_ARGS:
|
||||||
|
error = SCC_ARGPARSE_ERR_MISSING_VALUE;
|
||||||
|
break;
|
||||||
|
case SCC_OPT_ERROR_TOO_MANY_ARGS:
|
||||||
|
error = SCC_ARGPARSE_ERR_INVALID_ARG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = SCC_ARGPARSE_ERR_UNKNOWN_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_choices(const scc_argparse_spec_t *spec, const char *value,
|
||||||
|
int *out_index) {
|
||||||
|
if (!spec->choices.values || spec->choices.count == 0) {
|
||||||
|
return SCC_ARGPARSE_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == nullptr) {
|
||||||
|
return SCC_ARGPARSE_ERR_MISSING_VALUE;
|
||||||
|
}
|
||||||
|
if (out_index)
|
||||||
|
*out_index = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < spec->choices.count; i += 1) {
|
||||||
|
if (scc_strcmp(value, spec->choices.values[i]) == 0) {
|
||||||
|
if (out_index)
|
||||||
|
*out_index = i;
|
||||||
|
return SCC_ARGPARSE_ERR_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SCC_ARGPARSE_ERR_INVALID_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int validate_and_cleanup(scc_argparse_context_t *ctx,
|
||||||
|
scc_argparse_t *parser, int errcode) {
|
||||||
|
if (errcode != SCC_ARGPARSE_ERR_NONE) {
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
// 检查必需的位置参数
|
||||||
|
scc_vec_foreach(ctx->current_cmd->args, i) {
|
||||||
|
scc_argparse_arg_t *arg = &scc_vec_at(ctx->current_cmd->args, i);
|
||||||
|
if (arg->spec.flag_required) {
|
||||||
|
// 检查是否已存储(非空)
|
||||||
|
if (arg->spec.store.str_store == nullptr ||
|
||||||
|
*arg->spec.store.str_store == nullptr) {
|
||||||
|
errcode = SCC_ARGPARSE_ERR_MISSING_ARG;
|
||||||
|
ctx->err_ctx = arg->name;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scc_vec_foreach(ctx->current_cmd->opts, i) {
|
||||||
|
scc_argparse_opt_t *opt = &scc_vec_at(ctx->current_cmd->opts, i);
|
||||||
|
if (opt->spec.flag_required) {
|
||||||
|
int provided = 0;
|
||||||
|
if (opt->spec.flag_store_as_count) {
|
||||||
|
provided = (opt->spec.store.int_store &&
|
||||||
|
*opt->spec.store.int_store > 0);
|
||||||
|
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
|
||||||
|
provided =
|
||||||
|
(opt->spec.store.bool_store && *opt->spec.store.bool_store);
|
||||||
|
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
|
||||||
|
provided = (opt->spec.store.vec_store &&
|
||||||
|
scc_vec_size(*opt->spec.store.vec_store) > 0);
|
||||||
|
} else {
|
||||||
|
provided = (opt->spec.store.str_store &&
|
||||||
|
*opt->spec.store.str_store != nullptr);
|
||||||
|
}
|
||||||
|
if (!provided) {
|
||||||
|
errcode = SCC_ARGPARSE_ERR_MISSING_ARG;
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
END:
|
||||||
|
if (errcode != SCC_ARGPARSE_ERR_NONE) {
|
||||||
|
scc_argparse_print_error(ctx, parser, errcode);
|
||||||
|
}
|
||||||
|
scc_vec_free(ctx->opts);
|
||||||
|
scc_optparse_drop(&ctx->optparse);
|
||||||
|
return errcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
|
||||||
|
scc_argparse_opt_t *opt = (scc_argparse_opt_t *)ctx->result.opt->user_data;
|
||||||
|
|
||||||
|
if (parser->need_help && scc_strcmp(opt->long_name, "help") == 0) {
|
||||||
|
scc_argparse_print_help(parser, ctx->current_cmd);
|
||||||
|
ctx->parsing_done = true;
|
||||||
|
return SCC_ARGPARSE_ERR_PNT_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->need_version && scc_strcmp(opt->long_name, "version") == 0) {
|
||||||
|
// TODO default version print
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt->spec.flag_store_as_count) {
|
||||||
|
(*opt->spec.store.int_store)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
|
||||||
|
*opt->spec.store.bool_store = true;
|
||||||
|
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) {
|
||||||
|
return check_choices(&opt->spec, ctx->result.value,
|
||||||
|
opt->spec.store.int_store);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->result.value) {
|
||||||
|
return SCC_ARGPARSE_ERR_NONE;
|
||||||
|
}
|
||||||
|
opt->spec.raw_value = ctx->result.value;
|
||||||
|
|
||||||
|
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
|
||||||
|
scc_vec_push(*opt->spec.store.vec_store, ctx->result.value);
|
||||||
|
} else {
|
||||||
|
*opt->spec.store.str_store = ctx->result.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCC_ARGPARSE_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_positional_arg(scc_argparse_context_t *ctx,
|
||||||
|
scc_argparse_t *parser) {
|
||||||
|
(void)parser; // TODO
|
||||||
|
scc_argparse_cmd_t *subcmd =
|
||||||
|
is_subcommand(ctx->current_cmd, ctx->result.value);
|
||||||
|
if (subcmd != nullptr) {
|
||||||
|
ctx->current_cmd = subcmd;
|
||||||
|
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
|
||||||
|
return SCC_ARGPARSE_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->positional_index < (int)scc_vec_size(ctx->current_cmd->args)) {
|
||||||
|
scc_argparse_arg_t *arg =
|
||||||
|
&scc_vec_at(ctx->current_cmd->args, ctx->positional_index);
|
||||||
|
*arg->spec.store.str_store = ctx->result.value;
|
||||||
|
arg->spec.raw_value = ctx->result.value;
|
||||||
|
ctx->positional_index++;
|
||||||
|
} else {
|
||||||
|
ctx->parsing_done = true;
|
||||||
|
return SCC_ARGPARSE_ERR_UNKNOWN_ARG;
|
||||||
|
}
|
||||||
|
return SCC_ARGPARSE_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv) {
|
||||||
|
scc_argparse_context_t ctx = {0};
|
||||||
|
init_context(&ctx, parser, argc, argv); // 初始化上下文
|
||||||
|
int errcode = SCC_ARGPARSE_ERR_NONE;
|
||||||
|
|
||||||
|
while (!ctx.parsing_done && errcode == SCC_ARGPARSE_ERR_NONE &&
|
||||||
|
scc_optparse_parse(&ctx.optparse, &ctx.result)) {
|
||||||
|
if (parser->need_debug) {
|
||||||
|
scc_printf("debug:[%c:%s:%d] %s\n",
|
||||||
|
ctx.result.opt ? ctx.result.opt->short_name : '-',
|
||||||
|
ctx.result.opt ? ctx.result.opt->long_name : "--",
|
||||||
|
ctx.result.error,
|
||||||
|
ctx.result.value ? ctx.result.value : "<nullptr>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.result.error) {
|
||||||
|
errcode = transite_error(&ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.result.opt != nullptr) {
|
||||||
|
errcode = handle_option(&ctx, parser);
|
||||||
|
} else if (ctx.result.value != nullptr) {
|
||||||
|
errcode = handle_positional_arg(&ctx, parser);
|
||||||
|
} else {
|
||||||
|
UNREACHABLE(); // 不应到达此处
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate_and_cleanup(&ctx, parser, errcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_cmd_init(scc_argparse_cmd_t *cmd, const char *name,
|
||||||
|
const char *description) {
|
||||||
|
cmd->name = name ? name : "cmd_name";
|
||||||
|
cmd->description = description ? description : "cmd_description";
|
||||||
|
scc_vec_init(cmd->args);
|
||||||
|
scc_vec_init(cmd->opts);
|
||||||
|
scc_vec_init(cmd->subcmds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_cmd_drop(scc_argparse_cmd_t *cmd) {
|
||||||
|
scc_vec_free(cmd->args);
|
||||||
|
scc_vec_free(cmd->opts);
|
||||||
|
scc_vec_foreach(cmd->subcmds, i) {
|
||||||
|
scc_argparse_cmd_drop(&scc_vec_at(cmd->subcmds, i));
|
||||||
|
}
|
||||||
|
scc_vec_free(cmd->subcmds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_cmd_add_arg(scc_argparse_cmd_t *cmd,
|
||||||
|
const scc_argparse_arg_t *arg) {
|
||||||
|
scc_vec_push(cmd->args, *arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_cmd_add_opt(scc_argparse_cmd_t *cmd,
|
||||||
|
const scc_argparse_opt_t *opt) {
|
||||||
|
scc_vec_push(cmd->opts, *opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd,
|
||||||
|
const scc_argparse_cmd_t *subcmd) {
|
||||||
|
scc_vec_push(cmd->subcmds, *subcmd);
|
||||||
|
}
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
#include <argparse.h>
|
||||||
|
|
||||||
|
// 帮助信息处理函数
|
||||||
|
enum {
|
||||||
|
ARGPARSE_USAGE,
|
||||||
|
ARGPARSE_OPTION,
|
||||||
|
ARGPARSE_COMMAND,
|
||||||
|
ARGPARSE_ARGUMENT,
|
||||||
|
|
||||||
|
ARGPARSE_SHOW_ARG,
|
||||||
|
ARGPARSE_SHOW_OPT,
|
||||||
|
ARGPARSE_SHOW_CMD,
|
||||||
|
ARGPARSE_SHOW_HELP_MSG,
|
||||||
|
|
||||||
|
ARGPRASE_USING_HELP_HINT,
|
||||||
|
ARGPARSE_UNKNOWN_ARGUMENT,
|
||||||
|
ARGPARSE_INVALID_VALUE_FOR_OPTION,
|
||||||
|
ARGPARSE_OPTION_MISSING_VALUE,
|
||||||
|
ARGPARSE_DID_YOU_MEAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *fmt_en[] = {
|
||||||
|
[ARGPARSE_USAGE] = "Usage: %s",
|
||||||
|
[ARGPARSE_OPTION] = " [Options]",
|
||||||
|
[ARGPARSE_COMMAND] = " [Commands]",
|
||||||
|
|
||||||
|
[ARGPARSE_SHOW_ARG] = "Arguments:\n",
|
||||||
|
[ARGPARSE_SHOW_OPT] = "Options:\n",
|
||||||
|
[ARGPARSE_SHOW_CMD] = "Commands:\n",
|
||||||
|
[ARGPARSE_SHOW_HELP_MSG] = "Show this help message and exit",
|
||||||
|
|
||||||
|
[ARGPRASE_USING_HELP_HINT] =
|
||||||
|
"Using '-h' or '--help' to get more information\n",
|
||||||
|
[ARGPARSE_UNKNOWN_ARGUMENT] = "Unknown argument '%s'\n",
|
||||||
|
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "Invalid value for option '%s'\n",
|
||||||
|
[ARGPARSE_OPTION_MISSING_VALUE] = "Option '%s' missing value\n",
|
||||||
|
[ARGPARSE_DID_YOU_MEAN] = "Did you mean: '%s'?\n",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *fmt_zh[] = {
|
||||||
|
[ARGPARSE_USAGE] = "用法: %s",
|
||||||
|
[ARGPARSE_OPTION] = " [选项]",
|
||||||
|
[ARGPARSE_COMMAND] = " [命令]",
|
||||||
|
|
||||||
|
[ARGPARSE_SHOW_ARG] = "参数:\n",
|
||||||
|
[ARGPARSE_SHOW_OPT] = "选项:\n",
|
||||||
|
[ARGPARSE_SHOW_CMD] = "命令:\n",
|
||||||
|
[ARGPARSE_SHOW_HELP_MSG] = "显示帮助信息并退出",
|
||||||
|
|
||||||
|
[ARGPRASE_USING_HELP_HINT] = "使用 '-h' 或者 '--help' 获取更多信息\n",
|
||||||
|
[ARGPARSE_UNKNOWN_ARGUMENT] = "未知的参数 '%s'\n",
|
||||||
|
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "无效的选项值 '%s'\n",
|
||||||
|
[ARGPARSE_OPTION_MISSING_VALUE] = "选项 '%s' 缺少值\n",
|
||||||
|
[ARGPARSE_DID_YOU_MEAN] = "您是不是想要输入: '%s'?\n",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算两个字符串的编辑距离(Levenshtein距离)
|
||||||
|
* @param s1 字符串1(非空,以'\0'结尾)
|
||||||
|
* @param s2 字符串2(非空,以'\0'结尾)
|
||||||
|
* @return 编辑距离,内存分配失败返回 -1
|
||||||
|
*/
|
||||||
|
static int scc_levenshtein(const char *s1, const char *s2) {
|
||||||
|
usize len1 = scc_strlen(s1);
|
||||||
|
usize len2 = scc_strlen(s2);
|
||||||
|
|
||||||
|
// 保证 len1 >= len2 以减少内存占用
|
||||||
|
if (len1 < len2) {
|
||||||
|
const char *tmp = s1;
|
||||||
|
s1 = s2;
|
||||||
|
s2 = tmp;
|
||||||
|
usize tlen = len1;
|
||||||
|
len1 = len2;
|
||||||
|
len2 = tlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分配滚动数组(只需 len2+1 个 int)
|
||||||
|
int *dp = (int *)scc_malloc(sizeof(int) * (len2 + 1));
|
||||||
|
if (!dp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// 初始化第0行
|
||||||
|
for (usize j = 0; j <= len2; ++j)
|
||||||
|
dp[j] = (int)j;
|
||||||
|
|
||||||
|
for (usize i = 1; i <= len1; ++i) {
|
||||||
|
int prev = dp[0];
|
||||||
|
dp[0] = (int)i;
|
||||||
|
for (usize j = 1; j <= len2; ++j) {
|
||||||
|
int temp = dp[j];
|
||||||
|
int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
|
||||||
|
dp[j] = scc_min(scc_min(dp[j] + 1, dp[j - 1] + 1), prev + cost);
|
||||||
|
prev = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int dist = dp[len2];
|
||||||
|
scc_free(dp);
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_usage(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
|
||||||
|
const char **lines =
|
||||||
|
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
|
||||||
|
scc_printf(lines[ARGPARSE_USAGE],
|
||||||
|
cmd->name ? cmd->name : parser->prog_name);
|
||||||
|
if (scc_vec_size(cmd->opts) > 0)
|
||||||
|
scc_printf(lines[ARGPARSE_OPTION]);
|
||||||
|
scc_vec_foreach(cmd->args, i) {
|
||||||
|
scc_argparse_arg_t *arg = &scc_vec_at(cmd->args, i);
|
||||||
|
scc_printf(arg->spec.flag_required ? " <%s>" : " [%s]", arg->name);
|
||||||
|
}
|
||||||
|
if (scc_vec_size(cmd->subcmds) > 0)
|
||||||
|
scc_printf(lines[ARGPARSE_COMMAND]);
|
||||||
|
scc_printf("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
|
||||||
|
const char **lines =
|
||||||
|
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
|
||||||
|
|
||||||
|
print_usage(parser, cmd);
|
||||||
|
|
||||||
|
if (cmd->description)
|
||||||
|
scc_printf("%s\n\n", cmd->description);
|
||||||
|
|
||||||
|
if (scc_vec_size(cmd->args) > 0) {
|
||||||
|
scc_printf(lines[ARGPARSE_SHOW_ARG]);
|
||||||
|
scc_vec_foreach(cmd->args, i) {
|
||||||
|
scc_argparse_arg_t *arg = &scc_vec_at(cmd->args, i);
|
||||||
|
scc_printf(" %-20s %s", arg->name,
|
||||||
|
arg->description ? arg->description : "");
|
||||||
|
// if (!arg->spec.required && arg->spec.flags.has_default)
|
||||||
|
// scc_printf(" (default: %s)", arg->spec.default_value);
|
||||||
|
scc_printf("\n");
|
||||||
|
}
|
||||||
|
scc_printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scc_vec_size(cmd->opts) > 0) {
|
||||||
|
scc_printf(lines[ARGPARSE_SHOW_OPT]);
|
||||||
|
scc_vec_foreach(cmd->opts, i) {
|
||||||
|
scc_argparse_opt_t *opt = &scc_vec_at(cmd->opts, i);
|
||||||
|
if (opt->spec.flag_hidden)
|
||||||
|
continue;
|
||||||
|
if (opt->short_name == 'h')
|
||||||
|
opt->description = lines[ARGPARSE_SHOW_HELP_MSG];
|
||||||
|
scc_str_t buf;
|
||||||
|
scc_str_init(&buf);
|
||||||
|
int pos = 0;
|
||||||
|
if (opt->short_name) {
|
||||||
|
scc_str_append_ch(&buf, '-');
|
||||||
|
scc_str_append_ch(&buf, opt->short_name);
|
||||||
|
if (opt->long_name) {
|
||||||
|
scc_str_append_ch(&buf, ',');
|
||||||
|
scc_str_append_ch(&buf, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opt->long_name) {
|
||||||
|
scc_str_append_ch(&buf, '-');
|
||||||
|
scc_str_append_ch(&buf, '-');
|
||||||
|
scc_str_append_cstr(&buf, opt->long_name,
|
||||||
|
scc_strlen(opt->long_name));
|
||||||
|
}
|
||||||
|
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) {
|
||||||
|
scc_str_append_ch(&buf, '=');
|
||||||
|
scc_str_append_ch(&buf, '<');
|
||||||
|
for (int j = 0; j < opt->spec.choices.count; j += 1) {
|
||||||
|
if (opt->spec.choices.values[j] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j != 0) {
|
||||||
|
scc_str_append_ch(&buf, '|');
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_str_append_cstr(
|
||||||
|
&buf, opt->spec.choices.values[j],
|
||||||
|
scc_strlen(opt->spec.choices.values[j]));
|
||||||
|
}
|
||||||
|
scc_str_append_ch(&buf, '>');
|
||||||
|
}
|
||||||
|
scc_printf(" %-25s %s", scc_str_as_cstr(&buf),
|
||||||
|
opt->description ? opt->description : "");
|
||||||
|
// if (opt->spec.default_value)
|
||||||
|
// scc_printf(" (default: %s)", opt->spec.default_value);
|
||||||
|
scc_printf("\n");
|
||||||
|
}
|
||||||
|
scc_printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scc_vec_size(cmd->subcmds) > 0) {
|
||||||
|
scc_printf(lines[ARGPARSE_SHOW_CMD]);
|
||||||
|
scc_vec_foreach(cmd->subcmds, i) {
|
||||||
|
scc_argparse_cmd_t *sub = &scc_vec_at(cmd->subcmds, i);
|
||||||
|
scc_printf(" %-20s %s\n", sub->name,
|
||||||
|
sub->description ? sub->description : "");
|
||||||
|
}
|
||||||
|
scc_printf("\n");
|
||||||
|
}
|
||||||
|
if (parser->epilog)
|
||||||
|
scc_printf("%s\n", parser->epilog);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scc_argparse_find_similar_arg(scc_argparse_cmd_t *cmd,
|
||||||
|
const char *arg) {
|
||||||
|
if (arg == nullptr || cmd == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0') {
|
||||||
|
// opt arg
|
||||||
|
scc_vec_foreach(cmd->opts, i) {
|
||||||
|
if (scc_levenshtein(arg + 2, scc_vec_at(cmd->opts, i).long_name) <=
|
||||||
|
2) {
|
||||||
|
return scc_vec_at(cmd->opts, i).long_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// subcmd arg
|
||||||
|
scc_vec_foreach(cmd->subcmds, i) {
|
||||||
|
if (scc_levenshtein(arg, scc_vec_at(cmd->subcmds, i).name) <= 2) {
|
||||||
|
return scc_vec_at(cmd->subcmds, i).name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_argparse_print_error(scc_argparse_context_t *ctx,
|
||||||
|
scc_argparse_t *parser, scc_argparse_err_t err) {
|
||||||
|
const char **lines =
|
||||||
|
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
|
||||||
|
switch (err) {
|
||||||
|
case SCC_ARGPARSE_ERR_INVALID_ARG:
|
||||||
|
case SCC_ARGPARSE_ERR_INVALID_VALUE:
|
||||||
|
scc_printf(lines[ARGPARSE_INVALID_VALUE_FOR_OPTION], ctx->err_ctx);
|
||||||
|
break;
|
||||||
|
case SCC_ARGPARSE_ERR_MISSING_ARG:
|
||||||
|
case SCC_ARGPARSE_ERR_MISSING_VALUE:
|
||||||
|
scc_printf(lines[ARGPARSE_OPTION_MISSING_VALUE], ctx->err_ctx);
|
||||||
|
break;
|
||||||
|
case SCC_ARGPARSE_ERR_UNKNOWN_ARG:
|
||||||
|
case SCC_ARGPARSE_ERR_UNKNOWN_VALUE:
|
||||||
|
scc_printf(lines[ARGPARSE_UNKNOWN_ARGUMENT], ctx->result.raw_arg);
|
||||||
|
const char *similar_arg = scc_argparse_find_similar_arg(
|
||||||
|
ctx->current_cmd, ctx->result.raw_arg);
|
||||||
|
if (similar_arg != 0) {
|
||||||
|
scc_printf(lines[ARGPARSE_DID_YOU_MEAN], similar_arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// FACK ERROR
|
||||||
|
case SCC_ARGPARSE_ERR_PNT_DEFAULT:
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
scc_printf("Unknown error: %d\n", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (parser->need_help) {
|
||||||
|
scc_printf(lines[ARGPRASE_USING_HELP_HINT]);
|
||||||
|
}
|
||||||
|
if (parser->need_error_add_help) {
|
||||||
|
scc_argparse_print_help(parser, ctx->current_cmd);
|
||||||
|
} else if (parser->need_error_add_usage) {
|
||||||
|
print_usage(parser, ctx->current_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "scc_ast"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = []
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
{ name = "tree_dump", path = "../tree_dump" },
|
||||||
|
{ name = "scc_pos", path = "../sstream" },
|
||||||
|
{ name = "scc_utils", path = "../../runtime/scc_utils" },
|
||||||
|
]
|
||||||
|
# features = {}
|
||||||
|
# default_features = []
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef __SCC_AST_H__
|
||||||
|
#define __SCC_AST_H__
|
||||||
|
|
||||||
|
#include "scc_ast_def.h"
|
||||||
|
#include <scc_strpool.h>
|
||||||
|
|
||||||
|
typedef struct scc_ast_module {
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** 所有 AST 节点的 name 字符串由 name_pool 统一管理生命周期 */
|
||||||
|
scc_strpool_t name_pool;
|
||||||
|
} scc_ast_module_t;
|
||||||
|
|
||||||
|
void scc_ast_module_init(scc_ast_module_t *module);
|
||||||
|
void scc_ast_module_drop(scc_ast_module_t *module);
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *
|
||||||
|
scc_ast_module_get_builtin_type(scc_ast_module_t *module,
|
||||||
|
scc_ast_builtin_type_t kind);
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *scc_ast_module_alloc_type(scc_ast_module_t *module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将字符串 intern 到 module 的 name_pool 中
|
||||||
|
* @return 指向 module 所拥有的持久化副本的指针
|
||||||
|
* (在 module_drop 前始终有效)
|
||||||
|
*/
|
||||||
|
const char *scc_ast_module_intern(scc_ast_module_t *module, const char *str);
|
||||||
|
|
||||||
|
static inline void *scc_ast_module_alloc_node(scc_ast_module_t *module,
|
||||||
|
usize size) {
|
||||||
|
void *ptr = scc_malloc(size);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
Panic("Out of memory");
|
||||||
|
}
|
||||||
|
scc_memset(ptr, 0, size);
|
||||||
|
scc_vec_push(module->all_nodes, (scc_ast_node_t *)ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SCC_AST_ALLOC(module, type) \
|
||||||
|
((type *)scc_ast_module_alloc_node(module, sizeof(type)))
|
||||||
|
#define SCC_AST_ALLOC_QUAL_TYPE(module) SCC_AST_ALLOC(module, scc_ast_qual_type_t)
|
||||||
|
#define SCC_AST_ALLOC_DECL(module) SCC_AST_ALLOC(module, scc_ast_decl_t)
|
||||||
|
#define SCC_AST_ALLOC_EXPR(module) SCC_AST_ALLOC(module, scc_ast_expr_t)
|
||||||
|
#define SCC_AST_ALLOC_STMT(module) SCC_AST_ALLOC(module, scc_ast_stmt_t)
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
scc_ast_type_builtin_init(scc_ast_qual_type_t *type, scc_ast_module_t *module,
|
||||||
|
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_module_get_builtin_type(module, builtin_type);
|
||||||
|
type->quals = (scc_ast_decl_specifier_t){0};
|
||||||
|
}
|
||||||
|
|
||||||
|
#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,49 @@
|
|||||||
|
#include <scc_ast.h>
|
||||||
|
|
||||||
|
static scc_ast_canon_type_t *alloc_canonical_type(scc_ast_module_t *module) {
|
||||||
|
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(module->canonical_type_pool, type);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_ast_module_init(scc_ast_module_t *module) {
|
||||||
|
scc_vec_init(module->canonical_type_pool);
|
||||||
|
scc_vec_init(module->all_nodes);
|
||||||
|
scc_strpool_init(&module->name_pool);
|
||||||
|
|
||||||
|
for (int i = 0; i < SCC_AST_BUILTIN_TYPE_COUNT; i += 1) {
|
||||||
|
scc_ast_canon_type_t *t = alloc_canonical_type(module);
|
||||||
|
t->builtin.type = (scc_ast_builtin_type_t)i;
|
||||||
|
module->builtin_types[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_ast_module_drop(scc_ast_module_t *module) {
|
||||||
|
scc_vec_foreach(module->canonical_type_pool, i) {
|
||||||
|
scc_free(module->canonical_type_pool.data[i]);
|
||||||
|
}
|
||||||
|
scc_vec_foreach(module->all_nodes, i) { scc_free(module->all_nodes.data[i]); }
|
||||||
|
scc_vec_free(module->canonical_type_pool);
|
||||||
|
scc_strpool_drop(&module->name_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *
|
||||||
|
scc_ast_module_get_builtin_type(scc_ast_module_t *module,
|
||||||
|
scc_ast_builtin_type_t kind) {
|
||||||
|
assert(kind < SCC_AST_BUILTIN_TYPE_COUNT &&
|
||||||
|
kind > SCC_AST_BUILTIN_TYPE_UNKNOWN);
|
||||||
|
return module->builtin_types[kind];
|
||||||
|
}
|
||||||
|
|
||||||
|
scc_ast_canon_type_t *scc_ast_module_alloc_type(scc_ast_module_t *module) {
|
||||||
|
return alloc_canonical_type(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *scc_ast_module_intern(scc_ast_module_t *module, const char *str) {
|
||||||
|
if (str == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
return scc_strpool_intern(&module->name_pool, str);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void test_example() {
|
||||||
|
printf("Test passed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test_example();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "scc_ast2ir"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = []
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
{ name = "scc_ast", path = "../ast" },
|
||||||
|
{ name = "scc_hir", path = "../ir/hir" },
|
||||||
|
]
|
||||||
|
# features = {}
|
||||||
|
# default_features = []
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
#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_hashtable_t type_cache; ///< scc_ast_canon_type_t* -> scc_hir_type_ref_t
|
||||||
|
// scc_strpool_t strpool; ///< string pool
|
||||||
|
const scc_abi_type_calc_t *abi;
|
||||||
|
cbool hint_using_value; // 转换时尽可能使用value而不是alloc
|
||||||
|
|
||||||
|
scc_ast_module_t *ast_module;
|
||||||
|
} scc_ast2ir_ctx_t;
|
||||||
|
|
||||||
|
static inline scc_hir_module_t *scc_ast2ir_mir_module(scc_ast2ir_ctx_t *ctx) {
|
||||||
|
return &ctx->builder.cprog->module;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_ast2ir_ctx_init(scc_ast2ir_ctx_t *ctx, const scc_abi_type_calc_t *abi,
|
||||||
|
scc_ast_module_t *ast_module, 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);
|
||||||
|
|
||||||
|
scc_hir_type_ref_t
|
||||||
|
scc_ast2ir_parse_base_type(scc_ast2ir_ctx_t *ctx,
|
||||||
|
const scc_ast_qual_type_t *ast_type);
|
||||||
|
|
||||||
|
// ====== 类型提升(Type Promotion)接口 ======
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 整数提升(C11 6.3.1.1)
|
||||||
|
* 对 _Bool、char、short 等秩低于 int 的类型提升为 int/unsigned int
|
||||||
|
* @param type_ref 原始类型引用
|
||||||
|
* @return 提升后的类型的引用,若无需提升则返回原始类型
|
||||||
|
*/
|
||||||
|
scc_hir_type_ref_t scc_ast2ir_integer_promotion(scc_ast2ir_ctx_t *ctx,
|
||||||
|
scc_hir_type_ref_t type_ref);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 寻常算术转换(C11 6.3.1.8)
|
||||||
|
* 对二元算术操作的两个操作数类型找到公共类型
|
||||||
|
* @return 公共类型的引用
|
||||||
|
*/
|
||||||
|
scc_hir_type_ref_t
|
||||||
|
scc_ast2ir_usual_arithmetic_conversion(scc_ast2ir_ctx_t *ctx,
|
||||||
|
scc_hir_type_ref_t t1_ref,
|
||||||
|
scc_hir_type_ref_t t2_ref);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 插入类型转换指令
|
||||||
|
* 根据源类型和目标类型自动选择 SEXT/ZEXT/TRUNC/F2I/I2F/F2F
|
||||||
|
* @return 转换后的值引用
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_ast2ir_emit_conversion(scc_ast2ir_ctx_t *ctx,
|
||||||
|
scc_hir_value_ref_t value,
|
||||||
|
scc_hir_type_ref_t target_type);
|
||||||
|
|
||||||
|
// ====== 常量表达式求值 ======
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 对整数常量表达式求值(C11 6.6)
|
||||||
|
*
|
||||||
|
* 递归计算 AST 表达式,得到一个编译期可确定的整数值。
|
||||||
|
* 支持:整数字面量、字符字面量、枚举常量、sizeof / _Alignof、
|
||||||
|
* 一元运算(+ - ~ !)、二元运算(算术/位/移位/关系/逻辑)、
|
||||||
|
* 条件表达式(?:)、强制类型转换。
|
||||||
|
*
|
||||||
|
* @param ctx ast2ir 上下文
|
||||||
|
* @param result 输出参数,存放求值结果
|
||||||
|
* @param expr 待求值的 AST 表达式
|
||||||
|
* @return 成功返回 true;若表达式不是合法的常量表达式则返回 false
|
||||||
|
*/
|
||||||
|
cbool scc_ast2ir_eval_constant_int(scc_ast2ir_ctx_t *ctx, scc_ap_t *result,
|
||||||
|
const scc_ast_expr_t *expr);
|
||||||
|
|
||||||
|
#endif /* __SCC_AST2IR_H__ */
|
||||||
@@ -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__ */
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,381 @@
|
|||||||
|
#include <scc_ast2ir.h>
|
||||||
|
#include <scc_ast_def.h>
|
||||||
|
#include <scc_ast_utils.h>
|
||||||
|
|
||||||
|
/* ---------- internal helpers ---------- */
|
||||||
|
|
||||||
|
static cbool is_builtin_signed(scc_ast_builtin_type_t t) {
|
||||||
|
switch (t) {
|
||||||
|
case SCC_AST_BUILTIN_TYPE_BOOL:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_CHAR: /* implementation-defined, treat as signed */
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SHORT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_INT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_LONG_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_CHAR:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_SHORT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_INT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static cbool is_builtin_unsigned(scc_ast_builtin_type_t t) {
|
||||||
|
switch (t) {
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_INT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check whether an AST qual-type is an integer type (not pointer/float/struct) */
|
||||||
|
static cbool is_ast_integer_type(const scc_ast_qual_type_t *type) {
|
||||||
|
if (type->base.type != SCC_AST_TYPE_BUILTIN)
|
||||||
|
return false;
|
||||||
|
scc_ast_builtin_type_t bt = scc_ast_canon_type(type)->builtin.type;
|
||||||
|
return bt >= SCC_AST_BUILTIN_TYPE_BOOL &&
|
||||||
|
bt <= SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Truncate / sign-extend an AP value to match a target integer type width.
|
||||||
|
* Uses the ABI calculator to determine the type's size in bytes.
|
||||||
|
*/
|
||||||
|
static cbool apply_integer_truncation(scc_ast2ir_ctx_t *ctx, scc_ap_t *val,
|
||||||
|
const scc_ast_qual_type_t *target_type) {
|
||||||
|
if (!is_ast_integer_type(target_type))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
scc_abi_type_layout_t layout;
|
||||||
|
ctx->abi->compute_type_layout(ctx->abi, (void *)target_type, &layout);
|
||||||
|
int bits = layout.size * 8;
|
||||||
|
if (bits >= (int)sizeof(scc_ap_digit) * 8)
|
||||||
|
return true; /* no truncation needed */
|
||||||
|
|
||||||
|
/* Extract magnitude */
|
||||||
|
uint64_t mag = val->data.digit;
|
||||||
|
uint64_t mask = ((uint64_t)1 << bits) - 1;
|
||||||
|
uint64_t truncated = mag & mask;
|
||||||
|
|
||||||
|
/* For signed types, sign-extend if the MSB is set */
|
||||||
|
scc_ast_builtin_type_t bt = scc_ast_canon_type(target_type)->builtin.type;
|
||||||
|
if (is_builtin_signed(bt) && (truncated >> (bits - 1)))
|
||||||
|
truncated |= ~mask; /* set all bits above bits-1 */
|
||||||
|
|
||||||
|
val->data.digit = truncated;
|
||||||
|
val->len = (val->len < 0 && truncated == 0) ? 1 : val->len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- unary expression ---------- */
|
||||||
|
|
||||||
|
static cbool eval_unary(scc_ast2ir_ctx_t *ctx, scc_ap_t *result,
|
||||||
|
const scc_ast_expr_t *expr) {
|
||||||
|
scc_ap_t val;
|
||||||
|
scc_ap_init(&val);
|
||||||
|
|
||||||
|
if (!scc_ast2ir_eval_constant_int(ctx, &val, expr->unary.operand))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (expr->unary.op) {
|
||||||
|
case SCC_AST_OP_UNARY_PLUS:
|
||||||
|
result->capacity = -1;
|
||||||
|
result->len = val.len;
|
||||||
|
result->data.digit = val.data.digit;
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_UNARY_MINUS:
|
||||||
|
scc_ap_neg(result, &val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_BITWISE_NOT:
|
||||||
|
scc_ap_not(result, &val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_LOGICAL_NOT:
|
||||||
|
scc_ap_set_int(result, scc_ap_is_zero(&val));
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- binary expression ---------- */
|
||||||
|
|
||||||
|
static cbool eval_binary(scc_ast2ir_ctx_t *ctx, scc_ap_t *result,
|
||||||
|
const scc_ast_expr_t *expr) {
|
||||||
|
scc_ap_t lhs_val, rhs_val;
|
||||||
|
scc_ap_init(&lhs_val);
|
||||||
|
scc_ap_init(&rhs_val);
|
||||||
|
|
||||||
|
if (!scc_ast2ir_eval_constant_int(ctx, &lhs_val, expr->binary.lhs))
|
||||||
|
return false;
|
||||||
|
if (!scc_ast2ir_eval_constant_int(ctx, &rhs_val, expr->binary.rhs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (expr->binary.op) {
|
||||||
|
/* arithmetic */
|
||||||
|
case SCC_AST_OP_ADD:
|
||||||
|
scc_ap_add(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_SUB:
|
||||||
|
scc_ap_sub(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_MUL:
|
||||||
|
scc_ap_mul(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_DIV:
|
||||||
|
scc_ap_div(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_MOD:
|
||||||
|
scc_ap_mod(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
/* bitwise */
|
||||||
|
case SCC_AST_OP_BITWISE_AND:
|
||||||
|
scc_ap_and(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_BITWISE_OR:
|
||||||
|
scc_ap_or(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_BITWISE_XOR:
|
||||||
|
scc_ap_xor(result, &lhs_val, &rhs_val);
|
||||||
|
return true;
|
||||||
|
/* shift */
|
||||||
|
case SCC_AST_OP_LEFT_SHIFT:
|
||||||
|
scc_ap_shl(result, &lhs_val, (unsigned)rhs_val.data.digit);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_RIGHT_SHIFT:
|
||||||
|
scc_ap_shr(result, &lhs_val, (unsigned)rhs_val.data.digit);
|
||||||
|
return true;
|
||||||
|
/* relational */
|
||||||
|
case SCC_AST_OP_EQUAL:
|
||||||
|
scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) == 0);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_NOT_EQUAL:
|
||||||
|
scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) != 0);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_LESS:
|
||||||
|
scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) < 0);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_GREATER:
|
||||||
|
scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) > 0);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_LESS_EQUAL:
|
||||||
|
scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) <= 0);
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_GREATER_EQUAL:
|
||||||
|
scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) >= 0);
|
||||||
|
return true;
|
||||||
|
/* logical */
|
||||||
|
case SCC_AST_OP_LOGICAL_AND:
|
||||||
|
if (scc_ap_is_zero(&lhs_val)) {
|
||||||
|
scc_ap_set_int(result, 0);
|
||||||
|
} else {
|
||||||
|
scc_ap_set_int(result, !scc_ap_is_zero(&rhs_val));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case SCC_AST_OP_LOGICAL_OR:
|
||||||
|
if (!scc_ap_is_zero(&lhs_val)) {
|
||||||
|
scc_ap_set_int(result, 1);
|
||||||
|
} else {
|
||||||
|
scc_ap_set_int(result, !scc_ap_is_zero(&rhs_val));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- identifier (enum constant lookup) ---------- */
|
||||||
|
|
||||||
|
static cbool eval_identifier(scc_ast2ir_ctx_t *ctx, scc_ap_t *result,
|
||||||
|
const scc_ast_expr_t *expr) {
|
||||||
|
if (expr->identifier._target == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Try the ast2ir_cache — enum values are stored there as HIR integer
|
||||||
|
* constants during scc_ast2ir_decl(SCC_AST_DECL_ENUM) */
|
||||||
|
scc_hir_value_ref_t cached = (scc_hir_value_ref_t)(usize)scc_hashtable_get(
|
||||||
|
&ctx->ast2ir_cache, expr->identifier._target);
|
||||||
|
if (cached != 0) {
|
||||||
|
scc_hir_value_t *hir_val =
|
||||||
|
scc_hir_module_get_value(scc_ast2ir_mir_module(ctx), cached);
|
||||||
|
if (hir_val != nullptr && hir_val->tag == SCC_HIR_VALUE_TAG_INTEGER) {
|
||||||
|
/* shallow copy — digit mode has no heap allocation */
|
||||||
|
result->capacity = -1;
|
||||||
|
result->len = hir_val->data.integer.len;
|
||||||
|
result->data.digit = hir_val->data.integer.data.digit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If not cached yet, evaluate the var init recursively.
|
||||||
|
* This handles enum-internal forward references within a single enum decl:
|
||||||
|
* enum { A = 1, B = A + 1 }; — when evaluating B, A may not be cached
|
||||||
|
* yet, but we can look at A's init expression. */
|
||||||
|
if (expr->identifier._target->base.type == SCC_AST_DECL_VAR &&
|
||||||
|
expr->identifier._target->var.init != nullptr) {
|
||||||
|
return scc_ast2ir_eval_constant_int(ctx, result,
|
||||||
|
expr->identifier._target->var.init);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- character literal evaluation ---------- */
|
||||||
|
|
||||||
|
static int eval_char_literal(const scc_ast_expr_t *expr) {
|
||||||
|
const char *lexme = expr->literal.lexme;
|
||||||
|
Assert(lexme[0] == '\'');
|
||||||
|
|
||||||
|
if (lexme[1] != '\\')
|
||||||
|
return (unsigned char)lexme[1];
|
||||||
|
|
||||||
|
switch (lexme[2]) {
|
||||||
|
case 'a':
|
||||||
|
return '\a';
|
||||||
|
case 'b':
|
||||||
|
return '\b';
|
||||||
|
case 'f':
|
||||||
|
return '\f';
|
||||||
|
case 'n':
|
||||||
|
return '\n';
|
||||||
|
case 'r':
|
||||||
|
return '\r';
|
||||||
|
case 't':
|
||||||
|
return '\t';
|
||||||
|
case 'v':
|
||||||
|
return '\v';
|
||||||
|
case '\\':
|
||||||
|
return '\\';
|
||||||
|
case '\'':
|
||||||
|
return '\'';
|
||||||
|
case '"':
|
||||||
|
return '"';
|
||||||
|
case '0':
|
||||||
|
return '\0';
|
||||||
|
case 'x':
|
||||||
|
/* hex escape: \xNN — parse up to 2 hex digits */
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
int j = 3;
|
||||||
|
while (1) {
|
||||||
|
char c = lexme[j];
|
||||||
|
unsigned d;
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
d = c - '0';
|
||||||
|
else if (c >= 'a' && c <= 'f')
|
||||||
|
d = c - 'a' + 10;
|
||||||
|
else if (c >= 'A' && c <= 'F')
|
||||||
|
d = c - 'A' + 10;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
val = val * 16 + d;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
/* octal: \nnn */
|
||||||
|
if (lexme[2] >= '0' && lexme[2] <= '7') {
|
||||||
|
int val = 0;
|
||||||
|
int j = 2;
|
||||||
|
while (j < 5 && lexme[j] >= '0' && lexme[j] <= '7') {
|
||||||
|
val = val * 8 + (lexme[j] - '0');
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
return (unsigned char)lexme[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* Public API
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
cbool scc_ast2ir_eval_constant_int(scc_ast2ir_ctx_t *ctx, scc_ap_t *result,
|
||||||
|
const scc_ast_expr_t *expr) {
|
||||||
|
if (ctx == nullptr || result == nullptr || expr == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
scc_ap_init(result);
|
||||||
|
|
||||||
|
switch (expr->base.type) {
|
||||||
|
case SCC_AST_EXPR_INT_LITERAL:
|
||||||
|
scc_ap_from_cstr(result, expr->literal.lexme, 0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_CHAR_LITERAL:
|
||||||
|
scc_ap_set_int(result, eval_char_literal(expr));
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_BINARY:
|
||||||
|
return eval_binary(ctx, result, expr);
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_UNARY:
|
||||||
|
return eval_unary(ctx, result, expr);
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_COND: {
|
||||||
|
scc_ap_t cond_val;
|
||||||
|
scc_ap_init(&cond_val);
|
||||||
|
if (!scc_ast2ir_eval_constant_int(ctx, &cond_val, expr->cond.cond))
|
||||||
|
return false;
|
||||||
|
return scc_ast2ir_eval_constant_int(ctx, result,
|
||||||
|
scc_ap_is_zero(&cond_val)
|
||||||
|
? expr->cond.else_expr
|
||||||
|
: expr->cond.then_expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_CAST: {
|
||||||
|
if (!scc_ast2ir_eval_constant_int(ctx, result, expr->cast.expr))
|
||||||
|
return false;
|
||||||
|
/* Truncate / sign-extend to the target type width */
|
||||||
|
if (!apply_integer_truncation(ctx, result, expr->cast.type))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_SIZE_OF: {
|
||||||
|
scc_abi_type_layout_t layout;
|
||||||
|
if (expr->attr_of.type) {
|
||||||
|
ctx->abi->compute_type_layout(ctx->abi, (void *)expr->attr_of.type,
|
||||||
|
&layout);
|
||||||
|
} else if (expr->attr_of.expr) {
|
||||||
|
/* sizeof expression: try to get the type through the ABI */
|
||||||
|
/* For constant expressions, sema should have annotated the type */
|
||||||
|
/* TODO: handle sizeof(expr) where expr is not a type */
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scc_ap_set_int(result, layout.size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_ALIGN_OF: {
|
||||||
|
scc_abi_type_layout_t layout;
|
||||||
|
if (expr->attr_of.type) {
|
||||||
|
ctx->abi->compute_type_layout(ctx->abi, (void *)expr->attr_of.type,
|
||||||
|
&layout);
|
||||||
|
} else if (expr->attr_of.expr) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scc_ap_set_int(result, layout.alignment);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SCC_AST_EXPR_IDENTIFIER:
|
||||||
|
return eval_identifier(ctx, result, expr);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
#include <scc_ast2ir.h>
|
||||||
|
|
||||||
|
// 判断是否为整数类型(不包括指针、浮点)
|
||||||
|
static cbool is_integer_type(const scc_hir_type_t *type) {
|
||||||
|
switch (type->tag) {
|
||||||
|
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:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为浮点类型
|
||||||
|
static cbool is_floating_type(const scc_hir_type_t *type) {
|
||||||
|
switch (type->tag) {
|
||||||
|
case SCC_HIR_TYPE_f16:
|
||||||
|
case SCC_HIR_TYPE_f32:
|
||||||
|
case SCC_HIR_TYPE_f64:
|
||||||
|
case SCC_HIR_TYPE_f128:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取整数等级(rank),参考 C11 6.3.1.1
|
||||||
|
static int integer_tag_rank(scc_hir_type_tag_t tag) {
|
||||||
|
switch (tag) {
|
||||||
|
case SCC_HIR_TYPE_i8:
|
||||||
|
case SCC_HIR_TYPE_u8:
|
||||||
|
return 1;
|
||||||
|
case SCC_HIR_TYPE_i16:
|
||||||
|
case SCC_HIR_TYPE_u16:
|
||||||
|
return 2;
|
||||||
|
case SCC_HIR_TYPE_i32:
|
||||||
|
case SCC_HIR_TYPE_u32:
|
||||||
|
return 3;
|
||||||
|
case SCC_HIR_TYPE_i64:
|
||||||
|
case SCC_HIR_TYPE_u64:
|
||||||
|
return 4;
|
||||||
|
case SCC_HIR_TYPE_i128:
|
||||||
|
case SCC_HIR_TYPE_u128:
|
||||||
|
return 5;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取浮点等级(等级越高精度越高)
|
||||||
|
static int floating_rank(const scc_hir_type_t *type) {
|
||||||
|
switch (type->tag) {
|
||||||
|
case SCC_HIR_TYPE_f16:
|
||||||
|
return 1;
|
||||||
|
case SCC_HIR_TYPE_f32:
|
||||||
|
return 2;
|
||||||
|
case SCC_HIR_TYPE_f64:
|
||||||
|
return 3;
|
||||||
|
case SCC_HIR_TYPE_f128:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取类型是否为有符号
|
||||||
|
static cbool is_signed_tag(scc_hir_type_tag_t tag) {
|
||||||
|
switch (tag) {
|
||||||
|
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:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取无符号版本的 tag
|
||||||
|
static scc_hir_type_tag_t unsigned_tag(scc_hir_type_tag_t tag) {
|
||||||
|
switch (tag) {
|
||||||
|
case SCC_HIR_TYPE_i8:
|
||||||
|
case SCC_HIR_TYPE_u8:
|
||||||
|
return SCC_HIR_TYPE_u8;
|
||||||
|
case SCC_HIR_TYPE_i16:
|
||||||
|
case SCC_HIR_TYPE_u16:
|
||||||
|
return SCC_HIR_TYPE_u16;
|
||||||
|
case SCC_HIR_TYPE_i32:
|
||||||
|
case SCC_HIR_TYPE_u32:
|
||||||
|
return SCC_HIR_TYPE_u32;
|
||||||
|
case SCC_HIR_TYPE_i64:
|
||||||
|
case SCC_HIR_TYPE_u64:
|
||||||
|
return SCC_HIR_TYPE_u64;
|
||||||
|
case SCC_HIR_TYPE_i128:
|
||||||
|
case SCC_HIR_TYPE_u128:
|
||||||
|
return SCC_HIR_TYPE_u128;
|
||||||
|
default:
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取有符号版本的 tag
|
||||||
|
static scc_hir_type_tag_t signed_tag(scc_hir_type_tag_t tag) {
|
||||||
|
switch (tag) {
|
||||||
|
case SCC_HIR_TYPE_i8:
|
||||||
|
case SCC_HIR_TYPE_u8:
|
||||||
|
return SCC_HIR_TYPE_i8;
|
||||||
|
case SCC_HIR_TYPE_i16:
|
||||||
|
case SCC_HIR_TYPE_u16:
|
||||||
|
return SCC_HIR_TYPE_i16;
|
||||||
|
case SCC_HIR_TYPE_i32:
|
||||||
|
case SCC_HIR_TYPE_u32:
|
||||||
|
return SCC_HIR_TYPE_i32;
|
||||||
|
case SCC_HIR_TYPE_i64:
|
||||||
|
case SCC_HIR_TYPE_u64:
|
||||||
|
return SCC_HIR_TYPE_i64;
|
||||||
|
case SCC_HIR_TYPE_i128:
|
||||||
|
case SCC_HIR_TYPE_u128:
|
||||||
|
return SCC_HIR_TYPE_i128;
|
||||||
|
default:
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过 tag 快速创建类型引用
|
||||||
|
static scc_hir_type_ref_t make_type(scc_ast2ir_ctx_t *ctx,
|
||||||
|
scc_hir_type_tag_t tag) {
|
||||||
|
scc_hir_type_t desc;
|
||||||
|
scc_hir_type_init(&desc, tag);
|
||||||
|
return scc_hir_builder_type(&ctx->builder, &desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 整数提升(C11 6.3.1.1)
|
||||||
|
*
|
||||||
|
* 如果 int 可以表示原始类型的所有值,则提升为 int;
|
||||||
|
* 否则提升为 unsigned int。
|
||||||
|
*
|
||||||
|
* 对于秩 >= int 的类型,不做提升。
|
||||||
|
*/
|
||||||
|
scc_hir_type_ref_t scc_ast2ir_integer_promotion(scc_ast2ir_ctx_t *ctx,
|
||||||
|
scc_hir_type_ref_t type_ref) {
|
||||||
|
scc_hir_module_t *module = scc_ast2ir_mir_module(ctx);
|
||||||
|
scc_hir_type_t *type = scc_hir_module_get_type(module, type_ref);
|
||||||
|
if (type == nullptr || !is_integer_type(type))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int rank = integer_tag_rank(type->tag);
|
||||||
|
|
||||||
|
// 秩 >= int(rank >= 3),无需提升
|
||||||
|
if (rank >= 3)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return make_type(ctx, SCC_HIR_TYPE_i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 寻常算术转换(C11 6.3.1.8)
|
||||||
|
*
|
||||||
|
* 确定二元算术操作中两个操作数的公共类型。
|
||||||
|
*
|
||||||
|
* 规则:
|
||||||
|
* 1. 如果任一操作数是 long double,另一操作数转换为 long double
|
||||||
|
* 2. 如果任一操作数是 double,另一操作数转换为 double
|
||||||
|
* 3. 如果任一操作数是 float,另一操作数转换为 float
|
||||||
|
* 4. 否则,对两个操作数应用整数提升,然后:
|
||||||
|
* a. 如果两个类型相同,无需转换
|
||||||
|
* b. 如果两个都是有符号或无符号,较小秩的转换为较大秩的
|
||||||
|
* c. 如果无符号类型的秩 >= 有符号类型的秩,有符号转换为无符号
|
||||||
|
* d. 如果有符号类型可以表示无符号类型的所有值,无符号转换为有符号
|
||||||
|
* e. 否则,两个都转换为有符号类型的无符号版本
|
||||||
|
*/
|
||||||
|
scc_hir_type_ref_t
|
||||||
|
scc_ast2ir_usual_arithmetic_conversion(scc_ast2ir_ctx_t *ctx,
|
||||||
|
scc_hir_type_ref_t t1_ref,
|
||||||
|
scc_hir_type_ref_t t2_ref) {
|
||||||
|
scc_hir_module_t *module = scc_ast2ir_mir_module(ctx);
|
||||||
|
scc_hir_type_t *t1 = scc_hir_module_get_type(module, t1_ref);
|
||||||
|
scc_hir_type_t *t2 = scc_hir_module_get_type(module, t2_ref);
|
||||||
|
|
||||||
|
// --- 浮点类型处理 ---
|
||||||
|
if (is_floating_type(t1) || is_floating_type(t2)) {
|
||||||
|
int r1 = is_floating_type(t1) ? floating_rank(t1) : 0;
|
||||||
|
int r2 = is_floating_type(t2) ? floating_rank(t2) : 0;
|
||||||
|
scc_hir_type_tag_t wider = (r1 >= r2) ? t1->tag : t2->tag;
|
||||||
|
return make_type(ctx, wider);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 整数类型处理 ---
|
||||||
|
// 应用整数提升:得到提升后的 tag
|
||||||
|
scc_hir_type_ref_t p1_ref = scc_ast2ir_integer_promotion(ctx, t1_ref);
|
||||||
|
scc_hir_type_ref_t p2_ref = scc_ast2ir_integer_promotion(ctx, t2_ref);
|
||||||
|
|
||||||
|
scc_hir_type_tag_t tag1 =
|
||||||
|
(p1_ref != 0) ? scc_hir_module_get_type(module, p1_ref)->tag : t1->tag;
|
||||||
|
scc_hir_type_tag_t tag2 =
|
||||||
|
(p2_ref != 0) ? scc_hir_module_get_type(module, p2_ref)->tag : t2->tag;
|
||||||
|
|
||||||
|
// 如果提升后相同,直接返回
|
||||||
|
if (tag1 == tag2)
|
||||||
|
return make_type(ctx, tag1);
|
||||||
|
|
||||||
|
cbool s1 = is_signed_tag(tag1);
|
||||||
|
cbool s2 = is_signed_tag(tag2);
|
||||||
|
int r1 = integer_tag_rank(tag1);
|
||||||
|
int r2 = integer_tag_rank(tag2);
|
||||||
|
|
||||||
|
// 两个都是有符号,或两个都是无符号:较小秩转换为较大秩
|
||||||
|
if (s1 == s2) {
|
||||||
|
return make_type(ctx, (r1 >= r2) ? tag1 : tag2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一个有符号一个无符号
|
||||||
|
scc_hir_type_tag_t unsigned_t =
|
||||||
|
s1 ? unsigned_tag(tag1) : unsigned_tag(tag2);
|
||||||
|
scc_hir_type_tag_t signed_t = s1 ? tag1 : tag2;
|
||||||
|
scc_hir_type_tag_t unsigned_other = s1 ? tag2 : tag1; // 无符号的那个
|
||||||
|
int unsigned_rank = integer_tag_rank(unsigned_other);
|
||||||
|
|
||||||
|
// 如果无符号类型的秩 >= 有符号类型的秩:有符号转换为无符号
|
||||||
|
if (unsigned_rank >= integer_tag_rank(signed_t)) {
|
||||||
|
return make_type(ctx, unsigned_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有符号类型可以表示无符号类型的所有值:无符号转换为有符号
|
||||||
|
if (integer_tag_rank(signed_t) > integer_tag_rank(unsigned_other)) {
|
||||||
|
return make_type(ctx, signed_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_type(ctx, unsigned_tag(signed_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 确定类型转换的种类
|
||||||
|
*
|
||||||
|
* 根据源类型和目标类型选择合适的转换方式:
|
||||||
|
* - SEXT: 源有符号且目标更大
|
||||||
|
* - ZEXT: 源无符号且目标更大
|
||||||
|
* - TRUNC: 目标更小
|
||||||
|
* - 返回 -1 表示无需转换(同类型同大小)
|
||||||
|
*/
|
||||||
|
static int determine_conv_kind(scc_hir_type_tag_t src, scc_hir_type_tag_t dst) {
|
||||||
|
Assert(SCC_HIR_TYPE_VALID(src) && SCC_HIR_TYPE_VALID(dst));
|
||||||
|
if (src == dst)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
static const int size_map[] = {
|
||||||
|
[SCC_HIR_TYPE_i8] = 1, [SCC_HIR_TYPE_u8] = 1,
|
||||||
|
[SCC_HIR_TYPE_i16] = 2, [SCC_HIR_TYPE_u16] = 2,
|
||||||
|
[SCC_HIR_TYPE_i32] = 4, [SCC_HIR_TYPE_u32] = 4,
|
||||||
|
[SCC_HIR_TYPE_i64] = 8, [SCC_HIR_TYPE_u64] = 8,
|
||||||
|
[SCC_HIR_TYPE_i128] = 16, [SCC_HIR_TYPE_u128] = 16,
|
||||||
|
[SCC_HIR_TYPE_f32] = 4, [SCC_HIR_TYPE_f64] = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
cbool src_float = (src == SCC_HIR_TYPE_f32 || src == SCC_HIR_TYPE_f64);
|
||||||
|
cbool dst_float = (dst == SCC_HIR_TYPE_f32 || dst == SCC_HIR_TYPE_f64);
|
||||||
|
|
||||||
|
if (src_float && dst_float) {
|
||||||
|
if (size_map[src] < size_map[dst])
|
||||||
|
return SCC_HIR_CONV_SEXT;
|
||||||
|
else
|
||||||
|
return SCC_HIR_CONV_TRUNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_float && !dst_float)
|
||||||
|
return SCC_HIR_CONV_SEXT;
|
||||||
|
|
||||||
|
if (!src_float && dst_float)
|
||||||
|
return SCC_HIR_CONV_SEXT;
|
||||||
|
|
||||||
|
int src_size = size_map[src];
|
||||||
|
int dst_size = size_map[dst];
|
||||||
|
|
||||||
|
if (dst_size > src_size) {
|
||||||
|
return is_signed_tag(src) ? SCC_HIR_CONV_SEXT : SCC_HIR_CONV_ZEXT;
|
||||||
|
} else if (dst_size < src_size) {
|
||||||
|
return SCC_HIR_CONV_TRUNC;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 插入类型转换指令
|
||||||
|
*
|
||||||
|
* 在 IR 中插入一个 conv 节点将 value 转换为 target_type。
|
||||||
|
* 自动选择 SEXT/ZEXT/TRUNC 或直接返回(无需转换时)。
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_ast2ir_emit_conversion(scc_ast2ir_ctx_t *ctx,
|
||||||
|
scc_hir_value_ref_t value,
|
||||||
|
scc_hir_type_ref_t target_type) {
|
||||||
|
scc_hir_module_t *module = scc_ast2ir_mir_module(ctx);
|
||||||
|
scc_hir_type_t *src_type = scc_hir_module_get_type_by_value(module, value);
|
||||||
|
scc_hir_type_t *dst_type = scc_hir_module_get_type(module, target_type);
|
||||||
|
|
||||||
|
if (src_type == nullptr || dst_type == nullptr)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (src_type->tag == dst_type->tag)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
int kind = determine_conv_kind(src_type->tag, dst_type->tag);
|
||||||
|
if (kind < 0)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
return scc_hir_builder_conv(&ctx->builder, value, target_type, kind);
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#include <scc_ast2ir.h>
|
||||||
|
|
||||||
|
scc_hir_type_ref_t
|
||||||
|
scc_ast2ir_parse_base_type(scc_ast2ir_ctx_t *ctx,
|
||||||
|
const scc_ast_qual_type_t *ast_type) {
|
||||||
|
scc_abi_type_layout_t layout;
|
||||||
|
// 映射内置类型
|
||||||
|
ctx->abi->compute_type_layout(ctx->abi, (void *)ast_type, &layout);
|
||||||
|
switch (scc_ast_canon_type(ast_type)->builtin.type) {
|
||||||
|
// FIXME it not a void
|
||||||
|
case SCC_AST_BUILTIN_TYPE_VA_LIST:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_VOID:
|
||||||
|
return scc_hir_builder_type_void(&ctx->builder);
|
||||||
|
case SCC_AST_BUILTIN_TYPE_BOOL:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_CHAR:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SHORT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_SHORT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_INT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_INT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_LONG_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG:
|
||||||
|
switch (layout.size) {
|
||||||
|
case 1:
|
||||||
|
return scc_hir_builder_type_i8(&ctx->builder);
|
||||||
|
case 2:
|
||||||
|
return scc_hir_builder_type_i16(&ctx->builder);
|
||||||
|
case 4:
|
||||||
|
return scc_hir_builder_type_i32(&ctx->builder);
|
||||||
|
case 8:
|
||||||
|
return scc_hir_builder_type_i64(&ctx->builder);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_CHAR:
|
||||||
|
// TODO CHAR TO BE UNSIGNED CHAR
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_INT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG:
|
||||||
|
switch (layout.size) {
|
||||||
|
case 1:
|
||||||
|
return scc_hir_builder_type_u8(&ctx->builder);
|
||||||
|
case 2:
|
||||||
|
return scc_hir_builder_type_u16(&ctx->builder);
|
||||||
|
case 4:
|
||||||
|
return scc_hir_builder_type_u32(&ctx->builder);
|
||||||
|
case 8:
|
||||||
|
return scc_hir_builder_type_u64(&ctx->builder);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_FLOAT:
|
||||||
|
case SCC_AST_BUILTIN_TYPE_DOUBLE:
|
||||||
|
switch (layout.size) {
|
||||||
|
case 4:
|
||||||
|
return scc_hir_builder_type_f32(&ctx->builder);
|
||||||
|
case 8:
|
||||||
|
return scc_hir_builder_type_f64(&ctx->builder);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Panic("Unsupported AST type: %d",
|
||||||
|
scc_ast_canon_type(ast_type)->builtin.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return SCC_HIR_REF_nullptr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#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;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_FLOAT:
|
||||||
|
kind = SCC_ABI_TYPE_FLOAT;
|
||||||
|
break;
|
||||||
|
case SCC_AST_BUILTIN_TYPE_DOUBLE:
|
||||||
|
kind = SCC_ABI_TYPE_DOUBLE;
|
||||||
|
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,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,116 @@
|
|||||||
|
#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->symbols)) {
|
||||||
|
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)bb;
|
||||||
|
(void)pred_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_remove_pred(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t pred_id) {
|
||||||
|
(void)bb;
|
||||||
|
(void)pred_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_clear_pred(scc_cfg_bblock_t *bb) { (void)bb; }
|
||||||
|
|
||||||
|
void scc_cfg_bblock_add_succ(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t succ_id) {
|
||||||
|
(void)bb;
|
||||||
|
(void)succ_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_remove_succ(scc_cfg_bblock_t *bb,
|
||||||
|
scc_cfg_bblock_id_t succ_id) {
|
||||||
|
(void)bb;
|
||||||
|
(void)succ_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scc_cfg_bblock_clear_succs(scc_cfg_bblock_t *bb) { (void)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,313 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
static inline scc_hir_module_t *
|
||||||
|
scc_hir_builder_get_module(scc_hir_builder_t *builder) {
|
||||||
|
return &builder->cprog->module;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) \
|
||||||
|
SCC_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) {
|
||||||
|
Assert(builder != nullptr && val != nullptr);
|
||||||
|
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 创建类型转换指令(SEXT/ZEXT/TRUNC)
|
||||||
|
* @param operand 被转换的值
|
||||||
|
* @param target_type 目标类型
|
||||||
|
* @param conv_kind 转换种类(SCC_HIR_CONV_SEXT/ZEXT/TRUNC)
|
||||||
|
*/
|
||||||
|
scc_hir_value_ref_t scc_hir_builder_conv(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_value_ref_t operand,
|
||||||
|
scc_hir_type_ref_t target_type,
|
||||||
|
int conv_kind);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 基本块引用(真实 ID)
|
||||||
|
*/
|
||||||
|
scc_hir_bblock_ref_t scc_hir_builder_create_bblock(scc_hir_builder_t *builder,
|
||||||
|
const char *label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 将指定基本块追加到当前函数的基本块列表末尾
|
||||||
|
* @param bblock 基本块引用
|
||||||
|
*/
|
||||||
|
void scc_hir_builder_append_bblock(scc_hir_builder_t *builder,
|
||||||
|
scc_hir_bblock_ref_t bblock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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,279 @@
|
|||||||
|
#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_COUNT,
|
||||||
|
} scc_hir_type_tag_t;
|
||||||
|
#define SCC_HIR_TYPE_VALID(tag) ((tag) < SCC_HIR_TYPE_COUNT && (tag) >= 0)
|
||||||
|
|
||||||
|
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_JUMP_INDIRECT, ///< 无条件跳转(地址)
|
||||||
|
SCC_HIR_VALUE_TAG_CALL, ///< 调用函数
|
||||||
|
SCC_HIR_VALUE_TAG_CALL_INDIRECT, ///< 调用函数(地址)
|
||||||
|
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,
|
||||||
|
/// Unsigned greater than.
|
||||||
|
SCC_HIR_OP_UGT,
|
||||||
|
/// Unsigned less than.
|
||||||
|
SCC_HIR_OP_ULT,
|
||||||
|
/// Unsigned greater than or equal to.
|
||||||
|
SCC_HIR_OP_UGE,
|
||||||
|
/// Unsigned less than or equal to.
|
||||||
|
SCC_HIR_OP_ULE,
|
||||||
|
} 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;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCC_HIR_CONV_NONE,
|
||||||
|
SCC_HIR_CONV_SEXT,
|
||||||
|
SCC_HIR_CONV_ZEXT,
|
||||||
|
SCC_HIR_CONV_TRUNC,
|
||||||
|
} scc_hir_conv_type_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; // 目标类型
|
||||||
|
scc_hir_conv_type_t 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;
|
||||||
|
union {
|
||||||
|
scc_hir_value_ref_t target_ptr;
|
||||||
|
scc_hir_bblock_ref_t target_bblock;
|
||||||
|
} jump;
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
scc_hir_func_ref_t func_ref;
|
||||||
|
scc_hir_value_ref_t ptr_ref;
|
||||||
|
} callee;
|
||||||
|
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;
|
||||||
|
int defined;
|
||||||
|
cbool is_variadic;
|
||||||
|
} 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,16 @@
|
|||||||
|
#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_cprog_linear(scc_hir_dump_t *ctx);
|
||||||
|
|
||||||
|
#endif /* __SCC_HIR_DUMP_H__ */
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#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);
|
||||||
|
usize scc_hir_module_type_size(scc_hir_module_t *ctx, scc_hir_type_t *type);
|
||||||
|
|
||||||
|
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__ */
|
||||||
Loaded 100 of 317 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user