1 Commits

Author SHA1 Message Date
ca4ae143b4 v0.4.2-alpha
All checks were successful
Release Build / build-windows-x64 (push) Successful in 43s
Release Build / build-linux-x64 (push) Successful in 50s
[Fix][Impl] 为了消除类构造带来的语法歧义,同时保持实现简洁和括号省略的语法,自此版本,引入了 `new` 操作符
            造成歧义的原方法:
                if a == A{}
            条件是 a == A,还是 a == A{} ?

            因此,现在使用 new a_struct{}来构造类
[Opti] 相较于 Fig v0.4.1-alpha版本,release O3同编译条件下
       Fib普通递归法性能提升 ~50%
       具体方式:
            增加了小整数优化,-128~127的整数现在会直接从IntPool获取而不是新构造
            ...忘了
[Fix] 类构造 shorthand模式忘写了,现在补上了
[Feat][Impl] 类型声明现在接受一个表达式,原为Identifier。实现 var start: time.Time = time.now() 的效果
             这是符合语法和语言特性的支持,类型为一等公民。类似Python的 <class 'type'>

[Impl] 修改了部分错误输出的细节
2026-01-22 08:24:14 +08:00
27 changed files with 947 additions and 670 deletions

View File

@@ -11,3 +11,69 @@ invalid!
const Pi := 3.14; // recommended, auto detect type const Pi := 3.14; // recommended, auto detect type
// equal -> const Pi: Double = 3.14; // equal -> const Pi: Double = 3.14;
/*
In fig, we have 13 builtin-type
01 Any
02 Null
03 Int
04 String
05 Bool
06 Double
07 Function
08 StructType
09 StructInstance
10 List
11 Map
12 Module
13 InterfaceType
3, 4, 5, 6, 10, 11 are initable
value system:
object is immutable
(included basic types: Int, String...)
`variable` is a name, refers to an object
assignment is to bind name to value
Example: var a := 10;
[name] 'a' ---> variable slot (name, declared type, access modifier, [value) ---> ObjectPtr ---> raw Object class
bind bind (shared_ptr)
For example:
var a := 10;
var b := 10;
`a` and `b` reference to the same object in memory
a = 20;
now a refers to a new object (20, Int)
what about complex types?
they actually have same behaviors with basic types
var a := [1, 2, 3, 4];
var b := a;
> a
[1, 2, 3, 4]
> b
[1, 2, 3, 4]
set a[0] to 5
> a
[5, 2, 3, 4]
> b
[5, 2, 3, 4]
Why did such a result occur?
" `a` and `b` reference to the same object in memory "
If you wish to obtain a copy, use List {a} to deeply copy it
*/

View File

@@ -0,0 +1,65 @@
import std.io;
import std.time;
func benchmark(fn: Function, arg: Any) -> Null
{
io.println("Testing fn:", fn, "with arg:", arg);
const start := time.now();
const result := fn(arg);
const end := time.now();
const duration := new time.Time{
end.since(start)
};
io.println("=" * 50);
io.println("fn returns:", result, "\n");
io.println("Cost:", duration.toSeconds(), "s");
io.println(" ", duration.toMillis(), "ms");
}
var memo := {};
func fib_memo(x)
{
if memo.contains(x)
{
return memo.get(x);
}
if x <= 1
{
memo[x] = x;
return x;
}
var result := fib_memo(x - 1) + fib_memo(x - 2);
memo[x] = result;
return result;
}
func fib_iter(n)
{
var a := 0;
var b := 1;
for var i := 0; i < n; i = i + 1
{
var temp := a + b;
a = b;
b = temp;
}
return a;
}
func fib(x)
{
if x <= 1
{
return x;
}
return fib(x - 1) + fib(x - 2);
}
const n := 28;
benchmark(fib, n);
benchmark(fib_memo, n);
benchmark(fib_iter, n);

View File

