Compare commits

...

6 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
47 changed files with 2661 additions and 384 deletions

View File

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

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

@@ -1,5 +1,7 @@
# Fig
## tmd赶工代码质量太差了暑假我要重构
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">

View File

@@ -1,5 +1,7 @@
# Fig
## tmd赶工代码质量太差了暑假我要重构
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">

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

@@ -12,7 +12,7 @@ namespace Fig
{
struct Param : public AstNode {
String name;
TypeExpr *typeSpecifier;
Expr *typeSpecifier;
Expr *defaultValue;
Type resolvedType;
Param() { type = AstType::AstNode; }
@@ -20,7 +20,7 @@ namespace Fig
};
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; }
@@ -29,7 +29,7 @@ namespace Fig
struct FnDefStmt final : public Stmt {
String name;
DynArray<Param *> params;
TypeExpr *returnTypeSpecifier;
Expr *returnTypeSpecifier;
BlockStmt *body;
Type resolvedReturnType;
Symbol *resolvedSymbol = nullptr; // 连接物理符号
@@ -38,7 +38,7 @@ namespace Fig
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
{
String name;
TypeExpr *type;
bool isPublic;
bool typeInfer;
String name;
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

@@ -75,6 +75,7 @@ namespace Fig
stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
}
}
stream << "--- End Disassembly ---\n";
}
Disassembler::Format Disassembler::GetFormat(OpCode op)

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

@@ -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';
if (isFloat)
{
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));
return Value::FromDouble(dVal);
}
else
{
// 检查16进制/2进制前缀
bool isHexOrBin = false;
int base = 10;
const char *start = buffer;
if (j > 2 && buffer[0] == '0')
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(start, buffer + j, dVal, fmt);
if (ec != std::errc() || ptr != buffer + j)
return std::unexpected(
Error(ErrorType::SyntaxError, "invalid float literal", "", loc));
return Value::FromDouble(dVal);
}
else if (isHexOrBin)
{
// 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,7 +137,9 @@ 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))), &l->location);
emit(
Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(addConstant(*vRes))),
&l->location);
}
else if (tok.type == TokenType::LiteralString)
{
@@ -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)), &i->location);
emit(
Op::iABx(OpCode::Mov, target, static_cast<uint16_t>(sym->index)),
&i->location);
}
return target;
}
@@ -137,7 +184,9 @@ 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), &i->location);
emit(
Op::iABC(OpCode::GetUpval, r, static_cast<uint8_t>(sym->index), 0),
&i->location);
}
else if (sym->location == SymbolLocation::Global)
{
@@ -176,7 +225,9 @@ namespace Fig
{
isGlobalFastCall = true;
int protoIdx = id->resolvedSymbol->index;
emit(Op::iABC(OpCode::FastCall,
emit(
Op::iABC(
OpCode::FastCall,
static_cast<uint8_t>(protoIdx),
baseReg,
static_cast<uint8_t>(c->args.args.size())),
@@ -193,14 +244,16 @@ namespace Fig
return std::unexpected(r_fn.error());
// 使用动态 Call 指令RA 是指向堆闭包的寄存器
emit(Op::iABC(OpCode::Call,
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实现零开销回写
@@ -240,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), &lid->location);
emit(
Op::iABx(OpCode::Mov, static_cast<Register>(sym->index), *r_val),
&lid->location);
}
else if (sym->location == SymbolLocation::Upvalue)
{
emit(Op::iABC(
OpCode::SetUpval, *r_val, static_cast<Register>(sym->index), 0), &lid->location);
emit(
Op::iABC(
OpCode::SetUpval, *r_val, static_cast<Register>(sym->index), 0),
&lid->location);
}
else
{
emit(Op::iABx(OpCode::SetGlobal,
emit(
Op::iABx(
OpCode::SetGlobal,
*r_val,
static_cast<uint16_t>(getGlobalID(lid->name))), &lid->location);
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)
@@ -271,30 +331,62 @@ 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));
}
}
// 释放左右操作数产生的临时寄存器
current->freereg = mark;
@@ -318,6 +410,65 @@ namespace Fig
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>
@@ -35,7 +35,7 @@ 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());
@@ -74,7 +74,7 @@ namespace Fig
Proto *newProto = new Proto();
newProto->name = f->name;
newProto->numParams = static_cast<uint8_t>(f->params.size());
newProto->maxRegisters = newProto->numParams; // 同步水位线
newProto->maxRegisters = newProto->numParams; // sync
f->protoIndex = static_cast<int>(module->protos.size());
module->protos.push_back(newProto);
@@ -141,7 +141,7 @@ 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());
@@ -204,7 +204,7 @@ 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());
@@ -228,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)
@@ -253,7 +253,7 @@ namespace Fig
}
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

