Compare commits

...

11 Commits

Author SHA1 Message Date
680197aafe Refactor: 重构Parser和AST结构,以支持新的语言特性
- 更新了 ParserTest,以改进文件路径处理和输出格式。
- 在 StmtParser 中新增了 parseConstDecl 和 parseForStmt 方法,用于处理常量声明和 for 循环。
- TypeExpr现归类为Expr。TypeExpr属于Expr,语义阶段视为Expr
- 添加了新的 AST 节点:PostfixExpr、TernaryExpr、ForStmt 和 ImportStmt,用于表示新的语法结构。
2026-06-06 22:12:04 +08:00
4f87078a87 Plan 2026-06-01 18:52:06 +08:00
9338c21449 *(无用) feat: 在VM.cpp中添加likely属性以优化分支预测
refact: 在xmake.lua中优化构建设置
2026-05-10 21:25:05 +08:00
98de782760 feat: 增加repl入口,-r/--repl。 添加计时选项。 -- 我发现一个问题,analyzer没法保存环境。完了。 2026-04-30 21:24:11 +08:00
fafa2b4946 feat: 在解析器中实现 Lambda 和 new 表达式
- 增加了对 Lambda 表达式的初步解析支持,包括参数处理和返回类型。Lambda闭包尚未支持。
- 引入了用于对象初始化的新的表达式,支持可选的命名参数。
- 改进了表达式语法错误的错误报告。
- 更新了解析器和分析器以处理新的表达式类型并验证其语义。
- 修改了现有测试以涵盖新功能并确保其正确性。
- 改进了各种解析和语义错误的诊断。
2026-04-12 10:07:51 +08:00
570a87c3cd feat: 增加函数类型表达式支持,更新解析器和分析器 2026-03-18 17:30:09 +08:00
e1d9812f92 refact:实现参数解析器和入口点
- 新增了一个名为 ArgumentParser 的类来处理命令行参数,其中包括用于显示帮助、显示版本和显示许可证的标志。
- 更新了 main.cpp 以使用 ArgumentParser 来改进命令行界面。
- 创建了 Entry.cpp 和 Entry.hpp 来封装虚拟机执行逻辑,从而实现更好的关注点分离。
- 调整了 xmake.lua 以包含 ArgumentParser 和 Entry 组件的新源文件。
- 强化了命令行使用的错误处理和用户反馈。
2026-03-14 14:18:21 +08:00
6bcc98bdb3 删除无用的东西233 2026-03-13 20:11:46 +08:00
91b5a0e384 feat: 增加一个很简单的repl(还不能用) 写了readme 2026-03-13 01:28:43 +08:00
c0eacfd236 refactor: 修改Disassembler使用CoreIO的stream 2026-03-11 21:51:58 +08:00
51a939ac45 对编译器和虚拟机进行重构,以支持闭包和垃圾回收功能
- 去除了不再使用的结构,并更新了编译器以处理新的闭包语义。
- 改进了 Compiler,使其能够生成带有源位置跟踪的指令。
- 在 FunctionObject 和 VM 中引入了作用域变量管理,以支持动态闭包。
- 实现了使用标记-扫描(Mark-And-Sweep) (Tri-Color tracing) 算法的垃圾回收机制,包括对作用域变量的处理。
- 在 VM 中增加了函数加载和作用域变量检索的支持。
- 更新了对象模型,包括引入 InstanceObject 并改进内存管理。
- 添加了用于调试的全局变量打印功能。
2026-03-11 16:53:10 +08:00
67 changed files with 4706 additions and 3149 deletions

View File

@@ -6,7 +6,7 @@ Language: Cpp
AccessModifierOffset: -4
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
AlignAfterOpenBracket: DontAlign
AlignAfterOpenBracket: AlwaysBreak
# 连续赋值时,对齐所有等号
AlignConsecutiveAssignments: true

View 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")

View File

@@ -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)

View File

@@ -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
View File