@@ -2,7 +2,7 @@
"name": "fig-vscode", "name": "fig-vscode",
"displayName": "Fig Language", "displayName": "Fig Language",
"description": "VSCode extension for Fig language with syntax highlighting", "description": "VSCode extension for Fig language with syntax highlighting",
"version": "0.0.2", "version": "0.4.2",
"publisher": "PuqiAR", "publisher": "PuqiAR",
"engines": { "engines": {
"vscode": "^1.90.0" "vscode": "^1.90.0"

View File

@@ -57,7 +57,7 @@
"patterns": [ "patterns": [
{ {
"name": "keyword.control.fig", "name": "keyword.control.fig",
"match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|struct|interface|impl|public|return|break|continue|try|catch|throw)\\b" "match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|new|struct|interface|impl|public|return|break|continue|try|catch|throw)\\b"
}, },
{ "name": "constant.language.fig", "match": "\\b(true|false|null)\\b" } { "name": "constant.language.fig", "match": "\\b(true|false|null)\\b" }
] ]

View File

@@ -23,14 +23,14 @@ namespace Fig::Ast
FString name; FString name;
FunctionParameters paras; FunctionParameters paras;
bool isPublic; bool isPublic;
FString retType; Expression retType;
BlockStatement body; BlockStatement body;
FunctionDefSt() :
retType(ValueType::Null.name) FunctionDefSt()
{ {
type = AstType::FunctionDefSt; type = AstType::FunctionDefSt;
} }
FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, FString _retType, BlockStatement _body) FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, Expression _retType, BlockStatement _body)
{ {
type = AstType::FunctionDefSt; type = AstType::FunctionDefSt;

View File

@@ -25,7 +25,7 @@ namespace Fig::Ast
{ {
FString name; FString name;
FunctionParameters paras; FunctionParameters paras;
FString returnType; Expression returnType;
BlockStatement defaultBody = nullptr; // nullptr is non-default func BlockStatement defaultBody = nullptr; // nullptr is non-default func

View File

@@ -12,12 +12,12 @@ namespace Fig::Ast
{ {
AccessModifier am; AccessModifier am;
FString fieldName; FString fieldName;
FString tiName; Expression declaredType;
Expression defaultValueExpr; Expression defaultValueExpr;
StructDefField() {} StructDefField() {}
StructDefField(AccessModifier _am, FString _fieldName, FString _tiName, Expression _defaultValueExpr) : StructDefField(AccessModifier _am, FString _fieldName, Expression _declaredType, Expression _defaultValueExpr) :
am(std::move(_am)), fieldName(std::move(_fieldName)), tiName(std::move(_tiName)), defaultValueExpr(std::move(_defaultValueExpr)) am(std::move(_am)), fieldName(std::move(_fieldName)), declaredType(std::move(_declaredType)), defaultValueExpr(std::move(_defaultValueExpr))
{ {
} }
}; };

View File

@@ -11,22 +11,28 @@ namespace Fig::Ast
bool isPublic; bool isPublic;
bool isConst; bool isConst;
FString name; FString name;
FString typeName; // FString typeName;
Expression declaredType;
Expression expr; Expression expr;
VarDefAst() : bool followupType;
typeName(ValueType::Any.name)
VarDefAst()
{ {
type = AstType::VarDefSt; type = AstType::VarDefSt;
declaredType = nullptr;
expr = nullptr;
followupType = false;
} }
VarDefAst(bool _isPublic, bool _isConst, FString _name, FString _info, Expression _expr) : VarDefAst(bool _isPublic, bool _isConst, FString _name, Expression _declaredType, Expression _expr, bool _followupType)
typeName(std::move(_info))
{ {
type = AstType::VarDefSt; type = AstType::VarDefSt;
isPublic = _isPublic; isPublic = _isPublic;
isConst = _isConst; isConst = _isConst;
name = std::move(_name); name = std::move(_name);
declaredType = std::move(_declaredType);
expr = std::move(_expr); expr = std::move(_expr);
followupType = _followupType;
} }
}; };

View File

@@ -34,6 +34,7 @@ namespace Fig::Ast
ListExpr, // [1, "2", 3 ListExpr, // [1, "2", 3
TupleExpr, // (1, 2, 3) TupleExpr, // (1, 2, 3)
MapExpr, // {a: 1} MapExpr, // {a: 1}
InitExpr, // struct{"123", 456} InitExpr, // struct{"123", 456}
FunctionLiteralExpr, FunctionLiteralExpr,
@@ -119,30 +120,20 @@ namespace Fig::Ast
_AstBase() {} _AstBase() {}
void setAAI(AstAddressInfo _aai) void setAAI(AstAddressInfo _aai) { aai = std::move(_aai); }
{
aai = std::move(_aai);
}
virtual FString typeName() virtual FString typeName()
{ {
return FString::fromStringView( return FString::fromStringView(FStringView::fromBasicStringView(magic_enum::enum_name(type)));
FStringView::fromBasicStringView(magic_enum::enum_name(type)));
} }
virtual FString toString() virtual FString toString()
{ {
return FString(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column)); return FString(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
} }
AstAddressInfo getAAI() AstAddressInfo getAAI() { return aai; }
{
return aai;
}
AstType getType() AstType getType() { return type; }
{
return type;
}
}; };
class StatementAst : public _AstBase class StatementAst : public _AstBase
@@ -150,10 +141,7 @@ namespace Fig::Ast
public: public:
using _AstBase::_AstBase; using _AstBase::_AstBase;
using _AstBase::operator=; using _AstBase::operator=;
StatementAst() StatementAst() { type = AstType::StatementBase; }
{
type = AstType::StatementBase;
}
virtual FString toString() override virtual FString toString() override
{ {
@@ -164,10 +152,7 @@ namespace Fig::Ast
class EofStmt final : public StatementAst class EofStmt final : public StatementAst
{ {
public: public:
EofStmt() EofStmt() { type = AstType::StatementBase; }
{
type = AstType::StatementBase;
}
virtual FString toString() override virtual FString toString() override
{ {
@@ -180,10 +165,7 @@ namespace Fig::Ast
public: public:
using _AstBase::_AstBase; using _AstBase::_AstBase;
using _AstBase::operator=; using _AstBase::operator=;
ExpressionAst() ExpressionAst() { type = AstType::ExpressionBase; }
{
type = AstType::ExpressionBase;
}
virtual FString toString() override virtual FString toString() override
{ {
@@ -247,36 +229,17 @@ namespace Fig::Ast
Operator::BitAnd, // reference operator & Operator::BitAnd, // reference operator &
}; };
static const std::unordered_set<Operator> binaryOps{ static const std::unordered_set<Operator> binaryOps{
Operator::Add, Operator::Add, Operator::Subtract, Operator::Multiply, Operator::Divide,
Operator::Subtract, Operator::Modulo, Operator::Power, Operator::And, Operator::Or,
Operator::Multiply,
Operator::Divide,
Operator::Modulo,
Operator::Power,
Operator::And,
Operator::Or,
Operator::Equal, Operator::Equal, Operator::NotEqual, Operator::Less, Operator::LessEqual,
Operator::NotEqual, Operator::Greater, Operator::GreaterEqual, Operator::Is,
Operator::Less,
Operator::LessEqual,
Operator::Greater,
Operator::GreaterEqual,
Operator::Is,
Operator::BitAnd, Operator::BitAnd, Operator::BitOr, Operator::BitXor, Operator::BitNot,
Operator::BitOr, Operator::ShiftLeft, Operator::ShiftRight,
Operator::BitXor,
Operator::BitNot,
Operator::ShiftLeft,
Operator::ShiftRight,
Operator::Assign, Operator::Assign, Operator::PlusAssign, Operator::MinusAssign, Operator::AsteriskAssign,
Operator::PlusAssign, Operator::SlashAssign, Operator::CaretAssign
Operator::MinusAssign,
Operator::AsteriskAssign,
Operator::SlashAssign,
Operator::CaretAssign
// Operator::Walrus, // Operator::Walrus,
// Operator::Dot // Operator::Dot
@@ -352,19 +315,9 @@ namespace Fig::Ast
{ {
public: public:
std::vector<Statement> stmts; std::vector<Statement> stmts;
BlockStatementAst() BlockStatementAst() { type = AstType::BlockStatement; }
{ BlockStatementAst(std::vector<Statement> _stmts) : stmts(std::move(_stmts)) { type = AstType::BlockStatement; }
type = AstType::BlockStatement; virtual FString typeName() override { return FString(u8"BlockStatement"); }
}
BlockStatementAst(std::vector<Statement> _stmts) :
stmts(std::move(_stmts))
{
type = AstType::BlockStatement;
}
virtual FString typeName() override
{
return FString(u8"BlockStatement");
}
virtual FString toString() override virtual FString toString() override
{ {
return FString(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column)); return FString(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));

View File

@@ -4,7 +4,7 @@
#include <cstdint> #include <cstdint>
#include <string_view> #include <string_view>
#define __FCORE_VERSION "0.4.1-alpha" #define __FCORE_VERSION "0.4.2-alpha"
#if defined(_WIN32) #if defined(_WIN32)
#define __FCORE_PLATFORM "Windows" #define __FCORE_PLATFORM "Windows"

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "Ast/Statements/InterfaceDefSt.hpp"
#include "Value/interface.hpp" #include "Value/interface.hpp"
#include <Value/Type.hpp> #include <Value/Type.hpp>
#include <algorithm> #include <algorithm>
@@ -303,7 +304,7 @@ namespace Fig
return false; return false;
} }
Function getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName) Ast::InterfaceMethod getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName)
{ {
// O(N²) // O(N²)
// SLOW // SLOW
@@ -336,15 +337,12 @@ namespace Fig
if (method.name == functionName) if (method.name == functionName)
{ {
if (!method.hasDefaultBody()) assert(false); if (!method.hasDefaultBody()) assert(false);
return method;
return Function(
method.paras, TypeInfo(method.returnType), method.defaultBody, shared_from_this());
} }
} }
} }
assert(false); assert(false);
return Function(); // ignore warning
} }
const Function &getImplementedMethod(const TypeInfo &structType, const FString &functionName) const const Function &getImplementedMethod(const TypeInfo &structType, const FString &functionName) const

View File

@@ -0,0 +1,40 @@
#pragma once
#include "Value/value.hpp"
#include <Core/fig_string.hpp>
#include <Value/value_forward.hpp>
#include <Value/Type.hpp>
#include <array>
#include <memory>
namespace Fig
{
class IntPool
{
private:
static constexpr ValueType::IntClass CACHE_MIN = -128;
static constexpr ValueType::IntClass CACHE_MAX = 127;
std::array<ObjectPtr, CACHE_MAX - CACHE_MIN + 1> cache;
public:
IntPool()
{
for (ValueType::IntClass i = CACHE_MIN; i <= CACHE_MAX; ++i)
{
cache[i - CACHE_MIN] = std::make_shared<Object>(i);
}
}
ObjectPtr createInt(ValueType::IntClass val) const
{
if (val >= CACHE_MIN && val <= CACHE_MAX) { return cache[val - CACHE_MIN]; }
return std::make_shared<Object>(val);
}
static const IntPool &getInstance()
{
static IntPool pool;
return pool;
}
};
}; // namespace Fig

View File

@@ -87,7 +87,7 @@ namespace Fig
std::format("Variable `{}` expects type `{}`, but got '{}'", std::format("Variable `{}` expects type `{}`, but got '{}'",
s->name.toBasicString(), s->name.toBasicString(),
s->declaredType.toString().toBasicString(), s->declaredType.toString().toBasicString(),
v->getTypeInfo().toString().toBasicString()))); prettyType(v).toBasicString())));
} }
if (isAccessConst(s->am)) if (isAccessConst(s->am))
{ {

View File

@@ -4,11 +4,12 @@
#include <Ast/AccessModifier.hpp> #include <Ast/AccessModifier.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Value/Type.hpp> #include <Value/Type.hpp>
#include <Value/value_forward.hpp>
#include <memory> #include <memory>
namespace Fig namespace Fig
{ {
class Object;
using ObjectPtr = std::shared_ptr<Object>;
struct VariableSlot struct VariableSlot
{ {
FString name; FString name;

View File

@@ -1,3 +1,4 @@
#include "Value/structType.hpp"
#include <Value/Type.hpp> #include <Value/Type.hpp>
#include <Value/value.hpp> #include <Value/value.hpp>
#include <Context/context.hpp> #include <Context/context.hpp>
@@ -26,9 +27,7 @@ namespace Fig
{ {
if (!typeMap.contains(_name)) if (!typeMap.contains(_name))
{ {
throw RuntimeError(FString(std::format( throw RuntimeError(FString(std::format("No type named '{}'", _name.toBasicString())));
"No type named '{}'",
_name.toBasicString())));
// *this = ValueType::String; // *this = ValueType::String;
} }
id = typeMap.at(name); // may throw id = typeMap.at(name); // may throw
@@ -41,10 +40,7 @@ namespace Fig
ObjectPtr value = key.value; ObjectPtr value = key.value;
const TypeInfo &type = value->getTypeInfo(); const TypeInfo &type = value->getTypeInfo();
if (type == ValueType::Int) if (type == ValueType::Int) { return std::hash<ValueType::IntClass>{}(value->as<ValueType::IntClass>()); }
{
return std::hash<ValueType::IntClass>{}(value->as<ValueType::IntClass>());
}
if (type == ValueType::Double) if (type == ValueType::Double)
{ {
return std::hash<ValueType::DoubleClass>{}(value->as<ValueType::DoubleClass>()); return std::hash<ValueType::DoubleClass>{}(value->as<ValueType::DoubleClass>());
@@ -61,10 +57,7 @@ namespace Fig
{ {
auto HashFields = [](std::vector<Field> fields) { auto HashFields = [](std::vector<Field> fields) {
size_t r = 0; size_t r = 0;
for (auto &f : fields) for (auto &f : fields) { r += std::hash<Field>{}(f); }
{
r += std::hash<Field>{}(f);
}
return r; return r;
}; };
const StructType &st = value->as<StructType>(); const StructType &st = value->as<StructType>();
@@ -73,19 +66,30 @@ namespace Fig
if (type == ValueType::StructInstance) if (type == ValueType::StructInstance)
{ {
const StructInstance &si = value->as<StructInstance>(); const StructInstance &si = value->as<StructInstance>();
return std::hash<TypeInfo>{}(si.parentType) + std::hash<uint64_t>{}(reinterpret_cast<uint64_t>(std::addressof(*si.localContext))); return std::hash<TypeInfo>{}(si.parentType)
+ std::hash<uint64_t>{}(reinterpret_cast<uint64_t>(std::addressof(*si.localContext)));
} }
assert(false); assert(false);
throw ""; // ignore warning throw ""; // ignore warning
} }
} }
FString prettyType(std::shared_ptr<const Object> obj) TypeInfo actualType(std::shared_ptr<const Object> obj)
{ {
auto t = obj->getTypeInfo(); auto t = obj->getTypeInfo();
if (t == ValueType::StructInstance)
return obj->as<StructInstance>().parentType.toString(); // dispatch builtin struct types (like Int{}, List{} e.g...)
return t.toString(); if (t == ValueType::StructType)
{
return obj->as<StructType>().type;
}
if (t == ValueType::StructInstance) return obj->as<StructInstance>().parentType;
return t;
}
FString prettyType(std::shared_ptr<const Object> obj)
{
return actualType(obj).toString();
} }
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1 const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
@@ -102,8 +106,6 @@ namespace Fig
const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12 const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12
const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13 const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13
bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx) bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx)
{ {
return ctx->hasImplRegisted(structType, interfaceType); return ctx->hasImplRegisted(structType, interfaceType);
@@ -111,28 +113,25 @@ namespace Fig
bool isTypeMatch(const TypeInfo &expected, ObjectPtr obj, ContextPtr ctx) bool isTypeMatch(const TypeInfo &expected, ObjectPtr obj, ContextPtr ctx)
{ {
if (expected == ValueType::Any) if (expected == ValueType::Any) return true;
return true;
TypeInfo actual = obj->getTypeInfo(); TypeInfo actual = obj->getTypeInfo();
if (obj->is<StructInstance>()) if (obj->is<StructType>())
{
const StructType &t = obj->as<StructType>();
if (expected == t.type) // the StructType typeinfo
{
return true;
}
}
else if (obj->is<StructInstance>())
{ {
const StructInstance &si = obj->as<StructInstance>(); const StructInstance &si = obj->as<StructInstance>();
if (si.parentType == expected) if (si.parentType == expected) { return true; }
{ if (implements(si.parentType, expected, ctx)) { return true; }
return true;
} }
if (implements(si.parentType, expected, ctx))
{
return true;
}
return false;
}
else
{
return expected == actual; return expected == actual;
} }
}
} // namespace Fig } // namespace Fig

View File

@@ -6,6 +6,7 @@
#include <Value/Type.hpp> #include <Value/Type.hpp>
#include <Value/valueError.hpp> #include <Value/valueError.hpp>
#include <Value/module.hpp> #include <Value/module.hpp>
#include <Value/value_forward.hpp>
#include <memory> #include <memory>
#include <variant> #include <variant>
@@ -31,10 +32,8 @@ namespace Fig
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min()); static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
return d > intMaxAsDouble || d < intMinAsDouble; return d > intMaxAsDouble || d < intMinAsDouble;
} }
class Object;
using ObjectPtr = std::shared_ptr<Object>;
TypeInfo actualType(std::shared_ptr<const Object> obj);
FString prettyType(std::shared_ptr<const Object> obj); FString prettyType(std::shared_ptr<const Object> obj);
bool operator==(const Object &, const Object &); bool operator==(const Object &, const Object &);
@@ -509,7 +508,7 @@ namespace Fig
{ {
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() + rhs.getNumericValue(); auto result = lhs.getNumericValue() + rhs.getNumericValue();
if (bothInt && !isNumberExceededIntLimit(result)) if (bothInt)
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(result); return Object(result);
} }
@@ -526,7 +525,7 @@ namespace Fig
{ {
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() - rhs.getNumericValue(); auto result = lhs.getNumericValue() - rhs.getNumericValue();
if (bothInt && !isNumberExceededIntLimit(result)) if (bothInt)
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(result); return Object(result);
} }
@@ -541,10 +540,20 @@ namespace Fig
{ {
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() * rhs.getNumericValue(); auto result = lhs.getNumericValue() * rhs.getNumericValue();
if (bothInt && !isNumberExceededIntLimit(result)) if (bothInt)
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(result); return Object(result);
} }
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::IntClass>())
{
FString result;
const FString &l = lhs.as<ValueType::StringClass>();
for (size_t i=0; i < rhs.getNumericValue(); ++i)
{
result += l;
}
return Object(result);
}
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs)));
} }
@@ -559,7 +568,7 @@ namespace Fig
throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs)));
// bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); // bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() / rnv; auto result = lhs.getNumericValue() / rnv;
// if (bothInt && !isNumberExceededIntLimit(result)) // if (bothInt)
// return Object(static_cast<ValueType::IntClass>(result)); // return Object(static_cast<ValueType::IntClass>(result));
// int / int maybe decimals // int / int maybe decimals
@@ -573,15 +582,24 @@ namespace Fig
{ {
if (lhs.isNull() || rhs.isNull()) if (lhs.isNull() || rhs.isNull())
throw ValueError(FString(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs)));
if (lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>())
{
ValueType::IntClass lv = lhs.as<ValueType::IntClass>();
ValueType::IntClass rv = lhs.as<ValueType::IntClass>();
if (rv == 0) throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
ValueType::IntClass q = lv / rv;
ValueType::IntClass r = lv % rv;
if (r != 0 && ((lv < 0) != (rv < 0))) { q -= 1; }
return q;
}
if (lhs.isNumeric() && rhs.isNumeric()) if (lhs.isNumeric() && rhs.isNumeric())
{ {
auto rnv = rhs.getNumericValue(); auto rnv = rhs.getNumericValue();
if (rnv == 0) if (rnv == 0)
throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = std::fmod(lhs.getNumericValue(), rnv); auto result = std::fmod(lhs.getNumericValue(), rnv);
if (bothInt && !isNumberExceededIntLimit(result))
return Object(static_cast<ValueType::IntClass>(result));
return Object(result); return Object(result);
} }
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
@@ -698,7 +716,7 @@ namespace Fig
{ {
bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>(); bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>();
auto result = std::pow(base.getNumericValue(), exp.getNumericValue()); auto result = std::pow(base.getNumericValue(), exp.getNumericValue());
if (bothInt && !isNumberExceededIntLimit(result)) if (bothInt)
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(result); return Object(result);
} }
@@ -706,7 +724,6 @@ namespace Fig
} }
}; };
using ObjectPtr = std::shared_ptr<Object>;
using RvObject = ObjectPtr; using RvObject = ObjectPtr;
inline bool operator==(const ValueKey &l, const ValueKey &r) inline bool operator==(const ValueKey &l, const ValueKey &r)