@@ -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";
@@ -71,8 +77,8 @@ namespace Fig
{
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();
@@ -92,26 +98,34 @@ namespace Fig
uint8_t level = ErrorLevel(error.type);
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical"));
const char *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
const char *level_color =
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
err << "🔥 "
<< level_color
//<< '(' << level_name << ')'
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type)
<< TC::Reset << '\n';
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color
<< ErrorTypeToString(error.type) << TC::Reset << '\n';
const SourceLocation &location = error.location;
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.' << location.functionName
<< '\'' << " " << location.fileName << " (" << TC::DarkGray << location.sp.line << ":" << location.sp.column
<< TC::Cyan << ')' << TC::Reset << '\n';
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.'
<< location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray
<< location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset
<< '\n';
err << TC::DarkGray << "" << '\n' << "" << TC::Reset << '\n';
// 尝试打印上3行 下2行
int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2;
while (!srcManager.HasLine(line_end)) { --line_end; }
while (!srcManager.HasLine(line_start)) { ++line_start; }
while (!srcManager.HasLine(line_end))
{
--line_end;
}
while (!srcManager.HasLine(line_start))
{
++line_start;
}
const auto &getLineNumWidth = [](size_t l) {
unsigned int cnt = 0;
@@ -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

@@ -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,53 +434,113 @@ namespace Fig
break;
}
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
// is / as
if (token.type == TokenType::Is || token.type == TokenType::As)
{
BinaryOperator op = TokenToBinaryOp(token);
BindingPower lbp = GetBinaryOpLBp(op);
if (rbp >= lbp)
{
break;
}
consumeToken(); // consume `is` or `as`
auto typeRes = parseTypeExpr();
if (!typeRes)
{
return std::unexpected(typeRes.error());
}
lhs = arena.Allocate<InfixExpr>(lhs, op, *typeRes);
}
// binary
else if (IsTokenOp(token.type /* isBinary = true */))
{
BinaryOperator op = TokenToBinaryOp(token);
BindingPower lbp = GetBinaryOpLBp(op);
if (rbp >= lbp)
{
// 前操作数的右绑定力比当前操作数的左绑定力大
// lhs被吸走
break;
}
auto result = parseInfixExpr(lhs);
if (!result)
{
resetTermintors();
return result;
}
lhs = *result;
}
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
// 而不用走正常路径
else if (token.type == TokenType::LeftBracket) // `[`
// [index]
else if (token.type == TokenType::LeftBracket)
{
const auto &expr_result = parseIndexExpr(lhs);
if (!expr_result)
{
resetTermintors();
return expr_result;
}
lhs = *expr_result;
}
else if (token.type == TokenType::LeftParen) // `(`
// call
else if (token.type == TokenType::LeftParen)
{
const auto &expr_result = parseCallExpr(lhs);
if (!expr_result)
{
resetTermintors();
return expr_result;
}
lhs = *expr_result;
}
// .member
else if (token.type == TokenType::Dot)
{
consumeToken(); // consume `.`
if (!currentToken().isIdentifier())
{
return std::unexpected(
makeUnexpectTokenError("MemberExpr", "identifier after `.`", currentToken()));
}
const Token &nameToken = consumeToken();
const String &name =
srcManager.GetSub(nameToken.index, nameToken.length);
SourceLocation loc = makeSourceLocation(nameToken);
lhs = arena.Allocate<MemberExpr>(lhs, name, loc);
}
// x++ x--
else if (token.type == TokenType::DoublePlus || token.type == TokenType::DoubleMinus)
{
UnaryOperator op = TokenToUnaryOp(consumeToken());
lhs = arena.Allocate<PostfixExpr>(op, lhs);
}
// ?:
else if (token.type == TokenType::Question)
{
// ?: 最低优先
// 赋值 rbp = 101所以只有当 rbp < 100 时才可能进到三元
// 实际上三元是最低优先级的非赋值运算符,我们给一个很小的 lbp
constexpr BindingPower TERNARY_LBP = 150;
if (rbp >= TERNARY_LBP)
{
break;
}
consumeToken(); // consume `?`
auto thenRes = parseExpression(0); // 重置绑定力,右结合
if (!thenRes)
{
return std::unexpected(thenRes.error());
}
if (!match(TokenType::Colon))
{
return std::unexpected(
makeUnexpectTokenError("TernaryExpr", "`:` for else branch", currentToken()));
}
auto elseRes = parseExpression(TERNARY_LBP - 1); // 右结合
if (!elseRes)
{
return std::unexpected(elseRes.error());
}
lhs = arena.Allocate<TernaryExpr>(lhs, *thenRes, *elseRes, lhs->location);
}
else
{
return std::unexpected(Error(ErrorType::ExpectedExpression,
"expression unexpectedly ended",
"insert expressions",
makeSourceLocation(token)));
return lhs;
}
}
return lhs;