@@ -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)
![License](https://img.shields.io/badge/license-MIT-blue)
![Status](https://img.shields.io/badge/status-0.5.0--alpha-yellow)
![C++](https://img.shields.io/badge/C++-99.5%25-blue)
![xmake](https://img.shields.io/badge/xmake-build-green)
![LLVM](https://img.shields.io/badge/LLVM-clang%2021.1.8-purple)
![Platform](https://img.shields.io/badge/Windows%20·%20Linux%20·%20macOS-lightgrey)
![Performance](https://img.shields.io/badge/fib(30)-~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
View 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)
![License](https://img.shields.io/badge/license-MIT-blue)
![Status](https://img.shields.io/badge/status-0.5.0--alpha-yellow)
![C++](https://img.shields.io/badge/C++-99.5%25-blue)
![xmake](https://img.shields.io/badge/xmake-构建-green)
![LLVM](https://img.shields.io/badge/LLVM-clang%2021.1.8-purple)
![Platform](https://img.shields.io/badge/Windows%20·%20Linux%20·%20macOS-lightgrey)
![Performance](https://img.shields.io/badge/fib(30)-~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** 还在快速迭代中,如果你对它感兴趣,不妨试一试,或者加入我们一起塑造它的未来!

View File

@@ -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>

View File

@@ -29,7 +29,10 @@ namespace Fig
IndexExpr,
CallExpr,
MemberExpr, // obj.prop
ObjectInitExpr, // new Point{}
NewExpr, // new Point{}
LambdaExpr,
TernaryExpr, // cond ? then : else
PostfixExpr, // expr++ / expr--
/* Statements */
ExprStmt,
@@ -44,11 +47,16 @@ namespace Fig
ReturnStmt,
BreakStmt,
ContinueStmt,
ForStmt, // for loop
ImportStmt, // import
/* Type Expressions */
TypeExpr,
NamedTypeExpr,
NullableTypeExpr
NamedTypeExpr, // 废弃,用 IdentiExpr/MemberExpr/ApplyExpr 替代
NullableTypeExpr, // 废弃,用 NullableExpr 替代
FnTypeExpr,
ApplyExpr, // 泛型实例化: List<Int>
NullableExpr, // 可空后缀: Int?
};
struct AstNode
@@ -60,15 +68,6 @@ namespace Fig
virtual ~AstNode() {};
};
struct TypeExpr : public AstNode
{
TypeExpr()
{
type = AstType::TypeExpr;
}
virtual ~TypeExpr() = default;
};
struct Expr : public AstNode
{
// 语义分析后填充
@@ -114,4 +113,55 @@ namespace Fig
return "<BlockStmt>";
}
};
// --- Type Expressions (inherit Expr — 类型即值) ---
struct TypeExpr : public Expr
{
TypeExpr() { type = AstType::TypeExpr; }
virtual ~TypeExpr() = default;
};
// ApplyExpr: 泛型实例化List<Int> → ApplyExpr(base, [Int])
struct ApplyExpr final : public Expr
{
Expr *base; // 基础类型表达式
DynArray<Expr *> args; // 泛型参数
ApplyExpr() { type = AstType::ApplyExpr; }
ApplyExpr(Expr *_base, DynArray<Expr *> _args, SourceLocation _loc) :
base(_base), args(std::move(_args))
{
type = AstType::ApplyExpr;
location = std::move(_loc);
}
virtual String toString() const override
{
String s = base->toString() + "<";
for (size_t i = 0; i < args.size(); ++i)
{
if (i) s += ", ";
s += args[i]->toString();
}
s += ">";
return s;
}
};
// 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

View File

@@ -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

View 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

View File

@@ -11,30 +11,30 @@
namespace Fig
{
struct ObjectInitExpr final : public Expr
struct NewExpr final : public Expr
{
struct Arg
{
String name;
Expr *value;
};
TypeExpr *typeExpr;
Expr *typeExpr;
DynArray<Arg> args;
ObjectInitExpr()
NewExpr()
{
type = AstType::ObjectInitExpr;
type = AstType::NewExpr;
}
ObjectInitExpr(TypeExpr *_te, DynArray<Arg> _args, SourceLocation _loc) :
NewExpr(Expr *_te, DynArray<Arg> _args, SourceLocation _loc) :
typeExpr(_te), args(std::move(_args))
{
type = AstType::ObjectInitExpr;
type = AstType::NewExpr;
location = std::move(_loc);
}
virtual String toString() const override
{
String res = "<ObjectInitExpr 'new " + typeExpr->toString() + "{";
String res = "<NewExpr 'new " + typeExpr->toString() + "{";
for (size_t i = 0; i < args.size(); ++i)
{
if (!args[i].name.empty())

View 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

View 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

View File

@@ -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:
/*
左结合, 左绑定力 < 右

View File

@@ -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, // .

View File

@@ -6,35 +6,39 @@
#pragma once
#include <Ast/Base.hpp>
#include <Sema/Environment.hpp>
#include <Bytecode/Bytecode.hpp>
namespace Fig
{
struct Param : public AstNode {
String name;
TypeExpr *typeSpecifier;
Expr *defaultValue;
Type resolvedType;
String name;
Expr *typeSpecifier;
Expr *defaultValue;
Type resolvedType;
Param() { type = AstType::AstNode; }
virtual ~Param() = default;
};
struct PosParam final : public Param {
PosParam(String _n, TypeExpr *_ts, Expr *_dv, SourceLocation _loc) {
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 {
String name;
String name;
DynArray<Param *> params;
TypeExpr *returnTypeSpecifier;
BlockStmt *body;
Type resolvedReturnType;
Symbol *resolvedSymbol = nullptr; // 连接物理符号
Expr *returnTypeSpecifier;
BlockStmt *body;
Type resolvedReturnType;
Symbol *resolvedSymbol = nullptr; // 连接物理符号
int protoIndex = -1; // 在CompiledModule扁平化protos的下标
DynArray<UpvalueInfo> upvalues;
FnDefStmt() { type = AstType::FnDefStmt; }
FnDefStmt(bool _p, String _n, DynArray<Param *> _pa, TypeExpr *_rt, BlockStmt *_b, SourceLocation _loc)
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; isPublic = _p; location = std::move(_loc);

32
src/Ast/Stmt/ForStmt.hpp Normal file
View 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

View File

@@ -11,11 +11,11 @@ namespace Fig
{
struct ImplStmt final : public Stmt
{
TypeExpr *interfaceType;
TypeExpr *structType;
Expr *interfaceType;
Expr *structType;
DynArray<FnDefStmt *> methods;
ImplStmt(TypeExpr *_it, TypeExpr *_st, DynArray<FnDefStmt *> _m, SourceLocation _loc) :
ImplStmt(Expr *_it, Expr *_st, DynArray<FnDefStmt *> _m, SourceLocation _loc) :
interfaceType(_it), structType(_st), methods(std::move(_m))
{
type = AstType::ImplStmt;

View 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

View File

@@ -16,8 +16,8 @@ namespace Fig
struct Method
{
String name;
DynArray<TypeExpr*> params;
TypeExpr *retType;
DynArray<Expr*> params;
Expr *retType;
SourceLocation location;
};

View File

@@ -13,9 +13,12 @@ namespace Fig
{
struct Field
{
bool isPublic;
bool typeInfer;
String name;
TypeExpr *type;
bool isPublic;
Expr *type;
Expr *initExpr;
};
bool isPublic;
String name;

View File

@@ -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),

View File

@@ -12,13 +12,13 @@ namespace Fig
struct NamedTypeExpr final : public TypeExpr
{
DynArray<String> path;
DynArray<TypeExpr *> arguments;
DynArray<Expr *> arguments;
NamedTypeExpr()
{
type = AstType::NamedTypeExpr;
}
NamedTypeExpr(DynArray<String> _p, DynArray<TypeExpr *> _args, SourceLocation _loc) :
NamedTypeExpr(DynArray<String> _p, DynArray<Expr *> _args, SourceLocation _loc) :
path(std::move(_p)), arguments(std::move(_args))
{
type = AstType::NamedTypeExpr;
@@ -51,9 +51,9 @@ namespace Fig
struct NullableTypeExpr final : public TypeExpr
{
TypeExpr *inner;
Expr *inner;
NullableTypeExpr(TypeExpr *_inner, SourceLocation _loc) : inner(_inner)
NullableTypeExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
{
type = AstType::NullableTypeExpr;
location = std::move(_loc);
@@ -64,4 +64,37 @@ namespace Fig
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

View File

@@ -5,8 +5,13 @@
#pragma once
#include <Deps/Deps.hpp>
#include <Object/ObjectBase.hpp>
#include <Core/SourceLocations.hpp>
#include <cstdint>
namespace Fig
{
using Instruction = std::uint32_t;
@@ -80,4 +85,27 @@ namespace Fig
| (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

View File

@@ -4,26 +4,27 @@
*/
#include <Bytecode/Disassembler.hpp>
#include <iostream>
#include <format>
namespace Fig
{
void Disassembler::DisassembleModule(const CompiledModule *module)
void Disassembler::DisassembleModule(const CompiledModule *module, std::ostream &stream)
{
if (!module) return;
std::cout << "--- Module Disassembly ---" << std::endl;
stream << "--- Module Disassembly ---" << std::endl;
for (auto *proto : module->protos)
{
DisassembleProto(proto);
}
}
void Disassembler::DisassembleProto(const Proto *proto)
void Disassembler::DisassembleProto(const Proto *proto, std::ostream &stream)
{
if (!proto) return;
std::cout << std::format("\n--- Proto: {} (Regs: {}, Params: {}) ---\n",
stream << std::format("\n--- Proto: {} (Regs: {}, Params: {}) ---\n",
proto->name, proto->maxRegisters, proto->numParams);
for (size_t i = 0; i < proto->code.size(); ++i)
@@ -32,48 +33,49 @@ namespace Fig
OpCode op = static_cast<OpCode>(inst & 0xFF);
uint8_t a = (inst >> 8) & 0xFF;
std::cout << std::format("[{:04}] {:<12} ", i, magic_enum::enum_name(op));
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;
std::cout << std::format("A:{:<3} B:{:<3} C:{:<3}", a, b, c);
stream << std::format("A:{:<3} B:{:<3} C:{:<3}", a, b, c);
}
else if (fmt == Format::ABx)
{
uint16_t bx = (inst >> 16) & 0xFFFF;
std::cout << std::format("A:{:<3} Bx:{:<5}", a, bx);
stream << std::format("A:{:<3} Bx:{:<5}", a, bx);
// 自动关联常量池
if (op == OpCode::LoadK && bx < proto->constants.size())
{
std::cout << std::format(" ; {}", proto->constants[bx].ToString());
stream << std::format(" ; {}", proto->constants[bx].ToString());
}
}
else if (fmt == Format::AsBx)
{
int16_t sbx = static_cast<int16_t>((inst >> 16) & 0xFFFF);
std::cout << std::format("A:{:<3} sBx:{:<5}", a, sbx);
stream << std::format("A:{:<3} sBx:{:<5}", a, sbx);
// 计算跳转绝对地址
if (op == OpCode::Jmp || op == OpCode::JmpIfFalse)
{
std::cout << std::format(" ; to [{:04}]", i + sbx + 1);
stream << std::format(" ; to [{:04}]", i + sbx + 1);
}
}
std::cout << "\n";
stream << "\n";
}
if (!proto->constants.empty())
{
std::cout << "Constants:\n";
stream << "Constants:\n";
for (size_t i = 0; i < proto->constants.size(); ++i)
{
std::cout << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
}
}
stream << "--- End Disassembly ---\n";
}
Disassembler::Format Disassembler::GetFormat(OpCode op)

View File

@@ -9,6 +9,7 @@
#include <Bytecode/Bytecode.hpp>
#include <Compiler/Compiler.hpp>
#include <Core/CoreIO.hpp>
namespace Fig
{
@@ -16,10 +17,10 @@ namespace Fig
{
public:
// 反汇编整个模块
static void DisassembleModule(const CompiledModule *module);
static void DisassembleModule(const CompiledModule *module, std::ostream & = CoreIO::GetStdOut());
// 反汇编单个函数原型
static void DisassembleProto(const Proto *proto);
static void DisassembleProto(const Proto *proto, std::ostream & = CoreIO::GetStdOut());
private:
enum class Format

View File

@@ -27,7 +27,11 @@ int main()
}
Lexer lexer(source, filePath);
Parser parser(lexer, sm, filePath);
Diagnostics diagnostics;
Parser parser(lexer, sm, filePath, diagnostics);
diagnostics.EmitAll(sm);
auto pRes = parser.Parse();
if (!pRes)

View File

@@ -3,16 +3,20 @@
@brief 编译器主逻辑实现:物理 Bootstrapper 与双步扫描
*/
#include <Compiler/Compiler.hpp>
#include <Ast/Stmt/FnDefStmt.hpp>
#include <Compiler/Compiler.hpp>
namespace Fig
{
Result<CompiledModule *, Error> Compiler::Compile(Program *program)
{
module = new CompiledModule();
module = new CompiledModule();
if (program->nodes.empty())
{
return module;
}
// 1. 预留 Protos[0] 给 Bootstrapper
// 预留 Protos[0] 给 Bootstrapper
Proto *bootProto = new Proto();
bootProto->name = "[bootstrapper]";
module->protos.push_back(bootProto);
@@ -20,17 +24,22 @@ namespace Fig
int initIdx = -1;
int mainIdx = -1;
// 2. 第一步:预扫描顶层函数,锁定物理索引
SourceLocation *mainFnLoc = nullptr;
SourceLocation *initFnLoc = nullptr;
// 预扫描顶层函数
for (auto *stmt : program->nodes)
{
if (stmt->type == AstType::FnDefStmt)
{
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);
@@ -41,13 +50,19 @@ namespace Fig
}
if (f->name == "init")
initIdx = idx;
{
initIdx = idx;
initFnLoc = &stmt->location;
}
if (f->name == "main")
mainIdx = idx;
{
mainIdx = idx;
mainFnLoc = &stmt->location;
}
}
}
// 3. 第二步:在 Bootstrapper 环境中编译所有语句
// Bootstrapper 中编译所有语句
FuncState bootState(bootProto, nullptr);
current = &bootState;
@@ -60,18 +75,18 @@ namespace Fig
}
}
// 4. 发射 Bootstrapper 引导指令
// 发射 Bootstrapper 引导指令
if (initIdx != -1)
{
emit(Op::iABC(OpCode::FastCall, (uint8_t) initIdx, 0, 0));
emit(Op::iABC(OpCode::FastCall, (uint8_t) initIdx, 0, 0), initFnLoc);
}
if (mainIdx != -1)
{
emit(Op::iABC(OpCode::FastCall, (uint8_t) mainIdx, 0, 0));
emit(Op::iABC(OpCode::FastCall, (uint8_t) mainIdx, 0, 0), mainFnLoc);
}
emit(Op::iAsBx(OpCode::Exit, 0, 0));
emit(Op::iAsBx(OpCode::Exit, 0, 0), &program->nodes.back()->location);
return module;
}
@@ -89,7 +104,8 @@ namespace Fig
{
if (current->freereg >= MAX_REGISTERS)
{
return std::unexpected(Error(ErrorType::RegisterOverflow, "too many registers", "", loc));
return std::unexpected(
Error(ErrorType::RegisterOverflow, "too many registers", "", loc));
}
Register reg = current->freereg++;
@@ -112,14 +128,15 @@ namespace Fig
{
if (current->constantMap.contains(val))
return current->constantMap[val];
int idx = (int) current->proto->constants.size();
int idx = (int) current->proto->constants.size();
current->proto->constants.push_back(val);
current->constantMap[val] = idx;
return idx;
}
void Compiler::emit(Instruction instr)
void Compiler::emit(Instruction inst, SourceLocation *loc)
{
current->proto->code.push_back(instr);
current->proto->code.push_back(inst);
current->proto->locations.push_back(loc);
}
} // namespace Fig

View File

@@ -14,23 +14,6 @@ namespace Fig
{
using Register = std::uint8_t;
struct UpvalueInfo { uint8_t index; bool isLocal; };
struct Proto
{
String name;
DynArray<Instruction> code;
DynArray<Value> constants;
DynArray<UpvalueInfo> upvalues;
uint8_t maxRegisters = 0;
uint8_t numParams = 0;
};
struct CompiledModule
{
DynArray<Proto *> protos;
};
class Compiler
{
private:
@@ -60,7 +43,7 @@ namespace Fig
void freeReg(Register count = 1);
int addConstant(Value val);
void emit(Instruction instr);
void emit(Instruction inst, SourceLocation *loc);
Result<void, Error> compileStmt(Stmt *stmt);
Result<Register, Error> compileExpr(Expr *expr, Register target = NO_REG);

View File

@@ -1,6 +1,6 @@
/*!
@file src/Compiler/ExprCompiler.cpp
@brief 表达式编译器实现:引入水位线(Watermark)与零拷贝复用机制
@brief 表达式编译
*/
#include <Ast/Expr/CallExpr.hpp>
@@ -12,7 +12,6 @@
#include <limits>
#include <system_error>
namespace Fig
{
static Result<Value, Error> parsePhysicalNumber(const String &raw, const SourceLocation &loc)
@@ -20,46 +19,69 @@ namespace Fig
char buffer[128];
int j = 0;
bool isFloat = false;
for (size_t i = 0; i < raw.length() && j < 127; ++i)
{
char32_t c = raw[i];
if (c == '_')
continue;
// 检查开头的无效字符
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')
{
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;
}
}
if (isFloat)
{
// 如果既有浮点标记又是0x开头可能是16进制浮点
auto fmt =
(isHexOrBin && base == 16) ? std::chars_format::hex : std::chars_format::general;
double dVal;
auto [ptr, ec] = std::from_chars(buffer, buffer + j, dVal);
if (ec != std::errc())
return std::unexpected(Error(ErrorType::SyntaxError, "float overflow", "", loc));
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
else if (isHexOrBin)
{
int base = 10;
const char *start = buffer;
if (j > 2 && buffer[0] == '0')
{
if (buffer[1] == 'x' || buffer[1] == 'X')
{
base = 16;
start += 2;
}
else if (buffer[1] == 'b' || buffer[1] == 'B')
{
base = 2;
start += 2;
}
}
// 16进制或2进制整数
int64_t iVal;
auto [ptr, ec] = std::from_chars(start, buffer + j, iVal, base);
if (ec != std::errc())
return std::unexpected(Error(ErrorType::SyntaxError, "integer overflow", "", loc));
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())
@@ -71,6 +93,27 @@ namespace Fig
return Value::FromDouble(static_cast<double>(iVal));
}
}
else
{
// 10进制数字可能是整数或浮点数
double dVal;
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, std::chars_format::general);
if (ec != std::errc() || ptr != buffer + j)
return std::unexpected(
Error(ErrorType::SyntaxError, "invalid number literal", "", loc));
// 检查是否是整数(没有小数部分且不超出int32范围)
if (dVal == std::floor(dVal) && dVal >= std::numeric_limits<int32_t>::min()
&& dVal <= std::numeric_limits<int32_t>::max())
{
return Value::FromInt(static_cast<int32_t>(dVal));
}
else
{
return Value::FromDouble(dVal);
}
}
}
Result<Register, Error> Compiler::compileExpr(Expr *expr, Register target)
@@ -94,24 +137,26 @@ namespace Fig
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))));
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)));
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));
emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->location);
}
else if (tok.type == TokenType::LiteralTrue)
{
emit(Op::iABC(OpCode::LoadTrue, r, 0, 0));
emit(Op::iABC(OpCode::LoadTrue, r, 0, 0), &l->location);
}
else if (tok.type == TokenType::LiteralFalse)
{
emit(Op::iABC(OpCode::LoadFalse, r, 0, 0));
emit(Op::iABC(OpCode::LoadFalse, r, 0, 0), &l->location);
}
return r;
}
@@ -122,14 +167,16 @@ namespace Fig
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)));
emit(
Op::iABx(OpCode::Mov, target, static_cast<uint16_t>(sym->index)),
&i->location);
}
return target;
}
@@ -137,12 +184,14 @@ namespace Fig
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));
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)));
emit(Op::iABx(OpCode::GetGlobal, r, static_cast<uint16_t>(gId)), &i->location);
}
return r;
}
@@ -167,30 +216,47 @@ namespace Fig
return std::unexpected(res.error());
}
bool isGlobalFastCall = false;
if (c->callee->type == AstType::IdentiExpr)
{
// 静态去虚化:编译期直接跳板
auto *id = static_cast<IdentiExpr *>(c->callee);
int protoIdx = id->resolvedSymbol->index;
emit(Op::iABC(OpCode::FastCall,
static_cast<uint8_t>(protoIdx),
baseReg,
static_cast<uint8_t>(c->args.args.size())));
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);
}
}
else
if (!isGlobalFastCall)
{
// 动态闭包调用
// 先获取闭包对象所在的物理寄存器
auto r_fn = compileExpr(c->callee);
if (!r_fn)
return std::unexpected(r_fn.error());
emit(Op::iABC(
OpCode::Call, *r_fn, baseReg, static_cast<uint8_t>(c->args.args.size())));
// 使用动态 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实现零开销回写
// 目若 target 未指定allocateReg 将复用 baseReg实现零开销回写
Register r_dest;
if (target == NO_REG)
@@ -207,7 +273,7 @@ namespace Fig
if (r_dest != baseReg)
{
emit(Op::iABx(OpCode::Mov, r_dest, baseReg));
emit(Op::iABx(OpCode::Mov, r_dest, baseReg), &c->location);
}
return r_dest;
@@ -227,24 +293,31 @@ namespace Fig
Symbol *sym = lid->resolvedSymbol;
if (sym->location == SymbolLocation::Local)
{
emit(Op::iABx(OpCode::Mov, static_cast<Register>(sym->index), *r_val));
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));
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))));
emit(
Op::iABx(
OpCode::SetGlobal,
*r_val,
static_cast<uint16_t>(getGlobalID(lid->name))),
&lid->location);
}
}
return r_val;
}
Register mark = current->freereg; // 记录水位线
Register mark = current->freereg; // mark
auto r_l = compileExpr(in->left);
if (!r_l)
@@ -258,29 +331,61 @@ namespace Fig
OpCode op;
switch (in->op)
{
case BinaryOperator::Add: op = isInt ? OpCode::IntFastAdd : OpCode::Add; break;
case BinaryOperator::Subtract:
op = isInt ? OpCode::IntFastSub : OpCode::Sub;
case BinaryOperator::Add: {
op = (isInt ? (OpCode::IntFastAdd) : (OpCode::Add));
break;
case BinaryOperator::Multiply:
op = isInt ? OpCode::IntFastMul : OpCode::Mul;
}
case BinaryOperator::Subtract: {
op = (isInt ? (OpCode::IntFastSub) : (OpCode::Sub));
break;
case BinaryOperator::Divide:
op = isInt ? OpCode::IntFastDiv : OpCode::Div;
}
case BinaryOperator::Multiply: {
op = (isInt ? (OpCode::IntFastMul) : (OpCode::Mul));
break;
case BinaryOperator::Modulo: op = OpCode::Mod; break;
case BinaryOperator::BitXor: op = OpCode::BitXor; break;
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:
return std::unexpected(Error(ErrorType::InternalError,
}
case BinaryOperator::Divide: {
op = (isInt ? (OpCode::IntFastDiv) : (OpCode::Div));
break;
}
case BinaryOperator::Modulo: {
op = OpCode::Mod;
break;
}
case BinaryOperator::BitXor: {
op = OpCode::BitXor;
break;
}
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: {
return std::unexpected(Error(
ErrorType::InternalError,
"unsupported binary operator",
"",
in->location));
}
}
// 释放左右操作数产生的临时寄存器
@@ -300,11 +405,70 @@ namespace Fig
r_d = target;
}
emit(Op::iABC(op, r_d, *r_l, *r_r));
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)
{
auto result = compileExpr(static_cast<Expr *>(l->body));
if (!result)
{
return result;
}
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());
}
auto _r = allocateReg(l->body->location);
if (!_r)
{
return _r;
}
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);
}
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(

View File

@@ -1,6 +1,6 @@
/*!
@file src/Compiler/StmtCompiler.cpp
@brief 语句编译器实现:实装水位线机制,彻底消灭硬编码寄存器释放
@brief 语句编译
*/
#include <Ast/Stmt/FnDefStmt.hpp>
@@ -9,7 +9,6 @@
#include <Ast/Stmt/WhileStmt.hpp>
#include <Compiler/Compiler.hpp>
namespace Fig
{
Result<void, Error> Compiler::compileStmt(Stmt *stmt)
@@ -36,13 +35,15 @@ namespace Fig
auto *v = static_cast<VarDecl *>(stmt);
if (current->enclosing == nullptr) // 处理全局变量
{
Register mark = current->freereg; // 记录水位线
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))));
emit(Op::iABx(OpCode::SetGlobal,
*regRes,
static_cast<uint16_t>(getGlobalID(v->name))),
&v->location);
current->freereg = mark; // 释放初始化表达式的临时占用
}
else
@@ -68,8 +69,21 @@ namespace Fig
case AstType::FnDefStmt: {
auto *f = static_cast<FnDefStmt *>(stmt);
// 物理连线:对接 Compile() 第一阶段预分配的 Proto
Proto *p = module->protos[f->resolvedSymbol->index];
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;
@@ -79,13 +93,47 @@ namespace Fig
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));
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;
}
@@ -93,19 +141,19 @@ namespace Fig
auto *i = static_cast<IfStmt *>(stmt);
DynArray<int> exitJumps;
Register mark = current->freereg; // 记录水位线
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));
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));
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &i->location);
int targetIdx = static_cast<int>(current->proto->code.size());
current->proto->code[jmpToNext] = Op::iAsBx(
@@ -119,17 +167,22 @@ namespace Fig
return std::unexpected(ec.error());
int nextElif = static_cast<int>(current->proto->code.size());
emit(Op::iAsBx(OpCode::JmpIfFalse, *ec, 0));
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));
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &elif->location);
int target = static_cast<int>(current->proto->code.size());
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)
@@ -151,20 +204,21 @@ namespace Fig
auto *w = static_cast<WhileStmt *>(stmt);
int startIdx = static_cast<int>(current->proto->code.size());
Register mark = current->freereg; // 记录水位线
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));
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)));
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(
@@ -174,7 +228,7 @@ namespace Fig
case AstType::ReturnStmt: {
auto *rs = static_cast<ReturnStmt *>(stmt);
Register mark = current->freereg; // 记录水位线
Register mark = current->freereg; // mark
Register retReg;
if (rs->value)
@@ -189,17 +243,17 @@ namespace Fig
auto r = allocateReg(rs->location);
if (!r)
return std::unexpected(r.error());
emit(Op::iABC(OpCode::LoadNull, *r, 0, 0));
emit(Op::iABC(OpCode::LoadNull, *r, 0, 0), &rs->location);
retReg = *r;
}
emit(Op::iABC(OpCode::Return, retReg, 0, 0));
emit(Op::iABC(OpCode::Return, retReg, 0, 0), &rs->location);
current->freereg = mark; // 回收返回值计算的占用
break;
}
case AstType::ExprStmt: {
Register mark = current->freereg; // 记录水位线
Register mark = current->freereg; // mark
auto reg = compileExpr(static_cast<ExprStmt *>(stmt)->expr);
if (!reg)
return std::unexpected(reg.error());

View File

@@ -46,7 +46,7 @@
#if SIZE_MAX == 18446744073709551615ull
#define __FCORE_ARCH "64"
#else
#else
#define __FCORE_ARCH "86"
#endif
@@ -56,12 +56,88 @@ namespace Fig
{
namespace Core
{
inline constexpr std::string_view VERSION = __FCORE_VERSION;
inline constexpr std::string_view LICENSE = "MIT";
inline constexpr std::string_view AUTHOR = "PuqiAR";
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
inline constexpr std::string_view VERSION = __FCORE_VERSION;
inline constexpr std::string_view LICENSE = "MIT";
inline constexpr std::string_view AUTHOR = "PuqiAR";
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
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;
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

View File

@@ -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";
@@ -61,7 +66,8 @@ namespace Fig
case TooManyConstants: return "TooManyConstants";
case RegisterOverflow: return "RegisterOverflow";
case InternalError: return "InternalError";
case InternalError:
return "InternalError";
// default: return "Some one forgot to add case to `ErrorTypeToString`";
}
return "UnknownError";
@@ -69,10 +75,10 @@ namespace Fig
void PrintSystemInfos()
{
std::ostream &err = CoreIO::GetStdErr();
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();
@@ -83,35 +89,43 @@ namespace Fig
void PrintErrorInfo(const Error &error, const SourceManager &srcManager)
{
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 *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;
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 *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;
@@ -127,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';
}
@@ -158,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);

View File

@@ -24,6 +24,8 @@ namespace Fig
{
/* Minor */
UnusedSymbol = 0,
UnnecessarySemicolon,
TrailingComma,
/* Medium */
MayBeNull = 1001,
@@ -51,7 +53,7 @@ namespace Fig
TooManyLocals,
TooManyConstants,
// --- 新增编译器内部与VM约束 ---
// 编译器内部约束
RegisterOverflow,
InternalError,
};

View File

@@ -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);
}
};

