Compare commits
1 Commits
v0.4.1-alp
...
v0.4.2-alp
| Author | SHA1 | Date | |
|---|---|---|---|
| ca4ae143b4 |
@@ -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
|
||||||
|
*/
|
||||||
65
ExampleCodes/SpeedTest/fibBenchmark.fig
Normal file
65
ExampleCodes/SpeedTest/fibBenchmark.fig
Normal 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);
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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" }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
40
src/Evaluator/Value/IntPool.hpp
Normal file
40
src/Evaluator/Value/IntPool.hpp
Normal 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
|
||||||
@@ -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))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|||||||
10
src/Evaluator/Value/value_forward.hpp
Normal file
10
src/Evaluator/Value/value_forward.hpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class Object;
|
||||||
|
|
||||||
|
using ObjectPtr = std::shared_ptr<Object>;
|
||||||
|
}; // namespace Fig
|
||||||
@@ -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)));
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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},
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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()};
|
||||||
}
|
}
|
||||||
@@ -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))},
|
||||||
|
|||||||
@@ -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())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user