View File

@@ -15,6 +15,10 @@ namespace Fig
while (currentToken().type != TokenType::EndOfFile)
{
if (lexerError)
{
return std::unexpected(*lexerError);
}
auto result = parseStatement();
if (!result)
{

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

@@ -6,28 +6,38 @@ int main()
using namespace Fig;
String fileName = "test.fig";
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
String filePath =
"T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/" + fileName;
SourceManager srcManager(filePath);
String source = srcManager.Read();
if (!srcManager.read)
{
std::cerr << "Couldn't read file";
std::cerr << "Couldn't read file: " << filePath << '\n';
return 1;
}
Lexer lexer(source, fileName);
Parser parser(lexer, srcManager, fileName);
Diagnostics diagnostics;
Parser parser(lexer, srcManager, fileName, diagnostics);
auto result = parser.Parse();
if (!result)
{
ReportError(result.error(), srcManager);
return 1;
}
diagnostics.EmitAll(srcManager);
Program *program = *result;
for (Stmt *stmt : program->nodes)
std::cout << "Parsed " << program->nodes.size() << " statements\n";
for (size_t i = 0; i < program->nodes.size(); ++i)
{
std::cout << stmt->toString() << '\n';
std::cout << '[' << i << "] " << program->nodes[i]->toString() << '\n';
}
return 0;
}

File diff suppressed because it is too large Load Diff

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 (isEOF)
{
return std::unexpected(Error(
ErrorType::SyntaxError,
"unclosed `<` in type parameters",
"insert '>'",
makeSourceLocation(lab)));
}
if (match(TokenType::Greater)) // >
{
break;
}
if (!currentToken().isIdentifier())
{
return std::unexpected(
makeUnexpectTokenError("TypeParams", "tp name", currentToken()));
}
if (match(TokenType::Dot))
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()));
}
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("Type", "identifier", currentToken()));
{
return std::unexpected(
makeUnexpectTokenError("TypeExpr", "identifier after `.`", currentToken()));
}
else break;
const Token &tok = consumeToken();
const String &name = srcManager.GetSub(tok.index, tok.length);
SourceLocation loc = makeSourceLocation(tok);
base = arena.Allocate<MemberExpr>(base, name, loc);
}
DynArray<TypeExpr *> arguments;
if (match(TokenType::Less)) // `<`
// 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

View File

