Compare commits
13 Commits
91e4eb734e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 680197aafe | |||
| 4f87078a87 | |||
| 9338c21449 | |||
| 98de782760 | |||
| fafa2b4946 | |||
| 570a87c3cd | |||
| e1d9812f92 | |||
| 6bcc98bdb3 | |||
| 91b5a0e384 | |||
| c0eacfd236 | |||
| 51a939ac45 | |||
| 0f635ccf2b | |||
| 90448006ff |
@@ -6,7 +6,7 @@ Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
|
||||
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
|
||||
# 连续赋值时,对齐所有等号
|
||||
AlignConsecutiveAssignments: true
|
||||
|
||||
11
ExampleCodes/SpeedTest/fib.lua
Normal file
11
ExampleCodes/SpeedTest/fib.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
function fib(n)
|
||||
if (n <= 1) then
|
||||
return n
|
||||
else
|
||||
return fib(n - 1) + fib(n - 2) end
|
||||
end
|
||||
|
||||
local start = os.clock()
|
||||
local result = fib(30)
|
||||
local endt = os.clock()
|
||||
print(result, " cost: ", (endt - start) * 1000, "ms")
|
||||
@@ -1,11 +1,11 @@
|
||||
from time import time as tt
|
||||
|
||||
def fib(x:int) -> int:
|
||||
if x <= 1: return x;
|
||||
if x <= 1: return x
|
||||
return fib(x-1) + fib(x-2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
t0 = tt()
|
||||
result = fib(30)
|
||||
result = fib(35)
|
||||
t1 = tt()
|
||||
print('cost: ',t1-t0, 'result:', result)
|
||||
|
||||
9
LICENSE
9
LICENSE
@@ -13,15 +13,6 @@ furnished to do so, subject to the following conditions:
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
2. This project includes code from the following projects with their respective licenses:
|
||||
- argparse (MIT License)
|
||||
Copyright (c) 2018 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
- magic_enum (MIT License)
|
||||
Copyright (c) 2019 - 2024 Daniil Goncharov
|
||||
|
||||
|
||||
193
README.md
193
README.md
@@ -1,2 +1,195 @@
|
||||
# Fig
|
||||
|
||||
## tmd赶工代码质量太差了暑假我要重构
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||
</picture>
|
||||
|
||||
> **🔔 Main Repository: https://git.fig-lang.cn/PuqiAR/Fig**
|
||||
> *GitHub is only a mirror. Please submit issues and PRs to the main repository.*
|
||||
|
||||
[English](./README.md) | [中文](./README_zh-CN.md)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
-~28ms%20(i5--13490f)-brightgreen)
|
||||
|
||||
**Fig** is a programming language that blends dynamic typing with optional static type annotations, built on reference-based value semantics. It offers the flexibility of scripting languages while providing optional type constraints for more robust code.
|
||||
|
||||
> ⚠️ **Fig is currently at version 0.5.0-alpha, and its syntax and APIs are subject to change.** Feel free to try it out and provide feedback, but do not use it in production environments.
|
||||
|
||||
```fig
|
||||
// Get a feel for Fig
|
||||
var flexible = 10 // dynamic type, can be any value
|
||||
flexible = "hello" // works
|
||||
|
||||
var fixed := 20 // fixed to Int type
|
||||
// fixed = 3.14 // error! type mismatch
|
||||
|
||||
func greet(name) => "Hello, " + name + "!"
|
||||
println(greet("Fig")) // output: Hello, Fig!
|
||||
```
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
### 🎭 Dynamic & Static Blending
|
||||
Variables are dynamic by default (`Any` type), but you can lock their type using `:=` or `: Type` for static safety.
|
||||
|
||||
```fig
|
||||
var any; // type is Any
|
||||
any = 42; // now points to an Int
|
||||
any = [1,2,3]; // now points to a List
|
||||
|
||||
var safe := 100; // inferred as Int, can only be assigned Int values
|
||||
safe = 200; // ✅
|
||||
// safe = "oops" // ❌ compile error
|
||||
```
|
||||
|
||||
### 🔗 Reference-Based Value Semantics
|
||||
All objects are immutable; variables are just "names" that refer to objects. Multiple variables can share the same object, and modifications to complex types (like lists) are visible to all references—this is the essence of references.
|
||||
|
||||
```fig
|
||||
var a := [1, 2, 3];
|
||||
var b := a; // b and a refer to the same list
|
||||
a[0] = 99; // modify the list content
|
||||
println(b) // output: [99, 2, 3] — b sees the change
|
||||
|
||||
// Need a true copy? Use deep copy (syntax may change)
|
||||
var c := new List{a}; // deep copy of a
|
||||
c[0] = 0;
|
||||
println(a) // still [99, 2, 3]
|
||||
```
|
||||
|
||||
### 🏗 Object-Oriented Features (OOP)
|
||||
Fig provides lightweight OOP support based on structs, with polymorphism via interfaces.
|
||||
|
||||
- **Structs as classes**: defined with `struct`, can have fields and methods.
|
||||
- **Access control**: `public` keyword; fields are private by default.
|
||||
- **Concise construction**:
|
||||
```fig
|
||||
var p1 := new Point{1, 2}; // positional
|
||||
var p2 := new Point{x: 2, y: 3}; // named
|
||||
var x := 5, y := 10;
|
||||
var p3 := new Point{y, x}; // shorthand, auto field matching
|
||||
```
|
||||
- **Interfaces and implementations**:
|
||||
```fig
|
||||
interface Drawable { draw() -> String; }
|
||||
struct Circle { radius: Double }
|
||||
impl Drawable for Circle {
|
||||
draw() => "⚪ radius: " + radius;
|
||||
}
|
||||
```
|
||||
- **No inheritance, polymorphism via interfaces** (currently implicit `this`, may change to explicit `self.xxx` in the future).
|
||||
|
||||
### 🧩 Functional Features
|
||||
Functions are first-class citizens, with support for closures and concise arrow syntax.
|
||||
|
||||
```fig
|
||||
func multiplier(factor) {
|
||||
return func (n) => n * factor; // closure (upvalue)
|
||||
}
|
||||
|
||||
var double = multiplier(2);
|
||||
println(double(5)) // output: 10
|
||||
```
|
||||
|
||||
### ⚡ High Performance
|
||||
**Recursive fib(30) takes only 28ms** (i5-13490f, Windows 11, DDR4 2667, clang 21.1.8, libc++, C++23) — excellent performance among dynamic languages.
|
||||
|
||||
Fig achieves high performance through a series of low-level optimizations:
|
||||
- **Register-based VM**: replaces the tree-walk interpreter, dramatically improving execution speed.
|
||||
- **FastCall optimization**: global ordinary functions can be called directly as prototypes, avoiding runtime unwrapping overhead (traditional calls require checking if an object is a function and unwrapping it).
|
||||
- **Window Slicing** (originated from Lua): a stack sliding window technique that efficiently manages function calls and closures (upvalues), reducing memory allocations.
|
||||
- **Upvalue mechanism**: lightweight closure implementation, making functional programming performant.
|
||||
- **Computed Goto**: leverages GCC/Clang extensions to speed up bytecode dispatch.
|
||||
- **NaN-Boxing**: efficient storage and type tagging to boost dynamic typing performance.
|
||||
|
||||
#### Performance Comparison
|
||||
Rough comparison of recursive fib(30) execution times (environment may vary, for reference only):
|
||||
|
||||
| Language Type | Language | Time (ms) | Notes |
|
||||
|---------------|----------------|-----------|-------|
|
||||
| AOT compiled | C (clang -O2) | ~0.05 | Nanoseconds, as a speed reference |
|
||||
| AOT compiled | Rust (--release) | ~0.06 | Same as above |
|
||||
| **Dynamic** | **Fig** | **~28** | **Register VM + optimizations** |
|
||||
| Dynamic | Lua 5.4 | ~35 | Classic dynamic language |
|
||||
| Dynamic | Python 3.11 | ~450 | CPython |
|
||||
| Dynamic | Node.js 20 | ~60 | V8 engine |
|
||||
| Dynamic | Ruby 3.2 | ~800 | CRuby |
|
||||
|
||||
*Fig performs admirably among dynamic languages, approaching Lua and Node.js, and significantly outperforming Python and Ruby.*
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Installation
|
||||
|
||||
#### Option 1: Download Pre-built Binaries
|
||||
Download the binary for your platform from the [main repository Releases](https://git.fig-lang.cn/PuqiAR/Fig/releases) or the [GitHub mirror](https://github.com/PuqiAR/Fig/releases), extract it, and add it to your PATH.
|
||||
|
||||
#### Option 2: Build from Source with xmake
|
||||
```bash
|
||||
# Clone the main repository (GitHub is a mirror)
|
||||
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
|
||||
cd Fig
|
||||
|
||||
# Install xmake if you haven't: https://xmake.io
|
||||
|
||||
# Build (must use clang because computed goto requires compiler support)
|
||||
xmake f --toolchain=clang
|
||||
xmake
|
||||
|
||||
# After building, the binary is in the build/ directory
|
||||
```
|
||||
|
||||
### Run Your First Fig Program
|
||||
Create a file `hello.fig`:
|
||||
```fig
|
||||
import std.io; // io module must be imported; println and others reside here
|
||||
println("Hello, Fig!");
|
||||
```
|
||||
Run it:
|
||||
```bash
|
||||
./build/fig hello.fig
|
||||
```
|
||||
Or if you added `fig` to your PATH:
|
||||
```bash
|
||||
fig hello.fig
|
||||
```
|
||||
|
||||
## 📊 Current Status & Roadmap
|
||||
|
||||
| Status | Module | Description |
|
||||
|--------|----------------------------|-------------|
|
||||
| ✅ | Frontend | Lexer, parser, AST, semantic analysis |
|
||||
| ✅ | Core semantics | Variables, functions, closures, structs, interfaces, type system |
|
||||
| ✅ | Built-in types | 13 built-in types (Any, Null, Int, String, Bool, Double, Function, StructType, StructInstance, List, Map, Module, InterfaceType) |
|
||||
| 🚧 | Register VM | Original tree-walk interpreter deprecated; VM in development |
|
||||
| 🚧 | Garbage Collection | Basic GC implemented, optimizing |
|
||||
| 📝 | Standard Library | IO preliminarily available; more libraries to come |
|
||||
| 📝 | FFI | Foreign function interface (C ABI) |
|
||||
| 📝 | LSP | Language Server Protocol support |
|
||||
|
||||
## 📚 Documentation & Community
|
||||
|
||||
- **Documentation**: Check the [`/docs`](./docs) directory (continuously updated)
|
||||
- **Example Code**: [`/ExampleCodes`](./ExampleCodes) contains various usage demonstrations
|
||||
- **Contributing**: Issues and PRs are welcome at the main repository
|
||||
- Report bugs
|
||||
- Discuss features
|
||||
- Improve documentation
|
||||
- **License**: MIT © PuqiAR
|
||||
- **Author**: PuqiAR · [im@puqiar.top](mailto:im@puqiar.top)
|
||||
- **Links**:
|
||||
- [Main Repository](https://git.fig-lang.cn/PuqiAR/Fig)
|
||||
- [GitHub Mirror](https://github.com/PuqiAR/Fig)
|
||||
|
||||
---
|
||||
|
||||
**Fig** is evolving rapidly. If you're intrigued, give it a try or join us in shaping its future!
|
||||
195
README_zh-CN.md
Normal file
195
README_zh-CN.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Fig
|
||||
|
||||
## tmd赶工代码质量太差了暑假我要重构
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||
</picture>
|
||||
|
||||
> **🔔 主仓库:https://git.fig-lang.cn/PuqiAR/Fig**
|
||||
> *GitHub 仅为镜像,Issue 与 PR 请提交至主仓库*
|
||||
|
||||
[English](./README.md) | [中文](./README_zh-CN.md)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
-~28ms%20(i5--13490f)-brightgreen)
|
||||
|
||||
**Fig** 是一门动态类型与静态类型注解混合的编程语言,采用基于引用的值语义。它既保留了脚本语言的灵活性,又提供了可选的类型约束,让代码更健壮。
|
||||
|
||||
> ⚠️ **Fig 当前为 0.5.0-alpha 版本,语法和 API 仍可能发生变动。** 欢迎试用和反馈,但请勿用于生产环境。
|
||||
|
||||
```fig
|
||||
// 试试 Fig 的感觉
|
||||
var flexible = 10 // 动态类型,可以是任意值
|
||||
flexible = "hello" // 没问题
|
||||
|
||||
var fixed := 20 // 固定为 Int 类型
|
||||
// fixed = 3.14 // 错误!类型不匹配
|
||||
|
||||
func greet(name) => "Hello, " + name + "!"
|
||||
println(greet("Fig")) // 输出: Hello, Fig!
|
||||
```
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
### 🎭 动态与静态的融合
|
||||
变量默认是动态的(`Any` 类型),但你可以随时用 `:=` 或 `: Type` 锁定类型,享受静态检查的安全感。
|
||||
|
||||
```fig
|
||||
var any; // 类型为 Any
|
||||
any = 42; // 现在指向 Int
|
||||
any = [1,2,3]; // 现在指向 List
|
||||
|
||||
var safe := 100; // 推断为 Int,之后只能赋 Int 值
|
||||
safe = 200; // ✅
|
||||
// safe = "oops" // ❌ 编译错误
|
||||
```
|
||||
|
||||
### 🔗 基于引用的值语义
|
||||
所有对象都是不可变的,变量只是指向对象的“名字”。多个变量可以共享同一个对象,修改操作会创建新对象,但复杂类型(如列表)的修改会反映在所有引用上——这正是引用的含义。
|
||||
|
||||
```fig
|
||||
var a := [1, 2, 3];
|
||||
var b := a; // b 和 a 指向同一个列表
|
||||
a[0] = 99; // 修改列表内容
|
||||
println(b) // 输出: [99, 2, 3] —— 因为 b 也看到了变化
|
||||
|
||||
// 需要真正的副本?用深拷贝 (语法可能变动)
|
||||
var c := new List{a}; // 深拷贝 a
|
||||
c[0] = 0;
|
||||
println(a) // 仍是 [99, 2, 3]
|
||||
```
|
||||
|
||||
### 🏗 面向对象特性 (OOP)
|
||||
Fig 提供基于结构体的轻量面向对象支持,通过接口实现多态。
|
||||
|
||||
- **结构体即类**:用 `struct` 定义,可包含字段和方法
|
||||
- **访问控制**:`public` 关键字,默认为私有
|
||||
- **简洁构造**:
|
||||
```fig
|
||||
var p1 := new Point{1, 2}; // 位置参数
|
||||
var p2 := new Point{x: 2, y: 3}; // 命名参数
|
||||
var x := 5, y := 10;
|
||||
var p3 := new Point{y, x}; // 变量名简写,自动匹配字段
|
||||
```
|
||||
- **接口与实现**:
|
||||
```fig
|
||||
interface Drawable { draw() -> String; }
|
||||
struct Circle { radius: Double }
|
||||
impl Drawable for Circle {
|
||||
draw() => "⚪ 半径: " + radius;
|
||||
}
|
||||
```
|
||||
- **无继承,用接口实现多态**(当前为隐式 `this`,未来可能改为显式 `self.xxx`)
|
||||
|
||||
### 🧩 函数式特性
|
||||
函数是一等公民,支持闭包和简洁的箭头语法。
|
||||
|
||||
```fig
|
||||
func multiplier(factor) {
|
||||
return func (n) => n * factor; // 闭包(upvalue)
|
||||
}
|
||||
|
||||
var double = multiplier(2);
|
||||
println(double(5)) // 输出: 10
|
||||
```
|
||||
|
||||
### ⚡ 高性能实现
|
||||
**递归 fib(30) 仅需 28ms** (i5-13490f, Windows 11, DDR4 2667, clang 21.1.8, libc++, C++23) —— 在动态语言中表现优异。
|
||||
|
||||
Fig 通过一系列底层优化实现高性能:
|
||||
- **寄存器虚拟机**:替代树遍历解释器,执行效率大幅提升。
|
||||
- **FastCall 优化**:全局普通函数直接调用原型(proto),避免运行时解包开销(传统调用需检查是否为函数对象并解包)。
|
||||
- **Window Slicing**(源自 Lua):栈滑动窗口技术,高效管理函数调用和闭包(upvalue),减少内存分配。
|
||||
- **Upvalue 机制**:轻量闭包实现,让函数式编程无性能负担。
|
||||
- **Computed Goto**:利用 GCC/Clang 扩展,加速字节码分发。
|
||||
- **NaN-Boxing**:高效存储和类型判断,提升动态类型性能。
|
||||
|
||||
#### 性能对比
|
||||
以下为递归计算 fib(30) 的粗略对比(不同环境可能存在差异,仅供参考):
|
||||
|
||||
| 语言类型 | 语言 | 时间 (ms) | 备注 |
|
||||
|--------|------|----------|------|
|
||||
| AOT 编译 | C (clang -O2) | ~0.05 | 纳秒级,作为速度参照 |
|
||||
| AOT 编译 | Rust (--release) | ~0.06 | 同上 |
|
||||
| **动态语言** | **Fig** | **~28** | **寄存器 VM + 上述优化** |
|
||||
| 动态语言 | Lua 5.4 | ~35 | 经典的动态语言 |
|
||||
| 动态语言 | Python 3.11 | ~450 | CPython |
|
||||
| 动态语言 | Node.js 20 | ~60 | V8 引擎 |
|
||||
| 动态语言 | Ruby 3.2 | ~800 | CRuby |
|
||||
|
||||
*Fig 在动态语言阵营中表现出色,接近 Lua 和 Node.js 的水平,远超 Python 和 Ruby。*
|
||||
|
||||
## 🚀 快速上手
|
||||
|
||||
### 安装
|
||||
|
||||
#### 方法一:下载预编译二进制
|
||||
从 [主仓库 Releases](https://git.fig-lang.cn/PuqiAR/Fig/releases) 或 [GitHub 镜像](https://github.com/PuqiAR/Fig/releases) 下载对应平台的二进制,解压后加入 PATH。
|
||||
|
||||
#### 方法二:使用 xmake 从源码编译
|
||||
```bash
|
||||
# 克隆主仓库(GitHub 为镜像)
|
||||
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
|
||||
cd Fig
|
||||
|
||||
# 安装 xmake(如未安装):https://xmake.io
|
||||
|
||||
# 编译(必须用 clang,因为 computed goto 需要编译器支持)
|
||||
xmake f --toolchain=clang
|
||||
xmake
|
||||
|
||||
# 编译完成后,二进制位于 build/ 目录下
|
||||
```
|
||||
|
||||
### 运行第一个 Fig 程序
|
||||
创建文件 `hello.fig`:
|
||||
```fig
|
||||
import std.io; // io 模块必须导入,println 等函数位于其中
|
||||
println("Hello, Fig!");
|
||||
```
|
||||
运行:
|
||||
```bash
|
||||
./build/fig hello.fig
|
||||
```
|
||||
或已加入 PATH:
|
||||
```bash
|
||||
fig hello.fig
|
||||
```
|
||||
|
||||
## 📊 当前状态与路线图
|
||||
|
||||
| 状态 | 模块 | 说明 |
|
||||
|------|------|------|
|
||||
| ✅ | 前端 | 词法/语法分析、AST、语义分析 |
|
||||
| ✅ | 核心语义 | 变量、函数、闭包、结构体、接口、类型系统 |
|
||||
| ✅ | 内置类型 | 13 种内置类型(Any, Null, Int, String, Bool, Double, Function, StructType, StructInstance, List, Map, Module, InterfaceType) |
|
||||
| 🚧 | 寄存器虚拟机 | 原树遍历解释器已废弃,VM 开发中 |
|
||||
| 🚧 | 垃圾回收 | 基础 GC 已实现,正在优化 |
|
||||
| 📝 | 标准库 | IO 已初步可用,更多库待完善 |
|
||||
| 📝 | FFI | 外部函数接口(C ABI) |
|
||||
| 📝 | LSP | 语言服务器协议支持 |
|
||||
|
||||
## 📚 文档与社区
|
||||
|
||||
- **文档**:查看 [`/docs`](./docs) 目录(持续更新)
|
||||
- **示例代码**:[`/ExampleCodes`](./ExampleCodes) 包含各种用法演示
|
||||
- **贡献**:欢迎提交 Issue 或 PR(主仓库)
|
||||
- 报告 bug
|
||||
- 讨论特性
|
||||
- 改进文档
|
||||
- **许可证**:MIT © PuqiAR
|
||||
- **作者**:PuqiAR · [im@puqiar.top](mailto:im@puqiar.top)
|
||||
- **相关链接**:
|
||||
- [主仓库](https://git.fig-lang.cn/PuqiAR/Fig)
|
||||
- [GitHub 镜像](https://github.com/PuqiAR/Fig)
|
||||
|
||||
---
|
||||
|
||||
**Fig** 还在快速迭代中,如果你对它感兴趣,不妨试一试,或者加入我们一起塑造它的未来!
|
||||
@@ -11,13 +11,23 @@
|
||||
#include <Ast/Expr/IdentiExpr.hpp>
|
||||
#include <Ast/Expr/IndexExpr.hpp>
|
||||
#include <Ast/Expr/InfixExpr.hpp>
|
||||
#include <Ast/Expr/LambdaExpr.hpp>
|
||||
#include <Ast/Expr/LiteralExpr.hpp>
|
||||
#include <Ast/Expr/MemberExpr.hpp>
|
||||
#include <Ast/Expr/NewExpr.hpp>
|
||||
#include <Ast/Expr/PrefixExpr.hpp>
|
||||
#include <Ast/Expr/TernaryExpr.hpp>
|
||||
#include <Ast/Expr/PostfixExpr.hpp>
|
||||
|
||||
#include <Ast/Stmt/ControlFlowStmts.hpp>
|
||||
#include <Ast/Stmt/ExprStmt.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
#include <Ast/Stmt/IfStmt.hpp>
|
||||
#include <Ast/Stmt/ImplStmt.hpp>
|
||||
#include <Ast/Stmt/InterfaceDefStmt.hpp>
|
||||
#include <Ast/Stmt/StructDefStmt.hpp>
|
||||
#include <Ast/Stmt/ForStmt.hpp>
|
||||
#include <Ast/Stmt/ImportStmt.hpp>
|
||||
#include <Ast/Stmt/VarDecl.hpp>
|
||||
#include <Ast/Stmt/WhileStmt.hpp>
|
||||
#include <Ast/TypeExpr.hpp>
|
||||
159
src/Ast/Base.hpp
159
src/Ast/Base.hpp
@@ -2,52 +2,63 @@
|
||||
@file src/Ast/Base.hpp
|
||||
@brief AstNode基类定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-14
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Core/SourceLocations.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
enum class AstType : std::uint8_t
|
||||
{
|
||||
AstNode, // 基类
|
||||
Program, // 程序
|
||||
Expr, // 表达式
|
||||
Stmt, // 语句
|
||||
BlockStmt, // 块语句
|
||||
AstNode,
|
||||
Program,
|
||||
Expr,
|
||||
Stmt,
|
||||
BlockStmt,
|
||||
|
||||
/* Expressions */
|
||||
IdentiExpr, // 标识符表达式
|
||||
LiteralExpr, // 字面量表达式
|
||||
PrefixExpr, // 一元 前缀表达式
|
||||
InfixExpr, // 二元 中缀表达式
|
||||
|
||||
IndexExpr, // 后缀表达式,索引
|
||||
CallExpr, // 后缀表达式,函数调用
|
||||
IdentiExpr,
|
||||
LiteralExpr,
|
||||
PrefixExpr,
|
||||
InfixExpr,
|
||||
IndexExpr,
|
||||
CallExpr,
|
||||
MemberExpr, // obj.prop
|
||||
NewExpr, // new Point{}
|
||||
LambdaExpr,
|
||||
TernaryExpr, // cond ? then : else
|
||||
PostfixExpr, // expr++ / expr--
|
||||
|
||||
/* Statements */
|
||||
ExprStmt, // 表达式语句,如 println(1)
|
||||
VarDecl, // 变量声明
|
||||
IfStmt, // If语句
|
||||
ElseIfStmt, // ElseIf语句,不准悬空,平铺式else if
|
||||
WhileStmt, // while语句
|
||||
FnDefStmt, // func函数定义语句
|
||||
ReturnStmt, // 返回语句
|
||||
BreakStmt, // break语句
|
||||
ContinueStmt, // continue语句
|
||||
ExprStmt,
|
||||
VarDecl,
|
||||
IfStmt,
|
||||
ElseIfStmt,
|
||||
WhileStmt,
|
||||
FnDefStmt,
|
||||
StructDefStmt,
|
||||
InterfaceDefStmt,
|
||||
ImplStmt, // impl Document for File {}
|
||||
ReturnStmt,
|
||||
BreakStmt,
|
||||
ContinueStmt,
|
||||
ForStmt, // for loop
|
||||
ImportStmt, // import
|
||||
|
||||
/* Type Expressions */
|
||||
TypeExpr, // 基类
|
||||
NamedTypeExpr, // 命名类型,支持namespace, std.file这样的
|
||||
// 泛型等...
|
||||
TypeExpr,
|
||||
NamedTypeExpr, // 废弃,用 IdentiExpr/MemberExpr/ApplyExpr 替代
|
||||
NullableTypeExpr, // 废弃,用 NullableExpr 替代
|
||||
FnTypeExpr,
|
||||
ApplyExpr, // 泛型实例化: List<Int>
|
||||
NullableExpr, // 可空后缀: Int?
|
||||
};
|
||||
|
||||
struct AstNode
|
||||
{
|
||||
AstType type = AstType::AstNode;
|
||||
@@ -57,22 +68,10 @@ namespace Fig
|
||||
virtual ~AstNode() {};
|
||||
};
|
||||
|
||||
struct TypeExpr : public AstNode
|
||||
{
|
||||
TypeExpr()
|
||||
{
|
||||
type = AstType::TypeExpr;
|
||||
}
|
||||
virtual ~TypeExpr() = default;
|
||||
};
|
||||
|
||||
struct Program;
|
||||
|
||||
struct Expr : public AstNode
|
||||
{
|
||||
TypeInfo *resolvedType = nullptr;
|
||||
// TODO: 可选的常量折叠
|
||||
// 拓展 isConstExpr和 constValue槽位
|
||||
// 语义分析后填充
|
||||
Type resolvedType;
|
||||
|
||||
Expr()
|
||||
{
|
||||
@@ -82,7 +81,7 @@ namespace Fig
|
||||
|
||||
struct Stmt : public AstNode
|
||||
{
|
||||
bool isPublic;
|
||||
bool isPublic = false;
|
||||
Stmt()
|
||||
{
|
||||
type = AstType::Stmt;
|
||||
@@ -92,22 +91,10 @@ namespace Fig
|
||||
struct Program final : public AstNode
|
||||
{
|
||||
DynArray<Stmt *> nodes;
|
||||
|
||||
Program()
|
||||
{
|
||||
type = AstType::Program;
|
||||
}
|
||||
|
||||
Program(DynArray<Stmt *> _nodes)
|
||||
{
|
||||
type = AstType::Program;
|
||||
nodes = std::move(_nodes);
|
||||
if (!_nodes.empty())
|
||||
{
|
||||
location = std::move(_nodes.back()->location);
|
||||
}
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return "<Program>";
|
||||
@@ -121,36 +108,60 @@ namespace Fig
|
||||
{
|
||||
type = AstType::BlockStmt;
|
||||
}
|
||||
BlockStmt(DynArray<Stmt *> _nodes)
|
||||
{
|
||||
type = AstType::BlockStmt;
|
||||
nodes = std::move(_nodes);
|
||||
if (!_nodes.empty())
|
||||
{
|
||||
location = std::move(_nodes.back()->location);
|
||||
}
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
return "<BlockStmt>";
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
|
||||
namespace std
|
||||
// --- Type Expressions (inherit Expr — 类型即值) ---
|
||||
|
||||
struct TypeExpr : public Expr
|
||||
{
|
||||
template <>
|
||||
struct std::formatter<Fig::AstNode *, char>
|
||||
TypeExpr() { type = AstType::TypeExpr; }
|
||||
virtual ~TypeExpr() = default;
|
||||
};
|
||||
|
||||
// ApplyExpr: 泛型实例化,List<Int> → ApplyExpr(base, [Int])
|
||||
struct ApplyExpr final : public Expr
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context &ctx)
|
||||
Expr *base; // 基础类型表达式
|
||||
DynArray<Expr *> args; // 泛型参数
|
||||
|
||||
ApplyExpr() { type = AstType::ApplyExpr; }
|
||||
ApplyExpr(Expr *_base, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||
base(_base), args(std::move(_args))
|
||||
{
|
||||
return ctx.begin();
|
||||
type = AstType::ApplyExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Fig::AstNode *_node, FormatContext &ctx) const
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format_to(ctx.out(), "{}", _node->toString().toStdString());
|
||||
String s = base->toString() + "<";
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
if (i) s += ", ";
|
||||
s += args[i]->toString();
|
||||
}
|
||||
s += ">";
|
||||
return s;
|
||||
}
|
||||
};
|
||||
}; // namespace std
|
||||
|
||||
// NullableExpr: 可空后缀 Int? → NullableExpr(Int)
|
||||
struct NullableExpr final : public Expr
|
||||
{
|
||||
Expr *inner;
|
||||
|
||||
NullableExpr() { type = AstType::NullableExpr; }
|
||||
NullableExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||
{
|
||||
type = AstType::NullableExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
return inner->toString() + "?";
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
@@ -47,9 +47,10 @@ namespace Fig
|
||||
type = AstType::CallExpr;
|
||||
}
|
||||
|
||||
CallExpr(Expr *_callee, FnCallArgs _args) : callee(_callee), args(std::move(_args))
|
||||
CallExpr(Expr *_callee, FnCallArgs _args, SourceLocation _location) : callee(_callee), args(std::move(_args))
|
||||
{
|
||||
type = AstType::CallExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
|
||||
@@ -1,48 +1,32 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/IdentiExpr.hpp
|
||||
@brief IdentiExpr定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-14
|
||||
@brief 标识符表达式定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Sema/Environment.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct IdentiExpr final : Expr
|
||||
struct IdentiExpr final : public Expr
|
||||
{
|
||||
String name;
|
||||
|
||||
// Analyzer槽位
|
||||
|
||||
// 寻址空间
|
||||
bool isGlobal = false; // 是否全局, 全局变量存哈希/堆
|
||||
|
||||
// 仅 isGlobal = false 有效
|
||||
int resolvedDepth = -1; // 用于获取闭包上值, -1 代表未解析
|
||||
|
||||
// 栈内槽位
|
||||
int localId = -1; // 局部变量id, -1 未解析
|
||||
|
||||
Symbol *resolvedSymbol = nullptr; // 语义分析后填充,Compiler 唯一的依赖
|
||||
|
||||
IdentiExpr()
|
||||
{
|
||||
type = AstType::IdentiExpr;
|
||||
}
|
||||
|
||||
IdentiExpr(String _name, SourceLocation _loc)
|
||||
IdentiExpr(String _name, SourceLocation _location) : name(std::move(_name))
|
||||
{
|
||||
type = AstType::IdentiExpr;
|
||||
name = std::move(_name);
|
||||
location = std::move(_loc);
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<IdentiExpr: {}>", name);
|
||||
return std::format("<IdentiExpr '{}'>", name);
|
||||
}
|
||||
};
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
70
src/Ast/Expr/LambdaExpr.hpp
Normal file
70
src/Ast/Expr/LambdaExpr.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/LambdaExpr.hpp
|
||||
@brief Lambda表达式定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct LambdaExpr final : public Expr
|
||||
{
|
||||
// func (params) [-> return type] ([=> expr] / [ {stmt} ])
|
||||
|
||||
DynArray<Param *> params;
|
||||
Expr *returnType;
|
||||
AstNode *body; // expr/blockstmt
|
||||
bool isExprBody;
|
||||
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
|
||||
LambdaExpr()
|
||||
{
|
||||
type = AstType::LambdaExpr;
|
||||
}
|
||||
|
||||
LambdaExpr(
|
||||
DynArray<Param *> _params,
|
||||
Expr *_returnType,
|
||||
AstNode *_body,
|
||||
bool _isExprBody,
|
||||
SourceLocation _location) :
|
||||
params(std::move(_params)),
|
||||
returnType(_returnType),
|
||||
body(_body),
|
||||
isExprBody(_isExprBody)
|
||||
{
|
||||
type = AstType::LambdaExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String specifying = "<LambdaExpr 'func (";
|
||||
for (auto &p : params)
|
||||
{
|
||||
if (p != params.front())
|
||||
{
|
||||
specifying += ", ";
|
||||
}
|
||||
specifying += p->toString();
|
||||
}
|
||||
if (isExprBody)
|
||||
{
|
||||
specifying += ") => ";
|
||||
specifying += body->toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
specifying += ") {";
|
||||
specifying += body->toString();
|
||||
specifying.push_back(U'}');
|
||||
}
|
||||
specifying += "'>";
|
||||
return specifying;
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -1,36 +1,27 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/LiteralExpr.hpp
|
||||
@brief 字面量表达式定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Token/Token.hpp>
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct LiteralExpr final : Expr
|
||||
struct LiteralExpr final : public Expr
|
||||
{
|
||||
Token token;
|
||||
Token literal;
|
||||
|
||||
LiteralExpr()
|
||||
{
|
||||
type = AstType::LiteralExpr;
|
||||
}
|
||||
LiteralExpr(const Token& token, SourceLocation _location) : token(token)
|
||||
LiteralExpr() { type = AstType::LiteralExpr; }
|
||||
LiteralExpr(const Token &_literal, SourceLocation _location) : literal(_literal)
|
||||
{
|
||||
type = AstType::LiteralExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<LiteralExpr: {}>", magic_enum::enum_name(token.type));
|
||||
virtual String toString() const override {
|
||||
return std::format("<LiteralExpr: {}>", magic_enum::enum_name(literal.type));
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
}
|
||||
|
||||
35
src/Ast/Expr/MemberExpr.hpp
Normal file
35
src/Ast/Expr/MemberExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/MemberExpr.hpp
|
||||
@brief 成员访问表达式定义:obj.member
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct MemberExpr final : public Expr
|
||||
{
|
||||
Expr *target; // 访问对象
|
||||
String name; // 成员名字
|
||||
|
||||
MemberExpr()
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
}
|
||||
|
||||
MemberExpr(Expr *_t, String _n, SourceLocation _loc) : target(_t), name(std::move(_n))
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<MemberExpr {}.{}>", target->toString(), name);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
50
src/Ast/Expr/NewExpr.hpp
Normal file
50
src/Ast/Expr/NewExpr.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/ObjectInitExpr.hpp
|
||||
@brief 对象初始化表达式 AST 定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct NewExpr final : public Expr
|
||||
{
|
||||
struct Arg
|
||||
{
|
||||
String name;
|
||||
Expr *value;
|
||||
};
|
||||
Expr *typeExpr;
|
||||
DynArray<Arg> args;
|
||||
|
||||
NewExpr()
|
||||
{
|
||||
type = AstType::NewExpr;
|
||||
}
|
||||
NewExpr(Expr *_te, DynArray<Arg> _args, SourceLocation _loc) :
|
||||
typeExpr(_te), args(std::move(_args))
|
||||
{
|
||||
type = AstType::NewExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String res = "<NewExpr 'new " + typeExpr->toString() + "{";
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
if (!args[i].name.empty())
|
||||
res += args[i].name + ": ";
|
||||
res += args[i].value->toString();
|
||||
if (i < args.size() - 1)
|
||||
res += ", ";
|
||||
}
|
||||
res += "}'>";
|
||||
return res;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
35
src/Ast/Expr/PostfixExpr.hpp
Normal file
35
src/Ast/Expr/PostfixExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/PostfixExpr.hpp
|
||||
@brief expr++ / expr--
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Operator.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct PostfixExpr final : public Expr
|
||||
{
|
||||
UnaryOperator op;
|
||||
Expr *operand;
|
||||
|
||||
PostfixExpr() { type = AstType::PostfixExpr; }
|
||||
|
||||
PostfixExpr(UnaryOperator _op, Expr *_operand) :
|
||||
op(_op), operand(_operand)
|
||||
{
|
||||
type = AstType::PostfixExpr;
|
||||
location = _operand->location;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<PostfixExpr: {} '{}'>",
|
||||
operand->toString(), magic_enum::enum_name(op));
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
35
src/Ast/Expr/TernaryExpr.hpp
Normal file
35
src/Ast/Expr/TernaryExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/TernaryExpr.hpp
|
||||
@brief cond ? then : else
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct TernaryExpr final : public Expr
|
||||
{
|
||||
Expr *cond;
|
||||
Expr *thenExpr;
|
||||
Expr *elseExpr;
|
||||
|
||||
TernaryExpr() { type = AstType::TernaryExpr; }
|
||||
|
||||
TernaryExpr(Expr *_cond, Expr *_then, Expr *_else, SourceLocation _loc) :
|
||||
cond(_cond), thenExpr(_then), elseExpr(_else)
|
||||
{
|
||||
type = AstType::TernaryExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<TernaryExpr: {} ? {} : {}>",
|
||||
cond->toString(), thenExpr->toString(), elseExpr->toString());
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -16,6 +16,8 @@ namespace Fig
|
||||
{TokenType::Minus, UnaryOperator::Negate},
|
||||
{TokenType::Not, UnaryOperator::Not},
|
||||
{TokenType::Ampersand, UnaryOperator::AddressOf},
|
||||
{TokenType::DoublePlus, UnaryOperator::Increment},
|
||||
{TokenType::DoubleMinus, UnaryOperator::Decrement},
|
||||
};
|
||||
return unaryOpMap;
|
||||
}
|
||||
@@ -40,6 +42,8 @@ namespace Fig
|
||||
|
||||
{TokenType::And, BinaryOperator::LogicalAnd},
|
||||
{TokenType::Or, BinaryOperator::LogicalOr},
|
||||
{TokenType::DoubleAmpersand, BinaryOperator::LogicalAnd},
|
||||
{TokenType::DoublePipe, BinaryOperator::LogicalOr},
|
||||
|
||||
{TokenType::Power, BinaryOperator::Power},
|
||||
|
||||
@@ -49,6 +53,7 @@ namespace Fig
|
||||
{TokenType::AsteriskEqual, BinaryOperator::MultiplyAssign},
|
||||
{TokenType::SlashEqual, BinaryOperator::DivideAssign},
|
||||
{TokenType::PercentEqual, BinaryOperator::ModuloAssign},
|
||||
{TokenType::Caret, BinaryOperator::BitXor},
|
||||
{TokenType::CaretEqual, BinaryOperator::BitXorAssign},
|
||||
|
||||
{TokenType::Pipe, BinaryOperator::BitOr},
|
||||
@@ -56,7 +61,7 @@ namespace Fig
|
||||
{TokenType::ShiftLeft, BinaryOperator::ShiftLeft},
|
||||
{TokenType::ShiftRight, BinaryOperator::ShiftRight},
|
||||
|
||||
{TokenType::Dot, BinaryOperator::MemberAccess},
|
||||
{TokenType::As, BinaryOperator::As},
|
||||
};
|
||||
return binaryOpMap;
|
||||
}
|
||||
@@ -78,6 +83,8 @@ namespace Fig
|
||||
{UnaryOperator::Negate, 20001},
|
||||
{UnaryOperator::Not, 20001},
|
||||
{UnaryOperator::AddressOf, 20001},
|
||||
{UnaryOperator::Increment, 20001},
|
||||
{UnaryOperator::Decrement, 20001},
|
||||
};
|
||||
return unbpm;
|
||||
}
|
||||
@@ -109,6 +116,7 @@ namespace Fig
|
||||
{BinaryOperator::GreaterEqual, 2100},
|
||||
|
||||
{BinaryOperator::Is, 2100},
|
||||
{BinaryOperator::As, 2100},
|
||||
|
||||
{BinaryOperator::ShiftLeft, 3000},
|
||||
{BinaryOperator::ShiftRight, 3000},
|
||||
@@ -117,10 +125,9 @@ namespace Fig
|
||||
{BinaryOperator::Subtract, 4000},
|
||||
{BinaryOperator::Multiply, 4500},
|
||||
{BinaryOperator::Divide, 4500},
|
||||
{BinaryOperator::Modulo, 4500},
|
||||
|
||||
{BinaryOperator::Power, 5000},
|
||||
|
||||
{BinaryOperator::MemberAccess, 40001},
|
||||
};
|
||||
return bnbpm;
|
||||
}
|
||||
@@ -153,6 +160,8 @@ namespace Fig
|
||||
case BinaryOperator::BitXorAssign:
|
||||
case BinaryOperator::Power: return GetBinaryOpLBp(op) - 1;
|
||||
|
||||
case BinaryOperator::As: return GetBinaryOpLBp(op) + 1;
|
||||
|
||||
default:
|
||||
/*
|
||||
左结合, 左绑定力 < 右
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Fig
|
||||
Negate, // 取反 -
|
||||
Not, // 逻辑非 ! / not
|
||||
AddressOf, // 取引用 &
|
||||
Increment, // ++
|
||||
Decrement, // --
|
||||
|
||||
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
|
||||
};
|
||||
@@ -60,6 +62,8 @@ namespace Fig
|
||||
ShiftLeft, // 左移
|
||||
ShiftRight, // 右移
|
||||
|
||||
As, // as
|
||||
|
||||
// 成员访问
|
||||
MemberAccess, // .
|
||||
|
||||
|
||||
@@ -1,102 +1,51 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/FnDefStmt.hpp
|
||||
@brief FnDefStmt定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-25
|
||||
@brief 函数定义 AST 节点
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Sema/Environment.hpp>
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct Param
|
||||
{
|
||||
struct Param : public AstNode {
|
||||
String name;
|
||||
SourceLocation location;
|
||||
|
||||
TypeInfo *resolvedType = nullptr;
|
||||
int localId = -1;
|
||||
|
||||
virtual String toString() const = 0;
|
||||
};
|
||||
|
||||
struct PosParam final : public Param
|
||||
{
|
||||
TypeExpr *type;
|
||||
Expr *typeSpecifier;
|
||||
Expr *defaultValue;
|
||||
|
||||
PosParam() {}
|
||||
PosParam(String _name, TypeExpr *_type, Expr *_defaultValue, SourceLocation _location) :
|
||||
type(_type), defaultValue(_defaultValue)
|
||||
{
|
||||
name = std::move(_name);
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<Pos {}: {}{}>",
|
||||
name,
|
||||
(type ? type->toString() : "Any"),
|
||||
(defaultValue ? " =" + defaultValue->toString() : ""));
|
||||
}
|
||||
Type resolvedType;
|
||||
Param() { type = AstType::AstNode; }
|
||||
virtual ~Param() = default;
|
||||
};
|
||||
|
||||
/*
|
||||
(public) func foo([name: (type) (= default value)]) (-> return type)
|
||||
{
|
||||
...
|
||||
struct PosParam final : public Param {
|
||||
PosParam(String _n, Expr *_ts, Expr *_dv, SourceLocation _loc) {
|
||||
name = std::move(_n); typeSpecifier = _ts; defaultValue = _dv; location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override { return name; }
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
struct FnDefStmt final : public Stmt
|
||||
{
|
||||
struct FnDefStmt final : public Stmt {
|
||||
String name;
|
||||
DynArray<Param *> params;
|
||||
TypeExpr *returnType;
|
||||
Expr *returnTypeSpecifier;
|
||||
BlockStmt *body;
|
||||
Type resolvedReturnType;
|
||||
Symbol *resolvedSymbol = nullptr; // 连接物理符号
|
||||
|
||||
TypeInfo *resolvedReturnType = nullptr;
|
||||
int localId = -1;
|
||||
int protoIndex = -1; // 在CompiledModule扁平化protos的下标
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
|
||||
FnDefStmt()
|
||||
FnDefStmt() { type = AstType::FnDefStmt; }
|
||||
FnDefStmt(bool _p, String _n, DynArray<Param *> _pa, Expr *_rt, BlockStmt *_b, SourceLocation _loc)
|
||||
: name(std::move(_n)), params(std::move(_pa)), returnTypeSpecifier(_rt), body(_b)
|
||||
{
|
||||
type = AstType::FnDefStmt;
|
||||
type = AstType::FnDefStmt; isPublic = _p; location = std::move(_loc);
|
||||
}
|
||||
|
||||
FnDefStmt(bool _isPublic,
|
||||
String _name,
|
||||
DynArray<Param *> _params,
|
||||
TypeExpr *_returnType,
|
||||
BlockStmt *_body,
|
||||
SourceLocation _location) :
|
||||
name(std::move(_name)), params(std::move(_params)), returnType(_returnType), body(_body)
|
||||
{
|
||||
type = AstType::FnDefStmt;
|
||||
isPublic = _isPublic;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String pStr;
|
||||
for (const Param *p : params)
|
||||
{
|
||||
if (p != *params.begin())
|
||||
{
|
||||
pStr += ", ";
|
||||
}
|
||||
pStr += p->toString();
|
||||
}
|
||||
return std::format("<FnDefStmt {}{}({}) -> {} {{{}}}>",
|
||||
(isPublic ? "public " : ""),
|
||||
name,
|
||||
pStr,
|
||||
(returnType ? returnType->toString() : "Any"),
|
||||
body->toString());
|
||||
virtual String toString() const override {
|
||||
return std::format("<FnDefStmt '{}'>", name);
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
}
|
||||
|
||||
32
src/Ast/Stmt/ForStmt.hpp
Normal file
32
src/Ast/Stmt/ForStmt.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ForStmt.hpp
|
||||
@brief for loop
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ForStmt final : public Stmt
|
||||
{
|
||||
Stmt *init;
|
||||
Expr *cond;
|
||||
Expr *step;
|
||||
BlockStmt *body;
|
||||
|
||||
ForStmt() { type = AstType::ForStmt; }
|
||||
|
||||
ForStmt(Stmt *_init, Expr *_cond, Expr *_step, BlockStmt *_body, SourceLocation _loc) :
|
||||
init(_init), cond(_cond), step(_step), body(_body)
|
||||
{
|
||||
type = AstType::ForStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override { return "<ForStmt>"; }
|
||||
};
|
||||
} // namespace Fig
|
||||
32
src/Ast/Stmt/ImplStmt.hpp
Normal file
32
src/Ast/Stmt/ImplStmt.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ImplStmt.hpp
|
||||
@brief 实现块 AST 节点
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ImplStmt final : public Stmt
|
||||
{
|
||||
Expr *interfaceType;
|
||||
Expr *structType;
|
||||
DynArray<FnDefStmt *> methods;
|
||||
|
||||
ImplStmt(Expr *_it, Expr *_st, DynArray<FnDefStmt *> _m, SourceLocation _loc) :
|
||||
interfaceType(_it), structType(_st), methods(std::move(_m))
|
||||
{
|
||||
type = AstType::ImplStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail =
|
||||
(interfaceType ? interfaceType->toString() + " for " : "") + structType->toString();
|
||||
return std::format("<ImplStmt '{}'>", detail);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
33
src/Ast/Stmt/ImportStmt.hpp
Normal file
33
src/Ast/Stmt/ImportStmt.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ImportStmt.hpp
|
||||
@brief import
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ImportStmt final : public Stmt
|
||||
{
|
||||
String path;
|
||||
bool isFileImport;
|
||||
|
||||
ImportStmt() { type = AstType::ImportStmt; }
|
||||
|
||||
ImportStmt(String _path, bool _isFileImport, SourceLocation _loc) :
|
||||
path(std::move(_path)), isFileImport(_isFileImport)
|
||||
{
|
||||
type = AstType::ImportStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<ImportStmt '{}'{}>", path, isFileImport ? " [file]" : "");
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
39
src/Ast/Stmt/InterfaceDefStmt.hpp
Normal file
39
src/Ast/Stmt/InterfaceDefStmt.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/InterfaceDefStmt.hpp
|
||||
@brief 接口定义 AST 节点
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct InterfaceDefStmt final : public Stmt
|
||||
{
|
||||
struct Method
|
||||
{
|
||||
String name;
|
||||
DynArray<Expr*> params;
|
||||
Expr *retType;
|
||||
SourceLocation location;
|
||||
};
|
||||
|
||||
bool isPublic;
|
||||
String name;
|
||||
DynArray<Method> methods;
|
||||
|
||||
InterfaceDefStmt() { type = AstType::InterfaceDefStmt; }
|
||||
|
||||
InterfaceDefStmt(bool _p, String _n, DynArray<Method> _m, SourceLocation _loc)
|
||||
: isPublic(_p), name(std::move(_n)), methods(std::move(_m))
|
||||
{
|
||||
type = AstType::InterfaceDefStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override { return "<InterfaceDefStmt>"; }
|
||||
};
|
||||
} // namespace Fig
|
||||
59
src/Ast/Stmt/StructDefStmt.hpp
Normal file
59
src/Ast/Stmt/StructDefStmt.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/StructDefStmt.hpp
|
||||
@brief 结构体定义 AST 节点
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct StructDefStmt final : public Stmt
|
||||
{
|
||||
struct Field
|
||||
{
|
||||
bool isPublic;
|
||||
bool typeInfer;
|
||||
|
||||
String name;
|
||||
Expr *type;
|
||||
Expr *initExpr;
|
||||
};
|
||||
bool isPublic;
|
||||
String name;
|
||||
DynArray<String> typeParameters;
|
||||
DynArray<Field> fields;
|
||||
DynArray<FnDefStmt *> methods;
|
||||
|
||||
StructDefStmt()
|
||||
{
|
||||
type = AstType::StructDefStmt;
|
||||
}
|
||||
StructDefStmt(bool _p,
|
||||
String _n,
|
||||
DynArray<String> _tp,
|
||||
DynArray<Field> _f,
|
||||
DynArray<FnDefStmt *> _m,
|
||||
SourceLocation _loc) :
|
||||
isPublic(_p),
|
||||
name(std::move(_n)),
|
||||
typeParameters(std::move(_tp)),
|
||||
fields(std::move(_f)),
|
||||
methods(std::move(_m))
|
||||
{
|
||||
type = AstType::StructDefStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail = name;
|
||||
if (!typeParameters.empty())
|
||||
{
|
||||
detail += "<...>";
|
||||
}
|
||||
return std::format("<StructDefStmt '{}'>", detail);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -15,7 +15,7 @@ namespace Fig
|
||||
struct VarDecl final : public Stmt
|
||||
{
|
||||
String name;
|
||||
TypeExpr *typeSpecifier;
|
||||
Expr *typeSpecifier;
|
||||
bool isInfer; // 是否用了 := 类型推断
|
||||
Expr *initExpr;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Fig
|
||||
type = AstType::VarDecl;
|
||||
}
|
||||
|
||||
VarDecl(bool _isPublic, String _name, TypeExpr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
|
||||
VarDecl(bool _isPublic, String _name, Expr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
|
||||
name(std::move(_name)),
|
||||
typeSpecifier(_typeSpecifier),
|
||||
isInfer(_isInfer),
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/*!
|
||||
@file src/Ast/TypeExpr.hpp
|
||||
@brief TypeExpr定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-25
|
||||
@brief 类型表达式 AST 定义:支持泛型与空安全
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -11,26 +9,92 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
struct NamedTypeExpr final : public TypeExpr
|
||||
{
|
||||
DynArray<String> path; // {"std", "file"} etc.
|
||||
DynArray<String> path;
|
||||
DynArray<Expr *> arguments;
|
||||
|
||||
NamedTypeExpr()
|
||||
{
|
||||
type = AstType::NamedTypeExpr;
|
||||
}
|
||||
|
||||
NamedTypeExpr(DynArray<String> _path, SourceLocation _location) :
|
||||
path(std::move(_path))
|
||||
NamedTypeExpr(DynArray<String> _p, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||
path(std::move(_p)), arguments(std::move(_args))
|
||||
{
|
||||
type = AstType::NamedTypeExpr;
|
||||
location = std::move(_location);
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<NamedTypeExpr '{}'>", path);
|
||||
String detail = "";
|
||||
for (size_t i = 0; i < path.size(); ++i)
|
||||
{
|
||||
detail += path[i];
|
||||
if (i < path.size() - 1)
|
||||
detail += ".";
|
||||
}
|
||||
if (!arguments.empty())
|
||||
{
|
||||
detail += "<";
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
detail += arguments[i]->toString();
|
||||
if (i < arguments.size() - 1)
|
||||
detail += ", ";
|
||||
}
|
||||
detail += ">";
|
||||
}
|
||||
return std::format("<NamedTypeExpr '{}'>", detail);
|
||||
}
|
||||
};
|
||||
|
||||
struct NullableTypeExpr final : public TypeExpr
|
||||
{
|
||||
Expr *inner;
|
||||
|
||||
NullableTypeExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||
{
|
||||
type = AstType::NullableTypeExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<NullableTypeExpr '{}?'>", inner->toString());
|
||||
}
|
||||
};
|
||||
|
||||
struct FnTypeExpr final : public TypeExpr
|
||||
{
|
||||
// func (paratypes...) -> return_type
|
||||
|
||||
DynArray<Expr *> paraTypes;
|
||||
Expr *returnType;
|
||||
|
||||
FnTypeExpr(DynArray<Expr *> _paraTypes, Expr *_returnType) :
|
||||
paraTypes(std::move(_paraTypes)), returnType(_returnType)
|
||||
{
|
||||
type = AstType::FnTypeExpr;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail = "<FnTypeExpr 'func (";
|
||||
|
||||
for (auto &pt : paraTypes)
|
||||
{
|
||||
if (pt != paraTypes.front())
|
||||
{
|
||||
detail += ", ";
|
||||
}
|
||||
detail += pt->toString();
|
||||
}
|
||||
detail += ") -> ";
|
||||
detail += returnType->toString();
|
||||
detail += "'>";
|
||||
|
||||
return detail;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
@@ -1,80 +1,111 @@
|
||||
/*!
|
||||
@file src/Bytecode/Bytecode.hpp
|
||||
@brief 字节码Bytecode定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-18
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Object/ObjectBase.hpp>
|
||||
#include <Core/SourceLocations.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
// 定长 32-bit
|
||||
using Instruction = std::uint32_t;
|
||||
|
||||
enum class OpCode : std::uint8_t
|
||||
{
|
||||
Exit, // 结束运行
|
||||
LoadK, // iABx 模式: R[A] = Constants[Bx]
|
||||
LoadTrue, // iABC: R[A] = true
|
||||
LoadFalse, // iABC: R[A] = false
|
||||
LoadNull, // iABC: R[A] = null
|
||||
Exit,
|
||||
Exit_MaxRecursionDepthExceeded,
|
||||
|
||||
FastCall, // iABC: A: ProtoIdx, B: 函数起始寄存器
|
||||
Call, // 动态派发 iABC: A: 函数体对象寄存器 B: 函数起始寄存器
|
||||
Return, // iABC 模式: 返回 R[A] 的值
|
||||
LoadK,
|
||||
LoadTrue,
|
||||
LoadFalse,
|
||||
LoadNull,
|
||||
|
||||
LoadFn, // 惰性装修, iABx: R[A] = new FunctionObject...
|
||||
FastCall,
|
||||
Call,
|
||||
Return,
|
||||
|
||||
Jmp, // iAsBx: ip += sBx 无条件跳转
|
||||
JmpIfFalse, // iAsBx: 如果 R[A] 为假, ip += sBx
|
||||
LoadFn,
|
||||
|
||||
Mov, // iABx: R[A] = R[Bx]
|
||||
Add, // iABC: R[A] = R[B] + R[C]
|
||||
Sub, // iABC: R[A] = R[B] - R[C]
|
||||
Mul, // iABC: R[A] = R[B] * R[C]
|
||||
Div, // iABC: R[A] = R[B] / R[C]
|
||||
Mod, // iABC: R[A] = R[B] % R[C]
|
||||
BitXor, // iABC: R[A] = R[B] ^ R[C]
|
||||
Jmp,
|
||||
JmpIfFalse,
|
||||
|
||||
Equal, // iABC: R[A] = R[B] == R[C]
|
||||
NotEqual, // iABC: R[A] = R[B] != R[C]
|
||||
Greater, // iABC: R[A] = R[B] > R[C]
|
||||
Less, // iABC: R[A] = R[B] < R[C]
|
||||
GreaterEqual, // iABC: R[A] = R[B] >= R[C]
|
||||
LessEqual, // iABC: R[A] = R[B] <= R[C]
|
||||
Mov,
|
||||
|
||||
Count, // 哨兵
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
BitXor,
|
||||
|
||||
IntFastAdd,
|
||||
IntFastSub,
|
||||
IntFastMul,
|
||||
IntFastDiv,
|
||||
|
||||
Equal,
|
||||
NotEqual,
|
||||
Greater,
|
||||
Less,
|
||||
GreaterEqual,
|
||||
LessEqual,
|
||||
|
||||
GetGlobal,
|
||||
SetGlobal,
|
||||
GetUpval,
|
||||
SetUpval,
|
||||
Copy,
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
namespace Op
|
||||
{
|
||||
// [OpCode: 8] [A: 8] [Bx: 16]
|
||||
[[nodiscard]] inline constexpr Instruction iABx(OpCode op, std::uint8_t a, std::uint16_t bx)
|
||||
{
|
||||
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
||||
| (static_cast<std::uint32_t>(bx) << 16);
|
||||
}
|
||||
|
||||
// [OpCode: 8] [A: 8] [B: 8] [C: 8]
|
||||
[[nodiscard]] inline constexpr Instruction iABC(
|
||||
OpCode op, std::uint8_t a, std::uint8_t b, std::uint8_t c)
|
||||
[[nodiscard]] inline constexpr Instruction iABC(OpCode op, std::uint8_t a, std::uint8_t b, std::uint8_t c)
|
||||
{
|
||||
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
||||
| (static_cast<std::uint32_t>(b) << 16) | (static_cast<std::uint32_t>(c) << 24);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
inline constexpr Instruction iAsBx(OpCode op, std::uint8_t a, std::int16_t sbx)
|
||||
[[nodiscard]] inline constexpr Instruction iAsBx(OpCode op, std::uint8_t a, std::int16_t sbx)
|
||||
{
|
||||
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
||||
| (static_cast<std::uint32_t>(static_cast<std::uint16_t>(sbx)) << 16);
|
||||
}
|
||||
} // namespace Op
|
||||
|
||||
struct UpvalueInfo
|
||||
{
|
||||
uint8_t index;
|
||||
bool isLocal;
|
||||
};
|
||||
|
||||
struct Proto
|
||||
{
|
||||
String name;
|
||||
DynArray<Instruction> code;
|
||||
DynArray<SourceLocation *> locations;
|
||||
DynArray<Value> constants;
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
uint8_t maxRegisters = 0;
|
||||
uint8_t numParams = 0;
|
||||
};
|
||||
|
||||
struct CompiledModule
|
||||
{
|
||||
DynArray<Proto *> protos;
|
||||
};
|
||||
|
||||
} // namespace Fig
|
||||
102
src/Bytecode/Disassembler.cpp
Normal file
102
src/Bytecode/Disassembler.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/*!
|
||||
@file src/Bytecode/Disassembler.cpp
|
||||
@brief 字节码反汇编器实现
|
||||
*/
|
||||
|
||||
#include <Bytecode/Disassembler.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <format>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
void Disassembler::DisassembleModule(const CompiledModule *module, std::ostream &stream)
|
||||
{
|
||||
if (!module) return;
|
||||
stream << "--- Module Disassembly ---" << std::endl;
|
||||
for (auto *proto : module->protos)
|
||||
{
|
||||
DisassembleProto(proto);
|
||||
}
|
||||
}
|
||||
|
||||
void Disassembler::DisassembleProto(const Proto *proto, std::ostream &stream)
|
||||
{
|
||||
if (!proto) return;
|
||||
|
||||
stream << std::format("\n--- Proto: {} (Regs: {}, Params: {}) ---\n",
|
||||
proto->name, proto->maxRegisters, proto->numParams);
|
||||
|
||||
for (size_t i = 0; i < proto->code.size(); ++i)
|
||||
{
|
||||
Instruction inst = proto->code[i];
|
||||
OpCode op = static_cast<OpCode>(inst & 0xFF);
|
||||
uint8_t a = (inst >> 8) & 0xFF;
|
||||
|
||||
stream << std::format("[{:04}] {:<12} ", i, magic_enum::enum_name(op));
|
||||
|
||||
Format fmt = GetFormat(op);
|
||||
if (fmt == Format::ABC)
|
||||
{
|
||||
uint8_t b = (inst >> 16) & 0xFF;
|
||||
uint8_t c = (inst >> 24) & 0xFF;
|
||||
stream << std::format("A:{:<3} B:{:<3} C:{:<3}", a, b, c);
|
||||
}
|
||||
else if (fmt == Format::ABx)
|
||||
{
|
||||
uint16_t bx = (inst >> 16) & 0xFFFF;
|
||||
stream << std::format("A:{:<3} Bx:{:<5}", a, bx);
|
||||
|
||||
// 自动关联常量池
|
||||
if (op == OpCode::LoadK && bx < proto->constants.size())
|
||||
{
|
||||
stream << std::format(" ; {}", proto->constants[bx].ToString());
|
||||
}
|
||||
}
|
||||
else if (fmt == Format::AsBx)
|
||||
{
|
||||
int16_t sbx = static_cast<int16_t>((inst >> 16) & 0xFFFF);
|
||||
stream << std::format("A:{:<3} sBx:{:<5}", a, sbx);
|
||||
|
||||
// 计算跳转绝对地址
|
||||
if (op == OpCode::Jmp || op == OpCode::JmpIfFalse)
|
||||
{
|
||||
stream << std::format(" ; to [{:04}]", i + sbx + 1);
|
||||
}
|
||||
}
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
if (!proto->constants.empty())
|
||||
{
|
||||
stream << "Constants:\n";
|
||||
for (size_t i = 0; i < proto->constants.size(); ++i)
|
||||
{
|
||||
stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
|
||||
}
|
||||
}
|
||||
stream << "--- End Disassembly ---\n";
|
||||
}
|
||||
|
||||
Disassembler::Format Disassembler::GetFormat(OpCode op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case OpCode::LoadK:
|
||||
case OpCode::Mov:
|
||||
case OpCode::GetGlobal:
|
||||
case OpCode::SetGlobal:
|
||||
case OpCode::LoadFn:
|
||||
return Format::ABx;
|
||||
|
||||
case OpCode::Exit:
|
||||
case OpCode::Exit_MaxRecursionDepthExceeded:
|
||||
case OpCode::Jmp:
|
||||
case OpCode::JmpIfFalse:
|
||||
return Format::AsBx;
|
||||
|
||||
default:
|
||||
return Format::ABC;
|
||||
}
|
||||
}
|
||||
} // namespace Fig
|
||||
35
src/Bytecode/Disassembler.hpp
Normal file
35
src/Bytecode/Disassembler.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
@file src/Bytecode/Disassembler.hpp
|
||||
@brief 字节码反汇编器:物理还原指令语义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Core/CoreIO.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Disassembler
|
||||
{
|
||||
public:
|
||||
// 反汇编整个模块
|
||||
static void DisassembleModule(const CompiledModule *module, std::ostream & = CoreIO::GetStdOut());
|
||||
|
||||
// 反汇编单个函数原型
|
||||
static void DisassembleProto(const Proto *proto, std::ostream & = CoreIO::GetStdOut());
|
||||
|
||||
private:
|
||||
enum class Format
|
||||
{
|
||||
ABC,
|
||||
ABx,
|
||||
AsBx
|
||||
};
|
||||
|
||||
static Format GetFormat(OpCode op);
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,66 +1,67 @@
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Core/Core.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Bytecode/Disassembler.hpp>
|
||||
#include <iostream>
|
||||
#include <print>
|
||||
#include <filesystem>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Fig;
|
||||
|
||||
String fileName = "test.fig";
|
||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/tests/Compiler/test_basic.fig";
|
||||
|
||||
SourceManager manager(filePath);
|
||||
manager.Read();
|
||||
|
||||
if (!manager.read)
|
||||
if (!std::filesystem::exists(filePath.toStdString()))
|
||||
{
|
||||
std::cerr << "Couldn't read file";
|
||||
std::cerr << "CRITICAL: Test file not found at: " << filePath << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Lexer lexer(manager.GetSource(), fileName);
|
||||
Parser parser(lexer, manager, fileName);
|
||||
SourceManager sm{filePath};
|
||||
String source = sm.Read();
|
||||
|
||||
const auto &program_result = parser.Parse();
|
||||
if (!program_result)
|
||||
if (!sm.read || source.length() == 0)
|
||||
{
|
||||
ReportError(program_result.error(), manager);
|
||||
return 1;
|
||||
}
|
||||
Program *program = *program_result;
|
||||
|
||||
Compiler compiler(fileName, manager);
|
||||
const auto &comp_result = compiler.Compile(program);
|
||||
if (!comp_result)
|
||||
{
|
||||
ReportError(comp_result.error(), manager);
|
||||
std::cerr << "CRITICAL: SourceManager failed to read: " << filePath << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
CompiledModule *compiledModule = *comp_result;
|
||||
Lexer lexer(source, filePath);
|
||||
|
||||
size_t cnt = 0;
|
||||
for (Proto *proto : compiledModule->protos)
|
||||
Diagnostics diagnostics;
|
||||
Parser parser(lexer, sm, filePath, diagnostics);
|
||||
|
||||
diagnostics.EmitAll(sm);
|
||||
|
||||
auto pRes = parser.Parse();
|
||||
if (!pRes)
|
||||
{
|
||||
std::cout << "=====================\n"
|
||||
<< "Proto: " << cnt++ << '\n';
|
||||
std::cout << "=== Constant Pool ===" << '\n';
|
||||
for (size_t i = 0; i < proto->constants.size(); ++i)
|
||||
{
|
||||
std::print("[{}] {}\n", i, proto->constants[i].ToString());
|
||||
ReportError(pRes.error(), sm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DumpCode(proto->code);
|
||||
Program *program = *pRes;
|
||||
std::cout << "Successfully parsed nodes: " << program->nodes.size() << "\n";
|
||||
|
||||
std::cout << "\nMax Stack Size: " << (int) proto->maxStack << std::endl;
|
||||
Analyzer analyzer(sm);
|
||||
auto aRes = analyzer.Analyze(program);
|
||||
if (!aRes)
|
||||
{
|
||||
ReportError(aRes.error(), sm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Diagnostics diag;
|
||||
Compiler compiler(sm, diag);
|
||||
auto cRes = compiler.Compile(program);
|
||||
if (!cRes)
|
||||
{
|
||||
ReportError(cRes.error(), sm);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 使用正式的 Disassembler
|
||||
Disassembler::DisassembleModule(*cRes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,36 +1,142 @@
|
||||
/*!
|
||||
@file src/Compiler/Compiler.cpp
|
||||
@brief 编译器实现
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-18
|
||||
@brief 编译器主逻辑实现:物理 Bootstrapper 与双步扫描
|
||||
*/
|
||||
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<CompiledModule *, Error> Compiler::Compile(Program *program)
|
||||
{
|
||||
current->freeReg = 0;
|
||||
|
||||
for (Stmt *stmt : program->nodes)
|
||||
module = new CompiledModule();
|
||||
if (program->nodes.empty())
|
||||
{
|
||||
auto result = compileStmt(static_cast<Stmt *>(stmt));
|
||||
if (!result)
|
||||
return module;
|
||||
}
|
||||
|
||||
// 预留 Protos[0] 给 Bootstrapper
|
||||
Proto *bootProto = new Proto();
|
||||
bootProto->name = "[bootstrapper]";
|
||||
module->protos.push_back(bootProto);
|
||||
|
||||
int initIdx = -1;
|
||||
int mainIdx = -1;
|
||||
|
||||
SourceLocation *mainFnLoc = nullptr;
|
||||
SourceLocation *initFnLoc = nullptr;
|
||||
|
||||
// 预扫描顶层函数
|
||||
for (auto *stmt : program->nodes)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
}
|
||||
|
||||
if (mainFuncIndex != -1)
|
||||
if (stmt->type == AstType::FnDefStmt)
|
||||
{
|
||||
std::uint8_t baseReg = AllocReg();
|
||||
Emit(Op::iABC(OpCode::FastCall, mainFuncIndex, baseReg, 0));
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
int idx = (int) module->protos.size();
|
||||
|
||||
Proto *p = new Proto();
|
||||
p->name = f->name;
|
||||
p->numParams = (uint8_t) f->params.size();
|
||||
p->maxRegisters = p->numParams;
|
||||
f->protoIndex = idx;
|
||||
|
||||
module->protos.push_back(p);
|
||||
|
||||
// 连接物理符号到索引
|
||||
if (f->resolvedSymbol)
|
||||
{
|
||||
f->resolvedSymbol->index = idx;
|
||||
}
|
||||
|
||||
Emit(Op::iABC(OpCode::Exit, 0, 0, 0)); // 一定要退出,这是虚拟机退出信号,否则ub
|
||||
|
||||
CompiledModule *compiledModule = new CompiledModule(fileName, allProtos);
|
||||
return compiledModule;
|
||||
if (f->name == "init")
|
||||
{
|
||||
initIdx = idx;
|
||||
initFnLoc = &stmt->location;
|
||||
}
|
||||
}; // namespace Fig
|
||||
if (f->name == "main")
|
||||
{
|
||||
mainIdx = idx;
|
||||
mainFnLoc = &stmt->location;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrapper 中编译所有语句
|
||||
FuncState bootState(bootProto, nullptr);
|
||||
current = &bootState;
|
||||
|
||||
for (auto *stmt : program->nodes)
|
||||
{
|
||||
auto res = compileStmt(stmt);
|
||||
if (!res)
|
||||
{
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
}
|
||||
|
||||
// 发射 Bootstrapper 引导指令
|
||||
if (initIdx != -1)
|
||||
{
|
||||
emit(Op::iABC(OpCode::FastCall, (uint8_t) initIdx, 0, 0), initFnLoc);
|
||||
}
|
||||
|
||||
if (mainIdx != -1)
|
||||
{
|
||||
emit(Op::iABC(OpCode::FastCall, (uint8_t) mainIdx, 0, 0), mainFnLoc);
|
||||
}
|
||||
|
||||
emit(Op::iAsBx(OpCode::Exit, 0, 0), &program->nodes.back()->location);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
int Compiler::getGlobalID(const String &name)
|
||||
{
|
||||
if (globalIDMap.contains(name))
|
||||
return globalIDMap[name];
|
||||
int id = (int) globalIDMap.size();
|
||||
globalIDMap[name] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
Result<Register, Error> Compiler::allocateReg(const SourceLocation &loc)
|
||||
{
|
||||
if (current->freereg >= MAX_REGISTERS)
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::RegisterOverflow, "too many registers", "", loc));
|
||||
}
|
||||
|
||||
Register reg = current->freereg++;
|
||||
if (reg >= current->proto->maxRegisters)
|
||||
{
|
||||
current->proto->maxRegisters = reg + 1;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
void Compiler::freeReg(Register count)
|
||||
{
|
||||
if (current->freereg >= count)
|
||||
{
|
||||
current->freereg -= count;
|
||||
}
|
||||
}
|
||||
|
||||
int Compiler::addConstant(Value val)
|
||||
{
|
||||
if (current->constantMap.contains(val))
|
||||
return current->constantMap[val];
|
||||
int idx = (int) current->proto->constants.size();
|
||||
current->proto->constants.push_back(val);
|
||||
current->constantMap[val] = idx;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void Compiler::emit(Instruction inst, SourceLocation *loc)
|
||||
{
|
||||
current->proto->code.push_back(inst);
|
||||
current->proto->locations.push_back(loc);
|
||||
}
|
||||
} // namespace Fig
|
||||
|
||||
@@ -1,384 +1,55 @@
|
||||
/*!
|
||||
@file src/Compiler/Compiler.hpp
|
||||
@brief 编译器定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-19
|
||||
@brief 编译器定义:物理直连 Bootstrapper
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <Object/Object.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// 编译产物-函数
|
||||
struct Proto
|
||||
{
|
||||
DynArray<Instruction> code;
|
||||
DynArray<Value> constants;
|
||||
std::uint8_t maxStack = 0; // 函数运行所需寄存器数量
|
||||
};
|
||||
|
||||
struct LocalVar
|
||||
{
|
||||
int localId; // AST 传来的纯数字 ID
|
||||
std::uint8_t reg; // 分配到的物理寄存器 ID
|
||||
int depth; // 物理作用域深度(用于 EndScope 释放寄存器)
|
||||
};
|
||||
|
||||
inline constexpr int MAX_LOCALS = 250;
|
||||
inline constexpr int MAX_CONSTANTS = UINT16_MAX + 1;
|
||||
|
||||
// 任何跨函数、跨模块的编译,都压入弹出这个 State
|
||||
struct FuncState
|
||||
{
|
||||
String name;
|
||||
FuncState *enclosing = nullptr; // 指向外层状态 (支持闭包)
|
||||
Proto *proto = nullptr;
|
||||
|
||||
std::uint8_t freeReg = 0;
|
||||
int scopeDepth = 0;
|
||||
DynArray<LocalVar> locals;
|
||||
|
||||
std::uint8_t fastRegMap[UINT8_MAX + 1]; // 256, 索引 = localId, 值 = 寄存器 id
|
||||
|
||||
FuncState(String _name, FuncState *enc = nullptr) : name(std::move(_name)), enclosing(enc)
|
||||
{
|
||||
proto = new Proto();
|
||||
std::fill_n(fastRegMap, 256, UINT8_MAX); // 255代表未分配
|
||||
}
|
||||
// 注意:这里不 delete proto,因为 proto 是要作为编译产物吐出去的
|
||||
};
|
||||
|
||||
struct CompiledModule
|
||||
{
|
||||
String name; // 供调试/打印
|
||||
DynArray<Proto *> protos; // 扁平化函数原型
|
||||
|
||||
CompiledModule(String _name, DynArray<Proto *> _protos) :
|
||||
name(std::move(_name)), protos(std::move(_protos))
|
||||
{
|
||||
}
|
||||
|
||||
~CompiledModule()
|
||||
{
|
||||
for (auto *p : protos)
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
};
|
||||
using Register = std::uint8_t;
|
||||
|
||||
class Compiler
|
||||
{
|
||||
private:
|
||||
String fileName;
|
||||
static constexpr Register MAX_REGISTERS = 250;
|
||||
static constexpr Register NO_REG = 255;
|
||||
|
||||
struct FuncState
|
||||
{
|
||||
Proto *proto;
|
||||
Register freereg;
|
||||
FuncState *enclosing;
|
||||
HashMap<Value, int> constantMap;
|
||||
|
||||
FuncState(Proto *p, FuncState *e)
|
||||
: proto(p), freereg(p->numParams), enclosing(e) {}
|
||||
};
|
||||
|
||||
FuncState *current = nullptr;
|
||||
CompiledModule *module = nullptr;
|
||||
SourceManager &manager;
|
||||
FuncState *current = nullptr; // 永远指向当前正在编译的上下文
|
||||
Diagnostics &diag;
|
||||
|
||||
int mainFuncIndex = -1;
|
||||
HashMap<int, int> globalFuncMap; // localid -> ProtoIdx
|
||||
HashMap<String, int> globalIDMap;
|
||||
int getGlobalID(const String& name);
|
||||
|
||||
Result<Register, Error> allocateReg(const SourceLocation &loc);
|
||||
void freeReg(Register count = 1);
|
||||
int addConstant(Value val);
|
||||
|
||||
void emit(Instruction inst, SourceLocation *loc);
|
||||
|
||||
Result<void, Error> compileStmt(Stmt *stmt);
|
||||
Result<Register, Error> compileExpr(Expr *expr, Register target = NO_REG);
|
||||
|
||||
public:
|
||||
DynArray<Proto *> allProtos;
|
||||
|
||||
struct FuncStateProtector
|
||||
{
|
||||
Compiler *compiler;
|
||||
FuncState *prevState;
|
||||
|
||||
[[nodiscard]]
|
||||
FuncStateProtector(Compiler *comp, FuncState *newState) :
|
||||
compiler(comp), prevState(comp->current)
|
||||
{
|
||||
compiler->current = newState;
|
||||
}
|
||||
|
||||
~FuncStateProtector()
|
||||
{
|
||||
compiler->current = prevState;
|
||||
}
|
||||
};
|
||||
|
||||
Compiler(String _fileName, SourceManager &_manager) :
|
||||
fileName(std::move(_fileName)), manager(_manager)
|
||||
{
|
||||
// 初始化顶级作用域
|
||||
current = new FuncState("global", nullptr);
|
||||
allProtos.push_back(current->proto); // 最顶层, bootstrapper
|
||||
}
|
||||
|
||||
~Compiler()
|
||||
{
|
||||
// 内存清理 (如果有异常中断)
|
||||
while (current != nullptr)
|
||||
{
|
||||
FuncState *prev = current->enclosing;
|
||||
delete current;
|
||||
current = prev;
|
||||
}
|
||||
}
|
||||
|
||||
Compiler(SourceManager &m, Diagnostics &d) : manager(m), diag(d) {}
|
||||
Result<CompiledModule *, Error> Compile(Program *program);
|
||||
|
||||
private:
|
||||
void PushState(String _name)
|
||||
{
|
||||
current = new FuncState(std::move(_name));
|
||||
}
|
||||
|
||||
Proto *PopState()
|
||||
{
|
||||
FuncState *oldState = current;
|
||||
Proto *finishedProto = oldState->proto;
|
||||
|
||||
current = oldState->enclosing;
|
||||
delete oldState;
|
||||
|
||||
return finishedProto;
|
||||
}
|
||||
|
||||
std::uint8_t AllocReg()
|
||||
{
|
||||
if (current->freeReg >= 255)
|
||||
{
|
||||
assert(false && "Register overflow!");
|
||||
}
|
||||
std::uint8_t reg = current->freeReg++;
|
||||
if (current->freeReg > current->proto->maxStack)
|
||||
{
|
||||
current->proto->maxStack = current->freeReg;
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
void FreeReg(std::uint8_t reg)
|
||||
{
|
||||
// 如果这个寄存器被局部变量使用,不释放直接 Return
|
||||
for (const auto &local : current->locals)
|
||||
{
|
||||
if (local.reg == reg)
|
||||
{
|
||||
return; // 拒绝释放,保护局部变量生命周期
|
||||
}
|
||||
}
|
||||
|
||||
// 如果它是纯粹的临时计算结果),释放
|
||||
if (reg == current->freeReg - 1)
|
||||
{
|
||||
current->freeReg--;
|
||||
}
|
||||
}
|
||||
void Emit(Instruction inst)
|
||||
{
|
||||
current->proto->code.push_back(inst);
|
||||
}
|
||||
|
||||
std::uint16_t AddConstant(Value v)
|
||||
{
|
||||
auto it =
|
||||
std::find(current->proto->constants.begin(), current->proto->constants.end(), v);
|
||||
if (it != current->proto->constants.end())
|
||||
{
|
||||
return std::distance(current->proto->constants.begin(), it);
|
||||
}
|
||||
|
||||
current->proto->constants.push_back(v);
|
||||
return static_cast<std::uint16_t>(current->proto->constants.size() - 1);
|
||||
}
|
||||
|
||||
void BeginScope()
|
||||
{
|
||||
current->scopeDepth++;
|
||||
}
|
||||
|
||||
void EndScope()
|
||||
{
|
||||
current->scopeDepth--;
|
||||
while (!current->locals.empty() && current->locals.back().depth > current->scopeDepth)
|
||||
{
|
||||
FreeReg(current->locals.back().reg);
|
||||
current->locals.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
std::uint8_t DeclareLocal(int localId)
|
||||
{
|
||||
std::uint8_t reg = AllocReg();
|
||||
current->locals.push_back(LocalVar{localId, reg, current->scopeDepth});
|
||||
current->fastRegMap[localId] = reg;
|
||||
return reg;
|
||||
}
|
||||
|
||||
std::uint8_t DeclareLocal(int localId, std::uint8_t reg) // 表示复用哪个寄存器
|
||||
{
|
||||
current->locals.push_back(LocalVar{localId, reg, current->scopeDepth});
|
||||
current->fastRegMap[localId] = reg;
|
||||
return reg;
|
||||
}
|
||||
|
||||
// 发射一条跳转指令,并返回它在代码数组里的绝对索引 (Index)
|
||||
int EmitJump(OpCode op, std::uint8_t aReg = 0)
|
||||
{
|
||||
// 预填 0
|
||||
Emit(Op::iAsBx(op, aReg, 0));
|
||||
return current->proto->code.size() - 1;
|
||||
}
|
||||
|
||||
// 填真实偏移量到那条指令里
|
||||
void PatchJump(int instructionIndex)
|
||||
{
|
||||
// 目标地址就是当前代码数组的末尾
|
||||
int target = current->proto->code.size();
|
||||
|
||||
// 相对偏移量 = 目标地址 - 指令自身所在的地址 - 1
|
||||
// (因为 VM 里的 ip 在取指后会自动 +1,所以偏移要减去 1)
|
||||
int offset = target - instructionIndex - 1;
|
||||
|
||||
if (offset < INT16_MIN || offset > INT16_MAX)
|
||||
{
|
||||
assert(false && "PatchJump: Jump offset exceeds 16-bit signed limit!");
|
||||
}
|
||||
Instruction &inst = current->proto->code[instructionIndex];
|
||||
inst = (inst & 0x0000FFFF)
|
||||
| (static_cast<Instruction>(static_cast<std::uint16_t>(offset)) << 16);
|
||||
}
|
||||
|
||||
SourceLocation makeSourceLocation(AstNode *node)
|
||||
{
|
||||
SourceLocation location = node->location; // copy
|
||||
location.functionName = current->name;
|
||||
location.fileName = fileName;
|
||||
return location;
|
||||
}
|
||||
|
||||
Result<std::uint8_t, Error> compileIdentiExpr(IdentiExpr *);
|
||||
Result<std::uint8_t, Error> compileLiteral(LiteralExpr *);
|
||||
|
||||
Result<std::uint8_t, Error> compileAssignment(
|
||||
InfixExpr *); // 编译赋值,由 CompileInfixExpr调用
|
||||
Result<std::uint8_t, Error> compileInfixExpr(InfixExpr *);
|
||||
|
||||
Result<std::uint8_t, Error> compileLeftValue(
|
||||
Expr *); // 左值对象,可以是变量、结构体字段或模块对象
|
||||
|
||||
Result<std::uint8_t, Error> compileCallExpr(CallExpr *);
|
||||
|
||||
Result<std::uint8_t, Error> compileExpr(Expr *);
|
||||
|
||||
/* Statements */
|
||||
Result<void, Error> compileVarDecl(VarDecl *);
|
||||
Result<void, Error> compileBlockStmt(BlockStmt *);
|
||||
Result<void, Error> compileIfStmt(IfStmt *);
|
||||
Result<void, Error> compileWhileStmt(WhileStmt *);
|
||||
Result<void, Error> compileFnDefStmt(FnDefStmt *);
|
||||
Result<void, Error> compileReturnStmt(ReturnStmt *);
|
||||
|
||||
Result<void, Error> compileStmt(Stmt *);
|
||||
};
|
||||
|
||||
inline void DisassembleInstruction(Instruction inst, std::size_t index)
|
||||
{
|
||||
// 提取OpCode (低 8 位)
|
||||
auto op = static_cast<OpCode>(inst & 0xFF);
|
||||
|
||||
std::string_view opName = magic_enum::enum_name(op);
|
||||
|
||||
// 所有指令至少都有 A 操作数 (8~15 位)
|
||||
std::uint8_t a = (inst >> 8) & 0xFF;
|
||||
|
||||
// 地址补零,指令名左对齐占 10 字符
|
||||
std::cout << std::format("{:04d} {:<10} ", index, opName);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case OpCode::Exit: {
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::Mov: {
|
||||
// iABx 模式
|
||||
std::uint16_t bx = (inst >> 16) & 0xFFFF;
|
||||
std::cout << std::format("R{:<3} R[{}]", a, bx);
|
||||
break;
|
||||
}
|
||||
case OpCode::LoadK: {
|
||||
// iABx 模式:解析 Bx (16~31 位)
|
||||
std::uint16_t bx = (inst >> 16) & 0xFFFF;
|
||||
std::cout << std::format("R{:<3} K[{}]", a, bx);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::Jmp:
|
||||
case OpCode::JmpIfFalse: {
|
||||
// iAsBx
|
||||
std::int16_t sbx = static_cast<std::uint16_t>(inst >> 16);
|
||||
std::cout << std::format("R{:<3} [{}]", a, sbx);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::FastCall:
|
||||
{
|
||||
std::uint8_t b = (inst >> 16) & 0xFF;
|
||||
std::cout << std::format("Proto{:<3} R[{}]+", a, b);
|
||||
break;
|
||||
}
|
||||
case OpCode::Call:
|
||||
{
|
||||
std::uint8_t b = (inst >> 16) & 0xFF;
|
||||
std::cout << std::format("R{:<3} R[{}]+", a, b);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::LoadTrue:
|
||||
case OpCode::LoadFalse:
|
||||
case OpCode::LoadNull:
|
||||
case OpCode::Add:
|
||||
case OpCode::Sub:
|
||||
case OpCode::Mul:
|
||||
case OpCode::Div:
|
||||
case OpCode::Mod: {
|
||||
// iABC 模式:解析 B (16~23 位) 和 C (24~31 位)
|
||||
std::uint8_t b = (inst >> 16) & 0xFF;
|
||||
std::uint8_t c = (inst >> 24) & 0xFF;
|
||||
std::cout << std::format("R{:<3} R{:<3} R{}", a, b, c);
|
||||
break;
|
||||
}
|
||||
case OpCode::Return: {
|
||||
// iA 模式:只用到了 A
|
||||
std::cout << std::format("R{}", a);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::LoadFn: {
|
||||
std::uint16_t bx = (inst >> 16) & 0xFFFF;
|
||||
std::cout << std::format("R{:<3} Proto[{}]", a, bx);
|
||||
break;
|
||||
}
|
||||
|
||||
// default: {
|
||||
// std::cout << "?";
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
inline void DumpCode(const DynArray<Instruction> &code)
|
||||
{
|
||||
std::cout << " Bytecode\n";
|
||||
for (std::size_t i = 0; i < code.size(); ++i)
|
||||
{
|
||||
DisassembleInstruction(code[i], i);
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
@@ -1,334 +1,477 @@
|
||||
/*!
|
||||
@file src/Compiler/ExprCompiler.cpp
|
||||
@brief 编译器实现(表达式部分)
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-19
|
||||
@brief 表达式编译
|
||||
*/
|
||||
|
||||
#include <Ast/Expr/CallExpr.hpp>
|
||||
#include <Ast/Expr/IdentiExpr.hpp>
|
||||
#include <Ast/Expr/InfixExpr.hpp>
|
||||
#include <Ast/Expr/LiteralExpr.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <charconv>
|
||||
#include <limits>
|
||||
#include <system_error>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<std::uint8_t, Error> Compiler::compileIdentiExpr(IdentiExpr *ie)
|
||||
static Result<Value, Error> parsePhysicalNumber(const String &raw, const SourceLocation &loc)
|
||||
{
|
||||
// TODO: 处理全局变量和闭包 Upvalue
|
||||
std::uint8_t targetReg = current->fastRegMap[ie->localId];
|
||||
char buffer[128];
|
||||
int j = 0;
|
||||
bool isFloat = false;
|
||||
|
||||
if (targetReg == UINT8_MAX)
|
||||
for (size_t i = 0; i < raw.length() && j < 127; ++i)
|
||||
{
|
||||
assert(false && "Compiler Bug: Encountered unmapped localId in fastRegMap!");
|
||||
}
|
||||
return targetReg;
|
||||
}
|
||||
Result<std::uint8_t, Error> Compiler::compileLiteral(
|
||||
LiteralExpr *lit) // 编译字面量, 负责转换 token -> Value
|
||||
{
|
||||
const Token &token = lit->token;
|
||||
String lexeme = manager.GetSub(token.index, token.length);
|
||||
char32_t c = raw[i];
|
||||
if (c == '_')
|
||||
continue;
|
||||
|
||||
if (!token.isLiteral())
|
||||
// 检查开头的无效字符
|
||||
if (j == 0 && (c == '.' || c == 'e' || c == 'E'))
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "unexpected leading character", "", loc));
|
||||
|
||||
if (c == '.' || c == 'e' || c == 'E')
|
||||
isFloat = true;
|
||||
buffer[j++] = (char) c;
|
||||
}
|
||||
buffer[j] = '\0';
|
||||
|
||||
// 检查16进制/2进制前缀
|
||||
bool isHexOrBin = false;
|
||||
int base = 10;
|
||||
const char *start = buffer;
|
||||
|
||||
if (j >= 2 && buffer[0] == '0')
|
||||
{
|
||||
assert(false && "CompileLiteral: token is not literal");
|
||||
if (buffer[1] == 'x' || buffer[1] == 'X')
|
||||
{
|
||||
base = 16;
|
||||
start += 2;
|
||||
isHexOrBin = true;
|
||||
}
|
||||
else if (buffer[1] == 'b' || buffer[1] == 'B')
|
||||
{
|
||||
base = 2;
|
||||
start += 2;
|
||||
isHexOrBin = true;
|
||||
}
|
||||
}
|
||||
|
||||
Value v;
|
||||
if (isFloat)
|
||||
{
|
||||
// 如果既有浮点标记又是0x开头,可能是16进制浮点
|
||||
auto fmt =
|
||||
(isHexOrBin && base == 16) ? std::chars_format::hex : std::chars_format::general;
|
||||
|
||||
if (token.type == TokenType::LiteralNull)
|
||||
{
|
||||
v = Value::GetNullInstance();
|
||||
double dVal;
|
||||
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, fmt);
|
||||
|
||||
if (ec != std::errc() || ptr != buffer + j)
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "invalid float literal", "", loc));
|
||||
|
||||
return Value::FromDouble(dVal);
|
||||
}
|
||||
else if (token.type == TokenType::LiteralTrue)
|
||||
else if (isHexOrBin)
|
||||
{
|
||||
v = Value::GetTrueInstance();
|
||||
}
|
||||
else if (token.type == TokenType::LiteralFalse)
|
||||
// 16进制或2进制整数
|
||||
int64_t iVal;
|
||||
auto [ptr, ec] = std::from_chars(start, buffer + j, iVal, base);
|
||||
|
||||
if (ec != std::errc() || ptr != buffer + j)
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "integer overflow or invalid literal", "", loc));
|
||||
|
||||
if (iVal >= std::numeric_limits<int32_t>::min()
|
||||
&& iVal <= std::numeric_limits<int32_t>::max())
|
||||
{
|
||||
v = Value::GetFalseInstance();
|
||||
}
|
||||
else if (token.type == TokenType::LiteralNumber)
|
||||
{
|
||||
// TODO: 更换为无异常手写数字解析版本 (charconv也可)
|
||||
if (lexeme.contains(U'.') || lexeme.contains(U'e'))
|
||||
{
|
||||
// 非整数
|
||||
double d = std::stod(lexeme.toStdString());
|
||||
v = Value::FromDouble(d);
|
||||
return Value::FromInt(static_cast<int32_t>(iVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::int32_t i = std::stoi(lexeme.toStdString());
|
||||
v = Value::FromInt(i);
|
||||
return Value::FromDouble(static_cast<double>(iVal));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert("false" && "CompileLiteral: unsupport literal");
|
||||
}
|
||||
// 10进制数字,可能是整数或浮点数
|
||||
double dVal;
|
||||
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, std::chars_format::general);
|
||||
|
||||
std::uint8_t targetReg = AllocReg();
|
||||
if (ec != std::errc() || ptr != buffer + j)
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "invalid number literal", "", loc));
|
||||
|
||||
if (current->proto->constants.size() >= MAX_CONSTANTS)
|
||||
// 检查是否是整数(没有小数部分且不超出int32范围)
|
||||
if (dVal == std::floor(dVal) && dVal >= std::numeric_limits<int32_t>::min()
|
||||
&& dVal <= std::numeric_limits<int32_t>::max())
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TooManyConstants,
|
||||
std::format("constant limit exceeded: {}", MAX_CONSTANTS),
|
||||
"How did you write such code? try global variable or split file",
|
||||
makeSourceLocation(lit)));
|
||||
return Value::FromInt(static_cast<int32_t>(dVal));
|
||||
}
|
||||
|
||||
std::uint16_t kIndex = AddConstant(v);
|
||||
|
||||
Emit(Op::iABx(OpCode::LoadK, targetReg, kIndex));
|
||||
return targetReg;
|
||||
}
|
||||
Result<std::uint8_t, Error> Compiler::compileAssignment(
|
||||
InfixExpr *infix) // 编译赋值,由 CompileInfixExpr调用
|
||||
else
|
||||
{
|
||||
// op必须为 =
|
||||
const auto &_lhsReg = compileLeftValue(infix->left); // 必须为左值对象
|
||||
if (!_lhsReg)
|
||||
{
|
||||
return _lhsReg;
|
||||
return Value::FromDouble(dVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::uint8_t lhsReg = *_lhsReg;
|
||||
|
||||
const auto &_rhsReg = compileExpr(infix->right);
|
||||
std::uint8_t rhsReg = *_rhsReg;
|
||||
|
||||
FreeReg(rhsReg);
|
||||
switch (infix->op)
|
||||
Result<Register, Error> Compiler::compileExpr(Expr *expr, Register target)
|
||||
{
|
||||
case BinaryOperator::Assign: {
|
||||
Emit(Op::iABx(OpCode::Mov, lhsReg, rhsReg)); // lhsReg = rhsReg
|
||||
if (expr == nullptr)
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::InternalError, "null expr in compiler", "", {}));
|
||||
}
|
||||
|
||||
switch (expr->type)
|
||||
{
|
||||
case AstType::LiteralExpr: {
|
||||
auto *l = static_cast<LiteralExpr *>(expr);
|
||||
Register r = (target == NO_REG) ? *allocateReg(l->location) : target;
|
||||
|
||||
const Token &tok = l->literal;
|
||||
if (tok.type == TokenType::LiteralNumber)
|
||||
{
|
||||
auto vRes =
|
||||
parsePhysicalNumber(manager.GetSub(tok.index, tok.length), l->location);
|
||||
if (!vRes)
|
||||
return std::unexpected(vRes.error());
|
||||
emit(
|
||||
Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(addConstant(*vRes))),
|
||||
&l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralString)
|
||||
{
|
||||
int kIdx = addConstant(Value::GetNullInstance()); // TODO: String 支持
|
||||
emit(Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(kIdx)), &l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralNull)
|
||||
{
|
||||
emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralTrue)
|
||||
{
|
||||
emit(Op::iABC(OpCode::LoadTrue, r, 0, 0), &l->location);
|
||||
}
|
||||
else if (tok.type == TokenType::LiteralFalse)
|
||||
{
|
||||
emit(Op::iABC(OpCode::LoadFalse, r, 0, 0), &l->location);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
case AstType::IdentiExpr: {
|
||||
auto *i = static_cast<IdentiExpr *>(expr);
|
||||
Symbol *sym = i->resolvedSymbol;
|
||||
|
||||
if (sym->location == SymbolLocation::Local)
|
||||
{
|
||||
// no-copy for temp eval
|
||||
if (target == NO_REG)
|
||||
return static_cast<Register>(sym->index);
|
||||
|
||||
// 仅在被强制指定目标(如参数装填)时发射搬运指令
|
||||
if (target != sym->index)
|
||||
{
|
||||
emit(
|
||||
Op::iABx(OpCode::Mov, target, static_cast<uint16_t>(sym->index)),
|
||||
&i->location);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
Register r = (target == NO_REG) ? *allocateReg(i->location) : target;
|
||||
if (sym->location == SymbolLocation::Upvalue)
|
||||
{
|
||||
emit(
|
||||
Op::iABC(OpCode::GetUpval, r, static_cast<uint8_t>(sym->index), 0),
|
||||
&i->location);
|
||||
}
|
||||
else if (sym->location == SymbolLocation::Global)
|
||||
{
|
||||
int gId = getGlobalID(i->name);
|
||||
emit(Op::iABx(OpCode::GetGlobal, r, static_cast<uint16_t>(gId)), &i->location);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
case AstType::CallExpr: {
|
||||
auto *c = static_cast<CallExpr *>(expr);
|
||||
Register mark = current->freereg; // 记录调用前的栈顶水位
|
||||
Register baseReg = current->freereg; // 锁定滑窗基址
|
||||
|
||||
// 连续装填参数,占据 baseReg, baseReg+1, baseReg+2...
|
||||
for (auto *arg : c->args.args)
|
||||
{
|
||||
auto allocRes = allocateReg(arg->location);
|
||||
if (!allocRes)
|
||||
{
|
||||
return allocRes;
|
||||
}
|
||||
|
||||
Register argTarget = *allocRes;
|
||||
auto res = compileExpr(arg, argTarget);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
|
||||
bool isGlobalFastCall = false;
|
||||
if (c->callee->type == AstType::IdentiExpr)
|
||||
{
|
||||
auto *id = static_cast<IdentiExpr *>(c->callee);
|
||||
// 只有在全局区的函数,才能使用 FastCall
|
||||
if (id->resolvedSymbol->location == SymbolLocation::Global)
|
||||
{
|
||||
isGlobalFastCall = true;
|
||||
int protoIdx = id->resolvedSymbol->index;
|
||||
emit(
|
||||
Op::iABC(
|
||||
OpCode::FastCall,
|
||||
static_cast<uint8_t>(protoIdx),
|
||||
baseReg,
|
||||
static_cast<uint8_t>(c->args.args.size())),
|
||||
&c->location);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isGlobalFastCall)
|
||||
{
|
||||
// 动态闭包调用
|
||||
// 先获取闭包对象所在的物理寄存器
|
||||
auto r_fn = compileExpr(c->callee);
|
||||
if (!r_fn)
|
||||
return std::unexpected(r_fn.error());
|
||||
|
||||
// 使用动态 Call 指令,RA 是指向堆闭包的寄存器
|
||||
emit(
|
||||
Op::iABC(
|
||||
OpCode::Call,
|
||||
*r_fn,
|
||||
baseReg,
|
||||
static_cast<uint8_t>(c->args.args.size())),
|
||||
&c->location);
|
||||
}
|
||||
|
||||
// free arg temps
|
||||
current->freereg = mark;
|
||||
|
||||
// 目若 target 未指定,allocateReg 将复用 baseReg,实现零开销回写
|
||||
|
||||
Register r_dest;
|
||||
if (target == NO_REG)
|
||||
{
|
||||
auto res = allocateReg(c->location);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
r_dest = *res;
|
||||
}
|
||||
else
|
||||
{
|
||||
r_dest = target;
|
||||
}
|
||||
|
||||
if (r_dest != baseReg)
|
||||
{
|
||||
emit(Op::iABx(OpCode::Mov, r_dest, baseReg), &c->location);
|
||||
}
|
||||
|
||||
return r_dest;
|
||||
}
|
||||
|
||||
case AstType::InfixExpr: {
|
||||
auto *in = static_cast<InfixExpr *>(expr);
|
||||
if (in->op == BinaryOperator::Assign)
|
||||
{
|
||||
auto r_val = compileExpr(in->right, target);
|
||||
if (!r_val)
|
||||
return std::unexpected(r_val.error());
|
||||
|
||||
if (in->left->type == AstType::IdentiExpr)
|
||||
{
|
||||
auto *lid = static_cast<IdentiExpr *>(in->left);
|
||||
Symbol *sym = lid->resolvedSymbol;
|
||||
if (sym->location == SymbolLocation::Local)
|
||||
{
|
||||
emit(
|
||||
Op::iABx(OpCode::Mov, static_cast<Register>(sym->index), *r_val),
|
||||
&lid->location);
|
||||
}
|
||||
else if (sym->location == SymbolLocation::Upvalue)
|
||||
{
|
||||
emit(
|
||||
Op::iABC(
|
||||
OpCode::SetUpval, *r_val, static_cast<Register>(sym->index), 0),
|
||||
&lid->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(
|
||||
Op::iABx(
|
||||
OpCode::SetGlobal,
|
||||
*r_val,
|
||||
static_cast<uint16_t>(getGlobalID(lid->name))),
|
||||
&lid->location);
|
||||
}
|
||||
}
|
||||
return r_val;
|
||||
}
|
||||
|
||||
Register mark = current->freereg; // mark
|
||||
|
||||
auto r_l = compileExpr(in->left);
|
||||
if (!r_l)
|
||||
return std::unexpected(r_l.error());
|
||||
auto r_r = compileExpr(in->right);
|
||||
if (!r_r)
|
||||
return std::unexpected(r_r.error());
|
||||
|
||||
bool isInt = in->left->resolvedType.is(TypeTag::Int)
|
||||
&& in->right->resolvedType.is(TypeTag::Int);
|
||||
OpCode op;
|
||||
switch (in->op)
|
||||
{
|
||||
case BinaryOperator::Add: {
|
||||
op = (isInt ? (OpCode::IntFastAdd) : (OpCode::Add));
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::AddAssign: {
|
||||
Emit(Op::iABC(OpCode::Add, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg + rhsReg
|
||||
case BinaryOperator::Subtract: {
|
||||
op = (isInt ? (OpCode::IntFastSub) : (OpCode::Sub));
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::SubAssign: {
|
||||
Emit(Op::iABC(OpCode::Sub, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg - rhsReg
|
||||
case BinaryOperator::Multiply: {
|
||||
op = (isInt ? (OpCode::IntFastMul) : (OpCode::Mul));
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::MultiplyAssign: {
|
||||
Emit(Op::iABC(OpCode::Mul, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg * rhsReg
|
||||
case BinaryOperator::Divide: {
|
||||
op = (isInt ? (OpCode::IntFastDiv) : (OpCode::Div));
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::DivideAssign: {
|
||||
Emit(Op::iABC(OpCode::Div, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg / rhsReg
|
||||
case BinaryOperator::Modulo: {
|
||||
op = OpCode::Mod;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::ModuloAssign: {
|
||||
Emit(Op::iABC(OpCode::Mod, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg % rhsReg
|
||||
case BinaryOperator::BitXor: {
|
||||
op = OpCode::BitXor;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::BitXorAssign: {
|
||||
Emit(Op::iABC(OpCode::BitXor, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg ^ rhsReg
|
||||
case BinaryOperator::Equal: {
|
||||
op = OpCode::Equal;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::NotEqual: {
|
||||
op = OpCode::NotEqual;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Greater: {
|
||||
op = OpCode::Greater;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::Less: {
|
||||
op = OpCode::Less;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::GreaterEqual: {
|
||||
op = OpCode::GreaterEqual;
|
||||
break;
|
||||
}
|
||||
case BinaryOperator::LessEqual: {
|
||||
op = OpCode::LessEqual;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false && "CompileAssignment: op unsupported yet");
|
||||
return std::unexpected(Error(
|
||||
ErrorType::InternalError,
|
||||
"unsupported binary operator",
|
||||
"",
|
||||
in->location));
|
||||
}
|
||||
}
|
||||
return lhsReg; // 返回赋值的结果,支持连续赋值
|
||||
}
|
||||
Result<std::uint8_t, Error> Compiler::compileInfixExpr(
|
||||
InfixExpr *infix) // 编译中缀表达式,返回一个存放结果的寄存器 ID
|
||||
|
||||
// 释放左右操作数产生的临时寄存器
|
||||
current->freereg = mark;
|
||||
|
||||
// 复用已释放的物理槽位存放计算结果
|
||||
Register r_d;
|
||||
if (target == NO_REG)
|
||||
{
|
||||
if (infix->op >= BinaryOperator::Assign && infix->op <= BinaryOperator::BitXorAssign)
|
||||
{
|
||||
return compileAssignment(infix);
|
||||
}
|
||||
|
||||
const auto &_lhsReg = compileExpr(infix->left);
|
||||
if (!_lhsReg)
|
||||
{
|
||||
return _lhsReg;
|
||||
}
|
||||
std::uint8_t lhsReg = *_lhsReg;
|
||||
const auto &_rhsReg = compileExpr(infix->right);
|
||||
if (!_rhsReg)
|
||||
{
|
||||
return _rhsReg;
|
||||
}
|
||||
std::uint8_t rhsReg = *_rhsReg;
|
||||
|
||||
FreeReg(rhsReg);
|
||||
FreeReg(lhsReg);
|
||||
|
||||
std::uint8_t resultReg = AllocReg();
|
||||
switch (infix->op)
|
||||
{
|
||||
case BinaryOperator::Add: {
|
||||
Emit(Op::iABC(OpCode::Add, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::Subtract: {
|
||||
Emit(Op::iABC(OpCode::Sub, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::Multiply: {
|
||||
Emit(Op::iABC(OpCode::Mul, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::Divide: {
|
||||
Emit(Op::iABC(OpCode::Div, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::Modulo: {
|
||||
Emit(Op::iABC(OpCode::Mod, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::Greater: {
|
||||
Emit(Op::iABC(OpCode::Greater, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::GreaterEqual: {
|
||||
Emit(Op::iABC(OpCode::GreaterEqual, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::Less: {
|
||||
Emit(Op::iABC(OpCode::Less, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::LessEqual: {
|
||||
Emit(Op::iABC(OpCode::LessEqual, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
case BinaryOperator::Equal: {
|
||||
Emit(Op::iABC(OpCode::Equal, resultReg, lhsReg, rhsReg));
|
||||
break;
|
||||
}
|
||||
|
||||
default: assert(false && "CompileInfixExpr: op unsupported yet");
|
||||
}
|
||||
return resultReg;
|
||||
}
|
||||
Result<std::uint8_t, Error> Compiler::compileLeftValue(
|
||||
Expr *expr) // 左值对象,可以是变量、结构体字段或模块对象
|
||||
{
|
||||
switch (expr->type)
|
||||
{
|
||||
case AstType::IdentiExpr:
|
||||
return compileIdentiExpr(static_cast<IdentiExpr *>(expr));
|
||||
// TODO: 数组切片(a[0])或对象属性(a.b)
|
||||
|
||||
default:
|
||||
// Analyzer 有漏洞(编译器内部
|
||||
// 直接崩溃
|
||||
assert(false && "Compiler Bug: Invalid L-value bypassed Analyzer!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::uint8_t, Error> Compiler::compileCallExpr(CallExpr *expr)
|
||||
{
|
||||
bool isStatic = false; // 是否为单纯的 fn(...) 静态函数调用
|
||||
int protoIdx = -1;
|
||||
|
||||
if (expr->callee->type == AstType::IdentiExpr)
|
||||
{
|
||||
IdentiExpr *id = static_cast<IdentiExpr *>(expr->callee);
|
||||
// 如果是函数名且深度为 0 (全局/扁平函数池)
|
||||
if (id->resolvedType->tag == TypeTag::Function && id->resolvedDepth == 0)
|
||||
{
|
||||
if (globalFuncMap.contains(id->localId))
|
||||
{
|
||||
isStatic = true;
|
||||
protoIdx = globalFuncMap[id->localId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::uint8_t baseReg = AllocReg();
|
||||
|
||||
if (!isStatic)
|
||||
{
|
||||
auto calleeRes = compileExpr(expr->callee);
|
||||
if (!calleeRes)
|
||||
{
|
||||
return calleeRes;
|
||||
}
|
||||
|
||||
if (*calleeRes != baseReg)
|
||||
{
|
||||
Emit(Op::iABx(OpCode::Mov, baseReg, *calleeRes));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < expr->args.size(); ++i)
|
||||
{
|
||||
std::uint8_t argTarget = AllocReg();
|
||||
auto argRes = compileExpr(expr->args.args[i]);
|
||||
if (!argRes)
|
||||
{
|
||||
return argRes;
|
||||
}
|
||||
|
||||
if (*argRes != argTarget)
|
||||
{
|
||||
Emit(Op::iABx(OpCode::Mov, argTarget, *argRes));
|
||||
}
|
||||
}
|
||||
|
||||
std::uint8_t expectRet = 1;
|
||||
|
||||
if (isStatic)
|
||||
{
|
||||
Emit(Op::iABC(OpCode::FastCall, (std::uint8_t) protoIdx, baseReg, expectRet));
|
||||
auto res = allocateReg(in->location);
|
||||
if (!res)
|
||||
return std::unexpected(res.error());
|
||||
r_d = *res;
|
||||
}
|
||||
else
|
||||
{
|
||||
Emit(Op::iABC(OpCode::Call, baseReg, baseReg, expectRet));
|
||||
r_d = target;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < expr->args.args.size(); ++i)
|
||||
emit(Op::iABC(op, r_d, *r_l, *r_r), &in->location);
|
||||
|
||||
return r_d;
|
||||
}
|
||||
|
||||
case AstType::LambdaExpr: {
|
||||
auto l = static_cast<LambdaExpr *>(expr);
|
||||
|
||||
Proto *proto = new Proto;
|
||||
proto->name = l->toString();
|
||||
|
||||
FuncState fs(proto, current);
|
||||
FuncState *old = current;
|
||||
current = &fs;
|
||||
|
||||
if (l->isExprBody)
|
||||
{
|
||||
current->freeReg--;
|
||||
}
|
||||
return baseReg; // 返回值起点
|
||||
}
|
||||
|
||||
Result<std::uint8_t, Error> Compiler::compileExpr(
|
||||
Expr *expr) // 编译表达式,必定返回一个存放结果的寄存器 ID
|
||||
auto result = compileExpr(static_cast<Expr *>(l->body));
|
||||
if (!result)
|
||||
{
|
||||
switch (expr->type)
|
||||
{
|
||||
case AstType::Stmt:
|
||||
case AstType::Expr:
|
||||
case AstType::AstNode: assert(false && "CompileExpr: bad node type"); break;
|
||||
|
||||
case AstType::IdentiExpr: {
|
||||
return compileLeftValue(expr); // 左值直接转换成右值
|
||||
return result;
|
||||
}
|
||||
|
||||
case AstType::LiteralExpr: {
|
||||
LiteralExpr *lit = static_cast<LiteralExpr *>(expr);
|
||||
|
||||
auto result = compileLiteral(lit);
|
||||
emit(Op::iABC(OpCode::Return, *result, 0, 0), &l->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = compileStmt(static_cast<BlockStmt *>(l->body));
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
std::uint8_t targetReg = *result;
|
||||
return targetReg;
|
||||
auto _r = allocateReg(l->body->location);
|
||||
if (!_r)
|
||||
{
|
||||
return _r;
|
||||
}
|
||||
|
||||
case AstType::InfixExpr: {
|
||||
return compileInfixExpr(static_cast<InfixExpr *>(expr));
|
||||
Register r = *_r;
|
||||
emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->body->location);
|
||||
emit(Op::iABC(OpCode::Return, r, 0, 0), &l->body->location);
|
||||
}
|
||||
|
||||
case AstType::CallExpr: {
|
||||
return compileCallExpr(static_cast<CallExpr *>(expr));
|
||||
int protoIndex = (int) module->protos.size();
|
||||
module->protos.push_back(proto);
|
||||
|
||||
current = old;
|
||||
if (target == NO_REG)
|
||||
{
|
||||
auto _r = allocateReg(expr->location);
|
||||
if (!_r)
|
||||
{
|
||||
return _r;
|
||||
}
|
||||
emit(Op::iABx(OpCode::LoadFn, *_r, protoIndex), &l->body->location);
|
||||
return *_r;
|
||||
}
|
||||
else
|
||||
{
|
||||
emit(Op::iABx(OpCode::LoadFn, target, protoIndex), &l->body->location);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
return std::unexpected(
|
||||
Error(ErrorType::InternalError, "unsupported expr", "", expr->location));
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,282 +1,269 @@
|
||||
/*!
|
||||
@file src/Compiler/StmtCompiler.cpp
|
||||
@brief 编译器实现(语句部分)
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-19
|
||||
@brief 语句编译
|
||||
*/
|
||||
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
#include <Ast/Stmt/IfStmt.hpp>
|
||||
#include <Ast/Stmt/VarDecl.hpp>
|
||||
#include <Ast/Stmt/WhileStmt.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<void, Error> Compiler::compileVarDecl(VarDecl *varDecl)
|
||||
Result<void, Error> Compiler::compileStmt(Stmt *stmt)
|
||||
{
|
||||
if (current->freeReg > MAX_LOCALS)
|
||||
if (stmt == nullptr)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::TooManyLocals,
|
||||
std::format("local limit exceeded: {}", MAX_LOCALS),
|
||||
"try split function or use arrays/structs...",
|
||||
makeSourceLocation(varDecl)));
|
||||
}
|
||||
|
||||
std::uint8_t varReg;
|
||||
if (varDecl->initExpr)
|
||||
{
|
||||
auto result = compileExpr(varDecl->initExpr);
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
std::uint8_t resultReg = *result;
|
||||
varReg = DeclareLocal(varDecl->localId, resultReg); // 复用临时计算结果寄存器
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: 未初始化提供初始值
|
||||
varReg = DeclareLocal(varDecl->localId);
|
||||
}
|
||||
|
||||
return Result<void, Error>();
|
||||
}
|
||||
|
||||
Result<void, Error> Compiler::compileBlockStmt(BlockStmt *blockStmt)
|
||||
{
|
||||
for (Stmt *stmt : blockStmt->nodes)
|
||||
{
|
||||
auto result = compileStmt(stmt);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Compiler::compileIfStmt(IfStmt *stmt)
|
||||
{
|
||||
/*
|
||||
if cond1
|
||||
{
|
||||
}
|
||||
else if cond2 #1
|
||||
{
|
||||
}
|
||||
else if cond3 #2
|
||||
{
|
||||
}
|
||||
else #3
|
||||
{
|
||||
}
|
||||
quit #4
|
||||
|
||||
Bytecode:
|
||||
JmpIfFalse cond1 #1
|
||||
; consequent内容
|
||||
; ...
|
||||
; if条件为true, 跳过所有 else/elseif
|
||||
|
||||
Jmp #4
|
||||
|
||||
; #1
|
||||
JmpIfFalse cond2 #2
|
||||
; consequent
|
||||
|
||||
Jmp #4
|
||||
|
||||
; #2
|
||||
JmpIfFalse cond3 #3
|
||||
; consequent
|
||||
|
||||
Jmp #4
|
||||
|
||||
; #3
|
||||
; 没有一次执行分支
|
||||
; else部分
|
||||
; ...
|
||||
|
||||
#4
|
||||
|
||||
*/
|
||||
std::vector<int> exitJumps; // 所有分支都要跳到最后,收集所有jump最后回填
|
||||
const auto &condResult = compileExpr(stmt->cond);
|
||||
if (!condResult)
|
||||
{
|
||||
return std::unexpected(condResult.error());
|
||||
}
|
||||
std::uint8_t condReg = *condResult;
|
||||
int jumpToNext = EmitJump(OpCode::JmpIfFalse, condReg);
|
||||
FreeReg(condReg);
|
||||
|
||||
const auto &blockResult = compileStmt(stmt->consequent);
|
||||
if (!blockResult)
|
||||
{
|
||||
return blockResult;
|
||||
}
|
||||
|
||||
exitJumps.push_back(EmitJump(OpCode::Jmp)); // 执行完if直接跳到出口
|
||||
PatchJump(jumpToNext); // 回填,跳到下一个else/elseif
|
||||
|
||||
for (auto *elif : stmt->elifs)
|
||||
{
|
||||
const auto &elifCondResult = compileExpr(elif->cond);
|
||||
if (!elifCondResult)
|
||||
return std::unexpected(elifCondResult.error());
|
||||
std::uint8_t elifCondReg = *elifCondResult;
|
||||
|
||||
jumpToNext = EmitJump(OpCode::JmpIfFalse, elifCondReg);
|
||||
FreeReg(elifCondReg);
|
||||
|
||||
const auto &blockResult = compileStmt(elif->consequent);
|
||||
if (!blockResult)
|
||||
{
|
||||
return blockResult;
|
||||
}
|
||||
exitJumps.push_back(EmitJump(OpCode::Jmp)); // 执行完else if,跳到出口
|
||||
PatchJump(jumpToNext); // 跳到下一个分支
|
||||
}
|
||||
|
||||
if (stmt->alternate)
|
||||
{
|
||||
auto result = compileStmt(stmt->alternate);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
for (int exitIndex : exitJumps)
|
||||
{
|
||||
PatchJump(exitIndex); // 回填所有跳转出口的指令
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Compiler::compileWhileStmt(WhileStmt *stmt)
|
||||
{
|
||||
int beginIns = current->proto->code.size() - 1;
|
||||
|
||||
auto condRegResult = compileExpr(stmt->cond);
|
||||
if (!condRegResult)
|
||||
{
|
||||
return std::unexpected(condRegResult.error());
|
||||
}
|
||||
|
||||
std::uint8_t condReg = *condRegResult;
|
||||
|
||||
int exitJump = EmitJump(OpCode::JmpIfFalse, condReg);
|
||||
auto bodyResult = compileBlockStmt(stmt->body);
|
||||
|
||||
if (!bodyResult)
|
||||
{
|
||||
return bodyResult;
|
||||
}
|
||||
Emit(Op::iAsBx(
|
||||
OpCode::Jmp, 0, beginIns - current->proto->code.size())); // 回到开头对condition求值
|
||||
PatchJump(exitJump);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Compiler::compileFnDefStmt(FnDefStmt *stmt)
|
||||
{
|
||||
std::uint8_t funcReg = DeclareLocal(stmt->localId);
|
||||
|
||||
// 创建子函数编译状态
|
||||
// 传入 current 作为 enclosing,用于后续支持闭包 Upvalue 查找
|
||||
FuncState childState(stmt->name, current);
|
||||
|
||||
allProtos.push_back(childState.proto);
|
||||
std::uint16_t protoIdx = static_cast<std::uint16_t>(allProtos.size() - 1);
|
||||
|
||||
globalFuncMap[stmt->localId] = protoIdx; // 把函数的local id映射到protoIdx
|
||||
|
||||
{
|
||||
FuncStateProtector stateGuard(this, &childState);
|
||||
|
||||
AllocReg();
|
||||
|
||||
// 将参数映射为子函数的初始局部变量 (R0, R1...)
|
||||
for (Param *p : stmt->params)
|
||||
{
|
||||
PosParam *posParam = static_cast<PosParam *>(p); // TODO: 其他参数支持...
|
||||
// 按顺序分配寄存器
|
||||
DeclareLocal(posParam->localId);
|
||||
}
|
||||
|
||||
// B编译函数体语句
|
||||
auto bodyRes = compileStmt(stmt->body);
|
||||
if (!bodyRes)
|
||||
return bodyRes;
|
||||
|
||||
// 隐式返回 null
|
||||
std::uint8_t resReg = AllocReg();
|
||||
Emit(Op::iABC(OpCode::LoadNull, resReg, 0, 0));
|
||||
Emit(Op::iABC(OpCode::Return, resReg, 0, 0));
|
||||
}
|
||||
|
||||
|
||||
// 5. 检查是否是 main 函数
|
||||
if (stmt->name == U"main")
|
||||
{
|
||||
this->mainFuncIndex = protoIdx;
|
||||
}
|
||||
|
||||
Emit(Op::iABx(OpCode::LoadFn, funcReg, protoIdx));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Compiler::compileReturnStmt(ReturnStmt *stmt)
|
||||
{
|
||||
auto res = compileExpr(stmt->value);
|
||||
if (!res)
|
||||
{
|
||||
return std::unexpected(res.error());
|
||||
}
|
||||
|
||||
Emit(Op::iABC(OpCode::Return, *res, 0, 0));
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void, Error> Compiler::compileStmt(Stmt *stmt) // 编译语句
|
||||
{
|
||||
switch (stmt->type)
|
||||
{
|
||||
case AstType::ExprStmt: {
|
||||
ExprStmt *exprStmt = static_cast<ExprStmt *>(stmt);
|
||||
Expr *expr = exprStmt->expr;
|
||||
auto result = compileExpr(expr);
|
||||
if (!result)
|
||||
case AstType::BlockStmt: {
|
||||
auto *b = static_cast<BlockStmt *>(stmt);
|
||||
for (auto *n : b->nodes)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
auto res = compileStmt(n);
|
||||
if (!res)
|
||||
return res;
|
||||
}
|
||||
FreeReg(*result);
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::VarDecl: {
|
||||
return compileVarDecl(static_cast<VarDecl *>(stmt));
|
||||
auto *v = static_cast<VarDecl *>(stmt);
|
||||
if (current->enclosing == nullptr) // 处理全局变量
|
||||
{
|
||||
Register mark = current->freereg; // mark
|
||||
auto regRes = compileExpr(v->initExpr);
|
||||
if (!regRes)
|
||||
return std::unexpected(regRes.error());
|
||||
|
||||
emit(Op::iABx(OpCode::SetGlobal,
|
||||
*regRes,
|
||||
static_cast<uint16_t>(getGlobalID(v->name))),
|
||||
&v->location);
|
||||
current->freereg = mark; // 释放初始化表达式的临时占用
|
||||
}
|
||||
else
|
||||
{
|
||||
// 抬升水位,锁死局部变量的物理槽位
|
||||
Register targetReg = static_cast<Register>(v->localId);
|
||||
while (current->freereg <= targetReg)
|
||||
{
|
||||
auto allocRes = allocateReg(v->location);
|
||||
if (!allocRes)
|
||||
{
|
||||
return std::unexpected(allocRes.error());
|
||||
}
|
||||
}
|
||||
|
||||
case AstType::BlockStmt: {
|
||||
return compileBlockStmt(static_cast<BlockStmt *>(stmt));
|
||||
auto regRes = compileExpr(v->initExpr, targetReg);
|
||||
if (!regRes)
|
||||
return std::unexpected(regRes.error());
|
||||
}
|
||||
|
||||
case AstType::IfStmt: {
|
||||
return compileIfStmt(static_cast<IfStmt *>(stmt));
|
||||
}
|
||||
|
||||
case AstType::WhileStmt: {
|
||||
return compileWhileStmt(static_cast<WhileStmt *>(stmt));
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::FnDefStmt: {
|
||||
return compileFnDefStmt(static_cast<FnDefStmt *>(stmt));
|
||||
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||
|
||||
if (f->protoIndex == -1) // 闭包环境没有被扫到
|
||||
{
|
||||
Proto *newProto = new Proto();
|
||||
newProto->name = f->name;
|
||||
newProto->numParams = static_cast<uint8_t>(f->params.size());
|
||||
newProto->maxRegisters = newProto->numParams; // sync
|
||||
|
||||
f->protoIndex = static_cast<int>(module->protos.size());
|
||||
module->protos.push_back(newProto);
|
||||
}
|
||||
|
||||
// 获取静态原型 (flat protos)
|
||||
Proto *p = module->protos[f->protoIndex];
|
||||
|
||||
p->upvalues = f->upvalues;
|
||||
|
||||
FuncState fs(p, current);
|
||||
FuncState *old = current;
|
||||
current = &fs;
|
||||
|
||||
auto res = compileStmt(f->body);
|
||||
if (!res)
|
||||
return res;
|
||||
|
||||
if (p->code.empty() || static_cast<OpCode>(p->code.back() & 0xFF) != OpCode::Return)
|
||||
{
|
||||
emit(Op::iABC(OpCode::Return, 0, 0, 0), &f->location);
|
||||
}
|
||||
|
||||
current = old;
|
||||
|
||||
// 如果是局部闭包,在当前栈帧分配寄存器并生成 LoadFn
|
||||
if (f->resolvedSymbol->location == SymbolLocation::Local)
|
||||
{
|
||||
Register targetReg = static_cast<Register>(f->resolvedSymbol->index);
|
||||
|
||||
while (current->freereg <= targetReg)
|
||||
{
|
||||
auto allocRes = allocateReg(f->location);
|
||||
if (!allocRes)
|
||||
return std::unexpected(allocRes.error());
|
||||
}
|
||||
|
||||
// 生成 LoadFn: RA = 目标寄存器, Bx = Proto 在 module->protos 中的绝对索引
|
||||
emit(Op::iABx(OpCode::LoadFn, targetReg, static_cast<uint16_t>(f->protoIndex)),
|
||||
&f->location);
|
||||
}
|
||||
else if (f->resolvedSymbol->location == SymbolLocation::Global)
|
||||
{
|
||||
auto result = allocateReg(f->location);
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
Register r = *result;
|
||||
emit(Op::iABx(OpCode::LoadFn, r, static_cast<uint16_t>(f->protoIndex)),
|
||||
&f->location);
|
||||
|
||||
int gId = getGlobalID(f->name);
|
||||
emit(Op::iABx(OpCode::SetGlobal, r, static_cast<std::uint16_t>(gId)),
|
||||
&f->location);
|
||||
|
||||
freeReg();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::IfStmt: {
|
||||
auto *i = static_cast<IfStmt *>(stmt);
|
||||
DynArray<int> exitJumps;
|
||||
|
||||
Register mark = current->freereg; // mark
|
||||
auto r_cond = compileExpr(i->cond);
|
||||
if (!r_cond)
|
||||
return std::unexpected(r_cond.error());
|
||||
|
||||
int jmpToNext = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &i->location);
|
||||
current->freereg = mark; // 回收条件表达式临时槽位
|
||||
|
||||
if (auto r = compileStmt(i->consequent); !r)
|
||||
return r;
|
||||
exitJumps.push_back(static_cast<int>(current->proto->code.size()));
|
||||
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &i->location);
|
||||
|
||||
int targetIdx = static_cast<int>(current->proto->code.size());
|
||||
current->proto->code[jmpToNext] = Op::iAsBx(
|
||||
OpCode::JmpIfFalse, *r_cond, static_cast<int16_t>(targetIdx - jmpToNext - 1));
|
||||
|
||||
for (auto *elif : i->elifs)
|
||||
{
|
||||
Register elifMark = current->freereg;
|
||||
auto ec = compileExpr(elif->cond);
|
||||
if (!ec)
|
||||
return std::unexpected(ec.error());
|
||||
|
||||
int nextElif = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::JmpIfFalse, *ec, 0), &elif->location);
|
||||
current->freereg = elifMark; // 回收 elif 临时槽位
|
||||
|
||||
if (auto r = compileStmt(elif->consequent); !r)
|
||||
return r;
|
||||
exitJumps.push_back(static_cast<int>(current->proto->code.size()));
|
||||
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &elif->location);
|
||||
|
||||
int target = static_cast<int>(current->proto->code.size());
|
||||
|
||||
current->proto->code.resize(nextElif);
|
||||
current->proto->code[nextElif] = Op::iAsBx(
|
||||
OpCode::JmpIfFalse, *ec, static_cast<int16_t>(target - nextElif - 1));
|
||||
|
||||
current->proto->locations.resize(nextElif);
|
||||
current->proto->locations[nextElif] = &elif->location;
|
||||
}
|
||||
|
||||
if (i->alternate)
|
||||
{
|
||||
if (auto r = compileStmt(i->alternate); !r)
|
||||
return r;
|
||||
}
|
||||
|
||||
int endIdx = static_cast<int>(current->proto->code.size());
|
||||
for (int pos : exitJumps)
|
||||
{
|
||||
current->proto->code[pos] =
|
||||
Op::iAsBx(OpCode::Jmp, 0, static_cast<int16_t>(endIdx - pos - 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::WhileStmt: {
|
||||
auto *w = static_cast<WhileStmt *>(stmt);
|
||||
int startIdx = static_cast<int>(current->proto->code.size());
|
||||
|
||||
Register mark = current->freereg; // mark
|
||||
auto r_cond = compileExpr(w->cond);
|
||||
if (!r_cond)
|
||||
return std::unexpected(r_cond.error());
|
||||
|
||||
int exitJmpIdx = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &w->location);
|
||||
current->freereg = mark; // 回收循环条件临时槽位
|
||||
|
||||
if (auto r = compileStmt(w->body); !r)
|
||||
return r;
|
||||
|
||||
int backJmpIdx = static_cast<int>(current->proto->code.size());
|
||||
emit(Op::iAsBx(OpCode::Jmp, 0, static_cast<int16_t>(startIdx - backJmpIdx - 1)),
|
||||
&w->location);
|
||||
|
||||
int endIdx = static_cast<int>(current->proto->code.size());
|
||||
current->proto->code[exitJmpIdx] = Op::iAsBx(
|
||||
OpCode::JmpIfFalse, *r_cond, static_cast<int16_t>(endIdx - exitJmpIdx - 1));
|
||||
break;
|
||||
}
|
||||
|
||||
case AstType::ReturnStmt: {
|
||||
return compileReturnStmt(static_cast<ReturnStmt *>(stmt));
|
||||
auto *rs = static_cast<ReturnStmt *>(stmt);
|
||||
Register mark = current->freereg; // mark
|
||||
Register retReg;
|
||||
|
||||
if (rs->value)
|
||||
{
|
||||
auto r = compileExpr(rs->value);
|
||||
if (!r)
|
||||
return std::unexpected(r.error());
|
||||
retReg = *r;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto r = allocateReg(rs->location);
|
||||
if (!r)
|
||||
return std::unexpected(r.error());
|
||||
emit(Op::iABC(OpCode::LoadNull, *r, 0, 0), &rs->location);
|
||||
retReg = *r;
|
||||
}
|
||||
|
||||
return Result<void, Error>();
|
||||
emit(Op::iABC(OpCode::Return, retReg, 0, 0), &rs->location);
|
||||
current->freereg = mark; // 回收返回值计算的占用
|
||||
break;
|
||||
}
|
||||
}; // namespace Fig
|
||||
|
||||
case AstType::ExprStmt: {
|
||||
Register mark = current->freereg; // mark
|
||||
auto reg = compileExpr(static_cast<ExprStmt *>(stmt)->expr);
|
||||
if (!reg)
|
||||
return std::unexpected(reg.error());
|
||||
|
||||
current->freereg = mark; // 彻底抛弃孤立表达式的副作用
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -41,7 +41,9 @@ namespace Fig::CoreIO
|
||||
|
||||
void InitConsoleIO()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleCP(CP_UTF8);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@@ -63,5 +63,81 @@ namespace Fig
|
||||
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
||||
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
||||
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
||||
|
||||
const std::string LOGO =
|
||||
R"(
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒██████████▓▒▒▒▒
|
||||
▒▒▒▒▒██████████▓▒▒▒▒
|
||||
▒▒▒▒▒███▓▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒█████████▒▒▒▒▒▒
|
||||
▒▒▒▒▒█████████▒▒▒▒▒▒
|
||||
▒▒▒▒▒███▓▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
)";
|
||||
|
||||
const std::string LICENSE_TEXT =
|
||||
R"(
|
||||
MIT License (Fig)
|
||||
|
||||
Copyright (c) 2026 PuqiAR <im@puqiar.top>
|
||||
|
||||
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:
|
||||
|
||||
1. The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
2. This project includes code from the following projects with their respective licenses:
|
||||
- magic_enum (MIT License)
|
||||
Copyright (c) 2019 - 2024 Daniil Goncharov
|
||||
|
||||
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.
|
||||
|
||||
- json (MIT LICENSE) (for LSP Server JSON-RPC)
|
||||
Copyright (c) 2013-2026 Niels Lohmann
|
||||
|
||||
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.
|
||||
|
||||
)";
|
||||
|
||||
}; // namespace Core
|
||||
}; // namespace Fig
|
||||
47
src/Error/Diagnostics.hpp
Normal file
47
src/Error/Diagnostics.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*!
|
||||
@file src/Error/Diagnostics.hpp
|
||||
@brief 诊断信息收集器:用于存储警告与非致命错误
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Error/Error.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Diagnostics
|
||||
{
|
||||
private:
|
||||
DynArray<Error> errors;
|
||||
|
||||
public:
|
||||
void Report(Error err)
|
||||
{
|
||||
errors.push_back(std::move(err));
|
||||
}
|
||||
|
||||
// 统一打印所有收集到的信息
|
||||
void EmitAll(const SourceManager &manager)
|
||||
{
|
||||
for (const auto &err : errors)
|
||||
{
|
||||
ReportError(err, manager);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasErrors() const
|
||||
{
|
||||
for (const auto &err : errors)
|
||||
{
|
||||
if (!err.IsWarning()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const DynArray<Error>& GetErrors() const { return errors; }
|
||||
|
||||
void Clear() { errors.clear(); }
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -17,7 +17,8 @@ namespace Fig
|
||||
ost << color << msg << TerminalColors::Reset;
|
||||
}
|
||||
|
||||
void ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||
void
|
||||
ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||
{
|
||||
ost << color << msg << TerminalColors::Reset;
|
||||
}
|
||||
@@ -30,7 +31,10 @@ namespace Fig
|
||||
std::string MultipleStr(const char *c, size_t n)
|
||||
{
|
||||
std::string buf;
|
||||
for (size_t i = 0; i < n; ++i) { buf += c; }
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
buf += c;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -40,6 +44,7 @@ namespace Fig
|
||||
switch (type)
|
||||
{
|
||||
case UnusedSymbol: return "UnusedSymbol";
|
||||
case UnnecessarySemicolon: return "UnnecessarySemicolon";
|
||||
|
||||
case MayBeNull: return "MaybeNull";
|
||||
|
||||
@@ -59,16 +64,21 @@ namespace Fig
|
||||
|
||||
case TooManyLocals: return "TooManyLocals";
|
||||
case TooManyConstants: return "TooManyConstants";
|
||||
|
||||
case RegisterOverflow: return "RegisterOverflow";
|
||||
case InternalError:
|
||||
return "InternalError";
|
||||
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
||||
}
|
||||
return "UnknownError";
|
||||
}
|
||||
|
||||
void PrintSystemInfos()
|
||||
{
|
||||
std::ostream &err = CoreIO::GetStdErr();
|
||||
std::stringstream build_info;
|
||||
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH << '['
|
||||
<< Core::COMPILER << ']' << '\n'
|
||||
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH
|
||||
<< '[' << Core::COMPILER << ']' << '\n'
|
||||
<< " Build Time: " << Core::COMPILE_TIME;
|
||||
|
||||
const std::string &build_info_str = build_info.str();
|
||||
@@ -88,26 +98,34 @@ namespace Fig
|
||||
|
||||
uint8_t level = ErrorLevel(error.type);
|
||||
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical"));
|
||||
const char *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||
const char *level_color =
|
||||
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||
|
||||
err << "🔥 "
|
||||
<< level_color
|
||||
//<< '(' << level_name << ')'
|
||||
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type)
|
||||
<< TC::Reset << '\n';
|
||||
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color
|
||||
<< ErrorTypeToString(error.type) << TC::Reset << '\n';
|
||||
|
||||
const SourceLocation &location = error.location;
|
||||
|
||||
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.' << location.functionName
|
||||
<< '\'' << " " << location.fileName << " (" << TC::DarkGray << location.sp.line << ":" << location.sp.column
|
||||
<< TC::Cyan << ')' << TC::Reset << '\n';
|
||||
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.'
|
||||
<< location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray
|
||||
<< location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset
|
||||
<< '\n';
|
||||
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
||||
|
||||
// 尝试打印上3行 下2行
|
||||
|
||||
int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2;
|
||||
while (!srcManager.HasLine(line_end)) { --line_end; }
|
||||
while (!srcManager.HasLine(line_start)) { ++line_start; }
|
||||
while (!srcManager.HasLine(line_end))
|
||||
{
|
||||
--line_end;
|
||||
}
|
||||
while (!srcManager.HasLine(line_start))
|
||||
{
|
||||
++line_start;
|
||||
}
|
||||
|
||||
const auto &getLineNumWidth = [](size_t l) {
|
||||
unsigned int cnt = 0;
|
||||
@@ -123,30 +141,39 @@ namespace Fig
|
||||
{
|
||||
unsigned int offset = 2 + 2 + 1;
|
||||
// ' └─ '
|
||||
if (i == location.sp.line) { err << TC::DarkGray << " └─ " << TC::Reset; }
|
||||
else if (i < location.sp.line) { err << TC::DarkGray << " │ " << TC::Reset; }
|
||||
if (i == location.sp.line)
|
||||
{
|
||||
err << TC::DarkGray << " └─ " << TC::Reset;
|
||||
}
|
||||
else if (i < location.sp.line)
|
||||
{
|
||||
err << TC::DarkGray << " │ " << TC::Reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
err << MultipleStr(" ", offset);
|
||||
}
|
||||
unsigned int cur_line_number_width = getLineNumWidth(i);
|
||||
|
||||
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow << i << TC::Reset;
|
||||
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow
|
||||
<< i << TC::Reset;
|
||||
err << " │ " << srcManager.GetLine(i) << '\n';
|
||||
if (i == location.sp.line)
|
||||
{
|
||||
unsigned int error_col_offset = offset + 1 + max_line_number_width + 2;
|
||||
err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
||||
err << MultipleStr(" ", error_col_offset)
|
||||
<< MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
||||
<< MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n';
|
||||
|
||||
err << MultipleStr(" ", error_col_offset)
|
||||
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) << "╰─ " << level_color
|
||||
<< error.message << TC::Reset << "\n\n";
|
||||
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2)
|
||||
<< "╰─ " << level_color << error.message << TC::Reset << "\n\n";
|
||||
}
|
||||
}
|
||||
err << "\n";
|
||||
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
||||
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset << "\n";
|
||||
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset
|
||||
<< "\n";
|
||||
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
||||
err << '\n';
|
||||
}
|
||||
@@ -154,7 +181,7 @@ namespace Fig
|
||||
void ReportError(const Error &error, const SourceManager &srcManager)
|
||||
{
|
||||
assert(srcManager.read && "ReportError: srcManager doesn't read source");
|
||||
assert(srcManager.HasLine(error.location.sp.line));
|
||||
// assert(srcManager.HasLine(error.location.sp.line));
|
||||
|
||||
PrintSystemInfos();
|
||||
PrintErrorInfo(error, srcManager);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
|
||||
#include <source_location>
|
||||
|
||||
namespace Fig
|
||||
@@ -25,6 +24,8 @@ namespace Fig
|
||||
{
|
||||
/* Minor */
|
||||
UnusedSymbol = 0,
|
||||
UnnecessarySemicolon,
|
||||
TrailingComma,
|
||||
|
||||
/* Medium */
|
||||
MayBeNull = 1001,
|
||||
@@ -51,6 +52,10 @@ namespace Fig
|
||||
// compile errors
|
||||
TooManyLocals,
|
||||
TooManyConstants,
|
||||
|
||||
// 编译器内部约束
|
||||
RegisterOverflow,
|
||||
InternalError,
|
||||
};
|
||||
|
||||
const char *ErrorTypeToString(ErrorType type);
|
||||
@@ -77,6 +82,9 @@ namespace Fig
|
||||
location = _location;
|
||||
thrower_loc = _throwerloc;
|
||||
}
|
||||
|
||||
// 新增:适配诊断系统的辅助判定
|
||||
bool IsWarning() const { return static_cast<unsigned int>(type) < 2000; }
|
||||
};
|
||||
|
||||
namespace TerminalColors
|
||||
|
||||
@@ -138,9 +138,12 @@ namespace Fig
|
||||
manager.LoadFromMemory(sourceCode);
|
||||
|
||||
Lexer lexer(sourceCode, "");
|
||||
Parser parser(lexer, manager, "");
|
||||
|
||||
// 1. 语法检查拦截
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, "", diagnostics);
|
||||
|
||||
// 语法检查拦截
|
||||
auto parserResult = parser.Parse();
|
||||
if (!parserResult)
|
||||
{
|
||||
@@ -148,6 +151,11 @@ namespace Fig
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &diag : diagnostics.GetErrors())
|
||||
{
|
||||
SendDiagnostics(uri, &diag);
|
||||
}
|
||||
|
||||
Program *program = *parserResult;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
@@ -160,7 +168,12 @@ namespace Fig
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 一切完美,发射空数组清空过去的错误红线
|
||||
for (auto &diag : analyzer.GetDiagnostics().GetErrors())
|
||||
{
|
||||
SendDiagnostics(uri, &diag);
|
||||
}
|
||||
|
||||
// 一切完美,发射空数组清空过去的错误红线
|
||||
SendDiagnostics(uri, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
/*!
|
||||
@file src/Object/FunctionObject.hpp
|
||||
@brief 函数对象定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-28
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Object/ObjectBase.hpp>
|
||||
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// 运行时闭包对象 (24字节 Base + 8字节 Proto指针 = 32 bytes)
|
||||
// Upvalue (Stack Open / Heap Closed)
|
||||
struct Upvalue
|
||||
{
|
||||
Value *location; // Open 状态指向 VM 的 registerBase[x],Closed 状态指向下面的 closedValue
|
||||
Value closedValue; // 栈帧销毁时,数据物理迁移至此
|
||||
Upvalue *next; // 侵入式链表,供 VM 追踪当前 Open 的 Upvalue
|
||||
std::uint32_t refCount = 0; // 多少个闭包正在使用
|
||||
};
|
||||
|
||||
struct Proto;
|
||||
struct FunctionObject final : public Object
|
||||
{
|
||||
Proto *proto; // 指向编译器生成的只读字节码与常量池
|
||||
String name;
|
||||
Proto *proto; // 静态只读字节码
|
||||
std::uint8_t paraCount;
|
||||
std::uint32_t upvalueCount; // 捕获数量
|
||||
|
||||
// TODO: 实现闭包时 加一个 Upvalue 指针数组
|
||||
// Value* upvalues;
|
||||
// 柔性数组
|
||||
Upvalue *upvalues[];
|
||||
|
||||
FunctionObject(const String &_name, Proto *_proto, std::uint32_t _upvalueCount) :
|
||||
name(_name), proto(_proto), paraCount(_proto->numParams), upvalueCount(_upvalueCount)
|
||||
{
|
||||
type = ObjectType::Function;
|
||||
}
|
||||
|
||||
~FunctionObject() = default;
|
||||
};
|
||||
} // namespace Fig
|
||||
16
src/Object/InstanceObject.hpp
Normal file
16
src/Object/InstanceObject.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
/*!
|
||||
@file src/Object/InstanceObject.hpp
|
||||
@brief 实例(InstanceObject)定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-10
|
||||
*/
|
||||
|
||||
#include <Object/ObjectBase.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct InstanceObject final : public Object
|
||||
{
|
||||
Value fields[];
|
||||
};
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
@date 2026-02-19
|
||||
*/
|
||||
|
||||
#include <Object/ObjectBase.hpp>
|
||||
#include <Object/Object.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -29,7 +29,35 @@ namespace Fig
|
||||
}
|
||||
else if (IsObject())
|
||||
{
|
||||
return "Object"; // TODO: 分派
|
||||
Object *obj = AsObject();
|
||||
if (!obj)
|
||||
return "<Bad Object>";
|
||||
|
||||
// 物理分发, 扔掉虚函数!awa
|
||||
switch (obj->type)
|
||||
{
|
||||
case ObjectType::String: {
|
||||
auto *strObj = static_cast<StringObject *>(obj);
|
||||
return strObj->data;
|
||||
}
|
||||
case ObjectType::Function: {
|
||||
auto *fnObj = static_cast<FunctionObject *>(obj);
|
||||
return std::format("<Function: {}>", fnObj->name);
|
||||
}
|
||||
case ObjectType::Struct: {
|
||||
auto *structObj = static_cast<StructObject *>(obj);
|
||||
return std::format("<Struct: {}>", structObj->name);
|
||||
}
|
||||
case ObjectType::Instance: {
|
||||
// 利用你预留的神级指针 klass 溯源
|
||||
if (obj->klass)
|
||||
{
|
||||
return std::format("<Instance: {}>", obj->klass->name);
|
||||
}
|
||||
return "<Instance: Unknown>";
|
||||
}
|
||||
default: return "<Corrupted Object>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Object/FunctionObject.hpp>
|
||||
#include <Object/InstanceObject.hpp>
|
||||
#include <Object/ObjectBase.hpp>
|
||||
#include <Object/StringObject.hpp>
|
||||
#include <Object/StructObject.hpp>
|
||||
#include <Object/FunctionObject.hpp>
|
||||
@@ -165,7 +165,8 @@ namespace Fig
|
||||
// 让 VM 的 OP_EQ 指令极简:`if (RA == RB)`
|
||||
[[nodiscard]] constexpr bool operator==(const Value &other) const
|
||||
{
|
||||
// IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则,所以不直接比对raw bits而是转换成 double进行C++比对
|
||||
// IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则,所以不直接比对raw bits而是转换成
|
||||
// double进行C++比对
|
||||
if (IsDouble() && other.IsDouble())
|
||||
{
|
||||
return AsDouble() == other.AsDouble();
|
||||
@@ -187,7 +188,7 @@ namespace Fig
|
||||
|
||||
/*
|
||||
C风格继承 + 手动分发
|
||||
禁止任何 virtual 达到最高效率
|
||||
禁止核心使用任何 virtual 达到最高效率
|
||||
*/
|
||||
enum class ObjectType : uint8_t
|
||||
{
|
||||
@@ -199,13 +200,20 @@ namespace Fig
|
||||
|
||||
struct StructObject /* : public Object */; // 结构体基类的定义,前向声明
|
||||
|
||||
enum class GCColor : std::uint8_t
|
||||
{
|
||||
White = 0, // 垃圾(或新对象)!
|
||||
Gray = 1, // 已发现,子节点待扫描
|
||||
Black = 2, // 存活
|
||||
};
|
||||
|
||||
// Total 24 bytes size
|
||||
struct Object
|
||||
{
|
||||
Object *next; // 8 bytes: gc链表
|
||||
StructObject *klass; // 8 bytes: 一切皆对象,父类指针
|
||||
ObjectType type; // 1 byte : 类型
|
||||
bool isMarked = false; // 1 byte : gc标记
|
||||
GCColor color = GCColor::White; // 1 byte : gc标记
|
||||
// + 6 bytes padding
|
||||
|
||||
constexpr bool isString() const
|
||||
@@ -229,3 +237,15 @@ namespace Fig
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<Fig::Value>
|
||||
{
|
||||
std::size_t operator()(const Fig::Value &v) const noexcept
|
||||
{
|
||||
return std::hash<std::uint64_t>()(v.Raw());
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -39,6 +39,7 @@ namespace Fig
|
||||
0 - UnaryOperators::Count BinaryOperators::Count
|
||||
*/
|
||||
|
||||
|
||||
Object *GetUnaryOperator(UnaryOperator _op)
|
||||
{
|
||||
std::uint8_t idx = static_cast<std::uint8_t>(_op);
|
||||
|
||||
@@ -9,25 +9,26 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<LiteralExpr *, Error> Parser::parseLiteralExpr() // 当前token为literal时调用
|
||||
Result<Expr *, Error> Parser::parseLiteralExpr() // 当前token为literal时调用
|
||||
{
|
||||
StateProtector p(this, {State::ParsingLiteralExpr});
|
||||
|
||||
const Token &literal_token = consumeToken();
|
||||
LiteralExpr *node = new LiteralExpr(literal_token, makeSourceLocation(literal_token));
|
||||
LiteralExpr *node =
|
||||
arena.Allocate<LiteralExpr>(literal_token, makeSourceLocation(literal_token));
|
||||
return node;
|
||||
}
|
||||
Result<IdentiExpr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
|
||||
Result<Expr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
|
||||
{
|
||||
StateProtector p(this, {State::ParsingIdentiExpr});
|
||||
|
||||
const Token &identifier = consumeToken();
|
||||
IdentiExpr *node = new IdentiExpr(
|
||||
IdentiExpr *node = arena.Allocate<IdentiExpr>(
|
||||
srcManager.GetSub(identifier.index, identifier.length), makeSourceLocation(identifier));
|
||||
return node;
|
||||
}
|
||||
|
||||
Result<InfixExpr *, Error> Parser::parseInfixExpr(Expr *lhs) // 当前token为 op
|
||||
Result<Expr *, Error> Parser::parseInfixExpr(Expr *lhs) // 当前token为 op
|
||||
{
|
||||
StateProtector p(this, {State::ParsingInfixExpr});
|
||||
|
||||
@@ -42,11 +43,11 @@ namespace Fig
|
||||
}
|
||||
Expr *rhs = *rhs_result;
|
||||
|
||||
InfixExpr *node = new InfixExpr(lhs, op, rhs);
|
||||
InfixExpr *node = arena.Allocate<InfixExpr>(lhs, op, rhs);
|
||||
return node;
|
||||
}
|
||||
|
||||
Result<PrefixExpr *, Error> Parser::parsePrefixExpr() // 当前token为op
|
||||
Result<Expr *, Error> Parser::parsePrefixExpr() // 当前token为op
|
||||
{
|
||||
StateProtector p(this, {State::ParsingPrefixExpr});
|
||||
|
||||
@@ -61,12 +62,12 @@ namespace Fig
|
||||
}
|
||||
|
||||
Expr *rhs = *rhs_result;
|
||||
PrefixExpr *node = new PrefixExpr(op, rhs);
|
||||
PrefixExpr *node = arena.Allocate<PrefixExpr>(op, rhs);
|
||||
return node;
|
||||
}
|
||||
|
||||
Result<IndexExpr *, Error> Parser::parseIndexExpr(
|
||||
Expr *base) // 由 parseExpression调用, 当前token为 `[`
|
||||
Result<Expr *, Error>
|
||||
Parser::parseIndexExpr(Expr *base) // 由 parseExpression调用, 当前token为 `[`
|
||||
{
|
||||
StateProtector p(this, {State::ParsingIndexExpr});
|
||||
|
||||
@@ -80,23 +81,25 @@ namespace Fig
|
||||
|
||||
if (currentToken().type != TokenType::RightBracket) // `]`
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed brackets",
|
||||
"insert `]`",
|
||||
makeSourceLocation(lbracket_token)));
|
||||
}
|
||||
consumeToken(); // consume `]`
|
||||
|
||||
IndexExpr *indexExpr = new IndexExpr(base, *index_result);
|
||||
IndexExpr *indexExpr = arena.Allocate<IndexExpr>(base, *index_result);
|
||||
return indexExpr;
|
||||
}
|
||||
|
||||
Result<CallExpr *, Error> Parser::parseCallExpr(
|
||||
Expr *callee) // 由 parseExpression调用, 当前token为 `(`
|
||||
Result<Expr *, Error>
|
||||
Parser::parseCallExpr(Expr *callee) // 由 parseExpression调用, 当前token为 `(`
|
||||
{
|
||||
StateProtector p(this, {State::ParsingCallExpr});
|
||||
|
||||
const Token &lparen_token = consumeToken(); // consume `(`
|
||||
const SourceLocation &location = makeSourceLocation(lparen_token);
|
||||
|
||||
FnCallArgs callArgs;
|
||||
|
||||
@@ -104,14 +107,15 @@ namespace Fig
|
||||
if (currentToken().type == TokenType::RightParen)
|
||||
{
|
||||
consumeToken(); // consume `)`
|
||||
return new CallExpr(callee, callArgs);
|
||||
return arena.Allocate<CallExpr>(callee, callArgs, location);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (currentToken().type == TokenType::EndOfFile)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"fn call has unclosed parenthese",
|
||||
"insert `)`",
|
||||
makeSourceLocation(lparen_token)));
|
||||
@@ -131,7 +135,8 @@ namespace Fig
|
||||
|
||||
if (currentToken().type != TokenType::Comma)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expected `,` or `)` in argument list",
|
||||
"insert `,`",
|
||||
makeSourceLocation(currentToken())));
|
||||
@@ -140,7 +145,202 @@ namespace Fig
|
||||
consumeToken(); // consume `,`
|
||||
}
|
||||
|
||||
return new CallExpr(callee, callArgs);
|
||||
return arena.Allocate<CallExpr>(callee, callArgs, location);
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseNewExpr()
|
||||
{
|
||||
// new type{...}
|
||||
StateProtector p(this, {State::ParsingNewExpr});
|
||||
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `new`
|
||||
|
||||
SET_STOP_AT(TokenType::LeftBrace); // {
|
||||
auto type_result = parseTypeExpr();
|
||||
if (!type_result)
|
||||
{
|
||||
return std::unexpected(type_result.error());
|
||||
}
|
||||
Expr *type = *type_result;
|
||||
|
||||
if (!match(TokenType::LeftBrace))
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError("NewExpr", "lbrace {", currentToken()));
|
||||
}
|
||||
|
||||
const Token &lb_token = prevToken();
|
||||
|
||||
/*
|
||||
Positional:
|
||||
new Point{1, 2}
|
||||
Named:
|
||||
new Point{x = 1, y = 2}
|
||||
Shorthand:
|
||||
new Point{y, x}
|
||||
*/
|
||||
|
||||
DynArray<NewExpr::Arg> args;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed `{` in new expr",
|
||||
"insert '}'",
|
||||
makeSourceLocation(lb_token)
|
||||
));
|
||||
}
|
||||
if (args.empty() && match(TokenType::RightBrace)) // 空参
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// named arg
|
||||
if (currentToken().isIdentifier() && peekToken().type == TokenType::Colon)
|
||||
{
|
||||
const Token &name_token = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_token.index, name_token.length);
|
||||
consumeToken(); // consume `:`
|
||||
|
||||
SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / }
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
args.push_back(NewExpr::Arg{
|
||||
name,
|
||||
*result
|
||||
});
|
||||
}
|
||||
// shorthand
|
||||
else if (currentToken().isIdentifier()
|
||||
&& (peekToken().type == TokenType::Comma || peekToken().type == TokenType::RightBrace))
|
||||
{
|
||||
const Token &name_token = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_token.index, name_token.length);
|
||||
|
||||
|
||||
|
||||
IdentiExpr *ident =
|
||||
arena.Allocate<IdentiExpr>(name, makeSourceLocation(name_token));
|
||||
args.push_back(NewExpr::Arg{name, ident});
|
||||
}
|
||||
else
|
||||
{
|
||||
SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / }
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
args.push_back(NewExpr::Arg{
|
||||
.value = *result
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (match(TokenType::Comma))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match(TokenType::RightBrace))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NewExpr *newExpr = arena.Allocate<NewExpr>(type, args, location);
|
||||
return newExpr;
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseLambdaExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingLambdaExpr});
|
||||
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `func`
|
||||
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"lambda expression should not have a name",
|
||||
"remove the name",
|
||||
makeSourceLocation(currentToken())));
|
||||
}
|
||||
|
||||
if (currentToken().type != TokenType::LeftParen)
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("fn def stmt", "lparen '('", currentToken()));
|
||||
}
|
||||
|
||||
DynArray<Param *> params;
|
||||
|
||||
auto paraResult = parseFnParams();
|
||||
if (!paraResult)
|
||||
{
|
||||
return std::unexpected(paraResult.error());
|
||||
}
|
||||
params = *paraResult;
|
||||
|
||||
Expr *returnType = nullptr;
|
||||
Token rightArrowToken;
|
||||
if (match(TokenType::RightArrow)) // ->
|
||||
{
|
||||
rightArrowToken = consumeToken();
|
||||
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
returnType = *result;
|
||||
}
|
||||
|
||||
if (match(TokenType::DoubleArrow)) // =>
|
||||
{
|
||||
if (returnType)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"use of expr body but specified return type in lambda expr",
|
||||
"remove `-> ...`",
|
||||
makeSourceLocation(rightArrowToken)));
|
||||
}
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Expr *expr = *result;
|
||||
LambdaExpr *lambda =
|
||||
arena.Allocate<LambdaExpr>(params, returnType, expr, true, location);
|
||||
return lambda;
|
||||
}
|
||||
else if (currentToken().type == TokenType::LeftBrace)
|
||||
{
|
||||
auto result = parseBlockStmt();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
LambdaExpr *lambda =
|
||||
arena.Allocate<LambdaExpr>(params, returnType, *result, false, location);
|
||||
return lambda;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("LambdaExpr", "darrow => / lbrace {", currentToken()));
|
||||
}
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseExpression(BindingPower rbp)
|
||||
@@ -148,6 +348,7 @@ namespace Fig
|
||||
Expr *lhs = nullptr;
|
||||
Token token = currentToken();
|
||||
|
||||
// NUD
|
||||
if (token.isIdentifier())
|
||||
{
|
||||
const auto &lhs_result = parseIdentiExpr();
|
||||
@@ -166,7 +367,7 @@ namespace Fig
|
||||
}
|
||||
lhs = *lhs_result;
|
||||
}
|
||||
else if (IsTokenOp(token.type, false)) // 是否是一元运算符
|
||||
else if (IsTokenOp(token.type, false)) // 是否是一元前缀运算符
|
||||
{
|
||||
const auto &lhs_result = parsePrefixExpr();
|
||||
if (!lhs_result)
|
||||
@@ -186,22 +387,45 @@ namespace Fig
|
||||
const Token &rparen_token = consumeToken(); // consume `)`
|
||||
if (rparen_token.type != TokenType::RightParen)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed parenthese",
|
||||
"insert `)`",
|
||||
makeSourceLocation(lparen_token)));
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
else if (token.type == TokenType::Function)
|
||||
{
|
||||
auto result = parseLambdaExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
lhs = *result;
|
||||
}
|
||||
else if (token.type == TokenType::New)
|
||||
{
|
||||
auto result = parseNewExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
lhs = *result;
|
||||
}
|
||||
|
||||
if (!lhs)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||
return std::unexpected(Error(
|
||||
ErrorType::ExpectedExpression,
|
||||
"expected expression",
|
||||
"insert expressions",
|
||||
makeSourceLocation(prevToken())));
|
||||
}
|
||||
|
||||
// LED
|
||||
while (true)
|
||||
{
|
||||
token = currentToken();
|
||||
@@ -210,53 +434,113 @@ namespace Fig
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
|
||||
// is / as
|
||||
if (token.type == TokenType::Is || token.type == TokenType::As)
|
||||
{
|
||||
BinaryOperator op = TokenToBinaryOp(token);
|
||||
BindingPower lbp = GetBinaryOpLBp(op);
|
||||
if (rbp >= lbp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
consumeToken(); // consume `is` or `as`
|
||||
auto typeRes = parseTypeExpr();
|
||||
if (!typeRes)
|
||||
{
|
||||
return std::unexpected(typeRes.error());
|
||||
}
|
||||
lhs = arena.Allocate<InfixExpr>(lhs, op, *typeRes);
|
||||
}
|
||||
// binary
|
||||
else if (IsTokenOp(token.type /* isBinary = true */))
|
||||
{
|
||||
BinaryOperator op = TokenToBinaryOp(token);
|
||||
BindingPower lbp = GetBinaryOpLBp(op);
|
||||
if (rbp >= lbp)
|
||||
{
|
||||
// 前操作数的右绑定力比当前操作数的左绑定力大
|
||||
// lhs被吸走
|
||||
break;
|
||||
}
|
||||
|
||||
auto result = parseInfixExpr(lhs);
|
||||
if (!result)
|
||||
{
|
||||
resetTermintors();
|
||||
return result;
|
||||
}
|
||||
lhs = *result;
|
||||
}
|
||||
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
|
||||
// 而不用走正常路径
|
||||
else if (token.type == TokenType::LeftBracket) // `[`
|
||||
// [index]
|
||||
else if (token.type == TokenType::LeftBracket)
|
||||
{
|
||||
const auto &expr_result = parseIndexExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
resetTermintors();
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
else if (token.type == TokenType::LeftParen) // `(`
|
||||
// call
|
||||
else if (token.type == TokenType::LeftParen)
|
||||
{
|
||||
const auto &expr_result = parseCallExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
resetTermintors();
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
// .member
|
||||
else if (token.type == TokenType::Dot)
|
||||
{
|
||||
consumeToken(); // consume `.`
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("MemberExpr", "identifier after `.`", currentToken()));
|
||||
}
|
||||
const Token &nameToken = consumeToken();
|
||||
const String &name =
|
||||
srcManager.GetSub(nameToken.index, nameToken.length);
|
||||
SourceLocation loc = makeSourceLocation(nameToken);
|
||||
lhs = arena.Allocate<MemberExpr>(lhs, name, loc);
|
||||
}
|
||||
// x++ x--
|
||||
else if (token.type == TokenType::DoublePlus || token.type == TokenType::DoubleMinus)
|
||||
{
|
||||
UnaryOperator op = TokenToUnaryOp(consumeToken());
|
||||
lhs = arena.Allocate<PostfixExpr>(op, lhs);
|
||||
}
|
||||
// ?:
|
||||
else if (token.type == TokenType::Question)
|
||||
{
|
||||
// ?: 最低优先
|
||||
// 赋值 rbp = 101,所以只有当 rbp < 100 时才可能进到三元
|
||||
// 实际上三元是最低优先级的非赋值运算符,我们给一个很小的 lbp
|
||||
constexpr BindingPower TERNARY_LBP = 150;
|
||||
if (rbp >= TERNARY_LBP)
|
||||
{
|
||||
break;
|
||||
}
|
||||
consumeToken(); // consume `?`
|
||||
auto thenRes = parseExpression(0); // 重置绑定力,右结合
|
||||
if (!thenRes)
|
||||
{
|
||||
return std::unexpected(thenRes.error());
|
||||
}
|
||||
if (!match(TokenType::Colon))
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TernaryExpr", "`:` for else branch", currentToken()));
|
||||
}
|
||||
auto elseRes = parseExpression(TERNARY_LBP - 1); // 右结合
|
||||
if (!elseRes)
|
||||
{
|
||||
return std::unexpected(elseRes.error());
|
||||
}
|
||||
lhs = arena.Allocate<TernaryExpr>(lhs, *thenRes, *elseRes, lhs->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||
"expression unexpectedly ended",
|
||||
"insert expressions",
|
||||
makeSourceLocation(token)));
|
||||
return lhs;
|
||||
}
|
||||
}
|
||||
return lhs;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*!
|
||||
@file src/Parser/Parser.cpp
|
||||
@brief 语法分析器(Pratt + 手动递归下降) 实现
|
||||
@brief 语法分析器实现
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-14
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#include <Parser/Parser.hpp>
|
||||
@@ -11,21 +11,27 @@ namespace Fig
|
||||
{
|
||||
Result<Program *, Error> Parser::Parse()
|
||||
{
|
||||
Program *program = new Program;
|
||||
while (!isEOF)
|
||||
Program *program = arena.Allocate<Program>();
|
||||
|
||||
while (currentToken().type != TokenType::EndOfFile)
|
||||
{
|
||||
if (lexerError)
|
||||
{
|
||||
return std::unexpected(*lexerError);
|
||||
}
|
||||
auto result = parseStatement();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
Stmt *stmt = *result;
|
||||
if (!stmt)
|
||||
if (stmt)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
program->nodes.push_back(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
}; // namespace Fig
|
||||
@@ -2,115 +2,116 @@
|
||||
@file src/Parser/Parser.hpp
|
||||
@brief 语法分析器(Pratt + 手动递归下降) 定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-14
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Token/Token.hpp>
|
||||
#include <Utils/Arena.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
class Parser
|
||||
{
|
||||
private:
|
||||
Arena arena;
|
||||
Lexer &lexer;
|
||||
SourceManager &srcManager;
|
||||
|
||||
size_t index = 0; // token在buffer下标
|
||||
DynArray<Token> buffer;
|
||||
size_t index = 0; // 当前 Token 在 buffer 中的下标
|
||||
DynArray<Token> buffer; // 已从 Lexer 读取的 Token 缓存
|
||||
|
||||
String fileName;
|
||||
|
||||
bool isEOF = false;
|
||||
|
||||
Diagnostics &diagnostics;
|
||||
std::optional<Error> lexerError; // 词法错误缓存,避免 exit/abort
|
||||
|
||||
// 惰性获取下一个 Token,跳过注释
|
||||
Token nextToken()
|
||||
{
|
||||
assert(!isEOF && "nextToken: eof but called nextToken");
|
||||
if (index + 1 < buffer.size())
|
||||
{
|
||||
return buffer[++index];
|
||||
}
|
||||
if (isEOF)
|
||||
return buffer[index];
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = lexer.NextToken();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
std::exit(-1);
|
||||
lexerError = result.error();
|
||||
isEOF = true;
|
||||
Token eof = {0, 0, TokenType::EndOfFile};
|
||||
buffer.push_back(eof);
|
||||
index = buffer.size() - 1;
|
||||
return buffer[index];
|
||||
}
|
||||
const Token &token = result.value();
|
||||
if (token.type == TokenType::Comments)
|
||||
continue; // 惰性跳过注释
|
||||
|
||||
if (token.type == TokenType::EndOfFile)
|
||||
{
|
||||
isEOF = true;
|
||||
}
|
||||
buffer.push_back(token);
|
||||
index++;
|
||||
return token;
|
||||
index = buffer.size() - 1;
|
||||
return buffer[index];
|
||||
}
|
||||
}
|
||||
|
||||
inline Token prevToken()
|
||||
{
|
||||
if (buffer.size() < 2)
|
||||
{
|
||||
return currentToken();
|
||||
return (index > 0) ? buffer[index - 1] : buffer[0];
|
||||
}
|
||||
return buffer[buffer.size() - 2];
|
||||
}
|
||||
|
||||
inline Token currentToken()
|
||||
{
|
||||
if (buffer.empty())
|
||||
{
|
||||
return nextToken();
|
||||
}
|
||||
return buffer.back();
|
||||
return buffer[index];
|
||||
}
|
||||
|
||||
// 惰性窥视后续 Token
|
||||
Token peekToken(size_t lookahead = 1)
|
||||
{
|
||||
assert(!isEOF && "peekToken: eof but called peekToken");
|
||||
|
||||
size_t peekIndex = index + lookahead;
|
||||
while (peekIndex >= buffer.size() && !isEOF)
|
||||
size_t targetIndex = index + lookahead;
|
||||
while (targetIndex >= buffer.size() && !isEOF)
|
||||
{
|
||||
auto result = lexer.NextToken();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
std::abort();
|
||||
}
|
||||
const Token &token = result.value();
|
||||
if (token.type == TokenType::EndOfFile)
|
||||
{
|
||||
lexerError = result.error();
|
||||
isEOF = true;
|
||||
Token eof = {0, 0, TokenType::EndOfFile};
|
||||
buffer.push_back(eof);
|
||||
index = buffer.size() - 1;
|
||||
return buffer.back();
|
||||
}
|
||||
buffer.push_back(token);
|
||||
if (result->type == TokenType::Comments)
|
||||
continue;
|
||||
if (result->type == TokenType::EndOfFile)
|
||||
isEOF = true;
|
||||
buffer.push_back(*result);
|
||||
}
|
||||
if (peekIndex >= buffer.size()) // 没有那么多token
|
||||
{
|
||||
return buffer.back(); // back是EOF Token
|
||||
}
|
||||
return buffer[peekIndex];
|
||||
return (targetIndex >= buffer.size()) ? buffer.back() : buffer[targetIndex];
|
||||
}
|
||||
|
||||
inline Token consumeToken()
|
||||
{
|
||||
if (isEOF)
|
||||
return buffer.back();
|
||||
Token current = currentToken();
|
||||
if (current.type != TokenType::EndOfFile)
|
||||
nextToken();
|
||||
return current;
|
||||
}
|
||||
|
||||
inline bool match(TokenType type)
|
||||
{
|
||||
if (currentToken().type == type)
|
||||
@@ -121,31 +122,6 @@ namespace Fig
|
||||
return false;
|
||||
}
|
||||
|
||||
inline Error makeUnexpectTokenError(const String &stmtType,
|
||||
const String &expect,
|
||||
const Token &tokenGot,
|
||||
std::source_location loc = std::source_location::current())
|
||||
{
|
||||
return Error(ErrorType::SyntaxError,
|
||||
std::format("expect '{}' in {}, got `{}`",
|
||||
expect,
|
||||
stmtType,
|
||||
magic_enum::enum_name(tokenGot.type)),
|
||||
"none",
|
||||
makeSourceLocation(tokenGot),
|
||||
loc);
|
||||
}
|
||||
|
||||
inline Error makeExpectSemicolonError(
|
||||
std::source_location loc = std::source_location::current())
|
||||
{
|
||||
return Error(ErrorType::SyntaxError,
|
||||
"expect ';' after statement",
|
||||
"insert ';'",
|
||||
makeSourceLocation(currentToken()),
|
||||
loc);
|
||||
}
|
||||
|
||||
public:
|
||||
struct State
|
||||
{
|
||||
@@ -155,12 +131,12 @@ namespace Fig
|
||||
|
||||
ParsingLiteralExpr,
|
||||
ParsingIdentiExpr,
|
||||
|
||||
ParsingInfixExpr,
|
||||
ParsingPrefixExpr,
|
||||
|
||||
ParsingIndexExpr,
|
||||
ParsingCallExpr,
|
||||
ParsingLambdaExpr,
|
||||
ParsingNewExpr,
|
||||
|
||||
ParsingVarDecl,
|
||||
ParsingIf,
|
||||
@@ -169,9 +145,11 @@ namespace Fig
|
||||
ParsingReturn,
|
||||
ParsingBreak,
|
||||
ParsingContinue,
|
||||
ParsingStructDef,
|
||||
|
||||
ParsingTypeParameters,
|
||||
ParsingNamedTypeExpr,
|
||||
|
||||
ParsingFnTypeExpr,
|
||||
} type = StateType::Standby;
|
||||
std::unordered_set<TokenType> stopAt = {};
|
||||
};
|
||||
@@ -179,7 +157,8 @@ namespace Fig
|
||||
private:
|
||||
const std::unordered_set<TokenType> &getBaseTerminators()
|
||||
{
|
||||
static const std::unordered_set<TokenType> baseTerminators = {TokenType::Semicolon,
|
||||
static const std::unordered_set<TokenType> baseTerminators{
|
||||
TokenType::Semicolon,
|
||||
TokenType::RightParen,
|
||||
TokenType::RightBracket,
|
||||
TokenType::RightBrace,
|
||||
@@ -188,134 +167,139 @@ namespace Fig
|
||||
return baseTerminators;
|
||||
}
|
||||
|
||||
std::unordered_set<TokenType> &getTerminators() // 返回固定的终止符
|
||||
{
|
||||
/*
|
||||
Syntax terminators:
|
||||
; ) ] } , EOF
|
||||
*/
|
||||
|
||||
static std::unordered_set<TokenType> terminators(getBaseTerminators());
|
||||
return terminators;
|
||||
}
|
||||
|
||||
void resetTermintors()
|
||||
{
|
||||
getTerminators() = getBaseTerminators();
|
||||
}
|
||||
bool shouldTerminate() // 判断是否终结
|
||||
bool shouldTerminate()
|
||||
{
|
||||
const Token &token = currentToken();
|
||||
const auto &terminators = getTerminators();
|
||||
|
||||
if (terminators.contains(token.type))
|
||||
{
|
||||
if (getBaseTerminators().contains(token.type))
|
||||
return true;
|
||||
}
|
||||
for (auto it = stateStack.rbegin(); it < stateStack.rend(); ++it)
|
||||
{
|
||||
if (it->stopAt.contains(token.type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DynArray<State> stateStack;
|
||||
|
||||
State ¤tState()
|
||||
{
|
||||
return stateStack.back();
|
||||
}
|
||||
|
||||
void pushState(State _state)
|
||||
{
|
||||
stateStack.push_back(std::move(_state));
|
||||
}
|
||||
|
||||
void popState()
|
||||
{
|
||||
if (!stateStack.empty())
|
||||
{
|
||||
stateStack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
class StateProtector
|
||||
struct StateProtector
|
||||
{
|
||||
Parser *parser;
|
||||
|
||||
public:
|
||||
StateProtector(Parser *p, const State &newState) : parser(p)
|
||||
Parser *p;
|
||||
StateProtector(Parser *_p, State _s) : p(_p)
|
||||
{
|
||||
parser->pushState(newState);
|
||||
p->pushState(_s);
|
||||
}
|
||||
|
||||
~StateProtector()
|
||||
{
|
||||
parser->popState();
|
||||
p->popState();
|
||||
}
|
||||
|
||||
// 禁止拷贝
|
||||
StateProtector(const StateProtector &) = delete;
|
||||
StateProtector &operator=(const StateProtector &) = delete;
|
||||
};
|
||||
|
||||
public:
|
||||
Parser(Lexer &_lexer, SourceManager &_srcManager, String _fileName) :
|
||||
lexer(_lexer), srcManager(_srcManager), fileName(std::move(_fileName))
|
||||
{
|
||||
pushState(State());
|
||||
}
|
||||
|
||||
private:
|
||||
SourceLocation makeSourceLocation(const Token &tok)
|
||||
{
|
||||
auto [line, column] = srcManager.GetLineColumn(tok.index);
|
||||
return SourceLocation(SourcePosition(line, column, tok.length),
|
||||
// 防止因解析错位导致的异常列号引起终端 OOM
|
||||
if (column > 5000)
|
||||
column = 1;
|
||||
return SourceLocation(
|
||||
SourcePosition(line, column, tok.length),
|
||||
fileName,
|
||||
"[internal parser]",
|
||||
magic_enum::enum_name(currentState().type).data());
|
||||
}
|
||||
|
||||
/* TypeExpressions */
|
||||
inline Error makeUnexpectTokenError(
|
||||
const String &stmt,
|
||||
const String &exp,
|
||||
const Token &got,
|
||||
std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
std::format(
|
||||
"expect '{}' in {}, got `{}`", exp, stmt, magic_enum::enum_name(got.type)),
|
||||
"none",
|
||||
makeSourceLocation(got),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
Result<NamedTypeExpr *, Error> parseNamedTypeExpr(); // 当前token为identifier
|
||||
inline Error
|
||||
makeExpectSemicolonError(std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expect ';' after statement",
|
||||
"insert ';'",
|
||||
makeSourceLocation(currentToken()),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
Result<TypeExpr *, Error> parseTypeExpr();
|
||||
inline Error makeExpectSemicolonError(
|
||||
const Token &token, std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expect ';' after statement",
|
||||
"insert ';'",
|
||||
makeSourceLocation(token),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
/* Expressions */
|
||||
Result<LiteralExpr *, Error> parseLiteralExpr(); // 当前token为literal时调用
|
||||
Result<IdentiExpr *, Error> parseIdentiExpr(); // 当前token为Identifier调用
|
||||
Result<decltype(StructDefStmt::typeParameters), Error> parseTypeParameters();
|
||||
|
||||
Result<InfixExpr *, Error> parseInfixExpr(
|
||||
Expr *); // 由 parseExpression递归调用, 当前token为op
|
||||
Result<PrefixExpr *, Error> parsePrefixExpr(); // 由 parseExpression递归调用, 当前token为op
|
||||
|
||||
Result<IndexExpr *, Error> parseIndexExpr(
|
||||
Expr *); // 由 parseExpression调用, 当前token为 `[`
|
||||
Result<CallExpr *, Error> parseCallExpr(Expr *); // 由 parseExpression调用, 当前token为 `(`
|
||||
Result<Expr *, Error> parseTypeExpr();
|
||||
Result<Expr *, Error> parseNamedTypeExpr();
|
||||
Result<Expr *, Error> parseFnTypeExpr();
|
||||
|
||||
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||
Result<Expr *, Error> parseLiteralExpr();
|
||||
Result<Expr *, Error> parseIdentiExpr();
|
||||
Result<Expr *, Error> parsePrefixExpr();
|
||||
Result<Expr *, Error> parseInfixExpr(Expr *);
|
||||
Result<Expr *, Error> parseIndexExpr(Expr *);
|
||||
Result<Expr *, Error> parseCallExpr(Expr *);
|
||||
Result<Expr *, Error> parseNewExpr();
|
||||
Result<Expr *, Error> parseLambdaExpr();
|
||||
|
||||
/* Statements */
|
||||
Result<BlockStmt *, Error> parseBlockStmt(); // 当前token为 {
|
||||
Result<VarDecl *, Error> parseVarDecl(bool); // 由 parseStatement调用, 当前token为 var
|
||||
Result<IfStmt *, Error> parseIfStmt(); // 由 parseStatement调用, 当前token为 if
|
||||
Result<WhileStmt *, Error> parseWhileStmt(); // 由 parseStatement调用, 当前token为 while
|
||||
Result<BlockStmt *, Error> parseBlockStmt();
|
||||
Result<VarDecl *, Error> parseVarDecl(bool);
|
||||
Result<VarDecl *, Error> parseConstDecl(bool);
|
||||
Result<IfStmt *, Error> parseIfStmt();
|
||||
Result<WhileStmt *, Error> parseWhileStmt();
|
||||
Result<DynArray<Param *>, Error> parseFnParams();
|
||||
Result<FnDefStmt *, Error> parseFnDefStmt(bool);
|
||||
Result<ReturnStmt *, Error> parseReturnStmt();
|
||||
|
||||
Result<DynArray<Param *>, Error> parseFnParams(); // 由 parseFnDefStmt或lambda调用
|
||||
Result<FnDefStmt *, Error> parseFnDefStmt(bool); // 由 parseStatement调用, 当前token为 func
|
||||
|
||||
Result<ReturnStmt *, Error> parseReturnStmt(); // 由 parseStatement调用, 当前token为 return
|
||||
// continue break直接由parseStatement一步解析
|
||||
Result<Stmt *, Error> parseStructDef(bool);
|
||||
Result<Stmt *, Error> parseInterfaceDef(bool);
|
||||
Result<Stmt *, Error> parseImpl();
|
||||
Result<Stmt *, Error> parseForStmt();
|
||||
Result<Stmt *, Error> parseImportStmt();
|
||||
|
||||
Result<Stmt *, Error> parseStatement();
|
||||
|
||||
public:
|
||||
Parser(Lexer &_lexer, SourceManager &_src, String _file, Diagnostics &_diagnostics) :
|
||||
lexer(_lexer), srcManager(_src), fileName(std::move(_file)), diagnostics(_diagnostics)
|
||||
{
|
||||
pushState(State());
|
||||
}
|
||||
|
||||
Result<Program *, Error> Parse();
|
||||
};
|
||||
|
||||
#define SET_STOP_AT(...) currentState().stopAt = {__VA_ARGS__};
|
||||
}; // namespace Fig
|
||||
} // namespace Fig
|
||||
|
||||
@@ -6,27 +6,38 @@ int main()
|
||||
using namespace Fig;
|
||||
|
||||
String fileName = "test.fig";
|
||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||
String filePath =
|
||||
"T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/" + fileName;
|
||||
|
||||
SourceManager srcManager(filePath);
|
||||
|
||||
String source = srcManager.Read();
|
||||
if (!srcManager.read)
|
||||
{
|
||||
std::cerr << "Couldn't read file";
|
||||
std::cerr << "Couldn't read file: " << filePath << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
Parser parser(lexer, srcManager, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
Parser parser(lexer, srcManager, fileName, diagnostics);
|
||||
|
||||
auto result = parser.Parse();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
return 1;
|
||||
}
|
||||
|
||||
diagnostics.EmitAll(srcManager);
|
||||
|
||||
Program *program = *result;
|
||||
for (Stmt *stmt : program->nodes)
|
||||
std::cout << "Parsed " << program->nodes.size() << " statements\n";
|
||||
for (size_t i = 0; i < program->nodes.size(); ++i)
|
||||
{
|
||||
std::cout << stmt->toString() << '\n';
|
||||
std::cout << '[' << i << "] " << program->nodes[i]->toString() << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,56 +1,201 @@
|
||||
/*!
|
||||
@file src/Parser/TypeExprParser.cpp
|
||||
@brief 类型表达式解析器实现
|
||||
@brief 类型表达式解析器实现 — 类型即值,产生 Expr* 而非 TypeExpr*
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-25
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Parser/Parser.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<NamedTypeExpr *, Error> Parser::parseNamedTypeExpr() // 当前token为identifier
|
||||
Result<decltype(StructDefStmt::typeParameters), Error> Parser::parseTypeParameters()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingTypeParameters});
|
||||
decltype(StructDefStmt::typeParameters) tp;
|
||||
|
||||
const Token &lab = consumeToken(); // consume `<`
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed `<` in type parameters",
|
||||
"insert '>'",
|
||||
makeSourceLocation(lab)));
|
||||
}
|
||||
if (match(TokenType::Greater)) // >
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeParams", "tp name", currentToken()));
|
||||
}
|
||||
|
||||
const Token &name_tok = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_tok.index, name_tok.length);
|
||||
tp.push_back(name);
|
||||
|
||||
if (!match(TokenType::Comma))
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError(
|
||||
"TypeParams", "comma or type parameter", currentToken()));
|
||||
}
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Result<Expr *, Error> Parser::parseNamedTypeExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingNamedTypeExpr});
|
||||
|
||||
SourceLocation location = makeSourceLocation(currentToken());
|
||||
|
||||
DynArray<String> path;
|
||||
while (true)
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
const Token &subPathTok = consumeToken();
|
||||
const String &subPath = srcManager.GetSub(subPathTok.index, subPathTok.length);
|
||||
path.push_back(subPath);
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeExpr", "type name", currentToken()));
|
||||
}
|
||||
|
||||
if (match(TokenType::Dot))
|
||||
const Token &firstTok = consumeToken();
|
||||
SourceLocation firstLoc = makeSourceLocation(firstTok);
|
||||
const String &firstName = srcManager.GetSub(firstTok.index, firstTok.length);
|
||||
|
||||
IdentiExpr *ident = arena.Allocate<IdentiExpr>(firstName, firstLoc);
|
||||
Expr *base = ident;
|
||||
|
||||
// a.b.c
|
||||
while (match(TokenType::Dot))
|
||||
{
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("named type expr", "identifier", currentToken()));
|
||||
makeUnexpectTokenError("TypeExpr", "identifier after `.`", currentToken()));
|
||||
}
|
||||
const Token &tok = consumeToken();
|
||||
const String &name = srcManager.GetSub(tok.index, tok.length);
|
||||
SourceLocation loc = makeSourceLocation(tok);
|
||||
base = arena.Allocate<MemberExpr>(base, name, loc);
|
||||
}
|
||||
else
|
||||
|
||||
// generic args
|
||||
if (match(TokenType::Less))
|
||||
{
|
||||
DynArray<Expr *> args;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
return std::unexpected(result.error());
|
||||
args.push_back(*result);
|
||||
|
||||
if (match(TokenType::Greater))
|
||||
break; // `>`
|
||||
if (!match(TokenType::Comma))
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken()));
|
||||
}
|
||||
|
||||
base = arena.Allocate<ApplyExpr>(base, std::move(args), firstLoc);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseFnTypeExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingFnTypeExpr});
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `func`
|
||||
if (!match(TokenType::LeftParen)) // `(`
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "lparen (", currentToken()));
|
||||
}
|
||||
|
||||
DynArray<Expr *> paraTypes;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
paraTypes.push_back(*result);
|
||||
|
||||
if (match(TokenType::RightParen))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (isEOF)
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "rparen )", currentToken()));
|
||||
}
|
||||
if (!match(TokenType::Comma))
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "comma ,", currentToken()));
|
||||
}
|
||||
NamedTypeExpr *namedTypeExpr = new NamedTypeExpr(path, location);
|
||||
return namedTypeExpr;
|
||||
}
|
||||
|
||||
Result<TypeExpr *, Error> Parser::parseTypeExpr()
|
||||
Expr *returnType = nullptr;
|
||||
|
||||
if (match(TokenType::RightArrow)) // ->
|
||||
{
|
||||
// TODO: 泛型表达式解析
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
returnType = *result;
|
||||
}
|
||||
|
||||
FnTypeExpr *fnTypeExpr = arena.Allocate<FnTypeExpr>(paraTypes, returnType);
|
||||
return fnTypeExpr;
|
||||
}
|
||||
|
||||
|
||||
Result<Expr *, Error> Parser::parseTypeExpr()
|
||||
{
|
||||
Expr *base = nullptr;
|
||||
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
return parseNamedTypeExpr();
|
||||
auto result = parseNamedTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else if (currentToken().type == TokenType::Function)
|
||||
{
|
||||
auto result = parseFnTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError("type expr", "name/...", currentToken()));
|
||||
return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken()));
|
||||
}
|
||||
|
||||
// nullable
|
||||
if (currentToken().type == TokenType::Question) // ?
|
||||
{
|
||||
base = arena.Allocate<NullableExpr>(
|
||||
base, makeSourceLocation(consumeToken())); // consume `?`
|
||||
}
|
||||
}; // namespace Fig
|
||||
|
||||
return base;
|
||||
}
|
||||
} // namespace Fig
|
||||
|
||||
311
src/Repl/Repl.hpp
Normal file
311
src/Repl/Repl.hpp
Normal file
@@ -0,0 +1,311 @@
|
||||
/*!
|
||||
@file src/Repl/Repl.hpp
|
||||
@brief Repl定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-11
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <Core/SourceLocations.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
#include <Utils/ConsoleSize.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Repl
|
||||
{
|
||||
private:
|
||||
size_t rline = 1;
|
||||
|
||||
std::istream ∈
|
||||
std::ostream &out;
|
||||
std::ostream &err;
|
||||
|
||||
public:
|
||||
Repl(std::istream &_in, std::ostream &_out, std::ostream &_err) :
|
||||
in(_in), out(_out), err(_err)
|
||||
{
|
||||
}
|
||||
|
||||
void PrintInfo()
|
||||
{
|
||||
out << std::format(
|
||||
"Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
|
||||
Core::VERSION,
|
||||
Core::LICENSE);
|
||||
out << std::format(
|
||||
"Build time: {} [{} x{} on {}]\n",
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
Core::PLATFORM);
|
||||
out << "Type '#exit' to exit, '#clear' to clear the the screen, '#license' to see the full license, '#logo' to see a GREAT logo\n";
|
||||
out << '\n';
|
||||
}
|
||||
|
||||
void ClearConsole()
|
||||
{
|
||||
// \033[2J: 清除整个屏幕
|
||||
// \033[H: 将光标移动到左上角
|
||||
out << "\033[2J\033[H" << std::flush;
|
||||
}
|
||||
|
||||
void PrintLicense()
|
||||
{
|
||||
static DynArray<std::string> license_lines{};
|
||||
if (license_lines.empty())
|
||||
{
|
||||
std::string l;
|
||||
bool last_was_r = false;
|
||||
|
||||
for (size_t i = 0; i < Core::LICENSE_TEXT.size(); ++i)
|
||||
{
|
||||
char c = Core::LICENSE_TEXT[i];
|
||||
|
||||
if (c == '\r')
|
||||
{
|
||||
last_was_r = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
if (!l.empty() || last_was_r)
|
||||
{
|
||||
license_lines.push_back(l);
|
||||
l.clear();
|
||||
}
|
||||
last_was_r = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (last_was_r)
|
||||
{
|
||||
if (!l.empty())
|
||||
{
|
||||
license_lines.push_back(l);
|
||||
l.clear();
|
||||
}
|
||||
last_was_r = false;
|
||||
}
|
||||
|
||||
l += c;
|
||||
}
|
||||
|
||||
if (!l.empty() || last_was_r)
|
||||
{
|
||||
license_lines.push_back(l);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int lines_per_page = 50;
|
||||
unsigned int page_printed = 0;
|
||||
unsigned int lines_printed = 0;
|
||||
unsigned int total_lines = license_lines.size();
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto _csize = Utils::getConsoleSize();
|
||||
if (_csize)
|
||||
{
|
||||
lines_per_page = static_cast<unsigned int>((*_csize).first * 0.75);
|
||||
if (lines_per_page < 1)
|
||||
lines_per_page = 1;
|
||||
}
|
||||
|
||||
unsigned int lines_this_page =
|
||||
std::min(lines_per_page, total_lines - lines_printed);
|
||||
|
||||
for (unsigned int i = 0; i < lines_this_page; ++i)
|
||||
{
|
||||
out << license_lines[lines_printed + i] << '\n';
|
||||
}
|
||||
|
||||
lines_printed += lines_this_page;
|
||||
page_printed++;
|
||||
|
||||
if (lines_printed >= total_lines)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int pages_total = (total_lines + lines_per_page - 1) / lines_per_page;
|
||||
|
||||
out << "\n"
|
||||
<< std::format(
|
||||
"Press any key to continue... ({}/{})", page_printed, pages_total);
|
||||
|
||||
in.get();
|
||||
out << '\n';
|
||||
++rline;
|
||||
}
|
||||
}
|
||||
// void PrintError(const Error &error, const String &source)
|
||||
// {
|
||||
// err << "Oops! An error occurred!";
|
||||
// err << "🔥 " << 'E' << static_cast<int>(error.type) << ": " << error.message << '\n';
|
||||
// err << "Line " << rline << ", `" << source << "`\n";
|
||||
// err << "Suggestion: " << error.suggestion << '\n';
|
||||
// err << std::format(
|
||||
// "Thrower: {} ({}:{}:{})\n",
|
||||
// error.thrower_loc.function_name(),
|
||||
// error.thrower_loc.file_name(),
|
||||
// error.thrower_loc.line(),
|
||||
// error.thrower_loc.column());
|
||||
// }
|
||||
|
||||
void PrintError(const Error &error, const String &source)
|
||||
{
|
||||
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
||||
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
||||
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
|
||||
|
||||
namespace TC = TerminalColors;
|
||||
std::ostream &err = CoreIO::GetStdErr();
|
||||
|
||||
uint8_t level = ErrorLevel(error.type);
|
||||
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" :
|
||||
// "Critical"));
|
||||
const char *const &level_color =
|
||||
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||
|
||||
err << "\n";
|
||||
err << "🔥 "
|
||||
<< level_color
|
||||
//<< '(' << level_name << ')'
|
||||
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color
|
||||
<< ErrorTypeToString(error.type) << TC::Reset << '\n';
|
||||
|
||||
const SourceLocation &location = error.location;
|
||||
|
||||
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.'
|
||||
<< location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray
|
||||
<< location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset
|
||||
<< '\n';
|
||||
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
||||
err << TC::DarkGray << " └─ " << TC::Reset;
|
||||
err << source << "\n";
|
||||
|
||||
err << "\n";
|
||||
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
||||
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")"
|
||||
<< TC::Reset << "\n";
|
||||
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
||||
err << '\n';
|
||||
}
|
||||
|
||||
unsigned int Start() // exit code: unsigned int
|
||||
{
|
||||
// TODO: 多行输入 Repl
|
||||
|
||||
PrintInfo();
|
||||
|
||||
String fileName = "[REPL]";
|
||||
String filePath = "src/Repl/Repl.hpp";
|
||||
|
||||
SourceManager manager;
|
||||
|
||||
VM vm;
|
||||
|
||||
Value v = Value::GetNullInstance();
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::string buf;
|
||||
|
||||
out << ">";
|
||||
|
||||
std::getline(in, buf);
|
||||
|
||||
if (buf.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (buf == "#exit")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (buf == "#clear")
|
||||
{
|
||||
ClearConsole();
|
||||
continue;
|
||||
}
|
||||
else if (buf == "#license")
|
||||
{
|
||||
PrintLicense();
|
||||
continue;
|
||||
}
|
||||
else if (buf == "#logo")
|
||||
{
|
||||
out << Core::LOGO << '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
String source(buf);
|
||||
|
||||
Lexer lexer(buf, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, fileName, diagnostics);
|
||||
|
||||
auto _program = parser.Parse();
|
||||
if (!_program)
|
||||
{
|
||||
PrintError(_program.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
Program *program = *_program;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
auto result = analyzer.Analyze(program);
|
||||
|
||||
analyzer.GetDiagnostics().EmitAll(manager);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
PrintError(result.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
Compiler compiler(manager, diagnostics);
|
||||
|
||||
auto compile_result = compiler.Compile(program);
|
||||
if (!compile_result)
|
||||
{
|
||||
PrintError(compile_result.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnostics.EmitAll(manager);
|
||||
|
||||
CompiledModule *compiledModule = *compile_result;
|
||||
|
||||
auto exe_result = vm.Execute(compiledModule);
|
||||
if (!exe_result)
|
||||
{
|
||||
PrintError(exe_result.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
v = *exe_result;
|
||||
if (!v.IsNull())
|
||||
{
|
||||
out << v.ToString() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return (v.IsInt() ? v.AsInt() : 0);
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
10
src/Repl/ReplTest.cpp
Normal file
10
src/Repl/ReplTest.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <Repl/Repl.hpp>
|
||||
#include <Core/Core.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Fig;
|
||||
Repl repl(CoreIO::GetStdCin(), CoreIO::GetStdOut(), CoreIO::GetStdErr());
|
||||
|
||||
repl.Start();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,100 +1,53 @@
|
||||
/*!
|
||||
@file src/Sema/Analyzer.hpp
|
||||
@brief 前端类型检查器定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-23
|
||||
@brief 语义分析器定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Sema/Environment.hpp>
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Sema/Type.hpp>
|
||||
#include <Sema/Environment.hpp>
|
||||
#include <Utils/Arena.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Analyzer
|
||||
{
|
||||
private:
|
||||
Environment env;
|
||||
Arena arena;
|
||||
SourceManager &manager;
|
||||
|
||||
TypeContext typeCtx;
|
||||
Environment env;
|
||||
Diagnostics diag;
|
||||
|
||||
TypeInfo *currentReturnType = nullptr; // 正在分析的函数,预期返回类型
|
||||
HashMap<String, BaseType*> globalTypes;
|
||||
HashMap<String, Symbol*> globalSymbols;
|
||||
|
||||
struct ReturnTypeProtector
|
||||
{
|
||||
Analyzer *analyzer;
|
||||
TypeInfo *prevCurrentReturnType;
|
||||
bool hasInit = false;
|
||||
bool hasMain = false;
|
||||
|
||||
[[nodiscard]]
|
||||
ReturnTypeProtector(Analyzer *_analyzer, TypeInfo *current) :
|
||||
analyzer(_analyzer), prevCurrentReturnType(_analyzer->currentReturnType)
|
||||
{
|
||||
analyzer->currentReturnType = current;
|
||||
}
|
||||
// 核心递归查找:解决跨越函数边界的捕获问题
|
||||
Result<Symbol*, Error> resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope* startScope);
|
||||
|
||||
~ReturnTypeProtector()
|
||||
{
|
||||
analyzer->currentReturnType = prevCurrentReturnType;
|
||||
}
|
||||
Result<Type, Error> resolveTypeExpr(Expr *texpr);
|
||||
Result<void, Error> pass1(Program *prog);
|
||||
Result<void, Error> resolveTypes(Program *prog);
|
||||
Result<void, Error> checkBodies(Program *prog);
|
||||
|
||||
ReturnTypeProtector(const ReturnTypeProtector &) = delete;
|
||||
ReturnTypeProtector &operator=(const ReturnTypeProtector &) = delete;
|
||||
};
|
||||
Result<void, Error> analyzeStmt(Stmt *stmt);
|
||||
Result<Type, Error> analyzeExpr(Expr *expr);
|
||||
|
||||
|
||||
SourceLocation makeSourceLocation(
|
||||
AstNode *ast, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
return SourceLocation(ast->location.sp,
|
||||
ast->location.fileName,
|
||||
"[internal analyzer]",
|
||||
loc.function_name());
|
||||
}
|
||||
|
||||
bool isValidLvalue(Expr *expr)
|
||||
{
|
||||
if (expr->type == AstType::IdentiExpr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (expr->type == AstType::InfixExpr)
|
||||
{
|
||||
InfixExpr *infix = static_cast<InfixExpr *>(expr);
|
||||
if (infix->op == BinaryOperator::MemberAccess)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (expr->type == AstType::IndexExpr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Result<TypeInfo *, Error> resolveType(TypeExpr *);
|
||||
|
||||
Result<void, Error> analyzeVarDecl(VarDecl *);
|
||||
Result<void, Error> analyzeIfStmt(IfStmt *);
|
||||
Result<void, Error> analyzeWhileStmt(WhileStmt *);
|
||||
Result<void, Error> analyzeFnDefStmt(FnDefStmt *);
|
||||
Result<void, Error> analyzeReturnStmt(ReturnStmt *);
|
||||
|
||||
Result<void, Error> analyzeIdentiExpr(IdentiExpr *);
|
||||
Result<void, Error> analyzeInfixExpr(InfixExpr *);
|
||||
Result<void, Error> analyzeCallExpr(CallExpr *);
|
||||
|
||||
Result<void, Error> analyzeStmt(Stmt *);
|
||||
Result<void, Error> analyzeExpr(Expr *);
|
||||
int addUpvalue(Scope *scope, Symbol *target, bool isLocal);
|
||||
|
||||
public:
|
||||
Result<void, Error> Analyze(Program *);
|
||||
Analyzer(SourceManager &m) : manager(m) {}
|
||||
|
||||
Analyzer(SourceManager &_manager) : manager(_manager), typeCtx() {}
|
||||
Result<void, Error> Analyze(Program *prog);
|
||||
|
||||
Diagnostics& GetDiagnostics() { return diag; }
|
||||
TypeContext& GetTypeContext() { return typeCtx; }
|
||||
};
|
||||
}; // namespace Fig
|
||||
}
|
||||
|
||||
@@ -1,50 +1,64 @@
|
||||
#include <Sema/Analyzer.hpp>
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
void runTest(const std::string &path)
|
||||
{
|
||||
using namespace Fig;
|
||||
std::cout << "\n[TEST] Testing: " << path << std::endl;
|
||||
|
||||
SourceManager srcManager{String(path)};
|
||||
String source = srcManager.Read();
|
||||
if (!srcManager.read)
|
||||
{
|
||||
std::cerr << "FAILED: Could not read file" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Lexer lexer(source, String(path));
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, srcManager, String(path), diagnostics);
|
||||
|
||||
diagnostics.EmitAll(srcManager);
|
||||
|
||||
auto pRes = parser.Parse();
|
||||
if (!pRes)
|
||||
{
|
||||
std::cerr << "FAILED: Parser Error" << std::endl;
|
||||
ReportError(pRes.error(), srcManager);
|
||||
return;
|
||||
}
|
||||
|
||||
// 修复:确保 analyzer 存活直到错误打印完成
|
||||
Analyzer analyzer(srcManager);
|
||||
auto aRes = analyzer.Analyze(*pRes);
|
||||
|
||||
if (!aRes)
|
||||
{
|
||||
std::cout << "SUCCESS: Analyzer correctly caught error:" << std::endl;
|
||||
ReportError(aRes.error(), srcManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "FAILED: Analyzer missed the semantic error!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Fig;
|
||||
|
||||
const String &fileName = "test.fig";
|
||||
const String &filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||
|
||||
SourceManager manager(filePath);
|
||||
manager.Read();
|
||||
|
||||
if (!manager.read)
|
||||
std::string testDir = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/tests/Sema";
|
||||
for (const auto &entry : fs::directory_iterator(testDir))
|
||||
{
|
||||
std::cerr << "Read file failed \n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Lexer lexer(manager.GetSource(), fileName);
|
||||
Parser parser(lexer, manager, fileName);
|
||||
|
||||
auto result = parser.Parse();
|
||||
if (!result)
|
||||
if (entry.path().extension() == ".fig")
|
||||
{
|
||||
ReportError(result.error(), manager);
|
||||
return 1;
|
||||
runTest(entry.path().string());
|
||||
}
|
||||
|
||||
Program *program = *result;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
|
||||
const auto &analyzeResult = analyzer.Analyze(program);
|
||||
if (!analyzeResult)
|
||||
{
|
||||
ReportError(analyzeResult.error(), manager);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Analyze successfully, PROGRAM OK\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -1,147 +1,74 @@
|
||||
/*!
|
||||
@file src/Sema/Environment.hpp
|
||||
@brief 符号和作用域环境定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-23
|
||||
@brief 树状符号表定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// 记录在 Analyzer 中的符号元数据
|
||||
enum class SymbolLocation
|
||||
{
|
||||
Global,
|
||||
Local,
|
||||
Upvalue
|
||||
};
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
String name;
|
||||
TypeInfo *type;
|
||||
bool isPublic;
|
||||
int depth; // 词法作用域深度
|
||||
bool isConstant; // 是否是 const 声明的不可变常量 (用于报错: 尝试修改常量)
|
||||
Type type;
|
||||
SymbolLocation location;
|
||||
int index;
|
||||
bool isConst;
|
||||
|
||||
int localId = -1; // Analyzer 虚拟槽位分配
|
||||
Symbol(String n, Type t, SymbolLocation l, int i, bool c) :
|
||||
name(std::move(n)), type(t), location(l), index(i), isConst(c)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// 作用域回档水位线
|
||||
struct ScopeWatermark
|
||||
struct UpvalueCapture
|
||||
{
|
||||
std::size_t symbolCount;
|
||||
int savedLocalId;
|
||||
Symbol *target;
|
||||
int index;
|
||||
bool isLocal;
|
||||
};
|
||||
|
||||
// 语义分析函数上下文 (隔离局部变量 ID 空间)
|
||||
struct SemaFuncState
|
||||
struct Scope
|
||||
{
|
||||
SemaFuncState *enclosing = nullptr;
|
||||
int currentDepth = 0;
|
||||
Scope *parent = nullptr;
|
||||
bool isFunctionBoundary = false;
|
||||
|
||||
HashMap<String, Symbol *> locals;
|
||||
DynArray<UpvalueCapture> upvalues;
|
||||
|
||||
int nextLocalId = 0;
|
||||
DynArray<ScopeWatermark> scopeStack;
|
||||
|
||||
Scope(Scope *p, bool isFn) : parent(p), isFunctionBoundary(isFn)
|
||||
{
|
||||
if (p && !isFn)
|
||||
nextLocalId = p->nextLocalId;
|
||||
}
|
||||
};
|
||||
|
||||
class Environment
|
||||
{
|
||||
private:
|
||||
DynArray<Symbol> symbols;
|
||||
SemaFuncState *current = nullptr;
|
||||
|
||||
public:
|
||||
Environment()
|
||||
Scope *current = nullptr;
|
||||
|
||||
void Push(bool isFn)
|
||||
{
|
||||
current = new SemaFuncState();
|
||||
current = new Scope(current, isFn);
|
||||
}
|
||||
|
||||
~Environment()
|
||||
void Pop()
|
||||
{
|
||||
while (current)
|
||||
{
|
||||
SemaFuncState *prev = current->enclosing;
|
||||
delete current;
|
||||
current = prev;
|
||||
}
|
||||
}
|
||||
|
||||
// 函数边界控
|
||||
|
||||
void EnterFunction()
|
||||
{
|
||||
SemaFuncState *newState = new SemaFuncState();
|
||||
newState->enclosing = current;
|
||||
current = newState;
|
||||
}
|
||||
|
||||
void LeaveFunction()
|
||||
{
|
||||
assert(current && "Environment: Unmatched LeaveFunction");
|
||||
SemaFuncState *oldState = current;
|
||||
current = oldState->enclosing;
|
||||
delete oldState;
|
||||
}
|
||||
|
||||
// 词法作用域控制
|
||||
|
||||
void EnterScope()
|
||||
{
|
||||
current->currentDepth++;
|
||||
current->scopeStack.push_back({symbols.size(), current->nextLocalId});
|
||||
}
|
||||
|
||||
void LeaveScope()
|
||||
{
|
||||
assert(current->currentDepth > 0 && "Environment: Unmatched LeaveScope");
|
||||
current->currentDepth--;
|
||||
|
||||
assert(!current->scopeStack.empty());
|
||||
ScopeWatermark archive = current->scopeStack.back();
|
||||
current->scopeStack.pop_back();
|
||||
|
||||
// 物理截断符号表,回滚槽位发号器以复用物理寄存器
|
||||
while (symbols.size() > archive.symbolCount)
|
||||
{
|
||||
symbols.pop_back();
|
||||
}
|
||||
current->nextLocalId = archive.savedLocalId;
|
||||
}
|
||||
|
||||
// 符号操作
|
||||
|
||||
// 注册符号, 返回分配的 localId。调用前内部执行同级作用域重定义断言
|
||||
int Define(const String &name, TypeInfo *type, bool isPublic, bool isConst)
|
||||
{
|
||||
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
|
||||
{
|
||||
if (it->depth < current->currentDepth)
|
||||
break;
|
||||
if (it->name == name)
|
||||
{
|
||||
assert(false && "Environment.Define: redefinition");
|
||||
}
|
||||
}
|
||||
|
||||
int allocatedId = current->nextLocalId++;
|
||||
symbols.push_back({name, type, isPublic, current->currentDepth, isConst, allocatedId});
|
||||
return allocatedId;
|
||||
}
|
||||
|
||||
// 解析符号。找不到返回 nullopt
|
||||
std::optional<Symbol> Resolve(const String &name)
|
||||
{
|
||||
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
|
||||
{
|
||||
if (it->name == name)
|
||||
return *it;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int GetDepth() const
|
||||
{
|
||||
return current->currentDepth;
|
||||
Scope *old = current;
|
||||
current = current->parent;
|
||||
delete old;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
100
src/Sema/Type.cpp
Normal file
100
src/Sema/Type.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*!
|
||||
@file src/Sema/Type.cpp
|
||||
@brief 类型系统实现
|
||||
*/
|
||||
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
bool Type::is(TypeTag t) const
|
||||
{
|
||||
return base && base->tag == t;
|
||||
}
|
||||
|
||||
String Type::toString() const
|
||||
{
|
||||
if (!base)
|
||||
return "Unknown";
|
||||
if (base->tag == TypeTag::Function)
|
||||
{
|
||||
auto *ft = static_cast<FuncType *>(base);
|
||||
String sig = "func(";
|
||||
for (size_t i = 0; i < ft->paramTypes.size(); ++i)
|
||||
{
|
||||
sig += ft->paramTypes[i].toString();
|
||||
if (i < ft->paramTypes.size() - 1)
|
||||
sig += ", ";
|
||||
}
|
||||
sig += ") -> " + ft->retType.toString();
|
||||
return sig;
|
||||
}
|
||||
|
||||
String res = base->name;
|
||||
if (isNullable && base->tag != TypeTag::Null)
|
||||
res += "?";
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Type::isAssignableTo(const Type &target) const
|
||||
{
|
||||
if (target.is(TypeTag::Any) || this->is(TypeTag::Any))
|
||||
{
|
||||
return true; // Any 逃逸
|
||||
}
|
||||
if (this->is(TypeTag::Null) && target.isNullable)
|
||||
{
|
||||
return true; // Null 安全赋值
|
||||
}
|
||||
|
||||
return *this->base == *target.base && (!this->isNullable || target.isNullable);
|
||||
}
|
||||
|
||||
TypeContext::TypeContext()
|
||||
{
|
||||
intType = new BaseType(TypeTag::Int, "Int");
|
||||
doubleType = new BaseType(TypeTag::Double, "Double");
|
||||
stringType = new BaseType(TypeTag::String, "String");
|
||||
boolType = new BaseType(TypeTag::Bool, "Bool");
|
||||
anyType = new BaseType(TypeTag::Any, "Any");
|
||||
nullType = new BaseType(TypeTag::Null, "Null");
|
||||
|
||||
allTypes.push_back(intType);
|
||||
allTypes.push_back(doubleType);
|
||||
allTypes.push_back(stringType);
|
||||
allTypes.push_back(boolType);
|
||||
allTypes.push_back(anyType);
|
||||
allTypes.push_back(nullType);
|
||||
}
|
||||
|
||||
TypeContext::~TypeContext()
|
||||
{
|
||||
for (auto t : allTypes)
|
||||
{
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
Type TypeContext::GetBasic(TypeTag tag, bool nullable)
|
||||
{
|
||||
BaseType *b = nullptr;
|
||||
switch (tag)
|
||||
{
|
||||
case TypeTag::Int: b = intType; break;
|
||||
case TypeTag::Double: b = doubleType; break;
|
||||
case TypeTag::String: b = stringType; break;
|
||||
case TypeTag::Bool: b = boolType; break;
|
||||
case TypeTag::Any: b = anyType; break;
|
||||
case TypeTag::Null: b = nullType; break;
|
||||
default: break;
|
||||
}
|
||||
return {b, nullable};
|
||||
}
|
||||
|
||||
Type TypeContext::CreateFuncType(DynArray<Type> params, Type ret)
|
||||
{
|
||||
auto *ft = new FuncType(std::move(params), ret);
|
||||
allTypes.push_back(ft);
|
||||
return Type{ft, false};
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,136 +1,135 @@
|
||||
/*!
|
||||
@file src/Sema/Type.hpp
|
||||
@brief 前端类型检查的类型定义和类型驻留池
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-23
|
||||
@brief 类型系统定义:对齐 NaN-boxing 物理布局
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <cstdint>
|
||||
#include <Error/Error.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
enum class TypeTag : std::uint8_t
|
||||
{
|
||||
Any, // 动态类型底线
|
||||
Null, // 空值
|
||||
Int,
|
||||
Double,
|
||||
Bool,
|
||||
String,
|
||||
Bool,
|
||||
Null,
|
||||
Any,
|
||||
Function,
|
||||
Struct,
|
||||
Interface
|
||||
};
|
||||
|
||||
struct TypeInfo
|
||||
{
|
||||
TypeTag tag;
|
||||
String name; // 完整路径序列化, 如 Int, std.file.File
|
||||
class BaseType;
|
||||
|
||||
bool isAny() const
|
||||
struct Type
|
||||
{
|
||||
return tag == TypeTag::Any;
|
||||
BaseType *base = nullptr;
|
||||
bool isNullable = false;
|
||||
|
||||
bool operator==(const Type &other) const
|
||||
{
|
||||
return base == other.base && isNullable == other.isNullable;
|
||||
}
|
||||
bool operator!=(const Type &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool is(TypeTag tag) const;
|
||||
String toString() const;
|
||||
|
||||
bool isAssignableTo(const Type &target) const;
|
||||
};
|
||||
|
||||
class BaseType
|
||||
{
|
||||
public:
|
||||
TypeTag tag;
|
||||
String name;
|
||||
BaseType(TypeTag t, String n) : tag(t), name(std::move(n)) {}
|
||||
virtual ~BaseType() = default;
|
||||
|
||||
bool operator==(const BaseType &other) const
|
||||
{
|
||||
return tag == other.tag && name == other.name;
|
||||
}
|
||||
};
|
||||
|
||||
class FuncType : public BaseType
|
||||
{
|
||||
public:
|
||||
DynArray<Type> paramTypes;
|
||||
Type retType;
|
||||
FuncType(DynArray<Type> params, Type ret) :
|
||||
BaseType(TypeTag::Function, "Function"), paramTypes(std::move(params)), retType(ret)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const FuncType &other) const
|
||||
{
|
||||
return paramTypes == other.paramTypes && retType == other.retType;
|
||||
}
|
||||
};
|
||||
|
||||
class StructType : public BaseType
|
||||
{
|
||||
public:
|
||||
struct Field
|
||||
{
|
||||
String name;
|
||||
Type type;
|
||||
bool isPublic;
|
||||
int index;
|
||||
};
|
||||
DynArray<Field> fields;
|
||||
HashMap<String, size_t> fieldMap;
|
||||
HashMap<String, struct FnDefStmt *> methods;
|
||||
|
||||
StructType(String n) : BaseType(TypeTag::Struct, std::move(n)) {}
|
||||
void AddField(String name, Type type, bool isPublic)
|
||||
{
|
||||
size_t idx = fields.size();
|
||||
fields.push_back({name, type, isPublic, (int) idx});
|
||||
fieldMap[name] = idx;
|
||||
}
|
||||
|
||||
bool operator==(const StructType &other) const
|
||||
{
|
||||
return this == &other; // 即使是两个完全一样的struct, 也认作不同的type
|
||||
}
|
||||
};
|
||||
|
||||
class InterfaceType : public BaseType
|
||||
{
|
||||
public:
|
||||
struct MethodSig
|
||||
{
|
||||
String name;
|
||||
DynArray<Type> params;
|
||||
Type retType;
|
||||
};
|
||||
HashMap<String, MethodSig> methods;
|
||||
InterfaceType(String n) : BaseType(TypeTag::Interface, std::move(n)) {}
|
||||
|
||||
bool operator==(const InterfaceType &other) const
|
||||
{
|
||||
return this == &other; // 即使是两个完全一样的interface, 也认作不同的type
|
||||
}
|
||||
};
|
||||
|
||||
// 全局唯一类型驻留池
|
||||
class TypeContext
|
||||
{
|
||||
private:
|
||||
DynArray<TypeInfo *> allTypes;
|
||||
|
||||
// 缓存
|
||||
TypeInfo *typeAny;
|
||||
TypeInfo *typeNull;
|
||||
TypeInfo *typeInt;
|
||||
TypeInfo *typeDouble;
|
||||
TypeInfo *typeBool;
|
||||
TypeInfo *typeString;
|
||||
TypeInfo *typeFunction;
|
||||
TypeInfo *typeStruct;
|
||||
|
||||
public:
|
||||
TypeInfo *GetAny()
|
||||
{
|
||||
return typeAny;
|
||||
}
|
||||
TypeInfo *GetNull()
|
||||
{
|
||||
return typeNull;
|
||||
}
|
||||
TypeInfo *GetInt()
|
||||
{
|
||||
return typeInt;
|
||||
}
|
||||
TypeInfo *GetDouble()
|
||||
{
|
||||
return typeDouble;
|
||||
}
|
||||
TypeInfo *GetBool()
|
||||
{
|
||||
return typeBool;
|
||||
}
|
||||
TypeInfo *GetString()
|
||||
{
|
||||
return typeString;
|
||||
}
|
||||
TypeInfo *GetFunction()
|
||||
{
|
||||
return typeFunction;
|
||||
}
|
||||
TypeInfo *GetStruct()
|
||||
{
|
||||
return typeStruct;
|
||||
}
|
||||
DynArray<BaseType *> allTypes;
|
||||
BaseType *intType, *doubleType, *stringType, *boolType, *anyType, *nullType;
|
||||
|
||||
TypeInfo *ResolveTypePath(const String &fullName)
|
||||
{
|
||||
for (auto *t : allTypes)
|
||||
{
|
||||
if (t->name == fullName)
|
||||
return t;
|
||||
}
|
||||
return nullptr; // 没找到该类型
|
||||
}
|
||||
TypeContext();
|
||||
~TypeContext();
|
||||
|
||||
TypeInfo *ResolveTypePath(const DynArray<String> &path)
|
||||
{
|
||||
// TODO: 支持Module 系统, 查 Module 的导出表
|
||||
String fullName = path.empty() ? "" : path[0];
|
||||
for (size_t i = 1; i < path.size(); ++i)
|
||||
{
|
||||
fullName += "." + path[i];
|
||||
}
|
||||
|
||||
return ResolveTypePath(fullName);
|
||||
}
|
||||
|
||||
~TypeContext()
|
||||
{
|
||||
for (TypeInfo *t : allTypes)
|
||||
delete t;
|
||||
}
|
||||
|
||||
TypeContext()
|
||||
{
|
||||
typeAny = createBuiltin(TypeTag::Any, "Any");
|
||||
typeNull = createBuiltin(TypeTag::Null, "Null");
|
||||
typeInt = createBuiltin(TypeTag::Int, "Int");
|
||||
typeDouble = createBuiltin(TypeTag::Double, "Double");
|
||||
typeBool = createBuiltin(TypeTag::Bool, "Bool");
|
||||
typeString = createBuiltin(TypeTag::String, "String");
|
||||
typeFunction = createBuiltin(TypeTag::Function, "Function");
|
||||
typeStruct = createBuiltin(TypeTag::Struct, "Struct");
|
||||
}
|
||||
|
||||
private:
|
||||
TypeInfo *createBuiltin(TypeTag tag, String name)
|
||||
{
|
||||
TypeInfo *t = new TypeInfo{tag, std::move(name)};
|
||||
allTypes.push_back(t);
|
||||
return t;
|
||||
}
|
||||
Type GetBasic(TypeTag tag, bool nullable = false);
|
||||
Type CreateFuncType(DynArray<Type> params, Type ret);
|
||||
};
|
||||
}; // namespace Fig
|
||||
} // namespace Fig
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Fig
|
||||
{String("try"), TokenType::Try},
|
||||
{String("catch"), TokenType::Catch},
|
||||
{String("throw"), TokenType::Throw},
|
||||
{String("Finally"), TokenType::Finally},
|
||||
{String("finally"), TokenType::Finally},
|
||||
{String("as"), TokenType::As},
|
||||
{String("true"), TokenType::LiteralTrue},
|
||||
{String("false"), TokenType::LiteralFalse},
|
||||
|
||||
115
src/Utils/Arena.hpp
Normal file
115
src/Utils/Arena.hpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*!
|
||||
@file src/Utils/Arena.hpp
|
||||
@brief 线性分配内存池,支持非平凡析构对象的自动清理
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Arena
|
||||
{
|
||||
private:
|
||||
struct DestructorNode
|
||||
{
|
||||
void (*destructor)(void *);
|
||||
void *object;
|
||||
DestructorNode *next;
|
||||
};
|
||||
|
||||
static constexpr std::size_t CHUNK_SIZE = 64 * 1024; // 64KB 块大小
|
||||
|
||||
std::vector<char *> chunks;
|
||||
char *currentPtr = nullptr;
|
||||
std::size_t remaining = 0;
|
||||
DestructorNode *destructorHead = nullptr;
|
||||
|
||||
public:
|
||||
Arena() = default;
|
||||
|
||||
~Arena()
|
||||
{
|
||||
// 逆序调用析构函数
|
||||
DestructorNode *node = destructorHead;
|
||||
while (node)
|
||||
{
|
||||
node->destructor(node->object);
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
// 释放所有分配的内存块
|
||||
for (char *chunk : chunks)
|
||||
{
|
||||
delete[] chunk;
|
||||
}
|
||||
}
|
||||
|
||||
// 禁止拷贝和移动,防止内存所有权混乱
|
||||
Arena(const Arena &) = delete;
|
||||
Arena &operator=(const Arena &) = delete;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T *Allocate(Args &&...args)
|
||||
{
|
||||
std::size_t size = sizeof(T);
|
||||
std::size_t alignment = alignof(T);
|
||||
|
||||
// 在当前块中尝试对齐并分配
|
||||
void *ptr = allocateRaw(size, alignment);
|
||||
|
||||
// 在分配的内存上构造对象
|
||||
T *obj = new (ptr) T(std::forward<Args>(args)...);
|
||||
|
||||
// 如果 T 需要析构(如包含 String),注册到销毁链表
|
||||
if constexpr (!std::is_trivially_destructible_v<T>)
|
||||
{
|
||||
// 注意: DestructorNode 本身是 POD,直接在 Arena 里分配,不需要注册析构
|
||||
void *nodeRaw = allocateRaw(sizeof(DestructorNode), alignof(DestructorNode));
|
||||
DestructorNode *node = new (nodeRaw) DestructorNode();
|
||||
|
||||
node->object = obj;
|
||||
node->destructor = [](void *p) { static_cast<T *>(p)->~T(); };
|
||||
node->next = destructorHead;
|
||||
destructorHead = node;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
void *allocateRaw(std::size_t size, std::size_t alignment)
|
||||
{
|
||||
// 对齐计算
|
||||
std::size_t adjustment = 0;
|
||||
std::size_t currentAddr = reinterpret_cast<std::size_t>(currentPtr);
|
||||
if (alignment > 0 && (currentAddr % alignment) != 0)
|
||||
{
|
||||
adjustment = alignment - (currentAddr % alignment);
|
||||
}
|
||||
|
||||
if (remaining < (size + adjustment))
|
||||
{
|
||||
// 当前块空间不足,分配新块
|
||||
std::size_t nextChunkSize = (size > CHUNK_SIZE) ? size : CHUNK_SIZE;
|
||||
currentPtr = new char[nextChunkSize];
|
||||
chunks.push_back(currentPtr);
|
||||
remaining = nextChunkSize;
|
||||
adjustment = 0; // 新分配的块通常是最大对齐的
|
||||
}
|
||||
|
||||
currentPtr += adjustment;
|
||||
void *res = currentPtr;
|
||||
currentPtr += size;
|
||||
remaining -= (size + adjustment);
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
336
src/Utils/ArgParser/ArgParser.hpp
Normal file
336
src/Utils/ArgParser/ArgParser.hpp
Normal file
@@ -0,0 +1,336 @@
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <expected>
|
||||
#include <format>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace Fig::ArgParser
|
||||
{
|
||||
|
||||
enum class ParseErrorCode
|
||||
{
|
||||
UnknownOption,
|
||||
MissingValue,
|
||||
MissingRequired,
|
||||
InvalidFormat
|
||||
};
|
||||
|
||||
struct ParseError
|
||||
{
|
||||
ParseErrorCode code;
|
||||
String context;
|
||||
|
||||
String Format() const
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case ParseErrorCode::UnknownOption:
|
||||
return String(std::format("Unknown option: {}", context.toStdString()));
|
||||
case ParseErrorCode::MissingValue:
|
||||
return String(
|
||||
std::format("Missing value for option: {}", context.toStdString()));
|
||||
case ParseErrorCode::MissingRequired:
|
||||
return String(
|
||||
std::format("Missing required option: {}", context.toStdString()));
|
||||
case ParseErrorCode::InvalidFormat:
|
||||
return String(
|
||||
std::format("Invalid argument format: {}", context.toStdString()));
|
||||
default: return String("Unknown parse error");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class OptionDef
|
||||
{
|
||||
public:
|
||||
OptionDef(char short_name, String long_name) :
|
||||
short_name_(short_name), long_name_(std::move(long_name))
|
||||
{
|
||||
}
|
||||
|
||||
OptionDef &Help(String help_text)
|
||||
{
|
||||
help_ = std::move(help_text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionDef &DefaultValue(String value)
|
||||
{
|
||||
default_value_ = std::move(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionDef &Required(bool required = true)
|
||||
{
|
||||
required_ = required;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionDef &TakesValue(bool takes_value = true)
|
||||
{
|
||||
takes_value_ = takes_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
char short_name_;
|
||||
String long_name_;
|
||||
String help_;
|
||||
std::optional<String> default_value_;
|
||||
bool required_ = false;
|
||||
bool takes_value_ = false;
|
||||
};
|
||||
|
||||
class ParseResult
|
||||
{
|
||||
public:
|
||||
bool HasFlag(const String &long_name) const
|
||||
{
|
||||
auto it = options_.find(long_name);
|
||||
return it != options_.end() && it->second == String("true");
|
||||
}
|
||||
|
||||
std::optional<String> GetOption(const String &long_name) const
|
||||
{
|
||||
if (auto it = options_.find(long_name); it != options_.end())
|
||||
return it->second;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const DynArray<String> &GetPositionals() const
|
||||
{
|
||||
return positionals_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ArgumentParser;
|
||||
HashMap<String, String> options_;
|
||||
DynArray<String> positionals_;
|
||||
};
|
||||
|
||||
class ArgumentParser
|
||||
{
|
||||
public:
|
||||
explicit ArgumentParser(String prog_name, String description = String("")) :
|
||||
prog_name_(std::move(prog_name)), description_(std::move(description))
|
||||
{
|
||||
}
|
||||
|
||||
OptionDef &AddFlag(char short_name, const String &long_name)
|
||||
{
|
||||
return registerOption(short_name, long_name).TakesValue(false);
|
||||
}
|
||||
|
||||
OptionDef &AddFlag(const String &long_name)
|
||||
{
|
||||
return registerOption('\0', long_name).TakesValue(false);
|
||||
}
|
||||
|
||||
OptionDef &AddOption(char short_name, const String &long_name)
|
||||
{
|
||||
return registerOption(short_name, long_name).TakesValue(true);
|
||||
}
|
||||
|
||||
OptionDef &AddOption(const String &long_name)
|
||||
{
|
||||
return registerOption('\0', long_name).TakesValue(true);
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> Parse(int argc, const char *const *argv) const
|
||||
{
|
||||
if (argc <= 0)
|
||||
return ParseResult{};
|
||||
|
||||
return parseInternal(
|
||||
std::span<const char *const>{argv + 1, static_cast<size_t>(argc - 1)})
|
||||
.and_then([this](ParseResult res) { return validateRequired(std::move(res)); })
|
||||
.and_then([this](ParseResult res) { return applyDefaults(std::move(res)); });
|
||||
}
|
||||
|
||||
String FormatHelp() const
|
||||
{
|
||||
std::string help =
|
||||
std::format("Usage: {} [OPTIONS] [ARGS...]\n\n", prog_name_.toStdString());
|
||||
|
||||
if (!description_.empty())
|
||||
help += std::format("{}\n\n", description_.toStdString());
|
||||
|
||||
help += "Options:\n";
|
||||
for (const auto &opt : options_)
|
||||
{
|
||||
std::string flags;
|
||||
if (opt.short_name_ != '\0')
|
||||
flags = std::format("-{}, --{}", opt.short_name_, opt.long_name_.toStdString());
|
||||
else
|
||||
flags = std::format(" --{}", opt.long_name_.toStdString());
|
||||
|
||||
if (opt.takes_value_)
|
||||
flags += " <val>";
|
||||
|
||||
std::string desc = opt.help_.toStdString();
|
||||
if (opt.default_value_)
|
||||
desc += std::format(" (default: {})", opt.default_value_->toStdString());
|
||||
if (opt.required_)
|
||||
desc += " [required]";
|
||||
|
||||
help += std::format(" {:<25} {}\n", flags, desc);
|
||||
}
|
||||
|
||||
return String(help);
|
||||
}
|
||||
|
||||
private:
|
||||
OptionDef ®isterOption(char short_name, const String &long_name)
|
||||
{
|
||||
size_t idx = options_.size();
|
||||
options_.emplace_back(short_name, long_name);
|
||||
long_map_[long_name] = idx;
|
||||
if (short_name != '\0')
|
||||
short_map_[short_name] = idx;
|
||||
return options_.back();
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> parseInternal(std::span<const char *const> args) const
|
||||
{
|
||||
ParseResult result;
|
||||
bool only_positionals = false;
|
||||
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
std::string_view arg{args[i]};
|
||||
|
||||
if (only_positionals)
|
||||
{
|
||||
result.positionals_.emplace_back(String(std::string(arg)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg == "--")
|
||||
{
|
||||
only_positionals = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.starts_with("--"))
|
||||
{
|
||||
auto kv = arg.substr(2);
|
||||
if (kv.empty())
|
||||
return std::unexpected(
|
||||
ParseError{ParseErrorCode::InvalidFormat, String("--")});
|
||||
|
||||
auto eq_pos = kv.find('=');
|
||||
auto key = kv.substr(0, eq_pos);
|
||||
auto key_s = String(std::string(key));
|
||||
|
||||
auto it = long_map_.find(key_s);
|
||||
if (it == long_map_.end())
|
||||
return std::unexpected(
|
||||
ParseError{ParseErrorCode::UnknownOption, String(std::string(arg))});
|
||||
|
||||
const auto &def = options_[it->second];
|
||||
|
||||
if (def.takes_value_)
|
||||
{
|
||||
if (eq_pos != std::string_view::npos)
|
||||
{
|
||||
result.options_[def.long_name_] =
|
||||
String(std::string(kv.substr(eq_pos + 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i + 1 >= args.size())
|
||||
return std::unexpected(ParseError{
|
||||
ParseErrorCode::MissingValue, String(std::string(arg))});
|
||||
result.options_[def.long_name_] = String(std::string(args[++i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (eq_pos != std::string_view::npos)
|
||||
return std::unexpected(ParseError{
|
||||
ParseErrorCode::InvalidFormat, String(std::string(arg))});
|
||||
result.options_[def.long_name_] = String("true");
|
||||
}
|
||||
}
|
||||
else if (arg.starts_with('-') && arg.size() > 1)
|
||||
{
|
||||
auto group = arg.substr(1);
|
||||
for (size_t j = 0; j < group.size(); ++j)
|
||||
{
|
||||
char c = group[j];
|
||||
auto it = short_map_.find(c);
|
||||
if (it == short_map_.end())
|
||||
return std::unexpected(ParseError{
|
||||
ParseErrorCode::UnknownOption, String(std::string(1, c))});
|
||||
|
||||
const auto &def = options_[it->second];
|
||||
|
||||
if (def.takes_value_)
|
||||
{
|
||||
if (j + 1 < group.size())
|
||||
{
|
||||
result.options_[def.long_name_] =
|
||||
String(std::string(group.substr(j + 1)));
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i + 1 >= args.size())
|
||||
return std::unexpected(ParseError{ParseErrorCode::MissingValue,
|
||||
String(std::format("-{}", c))});
|
||||
result.options_[def.long_name_] = String(std::string(args[++i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.options_[def.long_name_] = String("true");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.positionals_.emplace_back(String(std::string(arg)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> validateRequired(ParseResult res) const
|
||||
{
|
||||
for (const auto &opt : options_)
|
||||
{
|
||||
if (opt.required_ && res.options_.find(opt.long_name_) == res.options_.end())
|
||||
{
|
||||
std::string flag =
|
||||
opt.short_name_ != '\0' ?
|
||||
std::format("-{}/--{}", opt.short_name_, opt.long_name_.toStdString()) :
|
||||
std::format("--{}", opt.long_name_.toStdString());
|
||||
return std::unexpected(
|
||||
ParseError{ParseErrorCode::MissingRequired, String(flag)});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> applyDefaults(ParseResult res) const
|
||||
{
|
||||
for (const auto &opt : options_)
|
||||
{
|
||||
if (opt.default_value_ && res.options_.find(opt.long_name_) == res.options_.end())
|
||||
{
|
||||
res.options_[opt.long_name_] = *opt.default_value_;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
String prog_name_;
|
||||
String description_;
|
||||
DynArray<OptionDef> options_;
|
||||
HashMap<String, size_t> long_map_;
|
||||
HashMap<char, size_t> short_map_;
|
||||
};
|
||||
|
||||
} // namespace Fig::ArgParser
|
||||
88
src/Utils/ConsoleSize.hpp
Normal file
88
src/Utils/ConsoleSize.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Fig::Utils
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取控制台窗口的大小(行数和列数)
|
||||
* @return 如果成功,返回包含 (rows, cols) 的 optional;失败返回 std::nullopt
|
||||
*/
|
||||
|
||||
inline std::optional<std::pair<int, int>> getConsoleSize()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Windows: GetConsoleScreenBufferInfo
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(hConsole, &csbi))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 窗口大小 = 右下角坐标 - 左上角坐标 + 1
|
||||
int cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
int rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
|
||||
return std::make_pair(rows, cols);
|
||||
|
||||
#else
|
||||
// Linux / macOS / Unix: ioctl
|
||||
struct winsize w;
|
||||
|
||||
// 标准输出被重定向到文件 ?
|
||||
if (!isatty(STDOUT_FILENO))
|
||||
{
|
||||
// 不是终端,获取环境变量
|
||||
char *cols_env = getenv("COLUMNS");
|
||||
char *rows_env = getenv("LINES");
|
||||
if (cols_env && rows_env)
|
||||
{
|
||||
int cols = std::stoi(cols_env);
|
||||
int rows = std::stoi(rows_env);
|
||||
return std::make_pair(rows, cols);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int cols = w.ws_col;
|
||||
int rows = w.ws_row;
|
||||
|
||||
// 如果 ws_col 或 ws_row 为 0,使用环境变量
|
||||
if (cols == 0 || rows == 0)
|
||||
{
|
||||
char *cols_env = getenv("COLUMNS");
|
||||
char *rows_env = getenv("LINES");
|
||||
if (cols_env)
|
||||
cols = std::stoi(cols_env);
|
||||
if (rows_env)
|
||||
rows = std::stoi(rows_env);
|
||||
}
|
||||
|
||||
if (cols > 0 && rows > 0)
|
||||
{
|
||||
return std::make_pair(rows, cols);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Fig::Utils
|
||||
File diff suppressed because it is too large
Load Diff
199
src/VM/Entry.cpp
Normal file
199
src/VM/Entry.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*!
|
||||
@file src/VM/Entry.hpp
|
||||
@brief vm入口实现
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-13
|
||||
*/
|
||||
|
||||
#include <VM/Entry.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <Bytecode/Disassembler.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Repl/Repl.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
namespace Fig::Entry
|
||||
{
|
||||
void RunFromPath(const String &path, const Config &conf)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
auto format_print_time = [](std::chrono::nanoseconds nsecs) {
|
||||
auto &out = CoreIO::GetStdOut();
|
||||
auto count = nsecs.count();
|
||||
|
||||
auto old_flags = out.flags();
|
||||
auto old_precision = out.precision();
|
||||
|
||||
if (count < 1'000)
|
||||
{
|
||||
// < 1μs 纳秒
|
||||
out << count << "ns";
|
||||
}
|
||||
else if (count < 1'000'000)
|
||||
{
|
||||
// 1μs ~ 1ms 微秒 保留 2 位小数
|
||||
out << std::fixed << std::setprecision(2) << (count / 1'000.0) << "μs";
|
||||
}
|
||||
else if (count < 1'000'000'000)
|
||||
{
|
||||
// 1ms ~ 1s 毫秒 保留 2 位小数
|
||||
out << std::fixed << std::setprecision(2) << (count / 1'000'000.0) << "ms";
|
||||
}
|
||||
else
|
||||
{
|
||||
// >= 1s 秒 保留 3 位小数
|
||||
out << std::fixed << std::setprecision(3) << (count / 1'000'000'000.0) << "s";
|
||||
}
|
||||
|
||||
out.flags(old_flags);
|
||||
out.precision(old_precision);
|
||||
};
|
||||
|
||||
fs::path _fspath(path.toStdString());
|
||||
|
||||
if (!fs::exists(_fspath))
|
||||
{
|
||||
CoreIO::GetStdErr() << "File not found: " << path << '\n';
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
if (!_fspath.has_extension() || _fspath.extension() != ".fig")
|
||||
{
|
||||
CoreIO::GetStdErr() << "Not a valid Fig-lang source code\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
String fileName(_fspath.filename().string());
|
||||
|
||||
SourceManager manager(path);
|
||||
manager.Read();
|
||||
|
||||
if (!manager.read)
|
||||
{
|
||||
CoreIO::GetStdErr() << "Could not read file: " << path << '\n';
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
const String &source = manager.GetSource();
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, fileName, diagnostics);
|
||||
|
||||
auto parse_start = clock::now();
|
||||
auto parse_result = parser.Parse();
|
||||
auto parse_end = clock::now();
|
||||
|
||||
if (!parse_result)
|
||||
{
|
||||
ReportError(parse_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Program *program = *parse_result;
|
||||
|
||||
Analyzer analyer(manager);
|
||||
|
||||
auto analyze_start = clock::now();
|
||||
auto analyze_result = analyer.Analyze(program);
|
||||
auto analyze_end = clock::now();
|
||||
|
||||
if (!analyze_result)
|
||||
{
|
||||
ReportError(analyze_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Compiler compiler(manager, diagnostics);
|
||||
|
||||
auto compile_start = clock::now();
|
||||
auto compile_result = compiler.Compile(program);
|
||||
auto compile_end = clock::now();
|
||||
|
||||
diagnostics.EmitAll(manager);
|
||||
|
||||
if (!compile_result)
|
||||
{
|
||||
ReportError(compile_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
CompiledModule *compiledModule = *compile_result;
|
||||
|
||||
if (conf.dump)
|
||||
{
|
||||
Disassembler disassembler;
|
||||
disassembler.DisassembleModule(compiledModule);
|
||||
}
|
||||
|
||||
VM vm;
|
||||
|
||||
auto execute_start = clock::now();
|
||||
auto execute_result = vm.Execute(compiledModule);
|
||||
auto execute_end = clock::now();
|
||||
|
||||
if (!execute_result)
|
||||
{
|
||||
ReportError(execute_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
if (conf.pregs)
|
||||
{
|
||||
vm.PrintRegisters();
|
||||
}
|
||||
|
||||
if (conf.time)
|
||||
{
|
||||
auto parse_time = parse_end - parse_start;
|
||||
CoreIO::GetStdOut() << "Parse: ";
|
||||
format_print_time(parse_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto analyze_time = analyze_end - analyze_start;
|
||||
CoreIO::GetStdOut() << "Analyze: ";
|
||||
format_print_time(analyze_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto compile_time = compile_end - compile_start;
|
||||
CoreIO::GetStdOut() << "Compile: ";
|
||||
format_print_time(compile_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto execute_time = execute_end - execute_start;
|
||||
CoreIO::GetStdOut() << "Execute: ";
|
||||
format_print_time(execute_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto total = parse_time + analyze_time + compile_time + execute_time;
|
||||
CoreIO::GetStdOut() << "Total: ";
|
||||
format_print_time(total);
|
||||
CoreIO::GetStdOut() << '\n';
|
||||
}
|
||||
|
||||
delete compiledModule;
|
||||
}
|
||||
|
||||
std::uint32_t RunRepl()
|
||||
{
|
||||
Repl repl(CoreIO::GetStdCin(), CoreIO::GetStdOut(), CoreIO::GetStdErr());
|
||||
std::uint32_t result = repl.Start();
|
||||
|
||||
CoreIO::GetStdOut() << "Repl exited with code " << result << '\n';
|
||||
return result;
|
||||
}
|
||||
}; // namespace Fig::Entry
|
||||
26
src/VM/Entry.hpp
Normal file
26
src/VM/Entry.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
@file src/VM/Entry.hpp
|
||||
@brief vm入口定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-13
|
||||
*/
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
|
||||
namespace Fig::Entry
|
||||
{
|
||||
struct Config
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
Debug,
|
||||
Normal
|
||||
} mode;
|
||||
bool dump;
|
||||
bool pregs;
|
||||
bool time;
|
||||
};
|
||||
|
||||
void RunFromPath(const String &, const Config &conf);
|
||||
std::uint32_t RunRepl();
|
||||
};
|
||||
238
src/VM/VM.cpp
238
src/VM/VM.cpp
@@ -1,13 +1,11 @@
|
||||
/*!
|
||||
@file src/VM/VM.cpp
|
||||
@brief 虚拟机核心执行引擎实现
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-19
|
||||
*/
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
// Computed GOTO!!!
|
||||
#define BINARY_ARITHMETIC_OP(opName, op) \
|
||||
do_##opName: \
|
||||
{ \
|
||||
@@ -83,38 +81,57 @@ namespace Fig
|
||||
Result<Value, Error> VM::Execute(CompiledModule *compiledModule)
|
||||
{
|
||||
Proto *entry = compiledModule->protos[0];
|
||||
pushFrame(entry, registers);
|
||||
(void) pushFrame(entry, registers); // 刚开始执行寄存器不会溢出
|
||||
|
||||
// 🔥 必须与 Bytecode.hpp 中的 OpCode 枚举严格一一对应!
|
||||
// 对齐 Bytecode.hpp 中的 OpCode 顺序
|
||||
static const void *dispatchTable[] = {&&do_Exit,
|
||||
&&do_Exit_MaxRecursionDepthExceeded,
|
||||
|
||||
&&do_LoadK,
|
||||
&&do_LoadTrue,
|
||||
&&do_LoadFalse,
|
||||
&&do_LoadNull,
|
||||
|
||||
&&do_FastCall,
|
||||
&&do_Call,
|
||||
&&do_Return,
|
||||
|
||||
&&do_LoadFn,
|
||||
|
||||
&&do_Jmp,
|
||||
&&do_JmpIfFalse,
|
||||
|
||||
&&do_Mov,
|
||||
|
||||
&&do_Add,
|
||||
&&do_Sub,
|
||||
&&do_Mul,
|
||||
&&do_Div,
|
||||
&&do_Mod,
|
||||
&&do_BitXor,
|
||||
|
||||
&&do_IntFastAdd,
|
||||
&&do_IntFastSub,
|
||||
&&do_IntFastMul,
|
||||
&&do_IntFastDiv,
|
||||
|
||||
&&do_Equal,
|
||||
&&do_NotEqual,
|
||||
&&do_Greater,
|
||||
&&do_Less,
|
||||
&&do_GreaterEqual,
|
||||
&&do_LessEqual,
|
||||
|
||||
&&do_GetGlobal,
|
||||
&&do_SetGlobal,
|
||||
&&do_GetUpval,
|
||||
&&do_SetUpval,
|
||||
&&do_Copy,
|
||||
|
||||
&&do_Count};
|
||||
|
||||
Instruction inst;
|
||||
|
||||
// 🔥 核心分发引擎:取指 -> 直接查表并 Jump
|
||||
#define DISPATCH() \
|
||||
do \
|
||||
{ \
|
||||
@@ -122,11 +139,20 @@ namespace Fig
|
||||
goto *dispatchTable[inst & 0xFF]; \
|
||||
} while (0)
|
||||
|
||||
// 引擎点火!
|
||||
// 引擎点火!! :3
|
||||
DISPATCH();
|
||||
|
||||
do_Exit: {
|
||||
[[unlikely]] return Value::GetNullInstance();
|
||||
return Value::FromInt(decodeSBx(inst));
|
||||
}
|
||||
|
||||
do_Exit_MaxRecursionDepthExceeded: {
|
||||
CoreIO::GetStdErr() << std::format(
|
||||
"Oops! max recursion depth limit {} exceeded in Fn `{}` , exiting...\n",
|
||||
MAX_RECURSION_DEPTH,
|
||||
(currentFrame - 1)->proto->name); // pushFrame失败了,但currentFrame仍然移动,所以
|
||||
// (currentFrame - 1)是 lastFrame
|
||||
std::exit(static_cast<int>(MAX_RECURSION_DEPTH));
|
||||
}
|
||||
|
||||
do_LoadK: {
|
||||
@@ -158,26 +184,127 @@ namespace Fig
|
||||
std::uint8_t a = decodeA(inst);
|
||||
Proto *proto = compiledModule->protos[a];
|
||||
std::uint8_t baseReg = decodeB(inst);
|
||||
pushFrame(proto, currentFrame->registerBase + baseReg);
|
||||
|
||||
currentFrame->ip = pushFrame(proto, currentFrame->registerBase + baseReg);
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Call: {
|
||||
// TODO: FunctionObject 动态解包
|
||||
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t baseReg = decodeB(inst);
|
||||
|
||||
Value callee = currentFrame->registerBase[a];
|
||||
|
||||
FunctionObject *closure = nullptr;
|
||||
|
||||
if (!callee.IsObject())
|
||||
{
|
||||
size_t ipIdx = currentFrame->ip - currentFrame->proto->code.data();
|
||||
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
std::format("Object `{}` is not callable", callee.ToString()),
|
||||
"none",
|
||||
*currentFrame->proto->locations[ipIdx]));
|
||||
}
|
||||
else [[likely]]
|
||||
{
|
||||
|
||||
Object *obj = callee.AsObject();
|
||||
if (!obj->isFunction())
|
||||
{
|
||||
size_t ipIdx = currentFrame->ip - currentFrame->proto->code.data();
|
||||
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
std::format("Object `{}` is not callable", callee.ToString()),
|
||||
"none",
|
||||
*currentFrame->proto->locations[ipIdx]));
|
||||
}
|
||||
closure = static_cast<FunctionObject *>(obj);
|
||||
}
|
||||
|
||||
currentFrame->ip = pushFrame(closure, currentFrame->registerBase + baseReg);
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Return: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
*currentFrame->registerBase = currentFrame->registerBase[a];
|
||||
Value retVal = currentFrame->registerBase[a];
|
||||
|
||||
closeUpvalues(currentFrame->registerBase);
|
||||
|
||||
// 此时 registerBase[0] 指向的是 Caller 的 baseReg 槽位
|
||||
currentFrame->registerBase[0] = retVal;
|
||||
popFrame();
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_LoadFn: {
|
||||
// std::uint8_t a = decodeA(inst);
|
||||
// std::uint16_t bx = decodeBx(inst);
|
||||
// TODO: R[a] = new FunctionObject(compiledModule->protos[bx])
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint16_t bx = decodeBx(inst);
|
||||
|
||||
Proto *p = compiledModule->protos[bx];
|
||||
|
||||
size_t upValSize = p->upvalues.size();
|
||||
size_t extraSize = upValSize * sizeof(Upvalue *);
|
||||
|
||||
FunctionObject *closure =
|
||||
(FunctionObject *) allocateObject<FunctionObject>(ObjectType::Function, extraSize);
|
||||
|
||||
// CoreIO::GetStdErr() << "DEBUG: p->name = " << p->name << '\n';
|
||||
new (&closure->name) String(p->name); // String非平凡类型,有自己的构造函数
|
||||
closure->proto = p;
|
||||
closure->paraCount = p->numParams;
|
||||
closure->upvalueCount = static_cast<std::uint32_t>(upValSize);
|
||||
|
||||
for (size_t i = 0; i < closure->upvalueCount; ++i)
|
||||
{
|
||||
auto &info = p->upvalues[i];
|
||||
if (info.isLocal)
|
||||
{
|
||||
Value *targetSlot = ¤tFrame->registerBase[info.index];
|
||||
Upvalue *prev = nullptr;
|
||||
Upvalue *curr = openUpvalues;
|
||||
|
||||
while (curr != nullptr && curr->location > targetSlot)
|
||||
{
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
if (curr != nullptr && curr->location == targetSlot)
|
||||
{
|
||||
// 如果别的闭包已经捕获了这个槽位,共享物理指针
|
||||
closure->upvalues[i] = curr;
|
||||
++curr->refCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 首次捕获
|
||||
Upvalue *uv = new Upvalue;
|
||||
|
||||
uv->location = targetSlot;
|
||||
uv->next = curr;
|
||||
++uv->refCount;
|
||||
|
||||
if (prev == nullptr)
|
||||
openUpvalues = uv;
|
||||
else
|
||||
prev->next = uv;
|
||||
closure->upvalues[i] = uv;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
closure->upvalues[i] = currentFrame->closure->upvalues[info.index];
|
||||
}
|
||||
}
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromObject(closure);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@@ -215,6 +342,55 @@ namespace Fig
|
||||
assert(false && "VM: Mod and BitXor not fully implemented yet!");
|
||||
DISPATCH();
|
||||
|
||||
do_IntFastAdd: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromInt(l.AsInt() + r.AsInt());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_IntFastSub: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromInt(l.AsInt() - r.AsInt());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_IntFastMul: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromInt(l.AsInt() * r.AsInt());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_IntFastDiv: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] =
|
||||
Value::FromDouble(static_cast<double>(l.AsInt()) / r.AsInt());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
BINARY_COMPARE_OP(Equal, ==);
|
||||
BINARY_COMPARE_OP(NotEqual, !=);
|
||||
BINARY_COMPARE_OP(Greater, >);
|
||||
@@ -222,6 +398,42 @@ namespace Fig
|
||||
BINARY_COMPARE_OP(GreaterEqual, >=);
|
||||
BINARY_COMPARE_OP(LessEqual, <=);
|
||||
|
||||
do_GetGlobal: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint16_t bx = decodeBx(inst);
|
||||
currentFrame->registerBase[a] = globals[bx];
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_SetGlobal: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint16_t bx = decodeBx(inst);
|
||||
globals[bx] = currentFrame->registerBase[a];
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_GetUpval: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
|
||||
currentFrame->registerBase[a] = *(currentFrame->closure->upvalues[b]->location);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_SetUpval: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
*(currentFrame->closure->upvalues[b]->location) = currentFrame->registerBase[a]; // copy
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Copy: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
currentFrame->registerBase[a] = currentFrame->registerBase[b];
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Count: {
|
||||
assert(false && "Hit Count sentinel!");
|
||||
return Value::GetNullInstance();
|
||||
|
||||
332
src/VM/VM.hpp
332
src/VM/VM.hpp
@@ -9,8 +9,9 @@
|
||||
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Object/Object.hpp>
|
||||
#include <cassert>
|
||||
#include <Core/Core.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream> // debug
|
||||
#include <print>
|
||||
|
||||
@@ -18,6 +19,7 @@ namespace Fig
|
||||
{
|
||||
struct CallFrame
|
||||
{
|
||||
FunctionObject *closure; // 动态闭包Context (FastCall时 null)
|
||||
Proto *proto; // 当前执行的原型
|
||||
Instruction *ip; // 当前指令指针
|
||||
Value *registerBase; // 寄存器起点
|
||||
@@ -28,16 +30,276 @@ namespace Fig
|
||||
}
|
||||
};
|
||||
|
||||
enum class GCPhase : std::uint8_t
|
||||
{
|
||||
Idle,
|
||||
MarkRoots,
|
||||
Marking,
|
||||
Sweeping
|
||||
};
|
||||
|
||||
class VM
|
||||
{
|
||||
private:
|
||||
static constexpr unsigned int MAX_REGISTERS = 1024;
|
||||
static constexpr unsigned int MAX_GLOBALS = 65536;
|
||||
static constexpr unsigned int MAX_RECURSION_DEPTH = 2233;
|
||||
|
||||
Instruction POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
|
||||
// 一次性分配
|
||||
Value registers[MAX_REGISTERS];
|
||||
Value globals[MAX_GLOBALS];
|
||||
|
||||
DynArray<CallFrame> frames;
|
||||
CallFrame frames[MAX_RECURSION_DEPTH];
|
||||
CallFrame *currentFrame;
|
||||
CallFrame *frameLimit;
|
||||
|
||||
Upvalue *openUpvalues = nullptr;
|
||||
|
||||
// GC
|
||||
Object *objects = nullptr; // 链表头
|
||||
DynArray<Object *> grayStack;
|
||||
|
||||
size_t allocatedBytes = 0;
|
||||
size_t nextGC = 1024 * 1024; // byte, 1MB初始阈值
|
||||
GCPhase gcPhase = GCPhase::Idle;
|
||||
|
||||
private:
|
||||
inline void closeUpvalues(Value *level)
|
||||
{
|
||||
// 函数销毁时,逃逸即将销毁的变量
|
||||
while (openUpvalues && openUpvalues->location >= level)
|
||||
{
|
||||
Upvalue *upval = openUpvalues;
|
||||
upval->closedValue = *upval->location;
|
||||
upval->location = &upval->closedValue;
|
||||
openUpvalues = upval->next;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T *allocateObject(ObjectType type, size_t extraBytes)
|
||||
{
|
||||
|
||||
if (allocatedBytes > nextGC) // 超出阈值
|
||||
{
|
||||
switch (gcPhase)
|
||||
{
|
||||
case GCPhase::Idle: markRoots(); break;
|
||||
case GCPhase::Marking: stepMarking(); break;
|
||||
case GCPhase::Sweeping: sweep(); break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t totalSize = sizeof(T) + extraBytes;
|
||||
|
||||
// 物理分配
|
||||
T *obj = static_cast<T *>(std::malloc(totalSize));
|
||||
if (!obj) [[unlikely]]
|
||||
{
|
||||
// 分配失败
|
||||
CoreIO::GetStdErr() << "Oops! Object allocating failed! Exiting...\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// 构造 Header
|
||||
obj->type = type;
|
||||
obj->color = GCColor::Black;
|
||||
obj->klass = nullptr;
|
||||
obj->next = objects; // 插入全局追踪链表
|
||||
objects = obj;
|
||||
|
||||
allocatedBytes += totalSize;
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline void writeBarrier(Object *parent, Value childVal)
|
||||
{
|
||||
if (!childVal.IsObject())
|
||||
return; // 栈对象无需
|
||||
|
||||
Object *child = childVal.AsObject();
|
||||
// 三色不变式, 黑色对象绝对不能指向白色对象
|
||||
if (parent->color == GCColor::Black && child->color == GCColor::White)
|
||||
{
|
||||
child->color = GCColor::Gray;
|
||||
grayStack.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
inline void markValue(Value value)
|
||||
{
|
||||
if (!value.IsObject())
|
||||
return;
|
||||
|
||||
Object *obj = value.AsObject();
|
||||
if (obj && obj->color == GCColor::White)
|
||||
{
|
||||
obj->color = GCColor::Gray;
|
||||
grayStack.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void markRoots()
|
||||
{
|
||||
// 扫描全局变量
|
||||
for (std::uint32_t i = 0; i < MAX_GLOBALS; ++i)
|
||||
{
|
||||
markValue(globals[i]);
|
||||
}
|
||||
|
||||
// 扫描vm全部栈 [registers[0], currentFrame]
|
||||
if (currentFrame && currentFrame->proto)
|
||||
{
|
||||
Value *stackTop = currentFrame->registerBase + currentFrame->proto->maxRegisters;
|
||||
for (Value *slot = registers; slot < stackTop; ++slot)
|
||||
{
|
||||
markValue(*slot);
|
||||
}
|
||||
}
|
||||
|
||||
// 扫描逃逸链表 (Open Upvalues)
|
||||
for (Upvalue *uv = openUpvalues; uv != nullptr; uv = uv->next)
|
||||
{
|
||||
markValue(uv->closedValue);
|
||||
}
|
||||
|
||||
gcPhase = GCPhase::Marking;
|
||||
}
|
||||
|
||||
void stepMarking()
|
||||
{
|
||||
// 每次步进处理的对象数量
|
||||
constexpr int WORK_LIMIT = 64;
|
||||
int workCount = 0;
|
||||
|
||||
while (!grayStack.empty() && workCount++ < WORK_LIMIT)
|
||||
{
|
||||
Object *obj = grayStack.back();
|
||||
grayStack.pop_back();
|
||||
|
||||
// 标记为黑色:表示该对象及其子引用已处理完毕
|
||||
obj->color = GCColor::Black;
|
||||
|
||||
switch (obj->type)
|
||||
{
|
||||
case ObjectType::Function: {
|
||||
auto *fn = static_cast<FunctionObject *>(obj);
|
||||
for (std::uint32_t i = 0; i < fn->upvalueCount; ++i)
|
||||
{
|
||||
if (fn->upvalues[i])
|
||||
markValue(*(fn->upvalues[i]->location));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ObjectType::Instance: {
|
||||
auto *inst = static_cast<InstanceObject *>(obj);
|
||||
if (inst->klass)
|
||||
markValue(Value::FromObject(inst->klass));
|
||||
// 扫描所有实例字段
|
||||
std::uint8_t fieldCount = inst->klass ? inst->klass->fieldCount : 0;
|
||||
for (std::uint8_t i = 0; i < fieldCount; ++i)
|
||||
{
|
||||
markValue(inst->fields[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ObjectType::Struct: {
|
||||
auto *st = static_cast<StructObject *>(obj);
|
||||
for (int i = 0; i < GetOperatorsSize(); ++i)
|
||||
{
|
||||
if (st->operators[i])
|
||||
markValue(Value::FromObject(st->operators[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ObjectType::String: break; // 叶子节点
|
||||
}
|
||||
}
|
||||
|
||||
if (grayStack.empty())
|
||||
gcPhase = GCPhase::Sweeping;
|
||||
}
|
||||
|
||||
void sweep()
|
||||
{
|
||||
Object **curr = &objects;
|
||||
size_t liveBytes = 0;
|
||||
|
||||
while (*curr != nullptr)
|
||||
{
|
||||
Object *obj = *curr;
|
||||
if (obj->color == GCColor::White)
|
||||
{
|
||||
*curr = obj->next;
|
||||
|
||||
// 函数 upvalue需要手动析构
|
||||
if (obj->type == ObjectType::Function)
|
||||
{
|
||||
auto *fn = static_cast<FunctionObject *>(obj);
|
||||
fn->name.~String();
|
||||
for (std::uint32_t i = 0; i < fn->upvalueCount; ++i)
|
||||
{
|
||||
Upvalue *uv = fn->upvalues[i];
|
||||
if (uv)
|
||||
{
|
||||
uv->refCount--; // 减引用
|
||||
if (uv->refCount == 0)
|
||||
{
|
||||
// 只有当所有闭包都死后,才 free 这个 Upvalue 结构体
|
||||
|
||||
std::free(uv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 持有 C++ 堆资源的成员手动析构!
|
||||
else if (obj->type == ObjectType::String)
|
||||
{
|
||||
static_cast<StringObject *>(obj)->data.~String();
|
||||
}
|
||||
|
||||
std::free(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 洗白,仍是一条好汉
|
||||
// 以备下次 GC,统计存活大小
|
||||
obj->color = GCColor::White;
|
||||
|
||||
// 计算存活对象的真实物理大小 (Header + 柔性数组/额外数据)
|
||||
size_t objectSize = 0;
|
||||
switch (obj->type)
|
||||
{
|
||||
case ObjectType::String: objectSize = sizeof(StringObject); break;
|
||||
case ObjectType::Instance:
|
||||
objectSize =
|
||||
sizeof(InstanceObject)
|
||||
+ (obj->klass ? obj->klass->fieldCount * sizeof(Value) : 0);
|
||||
break;
|
||||
case ObjectType::Function:
|
||||
objectSize = sizeof(FunctionObject)
|
||||
+ (static_cast<FunctionObject *>(obj)->upvalueCount
|
||||
* sizeof(Upvalue *));
|
||||
break;
|
||||
case ObjectType::Struct: objectSize = sizeof(StructObject); break;
|
||||
}
|
||||
|
||||
liveBytes += objectSize;
|
||||
|
||||
curr = &obj->next;
|
||||
}
|
||||
}
|
||||
|
||||
allocatedBytes = liveBytes;
|
||||
// 阈值调整, 当下一次分配超过存活内存的 2 倍时触发 GC
|
||||
nextGC = (liveBytes < 512 * 1024) ? 1024 * 1024 : liveBytes * 2;
|
||||
|
||||
gcPhase = GCPhase::Idle;
|
||||
}
|
||||
|
||||
public:
|
||||
VM()
|
||||
{
|
||||
@@ -45,27 +307,50 @@ namespace Fig
|
||||
{
|
||||
registers[i] = Value::GetNullInstance();
|
||||
}
|
||||
for (unsigned int i = 0; i < MAX_GLOBALS; ++i)
|
||||
{
|
||||
globals[i] = Value::GetNullInstance();
|
||||
}
|
||||
|
||||
currentFrame = frames;
|
||||
frameLimit = frames + MAX_RECURSION_DEPTH - 1;
|
||||
|
||||
POISON_MAX_RECURSION_DEPTH_EXCEED_INST =
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void pushFrame(Proto *proto, Value *base)
|
||||
[[nodiscard]]
|
||||
inline Instruction *pushFrame(Proto *proto, Value *base) // fastcall
|
||||
{
|
||||
frames.push_back({
|
||||
proto,
|
||||
proto->code.data(),
|
||||
base
|
||||
});
|
||||
currentFrame = &frames.back();
|
||||
if (++currentFrame >= frameLimit) [[unlikely]] // 达到最大递归层数
|
||||
{
|
||||
POISON_MAX_RECURSION_DEPTH_EXCEED_INST =
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
}
|
||||
[[likely]]
|
||||
*currentFrame = CallFrame{nullptr, proto, proto->code.data(), base};
|
||||
return currentFrame->ip;
|
||||
}
|
||||
|
||||
void popFrame()
|
||||
[[nodiscard]]
|
||||
inline Instruction *pushFrame(FunctionObject *closure, Value *base) // 普通调用
|
||||
{
|
||||
frames.pop_back();
|
||||
if (!frames.empty())
|
||||
if (++currentFrame >= frameLimit) [[unlikely]]
|
||||
{
|
||||
currentFrame = &frames.back();
|
||||
POISON_MAX_RECURSION_DEPTH_EXCEED_INST =
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
}
|
||||
[[likely]]
|
||||
*currentFrame = CallFrame{closure, closure->proto, closure->proto->code.data(), base};
|
||||
return currentFrame->ip;
|
||||
}
|
||||
|
||||
inline void popFrame()
|
||||
{
|
||||
--currentFrame;
|
||||
}
|
||||
|
||||
inline OpCode decodeOpCode(Instruction inst)
|
||||
@@ -97,15 +382,28 @@ namespace Fig
|
||||
// 执行入口:接收 Proto
|
||||
Result<Value, Error> Execute(CompiledModule *);
|
||||
|
||||
inline void PrintRegisters()
|
||||
void PrintRegisters(std::ostream &ostream = CoreIO::GetStdOut())
|
||||
{
|
||||
std::cout << "=== Registers ===" << '\n';
|
||||
ostream << "=== Registers ===\n";
|
||||
for (unsigned int i = 0; i < MAX_REGISTERS; ++i)
|
||||
{
|
||||
Value &v = registers[i];
|
||||
if (!v.IsNull())
|
||||
{
|
||||
std::println("[{}] {}", i, v.ToString());
|
||||
ostream << std::format("[{}] {}\n", i, v.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintGlobals(std::ostream &ostream = CoreIO::GetStdOut())
|
||||
{
|
||||
ostream << "== Globals ===\n";
|
||||
for (unsigned int i = 0; i < MAX_GLOBALS; ++i)
|
||||
{
|
||||
Value &v = globals[i];
|
||||
if (!v.IsNull())
|
||||
{
|
||||
ostream << std::format("[{}] {}\n", i, v.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
159
src/main.cpp
159
src/main.cpp
@@ -1,102 +1,101 @@
|
||||
#include <Compiler/Compiler.hpp>
|
||||
/*
|
||||
The Fig Programming Language
|
||||
Copyright © 2025-2026 PuqiAR (im@puqiar.top). All rights reserved.
|
||||
|
||||
Under MIT License, see /LICENSE for more
|
||||
*/
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
#include <VM/Entry.hpp>
|
||||
|
||||
#include <Utils/ArgParser/ArgParser.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <print>
|
||||
|
||||
|
||||
int main()
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
using namespace Fig;
|
||||
|
||||
String fileName = "test.fig";
|
||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||
std::ostream &err = CoreIO::GetStdErr();
|
||||
std::ostream &out = CoreIO::GetStdOut();
|
||||
|
||||
SourceManager manager(filePath);
|
||||
manager.Read();
|
||||
ArgParser::ArgumentParser argparser("Fig", "Fig Toolchain");
|
||||
|
||||
if (!manager.read)
|
||||
argparser.AddFlag('r', "repl");
|
||||
argparser.AddFlag('h', "help").Help("Print the help message");
|
||||
argparser.AddFlag('v', "version").Help("Show toolchain version");
|
||||
argparser.AddFlag("license").Help("Print the license text");
|
||||
argparser.AddFlag("dump").Help("Dump the bytecode");
|
||||
argparser.AddFlag("pregs").Help("Print vm non-null registers");
|
||||
argparser.AddFlag("time").Help("Print the execution time");
|
||||
|
||||
auto res = argparser.Parse(argc, argv);
|
||||
if (!res)
|
||||
{
|
||||
std::cerr << "Couldn't read file";
|
||||
err << res.error().Format() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
Lexer lexer(manager.GetSource(), fileName);
|
||||
Parser parser(lexer, manager, fileName);
|
||||
auto &args = *res;
|
||||
|
||||
const auto &program_result = parser.Parse();
|
||||
if (!program_result)
|
||||
bool runRepl = args.HasFlag("repl");
|
||||
bool showHelp = args.HasFlag("help");
|
||||
bool showVersion = args.HasFlag("version");
|
||||
bool showLicense = args.HasFlag("license");
|
||||
bool dump = args.HasFlag("dump");
|
||||
bool pregs = args.HasFlag("pregs");
|
||||
bool time = args.HasFlag("time");
|
||||
|
||||
if (showHelp)
|
||||
{
|
||||
ReportError(program_result.error(), manager);
|
||||
out << argparser.FormatHelp() << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (showVersion)
|
||||
{
|
||||
out << std::format(
|
||||
"Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
|
||||
Core::VERSION,
|
||||
Core::LICENSE);
|
||||
out << std::format(
|
||||
"Build time: {} [{} x{} on {}]\n",
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
Core::PLATFORM);
|
||||
}
|
||||
else if (showLicense)
|
||||
{
|
||||
out << Core::LICENSE_TEXT << '\n';
|
||||
}
|
||||
|
||||
auto &positionals = args.GetPositionals();
|
||||
const auto &posSize = positionals.size();
|
||||
|
||||
if (posSize != 1 && (showHelp || showVersion || showLicense))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (runRepl)
|
||||
{
|
||||
return Entry::RunRepl();
|
||||
}
|
||||
|
||||
if (posSize > 1)
|
||||
{
|
||||
err << "Error: Too more positionals, expect 1. Use Fig [Fig source code file (.fig)]\n";
|
||||
return 1;
|
||||
}
|
||||
Program *program = *program_result;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
const auto &analyzeResult = analyzer.Analyze(program);
|
||||
if (!analyzeResult)
|
||||
else if (posSize == 0)
|
||||
{
|
||||
ReportError(analyzeResult.error(), manager);
|
||||
return 1;
|
||||
}
|
||||
std::cout << "analyzer: Program OK, PASSED\n";
|
||||
|
||||
Compiler compiler(fileName, manager);
|
||||
const auto &comp_result = compiler.Compile(program);
|
||||
if (!comp_result)
|
||||
{
|
||||
ReportError(comp_result.error(), manager);
|
||||
err << "Error: No input source file\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
CompiledModule *compiledModule = *comp_result;
|
||||
Entry::Config config{.mode = Entry::Config::Normal, .dump = dump, .pregs = pregs, .time = time};
|
||||
|
||||
size_t cnt = 0;
|
||||
for (Proto *proto : compiledModule->protos)
|
||||
{
|
||||
std::cout << "\n"
|
||||
<< "Proto: " << cnt++ << '\n';
|
||||
std::cout << " Constant Pool" << '\n';
|
||||
for (size_t i = 0; i < proto->constants.size(); ++i)
|
||||
{
|
||||
std::print("[{}] {}\n", i, proto->constants[i].ToString());
|
||||
}
|
||||
|
||||
DumpCode(proto->code);
|
||||
|
||||
std::cout << "\nMax Stack Size: " << (int) proto->maxStack << std::endl;
|
||||
}
|
||||
|
||||
VM vm;
|
||||
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
|
||||
Clock clock;
|
||||
|
||||
auto start = clock.now();
|
||||
|
||||
auto result_ = vm.Execute(compiledModule);
|
||||
|
||||
auto end = clock.now();
|
||||
auto duration = end - start;
|
||||
|
||||
if (!result_)
|
||||
{
|
||||
ReportError(result_.error(), manager);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Value result = *result_;
|
||||
std::cout << "result: " << result.ToString() << "\n";
|
||||
std::cout << "execution cost: " << std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() << "ms\n";
|
||||
|
||||
vm.PrintRegisters();
|
||||
const String &path = positionals.front();
|
||||
Entry::RunFromPath(path, config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
9
tests/Compiler/test_basic.fig
Normal file
9
tests/Compiler/test_basic.fig
Normal file
@@ -0,0 +1,9 @@
|
||||
var x = 1_000;
|
||||
var y = 0x20;
|
||||
var z = x + y;
|
||||
|
||||
func fib(n: Int) -> Int {
|
||||
if n < 2 { return n; }
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
232
tests/Parser/parser_comprehensive_test.fig
Normal file
232
tests/Parser/parser_comprehensive_test.fig
Normal file
@@ -0,0 +1,232 @@
|
||||
/* ==============================
|
||||
Fig Parser Comprehensive Test
|
||||
============================== */
|
||||
|
||||
// --- 单行注释 ---
|
||||
|
||||
/* ---
|
||||
多行注释
|
||||
--- */
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 1. 变量声明 var
|
||||
// ─────────────────────────────────
|
||||
var x; // Any, 无初始化
|
||||
var a = 10; // 推断 Int
|
||||
var b: Int = 20; // 显式类型
|
||||
var c := 30; // 类型推断锁定
|
||||
var d: Int; // 显式类型,无初始化
|
||||
var e = "hello"; // 推断 String
|
||||
var f = true; // 推断 Bool
|
||||
var g = null; // 推断 Null
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 2. 常量声明 const
|
||||
// ─────────────────────────────────
|
||||
const PI = 3.14159; // 推断 Double
|
||||
const MAX: Int = 100; // 显式类型
|
||||
const NAME := "Fig"; // 类型推断锁定
|
||||
const T: Double = 2.718; // 显式类型
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 3. 函数定义 func
|
||||
// ─────────────────────────────────
|
||||
func noop() {}
|
||||
func add(a: Int, b: Int) -> Int { return a + b; }
|
||||
func greet(name: String) -> String { return "Hello " + name + "!"; }
|
||||
func quick(x) => x * 2; // 箭头简写
|
||||
func noReturn() -> Null { return null; }
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 4. Lambda 表达式
|
||||
// ─────────────────────────────────
|
||||
var lambda1 = func(x, y) => x + y;
|
||||
var lambda2 = func(x: Int) { return x * x; };
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 5. 控制流 if / while / for
|
||||
// ─────────────────────────────────
|
||||
func testControlFlow() {
|
||||
// if-else
|
||||
if true { var z = 1; }
|
||||
if (x > 0) { var z = 2; }
|
||||
if a > 10 { return 1; }
|
||||
else if a > 5 { return 2; }
|
||||
else { return 3; }
|
||||
|
||||
// while
|
||||
while a < 10 { a = a + 1; }
|
||||
while (a > 0) { a = a - 1; }
|
||||
|
||||
// for (C风格,括号可选)
|
||||
for var i = 0; i < 10; i = i + 1 { var _ = i; }
|
||||
for (var j = 0; j < 5; j += 1) { var _ = j; }
|
||||
for ; ; { break; } // 无限循环
|
||||
|
||||
// break / continue
|
||||
while true { break; }
|
||||
while true { continue; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 6. 表达式运算符
|
||||
// ─────────────────────────────────
|
||||
func testOperators() -> Int {
|
||||
var r = 0;
|
||||
|
||||
// 算术
|
||||
r = 1 + 2;
|
||||
r = 3 - 1;
|
||||
r = 2 * 3;
|
||||
r = 6 / 2;
|
||||
r = 7 % 3;
|
||||
r = 2 ** 3; // 幂运算
|
||||
|
||||
// 比较
|
||||
var b1 = 1 == 1;
|
||||
var b2 = 1 != 2;
|
||||
var b3 = 1 < 2;
|
||||
var b4 = 2 > 1;
|
||||
var b5 = 1 <= 1;
|
||||
var b6 = 2 >= 1;
|
||||
|
||||
// 逻辑
|
||||
var l1 = true and false;
|
||||
var l2 = true or false;
|
||||
var l3 = not true;
|
||||
var l4 = true && false; // &&
|
||||
var l5 = true || false; // ||
|
||||
|
||||
// 复合赋值
|
||||
r += 1;
|
||||
r -= 1;
|
||||
r *= 2;
|
||||
r /= 2;
|
||||
r %= 1;
|
||||
r ^= 1;
|
||||
|
||||
// 位运算
|
||||
var w1 = 1 & 2;
|
||||
var w2 = 1 | 2;
|
||||
var w3 = 1 ^ 2;
|
||||
var w4 = ~1;
|
||||
var w5 = 1 << 2;
|
||||
var w6 = 4 >> 1;
|
||||
|
||||
// 前后缀 ++/--
|
||||
var p = 0;
|
||||
var pre = ++p;
|
||||
var post = p--;
|
||||
var n = --p;
|
||||
|
||||
// 前缀取反
|
||||
var neg = -100;
|
||||
var notExpr = not true;
|
||||
|
||||
// 三元
|
||||
var t = a > 5 ? 1 : 0;
|
||||
|
||||
// 成员访问
|
||||
var m = someObj.field;
|
||||
|
||||
// 类型检查 / 转换
|
||||
var check = x is Int;
|
||||
var cast = x as String;
|
||||
|
||||
// 索引
|
||||
var idx = arr[0];
|
||||
|
||||
// 函数调用
|
||||
var callRes = add(1, 2);
|
||||
|
||||
// 分组
|
||||
var grouped = (1 + 2) * 3;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 7. struct 结构体
|
||||
// ─────────────────────────────────
|
||||
struct Point {
|
||||
public x: Int;
|
||||
public y: Int;
|
||||
|
||||
public func toString() -> String {
|
||||
return "(" + "x" + "," + "y" + ")";
|
||||
}
|
||||
}
|
||||
|
||||
struct Person {
|
||||
name: String;
|
||||
age: Int = 18; // 默认值
|
||||
|
||||
public func getName() -> String {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 8. new 表达式
|
||||
// ─────────────────────────────────
|
||||
func testNewExpr() {
|
||||
var p1 = new Point{1, 2}; // 位置参数
|
||||
var p2 = new Point{x: 2, y: 3}; // 命名参数
|
||||
var xx = 114;
|
||||
var yy = 514;
|
||||
var p3 = new Point{yy, xx}; // 简写
|
||||
var p4 = new Point{}; // 空构造
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 9. interface 接口
|
||||
// ─────────────────────────────────
|
||||
interface Drawable {
|
||||
func draw() -> String;
|
||||
func getLayer() -> Int;
|
||||
}
|
||||
|
||||
interface Printable {
|
||||
func toString() -> String;
|
||||
func getVersion() -> Int {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 10. impl 实现
|
||||
// ─────────────────────────────────
|
||||
struct Circle {
|
||||
public radius: Double;
|
||||
}
|
||||
|
||||
impl Drawable for Circle {
|
||||
func draw() {
|
||||
return "circle";
|
||||
}
|
||||
func getLayer() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 11. import 导入
|
||||
// ─────────────────────────────────
|
||||
import std.io;
|
||||
import "path/to/file.fig";
|
||||
|
||||
// ─────────────────────────────────
|
||||
// 12. 闭包与嵌套函数
|
||||
// ─────────────────────────────────
|
||||
func outer(x: Int) -> Function {
|
||||
func inner(n: Int) -> Int {
|
||||
return n * x;
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
|
||||
var closureMaker = func(factor: Int) {
|
||||
return func(n) => n * factor;
|
||||
};
|
||||
3
tests/Sema/err_control_flow.fig
Normal file
3
tests/Sema/err_control_flow.fig
Normal file
@@ -0,0 +1,3 @@
|
||||
func main() {
|
||||
break; // 错误:break 不在循环内
|
||||
}
|
||||
4
tests/Sema/err_redeclare.fig
Normal file
4
tests/Sema/err_redeclare.fig
Normal file
@@ -0,0 +1,4 @@
|
||||
func main() {
|
||||
var x = 10;
|
||||
var x = 20; // 错误:x 重复定义
|
||||
}
|
||||
4
tests/Sema/err_struct_field.fig
Normal file
4
tests/Sema/err_struct_field.fig
Normal file
@@ -0,0 +1,4 @@
|
||||
struct Point { x: Int, y: Int }
|
||||
func main() {
|
||||
var p = new Point{ x: 1, z: 2 }; // 错误:Point 没有字段 z
|
||||
}
|
||||
4
tests/Sema/err_undeclared.fig
Normal file
4
tests/Sema/err_undeclared.fig
Normal file
@@ -0,0 +1,4 @@
|
||||
func main() {
|
||||
var x = 10;
|
||||
io.println(y); // 错误:y 未定义
|
||||
}
|
||||
56
xmake.lua
56
xmake.lua
@@ -13,11 +13,10 @@ elseif is_plat("windows") then
|
||||
-- 2. local dev (Windows + llvm-mingw)
|
||||
set_toolchains("mingw") -- llvm-mingw
|
||||
add_ldflags("-Wl,--stack,268435456")
|
||||
-- set_toolchains("clang")
|
||||
-- static lib
|
||||
-- add_ldflags("-target x86_64-w64-mingw32", "-static")
|
||||
-- add_cxxflags("-stdlib=libc++")
|
||||
-- add_ldflags("-stdlib=libc++")
|
||||
end
|
||||
|
||||
if is_mode("release") then
|
||||
set_optimize("fastest")
|
||||
end
|
||||
|
||||
set_languages("c++23")
|
||||
@@ -33,7 +32,6 @@ target("LexerTest")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
|
||||
add_files("src/Lexer/LexerTest.cpp")
|
||||
|
||||
target("ParserTest")
|
||||
@@ -41,13 +39,11 @@ target("ParserTest")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
|
||||
add_files("src/Parser/ParserTest.cpp")
|
||||
|
||||
target("ObjectTest")
|
||||
@@ -59,13 +55,12 @@ target("AnalyzerTest")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
add_files("src/Sema/AnalyzerTest.cpp")
|
||||
|
||||
@@ -74,21 +69,18 @@ target("CompilerTest")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Bytecode/Disassembler.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
|
||||
add_files("src/Object/Object.cpp")
|
||||
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
|
||||
add_files("src/Compiler/ExprCompiler.cpp")
|
||||
add_files("src/Compiler/StmtCompiler.cpp")
|
||||
add_files("src/Compiler/Compiler.cpp")
|
||||
|
||||
add_files("src/Compiler/CompileTest.cpp")
|
||||
|
||||
target("LSP")
|
||||
@@ -96,24 +88,42 @@ target("LSP")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
|
||||
add_files("src/LSP/LSPServer.cpp")
|
||||
|
||||
set_filename("Fig-LSP")
|
||||
|
||||
target("Fig")
|
||||
target("ReplTest")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Bytecode/Disassembler.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
add_files("src/Parser/StmtParser.cpp")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
add_files("src/Object/Object.cpp")
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
add_files("src/Compiler/ExprCompiler.cpp")
|
||||
add_files("src/Compiler/StmtCompiler.cpp")
|
||||
add_files("src/Compiler/Compiler.cpp")
|
||||
add_files("src/VM/VM.cpp")
|
||||
add_files("src/Repl/ReplTest.cpp")
|
||||
|
||||
target("Fig")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Error/Error.cpp")
|
||||
|
||||
add_files("src/Token/Token.cpp")
|
||||
add_files("src/Lexer/Lexer.cpp")
|
||||
|
||||
add_files("src/Ast/Operator.cpp")
|
||||
add_files("src/Parser/ExprParser.cpp")
|
||||
@@ -121,13 +131,15 @@ target("Fig")
|
||||
add_files("src/Parser/TypeExprParser.cpp")
|
||||
add_files("src/Parser/Parser.cpp")
|
||||
|
||||
add_files("src/Object/Object.cpp")
|
||||
|
||||
add_files("src/Sema/Type.cpp")
|
||||
add_files("src/Sema/Analyzer.cpp")
|
||||
|
||||
add_files("src/Compiler/ExprCompiler.cpp")
|
||||
add_files("src/Compiler/StmtCompiler.cpp")
|
||||
add_files("src/Compiler/Compiler.cpp")
|
||||
add_files("src/Bytecode/Disassembler.cpp")
|
||||
|
||||
add_files("src/Object/Object.cpp")
|
||||
add_files("src/VM/VM.cpp")
|
||||
add_files("src/VM/Entry.cpp")
|
||||
add_files("src/main.cpp")
|
||||
Reference in New Issue
Block a user