View File

@@ -0,0 +1,10 @@
#pragma once
#include <memory>
namespace Fig
{
class Object;
using ObjectPtr = std::shared_ptr<Object>;
}; // namespace Fig

View File

@@ -1,3 +1,7 @@
#include <Ast/Expressions/VarExpr.hpp>
#include <Value/Type.hpp>
#include <Value/value.hpp>
#include <Value/IntPool.hpp>
#include <Ast/Statements/ErrorFlow.hpp> #include <Ast/Statements/ErrorFlow.hpp>
#include <Value/VariableSlot.hpp> #include <Value/VariableSlot.hpp>
#include <Value/value.hpp> #include <Value/value.hpp>
@@ -120,11 +124,11 @@ namespace Fig
} }
else if (ctx->hasDefaultImplementedMethod(si.parentType, member)) else if (ctx->hasDefaultImplementedMethod(si.parentType, member))
{ {
const auto &ifm = ctx->getDefaultImplementedMethod(si.parentType, member);
Function fn(ifm.paras, actualType(eval(ifm.returnType, ctx)), ifm.defaultBody, ctx);
return LvObject(std::make_shared<VariableSlot>( return LvObject(std::make_shared<VariableSlot>(
member, member, std::make_shared<Object>(fn), ValueType::Function, AccessModifier::PublicConst),
std::make_shared<Object>(ctx->getDefaultImplementedMethod(si.parentType, member)),
ValueType::Function,
AccessModifier::PublicConst),
ctx); ctx);
} }
else else
@@ -222,6 +226,362 @@ namespace Fig
} }
} }
RvObject Evaluator::evalInitExpr(Ast::InitExpr initExpr, ContextPtr ctx)
{
LvObject structeLv = evalLv(initExpr->structe, ctx);
ObjectPtr structTypeVal = structeLv.get();
const FString &structName = structeLv.name();
if (!structTypeVal->is<StructType>())
{
throw EvaluatorError(u8"NotAStructTypeError",
std::format("'{}' is not a structure type", structName.toBasicString()),
initExpr);
}
const StructType &structT = structTypeVal->as<StructType>();
if (structT.builtin)
{
const TypeInfo &type = structT.type;
auto &args = initExpr->args;
size_t argSize = args.size();
if (argSize > 1)
{
throw EvaluatorError(u8"StructInitArgumentMismatchError",
std::format("Builtin class `{}` expects 0 or 1 argument, but {} were provided",
type.toString().toBasicString(),
argSize),
initExpr);
}
// default value
if (argSize == 0)
{
if (type == ValueType::Any || type == ValueType::Null || type == ValueType::Function)
{
throw EvaluatorError(
u8"BuiltinNotConstructibleError",
std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()),
initExpr);
}
return std::make_shared<Object>(Object::defaultValue(type));
}
ObjectPtr val = eval(args[0].second, ctx);
auto err = [&](const char *msg) {
throw EvaluatorError(u8"BuiltinInitTypeMismatchError",
std::format("Builtin `{}` constructor {}", type.toString().toBasicString(), msg),
initExpr);
};
// ===================== Int =====================
if (type == ValueType::Int)
{
if (!val->is<ValueType::IntClass>()) err("expects Int");
return std::make_shared<Object>(val->as<ValueType::IntClass>());
}
// ===================== Double =====================
if (type == ValueType::Double)
{
if (!val->is<ValueType::DoubleClass>()) err("expects Double");
return std::make_shared<Object>(val->as<ValueType::DoubleClass>());
}
// ===================== Bool =====================
if (type == ValueType::Bool)
{
if (!val->is<ValueType::BoolClass>()) err("expects Bool");
return std::make_shared<Object>(val->as<ValueType::BoolClass>());
}
// ===================== String =====================
if (type == ValueType::String)
{
if (!val->is<ValueType::StringClass>()) err("expects String");
return std::make_shared<Object>(val->as<ValueType::StringClass>());
}
// ===================== Null =====================
if (type == ValueType::Null)
{
// Null basically ignores input but keep invariant strict:
if (!val->is<ValueType::NullClass>()) err("expects Null");
return Object::getNullInstance();
}
// ===================== List =====================
if (type == ValueType::List)
{
if (!val->is<List>()) err("expects List");
const auto &src = val->as<List>();
auto copied = std::make_shared<Object>(List{});
auto &dst = copied->as<List>();
dst.reserve(src.size());
for (auto &e : src) dst.push_back(e); // shallow element copy, but new container
return copied;
}
// ===================== Map =====================
if (type == ValueType::Map)
{
if (!val->is<Map>()) err("expects Map");
const auto &src = val->as<Map>();
auto copied = std::make_shared<Object>(Map{});
auto &dst = copied->as<Map>();
for (auto &[k, v] : src) dst.emplace(k, v);
return copied;
}
throw EvaluatorError(
u8"BuiltinNotConstructibleError",
std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()),
initExpr);
}
ContextPtr defContext = structT.defContext; // definition context
// check init args
size_t minArgs = 0;
size_t maxArgs = structT.fields.size();
for (auto &f : structT.fields)
{
if (f.defaultValue == nullptr) minArgs++;
}
size_t got = initExpr->args.size();
if (got > maxArgs || got < minArgs)
{
throw EvaluatorError(u8"StructInitArgumentMismatchError",
std::format("Structure '{}' expects {} to {} fields, but {} were provided",
structName.toBasicString(),
minArgs,
maxArgs,
initExpr->args.size()),
initExpr);
}
std::vector<std::pair<FString, ObjectPtr>> evaluatedArgs;
auto evalArguments = [&evaluatedArgs, initExpr, ctx, this]() {
for (const auto &[argName, argExpr] : initExpr->args)
{
evaluatedArgs.push_back({argName, eval(argExpr, ctx)});
}
};
ContextPtr instanceCtx =
std::make_shared<Context>(FString(std::format("<StructInstance {}>", structName.toBasicString())), ctx);
/*
3 ways of calling constructor
.1 Person {"Fig", 1, "IDK"};
.2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
.3 Person {name, age, sex};
*/
{
using enum Ast::InitExprAst::InitMode;
if (initExpr->initMode == Positional)
{
evalArguments();
for (size_t i = 0; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
const FString &fieldName = field.name;
const TypeInfo &expectedType = field.type;
if (i >= evaluatedArgs.size())
{
// we've checked argument count before, so here
// must be a default value
// evaluate default value in definition context
ObjectPtr defaultVal = eval(field.defaultValue,
ctx); // it can't be null here
// type check
if (!isTypeMatch(expectedType, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, expectedType, field.am, defaultVal);
continue;
}
const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(expectedType, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, expectedType, field.am, argVal);
}
}
else if (initExpr->initMode == Named)
{
evalArguments();
// named
for (size_t i = 0; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name);
if (instanceCtx->containsInThisScope(fieldName))
{
throw EvaluatorError(u8"StructFieldRedeclarationError",
std::format("Field '{}' already initialized in structure '{}'",
fieldName.toBasicString(),
structName.toBasicString()),
initExpr);
}
if (i + 1 > got)
{
// use default value //
// evaluate default value in definition context
ObjectPtr defaultVal = eval(field.defaultValue,
defContext); // it can't be null here
// type check
const TypeInfo &expectedType = field.type;
if (!isTypeMatch(expectedType, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, field.type, field.am, defaultVal);
continue;
}
const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(field.type, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
field.type.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, field.type, field.am, argVal);
}
}
else
{
// shorthand, can be unordered
// in this mode, initExpr args are all VarExpr
// field name is the variable name
for (const auto &[argName, argExpr] : initExpr->args)
{
// assert(argExpr->getType() == Ast::AstType::VarExpr);
// argName is var name
const ObjectPtr &argVal = eval(argExpr, ctx); // get the value
// find field
auto fieldIt = std::find_if(
structT.fields.begin(),
structT.fields.end(),
[&argName](const Field &f) { return f.name == argName; });
if (fieldIt == structT.fields.end())
{
throw EvaluatorError(u8"StructFieldNotFoundError",
std::format("Field '{}' not found in structure '{}'",
argName.toBasicString(),
structName.toBasicString()),
initExpr);
}
const Field &field = *fieldIt;
if (!isTypeMatch(field.type, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
field.name.toBasicString(),
field.type.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
// field.name is argName (var name)
// Point{x=x, y=y} --> Point{x, y}
instanceCtx->def(field.name, field.type, field.am, argVal);
}
// fill default values
size_t currentFieldCount = initExpr->args.size(); // we have already check argument count, min <= got <= max
// so remain fields start from currentFieldCount to maxArgs
for (size_t i = currentFieldCount; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
// evaluate default value in definition context
ObjectPtr defaultVal = eval(field.defaultValue,
defContext); // it can't be null here
// type check
if (!isTypeMatch(field.type, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
field.name.toBasicString(),
field.type.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(field.name, field.type, field.am, defaultVal);
}
}
}
ContextPtr stDefCtx = structT.defContext;
// load struct method
for (auto &[id, fn] : stDefCtx->getFunctions())
{
auto funcNameOpt = stDefCtx->getFunctionName(id);
assert(funcNameOpt.has_value());
const FString &funcName = *funcNameOpt;
auto funcSlot = stDefCtx->get(funcName);
instanceCtx->def(funcName,
ValueType::Function,
funcSlot->am,
std::make_shared<Object>(Function(fn.paras, fn.retType, fn.body, instanceCtx)));
}
return std::make_shared<Object>(StructInstance(structT.type, instanceCtx));
}
RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx) RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx)
{ {
using Ast::Operator; using Ast::Operator;
@@ -232,16 +592,37 @@ namespace Fig
case Operator::Add: { case Operator::Add: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() + rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs + *rhs); return std::make_shared<Object>(*lhs + *rhs);
} }
case Operator::Subtract: { case Operator::Subtract: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() - rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs - *rhs); return std::make_shared<Object>(*lhs - *rhs);
}; };
case Operator::Multiply: { case Operator::Multiply: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() * rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>((*lhs) * (*rhs)); return std::make_shared<Object>((*lhs) * (*rhs));
}; };
case Operator::Divide: { case Operator::Divide: {
@@ -252,11 +633,40 @@ namespace Fig
case Operator::Modulo: { case Operator::Modulo: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass lv = lhs->as<ValueType::IntClass>();
ValueType::IntClass rv = lhs->as<ValueType::IntClass>();
if (rv == 0)
{
throw ValueError(
FString(
std::format(
"Modulo by zero: {} % {}",
lv,
rv
)
)
);
}
ValueType::IntClass result = lv / rv;
ValueType::IntClass r = lv % rv;
if (r != 0 && ((lv < 0) != (rv < 0))) { result -= 1; }
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs % *rhs); return std::make_shared<Object>(*lhs % *rhs);
}; };
case Operator::Power: { case Operator::Power: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = std::pow(lhs->as<ValueType::IntClass>(),rhs->as<ValueType::IntClass>());
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(power(*lhs, *rhs)); return std::make_shared<Object>(power(*lhs, *rhs));
} }
case Operator::And: { case Operator::And: {
@@ -343,34 +753,64 @@ namespace Fig
throw EvaluatorError(u8"TypeError", throw EvaluatorError(u8"TypeError",
std::format("Unsupported operator `is` for '{}' && '{}'", std::format("Unsupported operator `is` for '{}' && '{}'",
lhsType.toString().toBasicString(), prettyType(lhs).toBasicString(),
rhsType.toString().toBasicString()), prettyType(rhs).toBasicString()),
bin->lexp); bin->lexp);
} }
case Operator::BitAnd: { case Operator::BitAnd: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() & rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_and(*lhs, *rhs)); return std::make_shared<Object>(bit_and(*lhs, *rhs));
} }
case Operator::BitOr: { case Operator::BitOr: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() | rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_or(*lhs, *rhs)); return std::make_shared<Object>(bit_or(*lhs, *rhs));
} }
case Operator::BitXor: { case Operator::BitXor: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() ^ rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_xor(*lhs, *rhs)); return std::make_shared<Object>(bit_xor(*lhs, *rhs));
} }
case Operator::ShiftLeft: { case Operator::ShiftLeft: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() << rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(shift_left(*lhs, *rhs)); return std::make_shared<Object>(shift_left(*lhs, *rhs));
} }
case Operator::ShiftRight: { case Operator::ShiftRight: {
ObjectPtr lhs = eval(lexp, ctx); ObjectPtr lhs = eval(lexp, ctx);
ObjectPtr rhs = eval(rexp, ctx); ObjectPtr rhs = eval(rexp, ctx);
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() >> rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(shift_right(*lhs, *rhs)); return std::make_shared<Object>(shift_right(*lhs, *rhs));
} }
@@ -439,7 +879,6 @@ namespace Fig
case Operator::BitNot: { case Operator::BitNot: {
return std::make_shared<Object>(bit_not((*value))); return std::make_shared<Object>(bit_not((*value)));
} }
default: { default: {
throw EvaluatorError(u8"UnsupportedOpError", throw EvaluatorError(u8"UnsupportedOpError",
std::format("Unsupported op '{}' for unary expression", magic_enum::enum_name(op)), std::format("Unsupported op '{}' for unary expression", magic_enum::enum_name(op)),
@@ -721,306 +1160,8 @@ namespace Fig
} }
case AstType::InitExpr: { case AstType::InitExpr: {
auto initExpr = std::static_pointer_cast<Ast::InitExprAst>(exp); auto initExpr = std::static_pointer_cast<Ast::InitExprAst>(exp);
LvObject structeLv = evalLv(initExpr->structe, ctx); assert(initExpr != nullptr);
ObjectPtr structTypeVal = structeLv.get(); return evalInitExpr(initExpr, ctx);
const FString &structName = structeLv.name();
if (!structTypeVal->is<StructType>())
{
throw EvaluatorError(u8"NotAStructTypeError",
std::format("'{}' is not a structure type", structName.toBasicString()),
initExpr);
}
const StructType &structT = structTypeVal->as<StructType>();
if (structT.builtin)
{
const TypeInfo &type = structT.type;
auto &args = initExpr->args;
size_t argSize = args.size();
if (argSize > 1)
{
throw EvaluatorError(
u8"StructInitArgumentMismatchError",
std::format("Builtin class `{}` expects 0 or 1 argument, but {} were provided",
type.toString().toBasicString(),
argSize),
initExpr);
}
// default value
if (argSize == 0)
{
if (type == ValueType::Any || type == ValueType::Null || type == ValueType::Function)
{
throw EvaluatorError(
u8"BuiltinNotConstructibleError",
std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()),
initExpr);
}
return std::make_shared<Object>(Object::defaultValue(type));
}
ObjectPtr val = eval(args[0].second, ctx);
auto err = [&](const char *msg) {
throw EvaluatorError(
u8"BuiltinInitTypeMismatchError",
std::format("Builtin `{}` constructor {}", type.toString().toBasicString(), msg),
initExpr);
};
// ===================== Int =====================
if (type == ValueType::Int)
{
if (!val->is<ValueType::IntClass>()) err("expects Int");
return std::make_shared<Object>(val->as<ValueType::IntClass>());
}
// ===================== Double =====================
if (type == ValueType::Double)
{
if (!val->is<ValueType::DoubleClass>()) err("expects Double");
return std::make_shared<Object>(val->as<ValueType::DoubleClass>());
}
// ===================== Bool =====================
if (type == ValueType::Bool)
{
if (!val->is<ValueType::BoolClass>()) err("expects Bool");
return std::make_shared<Object>(val->as<ValueType::BoolClass>());
}
// ===================== String =====================
if (type == ValueType::String)
{
if (!val->is<ValueType::StringClass>()) err("expects String");
return std::make_shared<Object>(val->as<ValueType::StringClass>());
}
// ===================== Null =====================
if (type == ValueType::Null)
{
// Null basically ignores input but keep invariant strict:
if (!val->is<ValueType::NullClass>()) err("expects Null");
return Object::getNullInstance();
}
// ===================== List =====================
if (type == ValueType::List)
{
if (!val->is<List>()) err("expects List");
const auto &src = val->as<List>();
auto copied = std::make_shared<Object>(List{});
auto &dst = copied->as<List>();
dst.reserve(src.size());
for (auto &e : src) dst.push_back(e); // shallow element copy, but new container
return copied;
}
// ===================== Map =====================
if (type == ValueType::Map)
{
if (!val->is<Map>()) err("expects Map");
const auto &src = val->as<Map>();
auto copied = std::make_shared<Object>(Map{});
auto &dst = copied->as<Map>();
for (auto &[k, v] : src) dst.emplace(k, v);
return copied;
}
throw EvaluatorError(
u8"BuiltinNotConstructibleError",
std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()),
initExpr);
}
ContextPtr defContext = structT.defContext; // definition context
// check init args
size_t minArgs = 0;
size_t maxArgs = structT.fields.size();
for (auto &f : structT.fields)
{
if (f.defaultValue == nullptr) minArgs++;
}
size_t got = initExpr->args.size();
if (got > maxArgs || got < minArgs)
{
throw EvaluatorError(u8"StructInitArgumentMismatchError",
std::format("Structure '{}' expects {} to {} fields, but {} were provided",
structName.toBasicString(),
minArgs,
maxArgs,
initExpr->args.size()),
initExpr);
}
std::vector<std::pair<FString, ObjectPtr>> evaluatedArgs;
for (const auto &[argName, argExpr] : initExpr->args)
{
evaluatedArgs.push_back({argName, eval(argExpr, ctx)});
}
ContextPtr instanceCtx = std::make_shared<Context>(
FString(std::format("<StructInstance {}>", structName.toBasicString())), ctx);
/*
3 ways of calling constructor
.1 Person {"Fig", 1, "IDK"};
.2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
.3 Person {name, age, sex};
*/
{
using enum Ast::InitExprAst::InitMode;
if (initExpr->initMode == Positional)
{
for (size_t i = 0; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
const FString &fieldName = field.name;
const TypeInfo &expectedType = field.type;
if (i >= evaluatedArgs.size())
{
// we've checked argument count before, so here
// must be a default value
// evaluate default value in definition context
ObjectPtr defaultVal = eval(field.defaultValue,
ctx); // it can't be null here
// type check
if (!isTypeMatch(expectedType, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format(
"In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, expectedType, field.am, defaultVal);
continue;
}
const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(expectedType, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, expectedType, field.am, argVal);
}
}
else
{
// named / shorthand init
for (size_t i = 0; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name);
if (instanceCtx->containsInThisScope(fieldName))
{
throw EvaluatorError(u8"StructFieldRedeclarationError",
std::format("Field '{}' already initialized in structure '{}'",
fieldName.toBasicString(),
structName.toBasicString()),
initExpr);
}
if (i + 1 > got)
{
// use default value //
// evaluate default value in definition context
ObjectPtr defaultVal = eval(field.defaultValue,
defContext); // it can't be null here
// type check
const TypeInfo &expectedType = field.type;
if (!isTypeMatch(expectedType, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format(
"In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, field.type, field.am, defaultVal);
continue;
}
const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(field.type, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
field.type.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, field.type, field.am, argVal);
}
}
}
// instanceCtx->merge(*structT.defContext);
// for (auto &[id, fn] : instanceCtx->getFunctions())
// {
// instanceCtx->_update(*instanceCtx->getFunctionName(id),
// std::make_shared<Object>(Function(fn.paras,
// fn.retType,
// fn.body,
// instanceCtx) // change its closureContext to
// // struct instance's context
// ));
// }
ContextPtr stDefCtx = structT.defContext;
// load struct method
for (auto &[id, fn] : stDefCtx->getFunctions())
{
auto funcNameOpt = stDefCtx->getFunctionName(id);
assert(funcNameOpt.has_value());
const FString &funcName = *funcNameOpt;
auto funcSlot = stDefCtx->get(funcName);
instanceCtx->def(
funcName,
ValueType::Function,
funcSlot->am,
std::make_shared<Object>(Function(
fn.paras,
fn.retType,
fn.body,
instanceCtx
))
);
}
return std::make_shared<Object>(StructInstance(structT.type, instanceCtx));
} }
case AstType::ListExpr: { case AstType::ListExpr: {
@@ -1081,18 +1222,22 @@ namespace Fig
RvObject value = nullptr; RvObject value = nullptr;
if (varDef->expr) { value = eval(varDef->expr, ctx); } if (varDef->expr) { value = eval(varDef->expr, ctx); }
TypeInfo declaredType; // default is Any TypeInfo declaredType; // default is Any
const FString &declaredTypeName = varDef->typeName; const Ast::Expression &declaredTypeExp = varDef->declaredType;
if (declaredTypeName == Parser::varDefTypeFollowed) { declaredType = value->getTypeInfo(); }
else if (!declaredTypeName.empty()) if (varDef->followupType) { declaredType = actualType(value); }
else if (declaredTypeExp)
{ {
declaredType = TypeInfo(declaredTypeName); ObjectPtr declaredTypeValue = eval(declaredTypeExp, ctx);
declaredType = actualType(declaredTypeValue);
if (value != nullptr && !isTypeMatch(declaredType, value, ctx)) if (value != nullptr && !isTypeMatch(declaredType, value, ctx))
{ {
throw EvaluatorError(u8"TypeError", throw EvaluatorError(u8"TypeError",
std::format("Variable `{}` expects init-value type `{}`, but got '{}'", std::format("Variable `{}` expects init-value type `{}`, but got '{}'",
varDef->name.toBasicString(), varDef->name.toBasicString(),
declaredTypeName.toBasicString(), prettyType(declaredTypeValue).toBasicString(),
prettyType(value).toBasicString()), prettyType(value).toBasicString()),
varDef->expr); varDef->expr);
} }
@@ -1120,7 +1265,14 @@ namespace Fig
std::format("Function `{}` already declared in this scope", fnName.toBasicString()), std::format("Function `{}` already declared in this scope", fnName.toBasicString()),
fnDef); fnDef);
} }
Function fn(fnDef->paras, TypeInfo(fnDef->retType), fnDef->body, ctx); TypeInfo returnType = ValueType::Any;
if (fnDef->retType)
{
ObjectPtr returnTypeValue = eval(fnDef->retType, ctx);
returnType = actualType(returnTypeValue);
}
Function fn(fnDef->paras, returnType, fnDef->body, ctx);
ctx->def(fnName, ctx->def(fnName,
ValueType::Function, ValueType::Function,
(fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const),
@@ -1151,7 +1303,14 @@ namespace Fig
stDef->name.toBasicString()), stDef->name.toBasicString()),
stDef); stDef);
} }
fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr)); TypeInfo fieldType = ValueType::Any;
if (field.declaredType)
{
ObjectPtr declaredTypeValue = eval(field.declaredType, ctx);
fieldType = actualType(declaredTypeValue);
}
fields.push_back(Field(field.am, field.fieldName, fieldType, field.defaultValueExpr));
} }
ContextPtr defContext = std::make_shared<Context>(FString(std::format("<Struct {} at {}:{}>", ContextPtr defContext = std::make_shared<Context>(FString(std::format("<Struct {} at {}:{}>",
stDef->name.toBasicString(), stDef->name.toBasicString(),
@@ -1322,8 +1481,10 @@ namespace Fig
implemented.insert(name); implemented.insert(name);
ObjectPtr returnTypeValue = eval(ifMethod.returnType, ctx);
record.implMethods[name] = record.implMethods[name] =
Function(implMethod.paras, TypeInfo(ifMethod.returnType), implMethod.body, ctx); Function(implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx);
} }
for (auto &m : interface.methods) for (auto &m : interface.methods)
@@ -1499,13 +1660,11 @@ namespace Fig
case BreakSt: { case BreakSt: {
if (!ctx->parent) if (!ctx->parent)
{ {
throw EvaluatorError( throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
} }
if (!ctx->isInLoopContext()) if (!ctx->isInLoopContext())
{ {
throw EvaluatorError( throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
} }
return StatementResult::breakFlow(); return StatementResult::breakFlow();
} }
@@ -1513,13 +1672,11 @@ namespace Fig
case ContinueSt: { case ContinueSt: {
if (!ctx->parent) if (!ctx->parent)
{ {
throw EvaluatorError( throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
} }
if (!ctx->isInLoopContext()) if (!ctx->isInLoopContext())
{ {
throw EvaluatorError( throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
} }
return StatementResult::continueFlow(); return StatementResult::continueFlow();
} }
@@ -1531,6 +1688,15 @@ namespace Fig
return StatementResult::normal(eval(exprStmt->exp, ctx)); return StatementResult::normal(eval(exprStmt->exp, ctx));
} }
case BlockStatement: {
auto block = std::static_pointer_cast<Ast::BlockStatementAst>(stmt);
assert(block != nullptr);
ContextPtr blockCtx = std::make_shared<Context>(
FString(std::format("<Block at {}:{}>", block->getAAI().line, block->getAAI().column)), ctx);
return evalBlockStatement(block, blockCtx);
}
default: default:
throw RuntimeError( throw RuntimeError(
FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType())))); FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType()))));
@@ -1690,9 +1856,8 @@ namespace Fig
if (ctx->containsInThisScope(modName)) if (ctx->containsInThisScope(modName))
{ {
throw EvaluatorError(u8"RedeclarationError", throw EvaluatorError(
std::format("{} has already been declared.", modName.toBasicString()), u8"RedeclarationError", std::format("{} has already been declared.", modName.toBasicString()), i);
i);
} }
ctx->def( ctx->def(
modName, ValueType::Module, AccessModifier::PublicConst, std::make_shared<Object>(Module(modName, modCtx))); modName, ValueType::Module, AccessModifier::PublicConst, std::make_shared<Object>(Module(modName, modCtx)));