@@ -38,15 +38,18 @@ namespace Fig
void PrintInfo()
{
out << std::format("Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
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",
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()
@@ -146,17 +149,58 @@ namespace Fig
++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)
{
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());
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
@@ -209,7 +253,10 @@ namespace Fig
String source(buf);
Lexer lexer(buf, fileName);
Parser parser(lexer, manager, fileName);
Diagnostics diagnostics;
Parser parser(lexer, manager, fileName, diagnostics);
auto _program = parser.Parse();
if (!_program)
@@ -223,13 +270,15 @@ namespace Fig
Analyzer analyzer(manager);
auto result = analyzer.Analyze(program);
analyzer.GetDiagnostics().EmitAll(manager);
if (!result)
{
PrintError(result.error(), source);
continue;
}
Compiler compiler(manager, analyzer.GetDiagnostics());
Compiler compiler(manager, diagnostics);
auto compile_result = compiler.Compile(program);
if (!compile_result)
@@ -238,6 +287,8 @@ namespace Fig
continue;
}
diagnostics.EmitAll(manager);
CompiledModule *compiledModule = *compile_result;
auto exe_result = vm.Execute(compiledModule);

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()
+ "'",
"",
@@ -210,7 +219,7 @@ namespace Fig
case AstType::FnDefStmt: {
auto *f = static_cast<FnDefStmt *>(stmt);
// 3.10: 局部闭包延迟类型推导
// 局部闭包延迟类型推导
if (!f->resolvedSymbol) // 闭包?
{
@@ -243,7 +252,8 @@ namespace Fig
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++,
@@ -279,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));
@@ -330,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);
@@ -345,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() + "'",
"",
@@ -407,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));
@@ -415,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)
{
@@ -431,7 +452,9 @@ 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))
@@ -456,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));
@@ -511,19 +535,22 @@ namespace Fig
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()
+ "'",
@@ -533,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)
@@ -578,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);
@@ -607,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)
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,8 +70,10 @@ 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
@@ -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

@@ -5,26 +5,62 @@
@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 <Compiler/Compiler.hpp>
#include <VM/VM.hpp>
namespace Fig::Entry
{
void RunFromPath(const String &path)
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))
@@ -53,18 +89,28 @@ namespace Fig::Entry
const String &source = manager.GetSource();
Lexer lexer(source, fileName);
Parser parser(lexer, manager, 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)
{
@@ -72,10 +118,12 @@ namespace Fig::Entry
std::exit(1);
}
Diagnostics diagnostics;
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)
@@ -86,15 +134,66 @@ namespace Fig::Entry
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

View File

@@ -9,5 +9,18 @@
namespace Fig::Entry
{
void RunFromPath(const String &);
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

@@ -209,8 +209,9 @@ namespace Fig
"none",
*currentFrame->proto->locations[ipIdx]));
}
else
else [[likely]]
{
Object *obj = callee.AsObject();
if (!obj->isFunction())
{

View File

@@ -329,7 +329,7 @@ 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;
}
@@ -343,7 +343,7 @@ namespace Fig
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;
}

View File

@@ -19,9 +19,13 @@ int main(int argc, char **argv)
ArgParser::ArgumentParser argparser("Fig", "Fig Toolchain");
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)
@@ -32,9 +36,13 @@ int main(int argc, char **argv)
auto &args = *res;
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)
{
@@ -44,10 +52,12 @@ int main(int argc, char **argv)
if (showVersion)
{
out << std::format("Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
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",
out << std::format(
"Build time: {} [{} x{} on {}]\n",
Core::COMPILE_TIME,
Core::COMPILER,
Core::ARCH,
@@ -66,6 +76,11 @@ int main(int argc, char **argv)
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";
@@ -77,8 +92,10 @@ int main(int argc, char **argv)
return 1;
}
Entry::Config config{.mode = Entry::Config::Normal, .dump = dump, .pregs = pregs, .time = time};
const String &path = positionals.front();
Entry::RunFromPath(path);
Entry::RunFromPath(path, config);
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")