View File

@@ -1,26 +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
{
String name; // 调试使用
Proto *proto; // 指向编译器生成的只读字节码与常量池
std::uint8_t paraCount;
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

View 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[];
};
}

View File

@@ -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
{

View File

@@ -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>

View File

@@ -125,7 +125,7 @@ namespace Fig
{
return (v_ & (SIGN_BIT | QNAN_MASK)) == (SIGN_BIT | QNAN_MASK);
}
// 提取数据 (Unbox / As)
[[nodiscard]] constexpr double AsDouble() const
{
@@ -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();
@@ -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标记
Object *next; // 8 bytes: gc链表
StructObject *klass; // 8 bytes: 一切皆对象,父类指针
ObjectType type; // 1 byte : 类型
GCColor color = GCColor::White; // 1 byte : gc标记
// + 6 bytes padding
constexpr bool isString() const
@@ -227,12 +235,6 @@ namespace Fig
{
return type == ObjectType::Instance;
}
// 调试输出
virtual String toString() const
{
return "Object";
}
};
} // namespace Fig

View File

@@ -25,10 +25,5 @@ namespace Fig
struct StringObject final : public Object
{
String data; // 40 bytes
virtual String toString() const override
{
return data;
}
};
};

View File

@@ -39,9 +39,6 @@ namespace Fig
0 - UnaryOperators::Count BinaryOperators::Count
*/
Value fields[];
Object *GetUnaryOperator(UnaryOperator _op)
{

View File

@@ -14,7 +14,8 @@ namespace Fig
StateProtector p(this, {State::ParsingLiteralExpr});
const Token &literal_token = consumeToken();
LiteralExpr *node = arena.Allocate<LiteralExpr>(literal_token, makeSourceLocation(literal_token));
LiteralExpr *node =
arena.Allocate<LiteralExpr>(literal_token, makeSourceLocation(literal_token));
return node;
}
Result<Expr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
@@ -65,8 +66,8 @@ namespace Fig
return node;
}
Result<Expr *, Error> Parser::parseIndexExpr(
Expr *base) // 由 parseExpression调用, 当前token为 `[`
Result<Expr *, Error>
Parser::parseIndexExpr(Expr *base) // 由 parseExpression调用, 当前token为 `[`
{
StateProtector p(this, {State::ParsingIndexExpr});
@@ -80,7 +81,8 @@ 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)));
@@ -91,12 +93,13 @@ namespace Fig
return indexExpr;
}
Result<Expr *, 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 arena.Allocate<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 arena.Allocate<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,56 +434,116 @@ 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;
}
}; // namespace Fig
}; // namespace Fig