View File

@@ -1,3 +1,4 @@
#include "Ast/Expressions/InitExpr.hpp"
#include <Ast/Statements/ImplementSt.hpp> #include <Ast/Statements/ImplementSt.hpp>
#include <Ast/Statements/InterfaceDefSt.hpp> #include <Ast/Statements/InterfaceDefSt.hpp>
#include <Value/Type.hpp> #include <Value/Type.hpp>
@@ -127,7 +128,7 @@ namespace Fig
LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b] LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b]
/* Right-value eval*/ /* Right-value eval*/
RvObject evalInitExpr(Ast::InitExpr, ContextPtr); // only allows evalUnary to call
RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *.... RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *....
RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr
RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr

View File

@@ -66,7 +66,10 @@ namespace Fig
{FString(u8"["), TokenType::LeftBracket}, {FString(u8"["), TokenType::LeftBracket},
{FString(u8"]"), TokenType::RightBracket}, {FString(u8"]"), TokenType::RightBracket},
{FString(u8"{"), TokenType::LeftBrace}, {FString(u8"{"), TokenType::LeftBrace},
{FString(u8"}"), TokenType::RightBrace}}; {FString(u8"}"), TokenType::RightBrace},
{FString(u8"?"), TokenType::Question},
{FString(u8"!"), TokenType::Not},
};
const std::unordered_map<FString, TokenType> Lexer::keyword_map{ const std::unordered_map<FString, TokenType> Lexer::keyword_map{
{FString(u8"and"), TokenType::And}, {FString(u8"and"), TokenType::And},
@@ -81,6 +84,7 @@ namespace Fig
{FString(u8"for"), TokenType::For}, {FString(u8"for"), TokenType::For},
{FString(u8"if"), TokenType::If}, {FString(u8"if"), TokenType::If},
{FString(u8"else"), TokenType::Else}, {FString(u8"else"), TokenType::Else},
{FString(u8"new"), TokenType::New},
{FString(u8"struct"), TokenType::Struct}, {FString(u8"struct"), TokenType::Struct},
{FString(u8"interface"), TokenType::Interface}, {FString(u8"interface"), TokenType::Interface},
{FString(u8"impl"), TokenType::Implement}, {FString(u8"impl"), TokenType::Implement},

View File

@@ -33,14 +33,14 @@ public func format(objects ...) -> Any
{ {
if objects.length() < 1 if objects.length() < 1
{ {
throw FormatError{"Require format string"}; throw new FormatError{"Require format string"};
} }
var fmt := objects[0]; var fmt := objects[0];
var fmtType := value.type(fmt); var fmtType := value.type(fmt);
if fmtType != "String" if fmtType != "String"
{ {
throw FormatError{"arg 0 (fmt) must be String type, got " + fmtType}; throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType};
} }
var result := ""; var result := "";
@@ -56,7 +56,7 @@ public func format(objects ...) -> Any
{ {
if (i + 1 >= length) if (i + 1 >= length)
{ {
throw FormatError{"unclosed brace"}; throw new FormatError{"unclosed brace"};
} }
var nextChar = fmt[i + 1]; var nextChar = fmt[i + 1];
@@ -80,12 +80,12 @@ public func format(objects ...) -> Any
if endIndex == -1 if endIndex == -1
{ {
throw FormatError{"unclosed brace"}; throw new FormatError{"unclosed brace"};
} }
if argIndex >= objects.length() if argIndex >= objects.length()
{ {
throw FormatError{"require enough format expression"}; throw new FormatError{"require enough format expression"};
} }
result += value.string_from(objects[argIndex]); result += value.string_from(objects[argIndex]);
@@ -102,7 +102,7 @@ public func format(objects ...) -> Any
continue; continue;
} }
throw FormatError{"invalid format syntax"}; throw new FormatError{"invalid format syntax"};
} }
else else
{ {
@@ -122,14 +122,14 @@ public func formatByListArgs(objects) -> Any
} }
if objects.length() < 1 if objects.length() < 1
{ {
throw FormatError{"Require format string"}; throw new FormatError{"Require format string"};
} }
var fmt := objects[0]; var fmt := objects[0];
var fmtType := value.type(fmt); var fmtType := value.type(fmt);
if fmtType != "String" if fmtType != "String"
{ {
throw FormatError{"arg 0 (fmt) must be String type, got " + fmtType}; throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType};
} }
var result := ""; var result := "";
@@ -145,7 +145,7 @@ if objects.length() < 1
{ {
if (i + 1 >= length) if (i + 1 >= length)
{ {
throw FormatError{"unclosed brace"}; throw new FormatError{"unclosed brace"};
} }
var nextChar = fmt[i + 1]; var nextChar = fmt[i + 1];
@@ -169,12 +169,12 @@ if objects.length() < 1
if endIndex == -1 if endIndex == -1
{ {
throw FormatError{"unclosed brace"}; throw new FormatError{"unclosed brace"};
} }
if argIndex >= objects.length() if argIndex >= objects.length()
{ {
throw FormatError{"require enough format expression"}; throw new FormatError{"require enough format expression"};
} }
result += value.string_from(objects[argIndex]); result += value.string_from(objects[argIndex]);
@@ -191,7 +191,7 @@ if objects.length() < 1
continue; continue;
} }
throw FormatError{"invalid format syntax"}; throw new FormatError{"invalid format syntax"};
} }
else else
{ {

View File

@@ -47,5 +47,5 @@ public struct Time
public func now() -> Time public func now() -> Time
{ {
return Time{__ftime_now_ns()}; return new Time{__ftime_now_ns()};
} }

View File

@@ -1,5 +1,8 @@
#pragma once #pragma once
#include "Ast/Expressions/VarExpr.hpp"
#include "Ast/Statements/VarDef.hpp"
#include "Ast/astBase.hpp"
#include <Ast/Statements/InterfaceDefSt.hpp> #include <Ast/Statements/InterfaceDefSt.hpp>
#include <Ast/functionParameters.hpp> #include <Ast/functionParameters.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
@@ -35,11 +38,19 @@ namespace Fig
{u8"true", Object::getTrueInstance()}, {u8"true", Object::getTrueInstance()},
{u8"false", Object::getFalseInstance()}, {u8"false", Object::getFalseInstance()},
{u8"Error", {u8"Error",
std::make_shared<Object>(InterfaceType( std::make_shared<Object>(InterfaceType(ErrorInterfaceTypeInfo,
ErrorInterfaceTypeInfo, {Ast::InterfaceMethod(u8"toString",
{Ast::InterfaceMethod(u8"toString", Ast::FunctionParameters({}, {}), u8"String", nullptr), Ast::FunctionParameters({}, {}),
Ast::InterfaceMethod(u8"getErrorClass", Ast::FunctionParameters({}, {}), u8"String", nullptr), std::make_shared<Ast::VarExprAst>(u8"String"),
Ast::InterfaceMethod(u8"getErrorMessage", Ast::FunctionParameters({}, {}), u8"String", nullptr)}))}, nullptr),
Ast::InterfaceMethod(u8"getErrorClass",
Ast::FunctionParameters({}, {}),
std::make_shared<Ast::VarExprAst>(u8"String"),
nullptr),
Ast::InterfaceMethod(u8"getErrorMessage",
Ast::FunctionParameters({}, {}),
std::make_shared<Ast::VarExprAst>(u8"String"),
nullptr)}))},
{u8"Any", std::make_shared<Object>(StructType(ValueType::Any, nullptr, {}, true))}, {u8"Any", std::make_shared<Object>(StructType(ValueType::Any, nullptr, {}, true))},
{u8"Int", std::make_shared<Object>(StructType(ValueType::Int, nullptr, {}, true))}, {u8"Int", std::make_shared<Object>(StructType(ValueType::Int, nullptr, {}, true))},

View File

@@ -1,9 +1,9 @@
#include "Ast/Statements/ErrorFlow.hpp" #include <Ast/Statements/ErrorFlow.hpp>
#include "Ast/Statements/ImplementSt.hpp" #include <Ast/Statements/ImplementSt.hpp>
#include "Ast/astBase.hpp" #include <Ast/astBase.hpp>
#include "Ast/functionParameters.hpp" #include <Ast/functionParameters.hpp>
#include "Error/error.hpp" #include <Error/error.hpp>
#include "Token/token.hpp" #include <Token/token.hpp>
#include <Parser/parser.hpp> #include <Parser/parser.hpp>
namespace Fig namespace Fig
@@ -54,6 +54,7 @@ namespace Fig
// // 点运算符 // // 点运算符
// {Ast::Operator::Dot, {40, 41}}, // {Ast::Operator::Dot, {40, 41}},
{Ast::Operator::TernaryCond, {3, 2}},
}; };
const std::unordered_map<Ast::Operator, Parser::Precedence> Parser::unaryOpPrecedence = { const std::unordered_map<Ast::Operator, Parser::Precedence> Parser::unaryOpPrecedence = {
@@ -63,8 +64,7 @@ namespace Fig
{Ast::Operator::Not, 150}, // ! {Ast::Operator::Not, 150}, // !
}; };
Ast::VarDef Ast::VarDef Parser::__parseVarDef(bool isPublic)
Parser::__parseVarDef(bool isPublic)
{ {
// entry: current is keyword `var` or `const` // entry: current is keyword `var` or `const`
bool isConst = (currentToken().getType() == TokenType::Const ? true : false); bool isConst = (currentToken().getType() == TokenType::Const ? true : false);
@@ -72,31 +72,31 @@ namespace Fig
expect(TokenType::Identifier); expect(TokenType::Identifier);
FString name = currentToken().getValue(); FString name = currentToken().getValue();
next(); next();
FString tiName = ValueType::Any.name; Ast::Expression declaredType = nullptr;
bool hasSpecificType = false; bool hasSpecificType = false;
if (isThis(TokenType::Colon)) // : if (isThis(TokenType::Colon)) // :
{ {
expectPeek(TokenType::Identifier, FString(u8"Type name")); next(); // consume `:`
next(); declaredType = parseExpression(0, TokenType::Assign, TokenType::Semicolon);
tiName = currentToken().getValue();
next();
hasSpecificType = true; hasSpecificType = true;
} }
if (isThis(TokenType::Semicolon)) if (isThis(TokenType::Semicolon))
{ {
next(); // consume `;`, no using expectConsume here cause we don't need to check again next(); // consume `;`, no using expectConsume here cause we don't need to check again
return makeAst<Ast::VarDefAst>(isPublic, isConst, name, tiName, nullptr); return makeAst<Ast::VarDefAst>(isPublic, isConst, name, declaredType, nullptr, false);
} }
if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus"); if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus");
bool followupType = false;
if (isThis(TokenType::Walrus)) if (isThis(TokenType::Walrus))
{ {
if (hasSpecificType) throwAddressableError<SyntaxError>(FString(u8"")); if (hasSpecificType) throwAddressableError<SyntaxError>(FString(u8""));
tiName = Parser::varDefTypeFollowed; followupType = true;
} }
next(); next();
Ast::Expression exp = parseExpression(0); Ast::Expression exp = parseExpression(0);
expectSemicolon(); expectSemicolon();
return makeAst<Ast::VarDefAst>(isPublic, isConst, name, tiName, exp); return makeAst<Ast::VarDefAst>(isPublic, isConst, name, declaredType, exp, followupType);
} }
ObjectPtr Parser::__parseValue() ObjectPtr Parser::__parseValue()
@@ -133,18 +133,12 @@ namespace Fig
return std::make_shared<Object>(i); return std::make_shared<Object>(i);
} }
} }
else if (currentToken().getType() == TokenType::LiteralString) else if (currentToken().getType() == TokenType::LiteralString) { return std::make_shared<Object>(_val); }
{
return std::make_shared<Object>(_val);
}
else if (currentToken().getType() == TokenType::LiteralBool) else if (currentToken().getType() == TokenType::LiteralBool)
{ {
return std::make_shared<Object>((_val == u8"true" ? true : false)); return std::make_shared<Object>((_val == u8"true" ? true : false));
} }
else if (currentToken().getType() == TokenType::LiteralNull) else if (currentToken().getType() == TokenType::LiteralNull) { return Object::getNullInstance(); }
{
return Object::getNullInstance();
}
else else
{ {
throw std::runtime_error(std::string("Internal Error at: ") + std::string(__func__)); throw std::runtime_error(std::string("Internal Error at: ") + std::string(__func__));
@@ -237,17 +231,17 @@ namespace Fig
next(); next();
expect(TokenType::LeftParen); expect(TokenType::LeftParen);
Ast::FunctionParameters params = __parseFunctionParameters(); Ast::FunctionParameters params = __parseFunctionParameters();
FString retTiName = ValueType::Any.name;
Ast::Expression returnType;
if (isThis(TokenType::RightArrow)) // -> if (isThis(TokenType::RightArrow)) // ->
{ {
next(); // skip `->` next(); // skip `->`
expect(TokenType::Identifier); returnType = parseExpression(0, TokenType::LeftBrace, TokenType::Semicolon);
retTiName = currentToken().getValue();
next(); // skip return type
} }
expect(TokenType::LeftBrace); expect(TokenType::LeftBrace);
Ast::BlockStatement body = __parseBlockStatement(); Ast::BlockStatement body = __parseBlockStatement();
return makeAst<Ast::FunctionDefSt>(funcName, params, isPublic, retTiName, body); return makeAst<Ast::FunctionDefSt>(funcName, params, isPublic, returnType, body);
} }
Ast::StructDef Parser::__parseStructDef(bool isPublic) Ast::StructDef Parser::__parseStructDef(bool isPublic)
{ {
@@ -289,13 +283,11 @@ namespace Fig
{ {
throwAddressableError<SyntaxError>(FString(std::format("expect field name or field attribute"))); throwAddressableError<SyntaxError>(FString(std::format("expect field name or field attribute")));
} }
FString tiName = ValueType::Any.name; Ast::Expression fieldType = nullptr;
if (isThis(TokenType::Colon)) if (isThis(TokenType::Colon))
{ {
next(); next(); // consume `:`
expect(TokenType::Identifier, u8"type name"); fieldType = parseExpression(0, TokenType::Assign, TokenType::Semicolon);
tiName = currentToken().getValue();
next();
} }
Ast::Expression initExpr = nullptr; Ast::Expression initExpr = nullptr;
if (isThis(TokenType::Assign)) if (isThis(TokenType::Assign))
@@ -305,7 +297,7 @@ namespace Fig
initExpr = parseExpression(0); initExpr = parseExpression(0);
} }
expectSemicolon(); expectSemicolon();
return Ast::StructDefField(am, fieldName, tiName, initExpr); return Ast::StructDefField(am, fieldName, fieldType, initExpr);
}; };
std::vector<Ast::Statement> stmts; std::vector<Ast::Statement> stmts;
std::vector<Ast::StructDefField> fields; std::vector<Ast::StructDefField> fields;
@@ -318,10 +310,7 @@ namespace Fig
next(); // consume `}` next(); // consume `}`
break; break;
} }
if (isThis(TokenType::Identifier)) if (isThis(TokenType::Identifier)) { fields.push_back(__parseStructField(false)); }
{
fields.push_back(__parseStructField(false));
}
else if (isThis(TokenType::Public)) else if (isThis(TokenType::Public))
{ {
if (isNext(TokenType::Const)) if (isNext(TokenType::Const))
@@ -361,23 +350,18 @@ namespace Fig
next(); // consume `struct` next(); // consume `struct`
stmts.push_back(__parseStructDef(false)); stmts.push_back(__parseStructDef(false));
} }
else if (isThis(TokenType::Const)) else if (isThis(TokenType::Const)) { fields.push_back(__parseStructField(false)); }
{
fields.push_back(__parseStructField(false));
}
else if (isThis(TokenType::Variable)) else if (isThis(TokenType::Variable))
{ {
throwAddressableError<SyntaxError>(FString("Variables are not allowed to be defined within a structure.")); throwAddressableError<SyntaxError>(
FString("Variables are not allowed to be defined within a structure."));
} }
else else
{ {
throwAddressableError<SyntaxError>(FString("Invalid syntax")); throwAddressableError<SyntaxError>(FString("Invalid syntax"));
} }
} }
if (!braceClosed) if (!braceClosed) { throwAddressableError<SyntaxError>(FString("braces are not closed")); }
{
throwAddressableError<SyntaxError>(FString("braces are not closed"));
}
return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts)); return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts));
} }
@@ -409,27 +393,18 @@ namespace Fig
expect(TokenType::RightArrow); // -> expect(TokenType::RightArrow); // ->
next(); // consume `->` next(); // consume `->`
expect(TokenType::Identifier, u8"return type"); Ast::Expression returnType = parseExpression(0, TokenType::LeftBrace, TokenType::Semicolon);
FString returnType = currentToken().getValue();
next(); // consume return type
if (isThis(TokenType::LeftBrace)) if (isThis(TokenType::LeftBrace))
{ {
Ast::BlockStatement block = __parseBlockStatement(); Ast::BlockStatement block = __parseBlockStatement();
methods.push_back(Ast::InterfaceMethod( methods.push_back(Ast::InterfaceMethod(funcName, paras, returnType, block));
funcName,
paras,
returnType,
block));
continue; continue;
} }
expectSemicolon(); expectSemicolon();
methods.push_back(Ast::InterfaceMethod( methods.push_back(Ast::InterfaceMethod(funcName, paras, returnType));
funcName,
paras,
returnType));
} }
else else
{ {
@@ -473,10 +448,7 @@ namespace Fig
Ast::FunctionParameters paras = __parseFunctionParameters(); Ast::FunctionParameters paras = __parseFunctionParameters();
expect(TokenType::LeftBrace); expect(TokenType::LeftBrace);
Ast::BlockStatement body = __parseBlockStatement(); Ast::BlockStatement body = __parseBlockStatement();
methods.push_back(Ast::ImplementMethod( methods.push_back(Ast::ImplementMethod(funcName, paras, body));
funcName,
paras,
body));
} }
else else
{ {
@@ -544,11 +516,9 @@ namespace Fig
expect(TokenType::LeftBrace); // { expect(TokenType::LeftBrace); // {
Ast::BlockStatement catchBody = __parseBlockStatement(); Ast::BlockStatement catchBody = __parseBlockStatement();
if (hasType) if (hasType) { catches.push_back(Ast::Catch(errVarName, errVarType, catchBody)); }
else
{ {
catches.push_back(Ast::Catch(errVarName, errVarType, catchBody));
}
else {
catches.push_back(Ast::Catch(errVarName, catchBody)); catches.push_back(Ast::Catch(errVarName, catchBody));
} }
} }
@@ -575,17 +545,11 @@ namespace Fig
{ {
Ast::Statement stmt; Ast::Statement stmt;
if (isThis(TokenType::EndOfFile)) { return makeAst<Ast::EofStmt>(); } if (isThis(TokenType::EndOfFile)) { return makeAst<Ast::EofStmt>(); }
else if (isThis(TokenType::Import)) else if (isThis(TokenType::Import)) { stmt = __parseImport(); }
{
stmt = __parseImport();
}
else if (isThis(TokenType::Public)) else if (isThis(TokenType::Public))
{ {
next(); // consume `public` next(); // consume `public`
if (isThis(TokenType::Variable) || isThis(TokenType::Const)) if (isThis(TokenType::Variable) || isThis(TokenType::Const)) { stmt = __parseVarDef(true); }
{
stmt = __parseVarDef(true);
}
else if (isThis(TokenType::Function) and isNext(TokenType::Identifier)) else if (isThis(TokenType::Function) and isNext(TokenType::Identifier))
{ {
next(); next();
@@ -596,19 +560,14 @@ namespace Fig
next(); next();
stmt = __parseStructDef(true); stmt = __parseStructDef(true);
} }
else if (isThis(TokenType::Interface)) else if (isThis(TokenType::Interface)) { stmt = __parseInterfaceDef(true); }
{
stmt = __parseInterfaceDef(true);
}
else else
{ {
throwAddressableError<SyntaxError>(FString(u8"Expected `var`, `const`, `function`, `struct` or `interface` after `public`")); throwAddressableError<SyntaxError>(
FString(u8"Expected `var`, `const`, `function`, `struct` or `interface` after `public`"));
} }
} }
else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) { stmt = __parseVarDef(false); }
{
stmt = __parseVarDef(false);
}
else if (isThis(TokenType::Function) and isNext(TokenType::Identifier)) else if (isThis(TokenType::Function) and isNext(TokenType::Identifier))
{ {
next(); next();
@@ -626,50 +585,20 @@ namespace Fig
next(); next();
stmt = __parseInterfaceDef(false); stmt = __parseInterfaceDef(false);
} }
else if (isThis(TokenType::Implement)) else if (isThis(TokenType::Implement)) { stmt = __parseImplement(); }
{ else if (isThis(TokenType::If)) { stmt = __parseIf(); }
stmt = __parseImplement();
}
else if (isThis(TokenType::If))
{
stmt = __parseIf();
}
else if (isThis(TokenType::Else)) else if (isThis(TokenType::Else))
{ {
throwAddressableError<SyntaxError>(FString(u8"`else` without matching `if`")); throwAddressableError<SyntaxError>(FString(u8"`else` without matching `if`"));
} }
else if (isThis(TokenType::LeftBrace)) else if (isThis(TokenType::LeftBrace)) { stmt = __parseBlockStatement(); }
{ else if (isThis(TokenType::While)) { stmt = __parseWhile(); }
stmt = __parseBlockStatement(); else if (isThis(TokenType::For)) { stmt = __parseFor(); }
} else if (isThis(TokenType::Return)) { stmt = __parseReturn(); }
else if (isThis(TokenType::While)) else if (isThis(TokenType::Break)) { stmt = __parseBreak(); }
{ else if (isThis(TokenType::Continue)) { stmt = __parseContinue(); }
stmt = __parseWhile(); else if (isThis(TokenType::Throw)) { stmt = __parseThrow(); }
} else if (isThis(TokenType::Try)) { stmt = __parseTry(); }
else if (isThis(TokenType::For))
{
stmt = __parseFor();
}
else if (isThis(TokenType::Return))
{
stmt = __parseReturn();
}
else if (isThis(TokenType::Break))
{
stmt = __parseBreak();
}
else if (isThis(TokenType::Continue))
{
stmt = __parseContinue();
}
else if (isThis(TokenType::Throw))
{
stmt = __parseThrow();
}
else if (isThis(TokenType::Try))
{
stmt = __parseTry();
}
else if (allowExp) else if (allowExp)
{ {
// expression statement // expression statement
@@ -766,7 +695,7 @@ namespace Fig
} }
else else
{ {
condition = parseExpression(0); condition = parseExpression(0, TokenType::LeftBrace);
} }
expect(TokenType::LeftBrace); // { expect(TokenType::LeftBrace); // {
Ast::BlockStatement body = __parseBlockStatement(); Ast::BlockStatement body = __parseBlockStatement();
@@ -784,7 +713,8 @@ namespace Fig
throwAddressableError<SyntaxError>(u8"BlockStatement cannot be used as for loop increment"); throwAddressableError<SyntaxError>(u8"BlockStatement cannot be used as for loop increment");
} }
if (isThis(TokenType::If) || isThis(TokenType::While) || isThis(TokenType::For) || isThis(TokenType::Return) || isThis(TokenType::Break) || isThis(TokenType::Continue)) if (isThis(TokenType::If) || isThis(TokenType::While) || isThis(TokenType::For) || isThis(TokenType::Return)
|| isThis(TokenType::Break) || isThis(TokenType::Continue))
{ {
throwAddressableError<SyntaxError>(u8"Control flow statements cannot be used as for loop increment"); throwAddressableError<SyntaxError>(u8"Control flow statements cannot be used as for loop increment");
} }
@@ -799,8 +729,7 @@ namespace Fig
// TODO: support enumeration // TODO: support enumeration
next(); // consume `for` next(); // consume `for`
bool paren = isThis(TokenType::LeftParen); bool paren = isThis(TokenType::LeftParen);
if (paren) if (paren) next(); // consume `(`
next(); // consume `(`
// support 3-part for loop // support 3-part for loop
// for init; condition; increment {} // for init; condition; increment {}
Ast::Statement initStmt = __parseStatement(false); // auto check `` Ast::Statement initStmt = __parseStatement(false); // auto check ``
@@ -813,8 +742,7 @@ namespace Fig
// auto guard = disableSemicolon(); // auto guard = disableSemicolon();
incrementStmt = __parseIncrementStatement(); incrementStmt = __parseIncrementStatement();
} // after parse increment, semicolon check state restored } // after parse increment, semicolon check state restored
if (paren) if (paren) expectConsume(TokenType::RightParen); // consume `)` if has `(`
expectConsume(TokenType::RightParen); // consume `)` if has `(`
expect(TokenType::LeftBrace); // { expect(TokenType::LeftBrace); // {
Ast::BlockStatement body = __parseBlockStatement(); // auto consume `}` Ast::BlockStatement body = __parseBlockStatement(); // auto consume `}`
return makeAst<Ast::ForSt>(initStmt, condition, incrementStmt, body); return makeAst<Ast::ForSt>(initStmt, condition, incrementStmt, body);
@@ -938,10 +866,7 @@ namespace Fig
{ {
if (mode == 0) if (mode == 0)
{ {
if (isThis(TokenType::Identifier) && isNext(TokenType::Colon)) if (isThis(TokenType::Identifier) && isNext(TokenType::Colon)) { mode = 2; }
{
mode = 2;
}
else if (isThis(TokenType::Identifier) && (isNext(TokenType::Comma) || isNext(TokenType::RightBrace))) else if (isThis(TokenType::Identifier) && (isNext(TokenType::Comma) || isNext(TokenType::RightBrace)))
{ {
mode = 3; mode = 3;
@@ -985,17 +910,19 @@ namespace Fig
} }
else if (!isThis(TokenType::RightBrace)) else if (!isThis(TokenType::RightBrace))
{ {
throwAddressableError<SyntaxError>(FString( throwAddressableError<SyntaxError>(
std::format("Expect `,` or `}}` in struct initialization expression, got {}", FString(std::format("Expect `,` or `}}` in struct initialization expression, got {}",
currentToken().toString().toBasicString()))); currentToken().toString().toBasicString())));
} }
} }
expect(TokenType::RightBrace); expect(TokenType::RightBrace);
next(); // consume `}` next(); // consume `}`
return makeAst<Ast::InitExprAst>(structe, args, return makeAst<Ast::InitExprAst>(
(mode == 1 ? Ast::InitExprAst::InitMode::Positional : structe,
(mode == 2 ? Ast::InitExprAst::InitMode::Named : Ast::InitExprAst::InitMode::Shorthand))); args,
static_cast<Ast::InitExprAst::InitMode>(mode));
} }
Ast::Expression Parser::__parseTupleOrParenExpr() Ast::Expression Parser::__parseTupleOrParenExpr()
{ {
next(); next();
@@ -1017,8 +944,7 @@ namespace Fig
{ {
next(); // consume ',' next(); // consume ','
if (currentToken().getType() == TokenType::RightParen) if (currentToken().getType() == TokenType::RightParen) break;
break;
elements.push_back(parseExpression(0)); elements.push_back(parseExpression(0));
} }
@@ -1069,10 +995,7 @@ namespace Fig
expect(TokenType::Identifier, u8"package name"); expect(TokenType::Identifier, u8"package name");
path.push_back(currentToken().getValue()); path.push_back(currentToken().getValue());
next(); // consume package name next(); // consume package name
if (isThis(TokenType::Semicolon)) if (isThis(TokenType::Semicolon)) { break; }
{
break;
}
else if (isThis(TokenType::Dot)) else if (isThis(TokenType::Dot))
{ {
next(); // consume `.` next(); // consume `.`
@@ -1092,8 +1015,7 @@ namespace Fig
Ast::Operator op; Ast::Operator op;
Token tok = currentToken(); Token tok = currentToken();
if (tok == EOFTok) if (tok == EOFTok) throwAddressableError<SyntaxError>(FString(u8"Unexpected end of expression"));
throwAddressableError<SyntaxError>(FString(u8"Unexpected end of expression"));
if (tok.getType() == stop || tok.getType() == stop2) if (tok.getType() == stop || tok.getType() == stop2)
{ {
if (lhs == nullptr) throwAddressableError<SyntaxError>(FString(u8"Expected expression")); if (lhs == nullptr) throwAddressableError<SyntaxError>(FString(u8"Expected expression"));
@@ -1139,6 +1061,14 @@ namespace Fig
next(); next();
lhs = makeAst<Ast::UnaryExprAst>(op, parseExpression(bp, stop, stop2)); lhs = makeAst<Ast::UnaryExprAst>(op, parseExpression(bp, stop, stop2));
} }
else if (tok.getType() == TokenType::New)
{
// `new` now is an independent syntax
next();
Ast::Expression operand = parseExpression(bp, TokenType::LeftBrace);
expect(TokenType::LeftBrace);
lhs = __parseInitExpr(operand);
}
else else
{ {
throwAddressableError<SyntaxError>(FString(u8"Unexpected token in expression:") + tok.toString()); throwAddressableError<SyntaxError>(FString(u8"Unexpected token in expression:") + tok.toString());
@@ -1148,21 +1078,31 @@ namespace Fig
while (true) while (true)
{ {
tok = currentToken(); tok = currentToken();
if (tok.getType() == stop || tok.getType() == stop2|| tok == EOFTok) break; if (tok.getType() == stop || tok.getType() == stop2 || tok == EOFTok) break;
/* Postfix */ /* Postfix */
if (tok.getType() == TokenType::LeftBrace)
{
throwAddressableError<SyntaxError>(
FString(u8"Since Fig v0.4.2, please use new struct{} to avoid syntax ambiguity"));
}
if (tok.getType() == TokenType::LeftParen) if (tok.getType() == TokenType::LeftParen)
{ {
lhs = __parseCall(lhs); lhs = __parseCall(lhs);
continue; continue;
} }
// else if (tok.getType() == TokenType::LeftBrace) { lhs = __parseInitExpr(lhs); }
/*
since Fig v0.4.2, use new struct{};
if (tok.getType() == TokenType::LeftBrace) if a == A{}
{ is A{} struct init?
lhs = __parseInitExpr(lhs); or A a variable, {} is the body?
continue;
} fuck.
*/
// member access: a.b // member access: a.b
if (tok.getType() == TokenType::Dot) if (tok.getType() == TokenType::Dot)
@@ -1189,15 +1129,17 @@ namespace Fig
lhs = makeAst<Ast::IndexExprAst>(lhs, indexExpr); lhs = makeAst<Ast::IndexExprAst>(lhs, indexExpr);
continue; continue;
} }
// ternary // ternary
if (tok.getType() == TokenType::Question) if (tok.getType() == TokenType::Question)
{ {
auto [lbp, rbp] = getBindingPower(Ast::Operator::TernaryCond);
if (bp >= lbp) break;
next(); // consume ? next(); // consume ?
Ast::Expression trueExpr = parseExpression(0, TokenType::Colon, stop2); Ast::Expression trueExpr = parseExpression(0, TokenType::Colon);
expect(TokenType::Colon); expectConsume(TokenType::Colon);
next(); // consume :
Ast::Expression falseExpr = parseExpression(0, TokenType::Semicolon, stop2); Ast::Expression falseExpr = parseExpression(0);
lhs = makeAst<Ast::TernaryExprAst>(lhs, trueExpr, falseExpr); lhs = makeAst<Ast::TernaryExprAst>(lhs, trueExpr, falseExpr);
continue; continue;
} }
@@ -1219,10 +1161,7 @@ namespace Fig
{ {
output.clear(); output.clear();
Token tok = currentToken(); Token tok = currentToken();
if (tok == EOFTok) if (tok == EOFTok) { return output; }
{
return output;
}
while (!isEOF()) while (!isEOF())
{ {

View File

@@ -1,13 +1,12 @@
#pragma once #pragma once
#include "Ast/astBase.hpp" #include <Ast/astBase.hpp>
#include <Ast/ast.hpp> #include <Ast/ast.hpp>
#include <Lexer/lexer.hpp> #include <Lexer/lexer.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Error/error.hpp> #include <Error/error.hpp>
#include <memory> #include <memory>
#include <print>
#include <source_location> #include <source_location>
#include <unordered_map> #include <unordered_map>
@@ -248,7 +247,7 @@ namespace Fig
[[nodiscard]] SemicolonDisabler disableSemicolon() { return SemicolonDisabler(this); } [[nodiscard]] SemicolonDisabler disableSemicolon() { return SemicolonDisabler(this); }
void expectSemicolon() void expectSemicolon(std::source_location loc = std::source_location::current())
{ {
// if need semicolon and stream has `;`, consume it. if not need semicolon, do nothing // if need semicolon and stream has `;`, consume it. if not need semicolon, do nothing
@@ -263,18 +262,18 @@ namespace Fig
} }
// normal semicolon check // normal semicolon check
expectConsume(TokenType::Semicolon); expectConsume(TokenType::Semicolon, loc);
} }
void expectConsume(TokenType type, FString expected) void expectConsume(TokenType type, FString expected, std::source_location loc = std::source_location::current())
{ {
expect(type, expected); expect(type, expected, loc);
next(); next();
} }
void expectConsume(TokenType type) void expectConsume(TokenType type, std::source_location loc = std::source_location::current())
{ {
expect(type); expect(type, loc);
next(); next();
} }
@@ -314,6 +313,8 @@ namespace Fig
Ast::MapExpr __parseMapExpr(); // entry: current is `{` Ast::MapExpr __parseMapExpr(); // entry: current is `{`
Ast::InitExpr __parseInitExpr(Ast::Expression); // entry: current is `{`, ahead is struct type exp. Ast::InitExpr __parseInitExpr(Ast::Expression); // entry: current is `{`, ahead is struct type exp.
Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(` Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(`
Ast::FunctionLiteralExpr __parseFunctionLiteralExpr(); // entry: current is Token::LParen after Token::Function Ast::FunctionLiteralExpr __parseFunctionLiteralExpr(); // entry: current is Token::LParen after Token::Function

View File

@@ -31,6 +31,7 @@ namespace Fig
For, // for For, // for
If, // if If, // if
Else, // else Else, // else
New, // new
Struct, // struct Struct, // struct
Interface, // interface Interface, // interface
Implement, // impl Implement, // impl

View File

@@ -111,7 +111,7 @@ private:
printFString(node->name, 0); printFString(node->name, 0);
printIndent(indent + 2); printIndent(indent + 2);
std::cout << "Type: "; std::cout << "Type: ";
printFString(node->typeName, 0); printFString(node->declaredType->toString(), 0);
if (node->expr) if (node->expr)
{ {
printIndent(indent + 2); printIndent(indent + 2);