修复EOF飘逸(去除末尾\n)以及其他修复...
This commit is contained in:
@@ -2,13 +2,16 @@
|
||||
@file src/Ast/Ast.hpp
|
||||
@brief Ast总链接
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-14
|
||||
@date 2026-02-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Expr/CallExpr.hpp>
|
||||
#include <Ast/Expr/IdentiExpr.hpp>
|
||||
#include <Ast/Expr/IndexExpr.hpp>
|
||||
#include <Ast/Expr/InfixExpr.hpp>
|
||||
#include <Ast/Expr/LiteralExpr.hpp>
|
||||
#include <Ast/Expr/PrefixExpr.hpp>
|
||||
|
||||
#include <Ast/Stmt/VarDecl.hpp>
|
||||
@@ -19,10 +19,17 @@ namespace Fig
|
||||
Expr, // 表达式
|
||||
Stmt, // 语句
|
||||
|
||||
/* Expressions */
|
||||
IdentiExpr, // 标识符表达式
|
||||
LiteralExpr, // 字面量表达式
|
||||
PrefixExpr, // 一元 前缀表达式
|
||||
InfixExpr, // 二元 中缀表达式
|
||||
|
||||
IndexExpr, // 后缀表达式,索引
|
||||
CallExpr, // 后缀表达式,函数调用
|
||||
|
||||
/* Statements */
|
||||
VarDecl,
|
||||
};
|
||||
struct AstNode
|
||||
{
|
||||
@@ -42,10 +49,28 @@ namespace Fig
|
||||
|
||||
struct Stmt : public AstNode
|
||||
{
|
||||
bool isPublic;
|
||||
Stmt()
|
||||
{
|
||||
type = AstType::Stmt;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Fig
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct std::formatter<Fig::AstNode *, char>
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context &ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Fig::AstNode *_node, FormatContext &ctx) const
|
||||
{
|
||||
return std::format_to(ctx.out(), "{}", _node->toString().toStdString());
|
||||
}
|
||||
};
|
||||
};
|
||||
60
src/Ast/Expr/CallExpr.hpp
Normal file
60
src/Ast/Expr/CallExpr.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/CallExpr.hpp
|
||||
@brief CallExpr等定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct FnCallArgs
|
||||
{
|
||||
DynArray<Expr *> args;
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return args.size();
|
||||
}
|
||||
|
||||
String toString() const
|
||||
{
|
||||
String str = "(";
|
||||
for (const Expr *expr : args)
|
||||
{
|
||||
if (expr != args.front())
|
||||
{
|
||||
str += ", ";
|
||||
}
|
||||
str += expr->toString();
|
||||
}
|
||||
str += ")";
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
struct CallExpr final : public Expr
|
||||
{
|
||||
Expr *callee;
|
||||
FnCallArgs args;
|
||||
|
||||
CallExpr()
|
||||
{
|
||||
type = AstType::CallExpr;
|
||||
}
|
||||
|
||||
CallExpr(Expr *_callee, FnCallArgs _args) : callee(_callee), args(std::move(_args))
|
||||
{
|
||||
type = AstType::CallExpr;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<CallExpr: '{}{}'>", callee->toString(), args.toString());
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
34
src/Ast/Expr/IndexExpr.hpp
Normal file
34
src/Ast/Expr/IndexExpr.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/IndexExpr.hpp
|
||||
@brief IndexExpr定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct IndexExpr final : public Expr
|
||||
{
|
||||
Expr *base;
|
||||
Expr *index;
|
||||
|
||||
IndexExpr()
|
||||
{
|
||||
type = AstType::IndexExpr;
|
||||
}
|
||||
|
||||
IndexExpr(Expr *_base, Expr *_index) : base(_base), index(_index)
|
||||
{
|
||||
location = base->location;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<IndexExpr: '{}[{}]'>", base->toString(), index->toString());
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
42
src/Ast/Stmt/VarDecl.hpp
Normal file
42
src/Ast/Stmt/VarDecl.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/VarDecl.hpp
|
||||
@brief VarDecl定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct VarDecl final : public Stmt
|
||||
{
|
||||
String name;
|
||||
Expr *typeSpecifier;
|
||||
Expr *initExpr;
|
||||
|
||||
VarDecl()
|
||||
{
|
||||
type = AstType::VarDecl;
|
||||
}
|
||||
|
||||
VarDecl(String _name, Expr *_typeSpecifier, Expr *_initExpr, SourceLocation _location) :
|
||||
name(std::move(_name)),
|
||||
typeSpecifier(_typeSpecifier),
|
||||
initExpr(_initExpr) // location 指向关键字 var/const位置
|
||||
{
|
||||
type = AstType::VarDecl;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
const String &typeSpecifierString = (typeSpecifier ? typeSpecifier->toString() : "Any");
|
||||
const String &initExprString = (initExpr ? initExpr->toString() : "(disprovided)");
|
||||
return std::format("<VarDecl {}: {} = {}>", name, typeSpecifierString, initExprString);
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
29
src/Bytecode/Bytecode.hpp
Normal file
29
src/Bytecode/Bytecode.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
@file src/Bytecode/Bytecode.hpp
|
||||
@brief 字节码Bytecode定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
using OpCodeType = uint8_t;
|
||||
|
||||
enum class OpCode : OpCodeType
|
||||
{
|
||||
LoadConst, // dst, const id
|
||||
LoadLocal, // dst, slot id
|
||||
StoreLocal, // slot, src(reg)
|
||||
|
||||
LoadLocalRef, // dst, slot
|
||||
LoadRef, // dst, refReg
|
||||
StoreRef, // refReg, srcReg
|
||||
|
||||
Add, // dst, a, b
|
||||
Move, // dst, src
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -8,6 +8,10 @@
|
||||
#include <Core/CoreIO.hpp>
|
||||
#include <Core/CoreInfos.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Fig::CoreIO
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__) || defined (__linux__) || defined (__unix__)
|
||||
@@ -34,4 +38,10 @@ namespace Fig::CoreIO
|
||||
#else
|
||||
// link
|
||||
#endif
|
||||
|
||||
void InitConsoleIO()
|
||||
{
|
||||
SetConsoleCP(CP_UTF8);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
}
|
||||
};
|
||||
@@ -15,4 +15,6 @@ namespace Fig::CoreIO
|
||||
std::ostream &GetStdErr();
|
||||
std::ostream &GetStdLog();
|
||||
std::istream &GetStdCin();
|
||||
};
|
||||
|
||||
void InitConsoleIO();
|
||||
}; // namespace Fig::CoreIO
|
||||
@@ -373,26 +373,6 @@ namespace Fig::Deps
|
||||
init(s.data(), s.size());
|
||||
}
|
||||
|
||||
static String fromPureAscii(const char *str)
|
||||
{
|
||||
String string;
|
||||
string._length = std::strlen(str);
|
||||
if (string._length <= SSO_SIZE)
|
||||
{
|
||||
memcpy(string.sso, str, string._length);
|
||||
}
|
||||
else
|
||||
{
|
||||
string.ascii.reserve(string._length);
|
||||
for (size_t i = 0; i < string._length; ++i)
|
||||
{
|
||||
string.ascii.push_back(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
String &operator=(const String &other)
|
||||
{
|
||||
if (this != &other)
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace Fig
|
||||
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';
|
||||
}
|
||||
|
||||
void ReportError(const Error &error, const SourceManager &srcManager)
|
||||
|
||||
@@ -152,18 +152,21 @@ namespace Fig
|
||||
|
||||
SourcePosition startPos = rd.currentPosition();
|
||||
|
||||
|
||||
Token tok(rd.currentIndex(), 1, TokenType::LiteralString); // "
|
||||
rd.next(); // skip " / '
|
||||
Token tok(rd.currentIndex(), 0, TokenType::LiteralString);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (state == State::ScanStringDQ && rd.current() == U'"')
|
||||
{
|
||||
tok.length ++;
|
||||
rd.next(); // skip '"'
|
||||
break;
|
||||
}
|
||||
else if (state == State::ScanStringSQ && rd.current() == U'\'')
|
||||
{
|
||||
tok.length ++;
|
||||
rd.next(); // skip `'`
|
||||
break;
|
||||
}
|
||||
@@ -230,7 +233,7 @@ namespace Fig
|
||||
while (!rd.isAtEnd())
|
||||
{
|
||||
char32_t current = rd.current();
|
||||
if (current == EOF || !CharUtils::isAsciiSpace(current)) // 检查 EOF
|
||||
if (!CharUtils::isAsciiSpace(current)) // 检查 EOF
|
||||
break;
|
||||
rd.next();
|
||||
}
|
||||
@@ -240,11 +243,7 @@ namespace Fig
|
||||
{
|
||||
if (rd.isAtEnd())
|
||||
{
|
||||
return Token(rd.currentIndex(), 0, TokenType::EndOfFile);
|
||||
}
|
||||
if (rd.current() == U'\0')
|
||||
{
|
||||
return Token(rd.currentIndex(), 1, TokenType::EndOfFile);
|
||||
return Token(rd.getEOFIndex(), 1, TokenType::EndOfFile);
|
||||
}
|
||||
if (rd.current() == U'/' && rd.peekIf() == U'/')
|
||||
{
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Token/Token.hpp>
|
||||
#include <Core/SourceLocations.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
#include <Token/Token.hpp>
|
||||
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -35,7 +36,10 @@ namespace Fig
|
||||
pos.line = pos.column = 1;
|
||||
}
|
||||
|
||||
SourcePosition ¤tPosition() { return pos; }
|
||||
SourcePosition ¤tPosition()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
inline char32_t current() const
|
||||
{
|
||||
@@ -52,7 +56,10 @@ namespace Fig
|
||||
return source[index];
|
||||
}
|
||||
|
||||
inline bool hasNext() const { return index < source.length() - 1; }
|
||||
inline bool hasNext() const
|
||||
{
|
||||
return index < source.length() - 1;
|
||||
}
|
||||
|
||||
inline char32_t peek() const
|
||||
{
|
||||
@@ -62,7 +69,10 @@ namespace Fig
|
||||
|
||||
inline char32_t peekIf() const
|
||||
{
|
||||
if ((index + 1) < source.length()) { return source[index + 1]; }
|
||||
if ((index + 1) < source.length())
|
||||
{
|
||||
return source[index + 1];
|
||||
}
|
||||
return 0xFFFD;
|
||||
}
|
||||
|
||||
@@ -92,12 +102,26 @@ namespace Fig
|
||||
|
||||
inline void skip(size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i) { next(); }
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t currentIndex() const { return index; }
|
||||
inline size_t currentIndex() const
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
inline bool isAtEnd() const { return index >= source.length(); }
|
||||
inline bool isAtEnd() const
|
||||
{
|
||||
return index >= source.length();
|
||||
}
|
||||
|
||||
inline size_t getEOFIndex() const
|
||||
{
|
||||
return source.length();
|
||||
}
|
||||
};
|
||||
|
||||
class Lexer
|
||||
|
||||
@@ -35,7 +35,7 @@ int main()
|
||||
const auto &type = magic_enum::enum_name(token.type);
|
||||
if (token.type == TokenType::EndOfFile)
|
||||
{
|
||||
std::cout << "EOF: " << type << '\n';
|
||||
std::cout << "EOF: " << type << " at " << token.index << '\n';
|
||||
break;
|
||||
}
|
||||
std::cout << lexeme << " --> " << type << '\n';
|
||||
|
||||
@@ -61,16 +61,95 @@ namespace Fig
|
||||
return node;
|
||||
}
|
||||
|
||||
std::unordered_set<TokenType> Parser::getTerminators() // 返回当前state的终止条件(终止符)
|
||||
Result<IndexExpr *, Error> Parser::parseIndexExpr(Expr *base) // 由 parseExpression调用, 当前token为 `[`
|
||||
{
|
||||
using enum State;
|
||||
state = State::ParsingIndexExpr;
|
||||
const Token &lbracket_token = consumeToken(); // consume `[`
|
||||
const auto &index_result = parseExpression();
|
||||
|
||||
static const std::unordered_set<TokenType> baseTerminators = {TokenType::EndOfFile, TokenType::Semicolon};
|
||||
|
||||
switch (state)
|
||||
if (!index_result)
|
||||
{
|
||||
default: return baseTerminators;
|
||||
return std::unexpected(index_result.error());
|
||||
}
|
||||
|
||||
if (currentToken().type != TokenType::RightBracket) // `]`
|
||||
{
|
||||
return std::unexpected(
|
||||
Error(ErrorType::SyntaxError, "unclosed brackets", "insert `]`", makeSourcelocation(lbracket_token)));
|
||||
}
|
||||
consumeToken(); // consume `]`
|
||||
|
||||
IndexExpr *indexExpr = new IndexExpr(base, *index_result);
|
||||
return indexExpr;
|
||||
}
|
||||
|
||||
Result<CallExpr *, Error> Parser::parseCallExpr(Expr *callee) // 由 parseExpression调用, 当前token为 `(`
|
||||
{
|
||||
state = State::ParsingCallExpr;
|
||||
const Token &lparen_token = consumeToken(); // consume `(`
|
||||
|
||||
FnCallArgs callArgs;
|
||||
|
||||
// 空参数列表
|
||||
if (currentToken().type == TokenType::RightParen)
|
||||
{
|
||||
consumeToken(); // consume `)`
|
||||
return new CallExpr(callee, callArgs);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (currentToken().type == TokenType::EndOfFile)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
"fn call has unclosed parenthese",
|
||||
"insert `)`",
|
||||
makeSourcelocation(lparen_token)));
|
||||
}
|
||||
|
||||
const auto &arg_result = parseExpression();
|
||||
if (!arg_result)
|
||||
return std::unexpected(arg_result.error());
|
||||
|
||||
callArgs.args.push_back(*arg_result);
|
||||
|
||||
if (currentToken().type == TokenType::RightParen)
|
||||
{
|
||||
consumeToken(); // consume `)`
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentToken().type != TokenType::Comma)
|
||||
{
|
||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
||||
"expected `,` or `)` in argument list",
|
||||
"insert `,`",
|
||||
makeSourcelocation(currentToken())));
|
||||
}
|
||||
|
||||
consumeToken(); // consume `,`
|
||||
}
|
||||
|
||||
return new CallExpr(callee, callArgs);
|
||||
}
|
||||
|
||||
std::unordered_set<TokenType> Parser::getTerminators()
|
||||
{
|
||||
/*
|
||||
|
||||
Syntax terminators:
|
||||
; ) ] } , EOF
|
||||
|
||||
*/
|
||||
static const std::unordered_set<TokenType> baseTerminators = {TokenType::Semicolon,
|
||||
TokenType::RightParen,
|
||||
TokenType::RightBracket,
|
||||
TokenType::RightBrace,
|
||||
TokenType::Comma,
|
||||
TokenType::EndOfFile
|
||||
|
||||
};
|
||||
return baseTerminators;
|
||||
}
|
||||
bool Parser::shouldTerminate()
|
||||
{
|
||||
@@ -111,6 +190,22 @@ namespace Fig
|
||||
}
|
||||
lhs = *lhs_result;
|
||||
}
|
||||
else if (token.type == TokenType::LeftParen)
|
||||
{
|
||||
const Token &lparen_token = consumeToken(); // consume `(`
|
||||
const auto &expr_result = parseExpression(0);
|
||||
if (!expr_result)
|
||||
{
|
||||
return expr_result;
|
||||
}
|
||||
const Token &rparen_token = consumeToken(); // consume `)`
|
||||
if (rparen_token.type != TokenType::RightParen)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError, "unclosed parenthese", "insert `)`", makeSourcelocation(lparen_token)));
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
|
||||
if (!lhs)
|
||||
{
|
||||
@@ -122,12 +217,12 @@ namespace Fig
|
||||
|
||||
while (true)
|
||||
{
|
||||
token = currentToken();
|
||||
if (shouldTerminate())
|
||||
{
|
||||
return lhs;
|
||||
break;
|
||||
}
|
||||
|
||||
token = currentToken();
|
||||
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
|
||||
{
|
||||
BinaryOperator op = TokenToBinaryOp(token);
|
||||
@@ -148,10 +243,30 @@ namespace Fig
|
||||
}
|
||||
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
|
||||
// 而不用走正常路径
|
||||
else if (0) {}
|
||||
else if (token.type == TokenType::LeftBracket) // `[`
|
||||
{
|
||||
const auto &expr_result = parseIndexExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
else if (token.type == TokenType::LeftParen) // `(`
|
||||
{
|
||||
const auto &expr_result = parseCallExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return lhs;
|
||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||
"expression unexpectedly ended",
|
||||
"insert expressions",
|
||||
makeSourcelocation(token)));
|
||||
}
|
||||
}
|
||||
return lhs;
|
||||
|
||||
@@ -121,6 +121,10 @@ namespace Fig
|
||||
|
||||
ParsingInfixExpr,
|
||||
ParsingPrefixExpr,
|
||||
|
||||
ParsingIndexExpr,
|
||||
ParsingCallExpr,
|
||||
|
||||
} state;
|
||||
|
||||
Parser(Lexer &_lexer, SourceManager &_srcManager, String _fileName) :
|
||||
@@ -148,15 +152,19 @@ namespace Fig
|
||||
Result<InfixExpr *, Error> parseInfixExpr(Expr *); // 由 parseExpression递归调用, 当前token为op
|
||||
Result<PrefixExpr *, Error> parsePrefixExpr(); // 由 parseExpression递归调用, 当前token为op
|
||||
|
||||
std::unordered_set<TokenType> getTerminators(); // 返回当前state的终止条件(终止符)
|
||||
bool shouldTerminate(); // 通过state判断该不该终止表达式解析
|
||||
Result<IndexExpr *, Error> parseIndexExpr(Expr *); // 由 parseExpression调用, 当前token为 `[`
|
||||
Result<CallExpr *, Error> parseCallExpr(Expr *); // 由 parseExpression调用, 当前token为 `(`
|
||||
|
||||
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||
std::unordered_set<TokenType> getTerminators(); // 返回固定的终止符
|
||||
bool shouldTerminate(); // 判断是否终结
|
||||
|
||||
// Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||
|
||||
/* Statements */
|
||||
|
||||
public:
|
||||
|
||||
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||
DynArray<AstNode *> parseAll();
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -18,12 +18,12 @@ int main()
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
Parser parser(lexer, srcManager, fileName);
|
||||
// const auto &result = parser.parseExpression();
|
||||
// if (!result)
|
||||
// {
|
||||
// ReportError(result.error(), srcManager);
|
||||
// return 1;
|
||||
// }
|
||||
// Expr *expr = *result;
|
||||
// std::cout << expr->toString() << '\n';
|
||||
const auto &result = parser.parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
return 1;
|
||||
}
|
||||
Expr *expr = *result;
|
||||
std::cout << expr->toString() << '\n';
|
||||
}
|
||||
@@ -60,6 +60,9 @@ namespace Fig
|
||||
source += line + '\n';
|
||||
lines.push_back(String(line));
|
||||
}
|
||||
if (!source.empty() && source.back() == U'\n')
|
||||
source.pop_back();
|
||||
|
||||
if (lines.empty())
|
||||
{
|
||||
lines.push_back(String()); // 填充一个空的
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
int main()
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user