View File

@@ -15,12 +15,16 @@ namespace Fig
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)
{

View File

@@ -9,6 +9,7 @@
#include <Ast/Ast.hpp>
#include <Deps/Deps.hpp>
#include <Error/Diagnostics.hpp>
#include <Error/Error.hpp>
#include <Lexer/Lexer.hpp>
#include <Token/Token.hpp>
@@ -33,6 +34,9 @@ namespace Fig
String fileName;
bool isEOF = false;
Diagnostics &diagnostics;
std::optional<Error> lexerError; // 词法错误缓存,避免 exit/abort
// 惰性获取下一个 Token跳过注释
Token nextToken()
{
@@ -46,8 +50,12 @@ namespace Fig
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)
@@ -81,8 +89,12 @@ namespace Fig
auto result = lexer.NextToken();
if (!result)
{
ReportError(result.error(), srcManager);
std::abort();
lexerError = result.error();
isEOF = true;
Token eof = {0, 0, TokenType::EndOfFile};
buffer.push_back(eof);
index = buffer.size() - 1;
return buffer.back();
}
if (result->type == TokenType::Comments)
continue;
@@ -116,12 +128,16 @@ namespace Fig
enum StateType : std::uint8_t
{
Standby,
ParsingLiteralExpr,
ParsingIdentiExpr,
ParsingInfixExpr,
ParsingPrefixExpr,
ParsingIndexExpr,
ParsingCallExpr,
ParsingLambdaExpr,
ParsingNewExpr,
ParsingVarDecl,
ParsingIf,
ParsingWhile,
@@ -129,7 +145,11 @@ namespace Fig
ParsingReturn,
ParsingBreak,
ParsingContinue,
ParsingStructDef,
ParsingTypeParameters,
ParsingNamedTypeExpr,
ParsingFnTypeExpr,
} type = StateType::Standby;
std::unordered_set<TokenType> stopAt = {};
};
@@ -137,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,
@@ -146,20 +167,10 @@ namespace Fig
return baseTerminators;
}
std::unordered_set<TokenType> &getTerminators()
{
static std::unordered_set<TokenType> terminators(getBaseTerminators());
return terminators;
}
void resetTermintors()
{
getTerminators() = getBaseTerminators();
}
bool shouldTerminate()
{
const Token &token = currentToken();
if (getTerminators().contains(token.type))
if (getBaseTerminators().contains(token.type))
return true;
for (auto it = stateStack.rbegin(); it < stateStack.rend(); ++it)
{
@@ -200,34 +211,58 @@ namespace Fig
SourceLocation makeSourceLocation(const Token &tok)
{
auto [line, column] = srcManager.GetLineColumn(tok.index);
// 物理防爆盾:防止因解析错位导致的异常列号引起终端 OOM
// 防止因解析错位导致的异常列号引起终端 OOM
if (column > 5000)
column = 1;
return SourceLocation(SourcePosition(line, column, tok.length),
return SourceLocation(
SourcePosition(line, column, tok.length),
fileName,
"[internal parser]",
magic_enum::enum_name(currentState().type).data());
}
inline Error makeUnexpectTokenError(const String &stmt, const String &exp, const Token &got)
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,
return Error(
ErrorType::SyntaxError,
std::format(
"expect '{}' in {}, got `{}`", exp, stmt, magic_enum::enum_name(got.type)),
"none",
makeSourceLocation(got));
makeSourceLocation(got),
th_loc);
}
inline Error makeExpectSemicolonError()
inline Error
makeExpectSemicolonError(std::source_location th_loc = std::source_location::current())
{
return Error(ErrorType::SyntaxError,
return Error(
ErrorType::SyntaxError,
"expect ';' after statement",
"insert ';'",
makeSourceLocation(currentToken()));
makeSourceLocation(currentToken()),
th_loc);
}
Result<TypeExpr *, Error> parseTypeExpr();
Result<TypeExpr *, Error> parseNamedTypeExpr();
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);
}
Result<decltype(StructDefStmt::typeParameters), Error> parseTypeParameters();
Result<Expr *, Error> parseTypeExpr();
Result<Expr *, Error> parseNamedTypeExpr();
Result<Expr *, Error> parseFnTypeExpr();
Result<Expr *, Error> parseExpression(BindingPower = 0);
Result<Expr *, Error> parseLiteralExpr();
@@ -237,9 +272,11 @@ namespace Fig
Result<Expr *, Error> parseIndexExpr(Expr *);
Result<Expr *, Error> parseCallExpr(Expr *);
Result<Expr *, Error> parseNewExpr();
Result<Expr *, Error> parseLambdaExpr();
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();
@@ -249,12 +286,14 @@ namespace Fig
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) :
lexer(_lexer), srcManager(_src), fileName(std::move(_file))
Parser(Lexer &_lexer, SourceManager &_src, String _file, Diagnostics &_diagnostics) :
lexer(_lexer), srcManager(_src), fileName(std::move(_file)), diagnostics(_diagnostics)
{
pushState(State());
}

View File

@@ -5,29 +5,39 @@ int main()
{
using namespace Fig;
String fileName = "test.fig";
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
String fileName = "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);
auto result = parser.Parse();
Lexer lexer(source, 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

View File

@@ -1,73 +1,201 @@
/*!
@file src/Parser/TypeExprParser.cpp
@brief 类型表达式解析器实现:支持泛型与空安全
@brief 类型表达式解析器实现 — 类型即值,产生 Expr* 而非 TypeExpr*
@author PuqiAR (im@puqiar.top)
@date 2026-03-08
@date 2026-06-06
*/
#include <Parser/Parser.hpp>
namespace Fig
{
// 解析基础命名类型与泛型: List<Int>
Result<TypeExpr *, Error> Parser::parseNamedTypeExpr()
Result<decltype(StructDefStmt::typeParameters), Error> Parser::parseTypeParameters()
{
StateProtector p(this, {State::ParsingNamedTypeExpr});
SourceLocation location = makeSourceLocation(currentToken());
StateProtector p(this, {State::ParsingTypeParameters});
decltype(StructDefStmt::typeParameters) tp;
const Token &lab = consumeToken(); // consume `<`
DynArray<String> path;
while (true)
{
const Token &tok = consumeToken();
const String &name = srcManager.GetSub(tok.index, tok.length);
path.push_back(name);
if (match(TokenType::Dot))
if (isEOF)
{
if (!currentToken().isIdentifier())
return std::unexpected(makeUnexpectTokenError("Type", "identifier", currentToken()));
return std::unexpected(Error(
ErrorType::SyntaxError,
"unclosed `<` in type parameters",
"insert '>'",
makeSourceLocation(lab)));
}
else break;
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});
if (!currentToken().isIdentifier())
{
return std::unexpected(
makeUnexpectTokenError("TypeExpr", "type name", currentToken()));
}
DynArray<TypeExpr *> arguments;
if (match(TokenType::Less)) // `<`
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("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);
}
// generic args
if (match(TokenType::Less))
{
DynArray<Expr *> args;
while (true)
{
auto result = parseTypeExpr();
if (!result) return std::unexpected(result.error());
arguments.push_back(*result);
if (!result)
return std::unexpected(result.error());
args.push_back(*result);
if (match(TokenType::Greater)) break; // `>`
if (match(TokenType::Greater))
break; // `>`
if (!match(TokenType::Comma))
return std::unexpected(makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken()));
return std::unexpected(
makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken()));
}
}
return arena.Allocate<NamedTypeExpr>(path, arguments, location);
}
// 解析主入口: 处理 `?` 后缀
Result<TypeExpr *, Error> Parser::parseTypeExpr()
{
TypeExpr *base = nullptr;
// 目前只支持命名类型 (以后可以加函数类型 (Int)->Int)
if (currentToken().isIdentifier())
{
auto res = parseNamedTypeExpr();
if (!res) return std::unexpected(res.error());
base = *res;
}
else return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken()));
// 空安全处理: Int?? 也可以,但 Analyzer 会规范化它
while (match(TokenType::Question))
{
base = arena.Allocate<NullableTypeExpr>(base, makeSourceLocation(prevToken()));
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()));
}
}
Expr *returnType = nullptr;
if (match(TokenType::RightArrow)) // ->
{
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())
{
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("TypeExpr", "name", currentToken()));
}
// nullable
if (currentToken().type == TokenType::Question) // ?
{
base = arena.Allocate<NullableExpr>(
base, makeSourceLocation(consumeToken())); // consume `?`
}
return base;
}
} // namespace Fig

311
src/Repl/Repl.hpp Normal file
View 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 &in;
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
View 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();
}

View File

@@ -5,7 +5,6 @@
#include <Ast/Ast.hpp>
#include <Ast/Expr/MemberExpr.hpp>
#include <Ast/Expr/ObjectInitExpr.hpp>
#include <Ast/Stmt/ImplStmt.hpp>
#include <Ast/Stmt/InterfaceDefStmt.hpp>
#include <Ast/Stmt/StructDefStmt.hpp>
@@ -82,8 +81,11 @@ namespace Fig
{
auto *s = static_cast<StructDefStmt *>(stmt);
if (globalTypes.contains(s->name))
{
return std::unexpected(
Error(ErrorType::RedeclarationError, "type redeclared", "", s->location));
}
auto *t = arena.Allocate<StructType>(s->name);
typeCtx.allTypes.push_back(t);
globalTypes[s->name] = t;
@@ -92,10 +94,14 @@ namespace Fig
{
auto *f = static_cast<FnDefStmt *>(stmt);
if (f->name == "main")
{
hasMain = true;
}
if (globalSymbols.contains(f->name))
{
return std::unexpected(
Error(ErrorType::RedeclarationError, "func redeclared", "", f->location));
}
Symbol *sym =
arena.Allocate<Symbol>(f->name, Type{}, SymbolLocation::Global, 0, true);
globalSymbols[f->name] = sym;
@@ -118,7 +124,9 @@ namespace Fig
{
auto res = resolveTypeExpr(f.type);
if (!res)
{
return std::unexpected(res.error());
}
st->AddField(f.name, *res, f.isPublic);
}
}
@@ -185,10 +193,11 @@ namespace Fig
}
Type declT = v->typeSpecifier ? *resolveTypeExpr(v->typeSpecifier) : initT;
// 🔥 强类型校验:赋值拦截
// 赋值拦截
if (v->initExpr && !initT.isAssignableTo(declT))
{
return std::unexpected(Error(ErrorType::TypeError,
return std::unexpected(Error(
ErrorType::TypeError,
"cannot assign '" + initT.toString() + "' to type '" + declT.toString()
+ "'",
"",
@@ -206,13 +215,45 @@ namespace Fig
v->localId = idx;
break;
}
case AstType::FnDefStmt: {
auto *f = static_cast<FnDefStmt *>(stmt);
auto *f = static_cast<FnDefStmt *>(stmt);
// 局部闭包延迟类型推导
if (!f->resolvedSymbol) // 闭包?
{
SymbolLocation loc =
env.current->parent ? SymbolLocation::Local : SymbolLocation::Global;
int idx = (loc == SymbolLocation::Local) ? env.current->nextLocalId++ : 0;
Symbol *sym = arena.Allocate<Symbol>(f->name, Type{}, loc, idx, true);
f->resolvedSymbol = sym;
env.current->locals[f->name] = sym;
auto res = resolveTypeExpr(f->returnTypeSpecifier);
if (!res)
return std::unexpected(res.error());
f->resolvedReturnType = *res;
DynArray<Type> paramTypes;
for (auto *p : f->params)
{
auto pres = resolveTypeExpr(p->typeSpecifier);
if (!pres)
return std::unexpected(pres.error());
p->resolvedType = *pres;
paramTypes.push_back(*pres);
}
f->resolvedSymbol->type = typeCtx.CreateFuncType(std::move(paramTypes), *res);
}
FnStateGuard fnGuard(state.currentFn, f);
ScopeGuard scopeGuard(env, true);
for (auto *p : f->params)
{
env.current->locals[p->name] = arena.Allocate<Symbol>(p->name,
env.current->locals[p->name] = arena.Allocate<Symbol>(
p->name,
p->resolvedType,
SymbolLocation::Local,
env.current->nextLocalId++,
@@ -220,8 +261,15 @@ namespace Fig
}
if (auto r = analyzeStmt(f->body); !r)
return r;
for (const auto &upval : env.current->upvalues)
{
f->upvalues.push_back({static_cast<std::uint8_t>(upval.index), upval.isLocal});
}
break;
}
case AstType::IfStmt: {
auto *i = static_cast<IfStmt *>(stmt);
@@ -241,7 +289,8 @@ namespace Fig
return std::unexpected(c.error());
else if (!c->isAssignableTo(typeCtx.GetBasic(TypeTag::Bool)))
{
return std::unexpected(Error(ErrorType::TypeError,
return std::unexpected(Error(
ErrorType::TypeError,
"condition must be Bool",
"",
elif->cond->location));
@@ -292,11 +341,15 @@ namespace Fig
break;
}
case AstType::BreakStmt:
case AstType::ContinueStmt:
case AstType::ContinueStmt: {
if (state.loopDepth <= 0)
{
return std::unexpected(
Error(ErrorType::SyntaxError, "outside loop", "", stmt->location));
}
break;
}
case AstType::ReturnStmt: {
auto *rs = static_cast<ReturnStmt *>(stmt);
Type retT = typeCtx.GetBasic(TypeTag::Null);
@@ -307,10 +360,11 @@ namespace Fig
return std::unexpected(res.error());
retT = *res;
}
// 🔥 强类型校验:返回值拦截
// 返回值校验
if (state.currentFn && !retT.isAssignableTo(state.currentFn->resolvedReturnType))
{
return std::unexpected(Error(ErrorType::TypeError,
return std::unexpected(Error(
ErrorType::TypeError,
"cannot return '" + retT.toString() + "' from function expecting '"
+ state.currentFn->resolvedReturnType.toString() + "'",
"",
@@ -369,7 +423,8 @@ namespace Fig
auto *st = static_cast<StructType *>(targetType.base);
if (!st->fieldMap.contains(m->name))
{
return std::unexpected(Error(ErrorType::TypeError,
return std::unexpected(Error(
ErrorType::TypeError,
"struct '" + st->name + "' has no field named '" + m->name + "'",
"",
m->location));
@@ -377,14 +432,18 @@ namespace Fig
// 字段类型
return expr->resolvedType = st->fields[st->fieldMap[m->name]].type;
}
case AstType::ObjectInitExpr: {
auto *o = static_cast<ObjectInitExpr *>(expr);
case AstType::NewExpr: {
auto *o = static_cast<NewExpr *>(expr);
auto res = resolveTypeExpr(o->typeExpr);
if (!res)
{
return std::unexpected(res.error());
}
if (!res->base || res->base->tag != TypeTag::Struct)
{
return std::unexpected(
Error(ErrorType::TypeError, "requires struct", "", o->location));
}
auto *st = static_cast<StructType *>(res->base);
for (auto &arg : o->args)
{
@@ -393,8 +452,10 @@ namespace Fig
Error(ErrorType::TypeError, "unknown field", "", arg.value->location));
auto r = analyzeExpr(arg.value);
if (!r)
{
return std::unexpected(r.error());
// 顺手做字段赋值类型检查
}
// 字段赋值类型检查
if (!arg.name.empty()
&& !r->isAssignableTo(st->fields[st->fieldMap[arg.name]].type))
{
@@ -418,7 +479,8 @@ namespace Fig
if (in->op == BinaryOperator::Assign)
{
if (!r.isAssignableTo(l))
return std::unexpected(Error(ErrorType::TypeError,
return std::unexpected(Error(
ErrorType::TypeError,
"cannot assign '" + r.toString() + "' to '" + l.toString() + "'",
"",
in->location));
@@ -436,7 +498,7 @@ namespace Fig
if (l.is(TypeTag::Any) || r.is(TypeTag::Any))
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any);
// 🔥 算术操作强检查
// 算术操作强检查
if (in->op == BinaryOperator::Add && l.is(TypeTag::String) && r.is(TypeTag::String))
return expr->resolvedType = typeCtx.GetBasic(TypeTag::String);
if (l.is(TypeTag::Int) && r.is(TypeTag::Int))
@@ -467,25 +529,28 @@ namespace Fig
if (calleeType.is(TypeTag::Any))
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any);
// 🔥 终极函数签名校验
// 函数签名校验
if (!calleeType.is(TypeTag::Function))
return std::unexpected(
Error(ErrorType::TypeError, "callee is not a function", "", c->location));
auto *ft = static_cast<FuncType *>(calleeType.base);
if (ft->paramTypes.size() != argTypes.size())
{
return std::unexpected(Error(ErrorType::TypeError,
"expected " + std::to_string(ft->paramTypes.size()) + " arguments, got "
+ std::to_string(argTypes.size()),
"",
return std::unexpected(Error(
ErrorType::SyntaxError,
std::format(
"expected {} arguments, got {}", ft->paramTypes.size(), argTypes.size()),
"none",
c->location));
}
for (size_t i = 0; i < argTypes.size(); ++i)
{
if (!argTypes[i].isAssignableTo(ft->paramTypes[i]))
{
return std::unexpected(Error(ErrorType::TypeError,
return std::unexpected(Error(
ErrorType::TypeError,
"argument " + std::to_string(i + 1) + " expects '"
+ ft->paramTypes[i].toString() + "', got '" + argTypes[i].toString()
+ "'",
@@ -495,13 +560,86 @@ namespace Fig
}
return expr->resolvedType = ft->retType;
}
case AstType::LambdaExpr: {
auto l = static_cast<LambdaExpr *>(expr);
Type returnType = typeCtx.GetBasic(TypeTag::Any);
if (l->returnType)
{
auto tres = resolveTypeExpr(l->returnType);
if (!tres)
{
return tres;
}
returnType = *tres;
}
FnDefStmt *f = arena.Allocate<FnDefStmt>(
false, "LambdaFn", l->params, l->returnType, nullptr, l->location);
FnStateGuard fnGuard(state.currentFn, f);
ScopeGuard scopeGuard(env, true);
DynArray<Type> paramTypes;
for (auto *p : l->params)
{
auto pres = resolveTypeExpr(p->typeSpecifier);
if (!pres)
{
return pres;
}
p->resolvedType = *pres;
paramTypes.push_back(*pres);
env.current->locals[p->name] = arena.Allocate<Symbol>(
p->name,
p->resolvedType,
SymbolLocation::Local,
env.current->nextLocalId++,
false);
}
if (l->isExprBody)
{
Expr *expr = static_cast<Expr *>(l->body);
if (auto r = analyzeExpr(expr); !r)
{
return r;
}
if (!expr->resolvedType.isAssignableTo(state.currentFn->resolvedReturnType))
{
return std::unexpected(Error(
ErrorType::TypeError,
"cannot return '" + state.currentFn->resolvedReturnType.toString()
+ "' from lambda function expecting '"
+ state.currentFn->resolvedReturnType.toString() + "'",
"",
expr->location));
}
}
else
{
Stmt *stmt = static_cast<Stmt *>(l->body);
if (auto r = analyzeStmt(stmt); !r)
{
return std::unexpected(r.error());
}
}
return l->resolvedType = typeCtx.CreateFuncType(paramTypes, returnType);
}
default: break;
}
return expr->resolvedType = typeCtx.GetBasic(TypeTag::Any);
}
Result<Symbol *, Error> Analyzer::resolveSymbolInternal(
const String &name, const SourceLocation &loc, Scope *s)
Result<Symbol *, Error>
Analyzer::resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope *s)
{
Scope *curr = s;
while (curr)
@@ -540,7 +678,7 @@ namespace Fig
return idx;
}
Result<Type, Error> Analyzer::resolveTypeExpr(TypeExpr *texpr)
Result<Type, Error> Analyzer::resolveTypeExpr(Expr *texpr)
{
if (!texpr)
return typeCtx.GetBasic(TypeTag::Any);
@@ -569,13 +707,46 @@ namespace Fig
return std::unexpected(
Error(ErrorType::UseUndeclaredIdentifier, "unknown type", "", texpr->location));
}
if (texpr->type == AstType::NullableTypeExpr)
else if (texpr->type == AstType::NullableTypeExpr)
{
auto res = resolveTypeExpr(static_cast<NullableTypeExpr *>(texpr)->inner);
if (res)
res->isNullable = true;
if (!res)
{
return res;
}
res->isNullable = true;
return res;
}
else if (texpr->type == AstType::FnTypeExpr)
{
auto f = static_cast<FnTypeExpr *>(texpr);
DynArray<Type> paraTypes;
Type returnType = typeCtx.GetBasic(TypeTag::Any);
for (auto &pt : f->paraTypes)
{
auto result = resolveTypeExpr(pt);
if (!result)
{
return result;
}
paraTypes.push_back(*result);
}
if (f->returnType)
{
auto result = resolveTypeExpr(f->returnType);
if (!result)
{
return result;
}
returnType = *result;
}
return typeCtx.CreateFuncType(paraTypes, returnType);
}
return typeCtx.GetBasic(TypeTag::Any);
}
} // namespace Fig

View File

@@ -32,7 +32,7 @@ namespace Fig
// 核心递归查找:解决跨越函数边界的捕获问题
Result<Symbol*, Error> resolveSymbolInternal(const String &name, const SourceLocation &loc, Scope* startScope);
Result<Type, Error> resolveTypeExpr(TypeExpr *texpr);
Result<Type, Error> resolveTypeExpr(Expr *texpr);
Result<void, Error> pass1(Program *prog);
Result<void, Error> resolveTypes(Program *prog);
Result<void, Error> checkBodies(Program *prog);

View File

@@ -20,7 +20,12 @@ void runTest(const std::string &path)
}
Lexer lexer(source, String(path));
Parser parser(lexer, srcManager, String(path));
Diagnostics diagnostics;
Parser parser(lexer, srcManager, String(path), diagnostics);
diagnostics.EmitAll(srcManager);
auto pRes = parser.Parse();
if (!pRes)

View File

@@ -39,10 +39,15 @@ namespace Fig
bool Type::isAssignableTo(const Type &target) const
{
if (target.is(TypeTag::Any) || this->is(TypeTag::Any))
return true; // Any 逃逸通道
{
return true; // Any 逃逸
}
if (this->is(TypeTag::Null) && target.isNullable)
{
return true; // Null 安全赋值
return this->base == target.base && (!this->isNullable || target.isNullable); // 严格匹配
}
return *this->base == *target.base && (!this->isNullable || target.isNullable);
}
TypeContext::TypeContext()
@@ -65,7 +70,9 @@ namespace Fig
TypeContext::~TypeContext()
{
for (auto t : allTypes)
{
delete t;
}
}
Type TypeContext::GetBasic(TypeTag tag, bool nullable)

View File

@@ -51,6 +51,11 @@ namespace Fig
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
@@ -62,6 +67,11 @@ namespace Fig
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
@@ -76,7 +86,7 @@ namespace Fig
};
DynArray<Field> fields;
HashMap<String, size_t> fieldMap;
HashMap<String, class FnDefStmt *> methods;
HashMap<String, struct FnDefStmt *> methods;
StructType(String n) : BaseType(TypeTag::Struct, std::move(n)) {}
void AddField(String name, Type type, bool isPublic)
@@ -85,6 +95,11 @@ namespace Fig
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
@@ -98,6 +113,11 @@ namespace Fig
};
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

View File

@@ -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},

View 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 &registerOption(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
View 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
View 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
View 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();
};

View File

@@ -150,7 +150,8 @@ namespace Fig
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
(currentFrame - 1)->proto->name); // pushFrame失败了但currentFrame仍然移动所以
// (currentFrame - 1)是 lastFrame
std::exit(static_cast<int>(MAX_RECURSION_DEPTH));
}
@@ -191,6 +192,41 @@ namespace Fig
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();
}
@@ -198,8 +234,9 @@ namespace Fig
std::uint8_t a = decodeA(inst);
Value retVal = currentFrame->registerBase[a];
// 此时 registerBase[0] 指向的是 Caller 的 baseReg 槽位
closeUpvalues(currentFrame->registerBase);
// 此时 registerBase[0] 指向的是 Caller 的 baseReg 槽位
currentFrame->registerBase[0] = retVal;
popFrame();
@@ -207,6 +244,67 @@ namespace Fig
}
do_LoadFn: {
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 = &currentFrame->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();
}
@@ -315,12 +413,17 @@ namespace Fig
}
do_GetUpval: {
assert(false && "VM: GetUpval requires FunctionObject (Closure) implementation");
std::uint8_t a = decodeA(inst);
std::uint8_t b = decodeB(inst);
currentFrame->registerBase[a] = *(currentFrame->closure->upvalues[b]->location);
DISPATCH();
}
do_SetUpval: {
assert(false && "VM: SetUpval requires FunctionObject (Closure) implementation");
std::uint8_t a = decodeA(inst);
std::uint8_t b = decodeB(inst);
*(currentFrame->closure->upvalues[b]->location) = currentFrame->registerBase[a]; // copy
DISPATCH();
}

View File

@@ -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,9 +19,10 @@ namespace Fig
{
struct CallFrame
{
Proto *proto; // 当前执行的原型
Instruction *ip; // 当前指令指针
Value *registerBase; // 寄存器起点
FunctionObject *closure; // 动态闭包Context (FastCall时 null)
Proto *proto; // 当前执行的原型
Instruction *ip; // 当前指令指针
Value *registerBase; // 寄存器起点
inline Value getConstant(std::uint16_t idx)
{
@@ -28,6 +30,14 @@ namespace Fig
}
};
enum class GCPhase : std::uint8_t
{
Idle,
MarkRoots,
Marking,
Sweeping
};
class VM
{
private:
@@ -45,6 +55,251 @@ namespace Fig
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()
{
@@ -66,7 +321,7 @@ namespace Fig
private:
[[nodiscard]]
inline Instruction *pushFrame(Proto *proto, Value *base)
inline Instruction *pushFrame(Proto *proto, Value *base) // fastcall
{
if (++currentFrame >= frameLimit) [[unlikely]] // 达到最大递归层数
{
@@ -74,8 +329,22 @@ namespace Fig
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;
}
*currentFrame = CallFrame{proto, proto->code.data(), base};
[[nodiscard]]
inline Instruction *pushFrame(FunctionObject *closure, Value *base) // 普通调用
{
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{closure, closure->proto, closure->proto->code.data(), base};
return currentFrame->ip;
}
@@ -113,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());
}
}
}

View File

@@ -1,86 +1,101 @@
#include <Bytecode/Disassembler.hpp>
#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 <chrono>
#include <iostream>
#include <print>
#include <Utils/ArgParser/ArgParser.hpp>
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 << "CRITICAL: Could not read source file: " << filePath << "\n";
err << res.error().Format() << '\n';
return 1;
}
Lexer lexer(manager.GetSource(), fileName);
Parser parser(lexer, manager, fileName);
auto &args = *res;
auto pRes = parser.Parse();
if (!pRes)
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(pRes.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 = *pRes;
Analyzer analyzer(manager);
auto aRes = analyzer.Analyze(program);
if (!aRes)
else if (posSize == 0)
{
ReportError(aRes.error(), manager);
return 1;
}
std::cout << "Analyzer: Program OK\n";
Diagnostics diag;
Compiler compiler(manager, diag);
auto cRes = compiler.Compile(program);
if (!cRes)
{
ReportError(cRes.error(), manager);
err << "Error: No input source file\n";
return 1;
}
CompiledModule *compiledModule = *cRes;
Entry::Config config{.mode = Entry::Config::Normal, .dump = dump, .pregs = pregs, .time = time};
Disassembler::DisassembleModule(compiledModule);
const String &path = positionals.front();
Entry::RunFromPath(path, config);
VM vm;
using Clock = std::chrono::high_resolution_clock;
Clock clock;
std::cout << "\n--- VM Execution Start ---\n";
auto start = clock.now();
auto result_ = vm.Execute(compiledModule);
auto end = clock.now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
if (!result_)
{
ReportError(result_.error(), manager);
return 1;
}
std::cout << "Result: " << (*result_).ToString() << "\n";
std::cout << "Execution Cost: " << duration.count() << "ms\n";
vm.PrintRegisters();
return 0;
}
}

View 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;
};

View File

@@ -15,6 +15,10 @@ elseif is_plat("windows") then
add_ldflags("-Wl,--stack,268435456")
end
if is_mode("release") then
set_optimize("fastest")
end
set_languages("c++23")
add_includedirs("src")
@@ -94,7 +98,7 @@ target("LSP")
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")
@@ -112,4 +116,30 @@ target("Fig")
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")
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/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")