修复了函数调用时求值类型使用的作用域错误的问题。结构体中现在可以使用自己
This commit is contained in:
29
ExampleCodes/use_std_time.fig
Normal file
29
ExampleCodes/use_std_time.fig
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import std.io;
|
||||||
|
import std.test;
|
||||||
|
|
||||||
|
var ascii_string_test := new test.Test{
|
||||||
|
"ascii_string_test",
|
||||||
|
func () => io.println("Hello," + " world!"),
|
||||||
|
2
|
||||||
|
};
|
||||||
|
|
||||||
|
var unicode_string_test := new test.Test{
|
||||||
|
"unicode_string_test",
|
||||||
|
func () => io.println("你好," + " 世界!"),
|
||||||
|
2
|
||||||
|
};
|
||||||
|
|
||||||
|
var unicode_string_inserting_test := new test.Test{
|
||||||
|
"unicode_string_inserting_test",
|
||||||
|
func (){
|
||||||
|
var str := "我是你的粑粑";
|
||||||
|
str.insert(1, "不");
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
"我不是你的粑粑"
|
||||||
|
};
|
||||||
|
|
||||||
|
var tests := [ascii_string_test, unicode_string_test, unicode_string_inserting_test];
|
||||||
|
|
||||||
|
var tester := new test.Tester{tests};
|
||||||
|
tester.TestAll();
|
||||||
@@ -36,25 +36,13 @@ namespace Fig
|
|||||||
std::unordered_map<Ast::Operator, UnaryOpFn> unOpRec;
|
std::unordered_map<Ast::Operator, UnaryOpFn> unOpRec;
|
||||||
std::unordered_map<Ast::Operator, BinaryOpFn> binOpRec;
|
std::unordered_map<Ast::Operator, BinaryOpFn> binOpRec;
|
||||||
|
|
||||||
bool hasUnaryOp(Ast::Operator op) const
|
bool hasUnaryOp(Ast::Operator op) const { return unOpRec.contains(op); }
|
||||||
{
|
|
||||||
return unOpRec.contains(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasBinaryOp(Ast::Operator op) const
|
bool hasBinaryOp(Ast::Operator op) const { return binOpRec.contains(op); }
|
||||||
{
|
|
||||||
return binOpRec.contains(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
const UnaryOpFn &getUnaryOpFn(Ast::Operator op) const
|
const UnaryOpFn &getUnaryOpFn(Ast::Operator op) const { return unOpRec.at(op); }
|
||||||
{
|
|
||||||
return unOpRec.at(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
const BinaryOpFn &getBinaryOpFn(Ast::Operator op) const
|
const BinaryOpFn &getBinaryOpFn(Ast::Operator op) const { return binOpRec.at(op); }
|
||||||
{
|
|
||||||
return binOpRec.at(op);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Context : public std::enable_shared_from_this<Context>
|
class Context : public std::enable_shared_from_this<Context>
|
||||||
@@ -69,6 +57,7 @@ namespace Fig
|
|||||||
// implRegistry <Struct, ordered list of ImplRecord>
|
// implRegistry <Struct, ordered list of ImplRecord>
|
||||||
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> implRegistry;
|
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> implRegistry;
|
||||||
std::unordered_map<TypeInfo, OperationRecord, TypeInfoHash> opRegistry;
|
std::unordered_map<TypeInfo, OperationRecord, TypeInfoHash> opRegistry;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ContextPtr parent;
|
ContextPtr parent;
|
||||||
|
|
||||||
@@ -394,6 +383,8 @@ namespace Fig
|
|||||||
throw ""; // ignore warning
|
throw ""; // ignore warning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_map<TypeInfo, OperationRecord, TypeInfoHash> &getOpRegistry() { return opRegistry; }
|
||||||
|
|
||||||
bool hasOperatorImplemented(const TypeInfo &type, Ast::Operator op, bool isUnary = false) const
|
bool hasOperatorImplemented(const TypeInfo &type, Ast::Operator op, bool isUnary = false) const
|
||||||
{
|
{
|
||||||
auto it = opRegistry.find(type);
|
auto it = opRegistry.find(type);
|
||||||
|
|||||||
@@ -96,11 +96,11 @@ namespace Fig
|
|||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < fnParas.posParas.size(); i++)
|
for (i = 0; i < fnParas.posParas.size(); i++)
|
||||||
{
|
{
|
||||||
const TypeInfo &expectedType = actualType(eval(fnParas.posParas[i].second, ctx)); // look up type info, if exists a type
|
const TypeInfo &expectedType = actualType(eval(fnParas.posParas[i].second, fn.closureContext)); // look up type info, if exists a type
|
||||||
// with the name, use it, else throw
|
// with the name, use it, else throw
|
||||||
ObjectPtr argVal = eval(fnArgs.argv[i], ctx);
|
ObjectPtr argVal = eval(fnArgs.argv[i], ctx);
|
||||||
TypeInfo actualType = argVal->getTypeInfo();
|
TypeInfo actualType = argVal->getTypeInfo();
|
||||||
if (!isTypeMatch(expectedType, argVal, ctx))
|
if (!isTypeMatch(expectedType, argVal, fn.closureContext))
|
||||||
{
|
{
|
||||||
throw EvaluatorError(u8"ArgumentTypeMismatchError",
|
throw EvaluatorError(u8"ArgumentTypeMismatchError",
|
||||||
std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'",
|
std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'",
|
||||||
@@ -116,10 +116,10 @@ namespace Fig
|
|||||||
for (; i < fnArgs.getLength(); i++)
|
for (; i < fnArgs.getLength(); i++)
|
||||||
{
|
{
|
||||||
size_t defParamIndex = i - fnParas.posParas.size();
|
size_t defParamIndex = i - fnParas.posParas.size();
|
||||||
const TypeInfo &expectedType = actualType(eval(fnParas.defParas[defParamIndex].second.first, ctx));
|
const TypeInfo &expectedType = actualType(eval(fnParas.defParas[defParamIndex].second.first, fn.closureContext));
|
||||||
|
|
||||||
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx);
|
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, fn.closureContext);
|
||||||
if (!isTypeMatch(expectedType, defaultVal, ctx))
|
if (!isTypeMatch(expectedType, defaultVal, fn.closureContext))
|
||||||
{
|
{
|
||||||
throw EvaluatorError(
|
throw EvaluatorError(
|
||||||
u8"DefaultParameterTypeError",
|
u8"DefaultParameterTypeError",
|
||||||
@@ -134,7 +134,7 @@ namespace Fig
|
|||||||
|
|
||||||
ObjectPtr argVal = eval(fnArgs.argv[i], ctx);
|
ObjectPtr argVal = eval(fnArgs.argv[i], ctx);
|
||||||
TypeInfo actualType = argVal->getTypeInfo();
|
TypeInfo actualType = argVal->getTypeInfo();
|
||||||
if (!isTypeMatch(expectedType, argVal, ctx))
|
if (!isTypeMatch(expectedType, argVal, fn.closureContext))
|
||||||
{
|
{
|
||||||
throw EvaluatorError(u8"ArgumentTypeMismatchError",
|
throw EvaluatorError(u8"ArgumentTypeMismatchError",
|
||||||
std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'",
|
std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'",
|
||||||
@@ -150,7 +150,7 @@ namespace Fig
|
|||||||
for (; i < fnParas.size(); i++)
|
for (; i < fnParas.size(); i++)
|
||||||
{
|
{
|
||||||
size_t defParamIndex = i - fnParas.posParas.size();
|
size_t defParamIndex = i - fnParas.posParas.size();
|
||||||
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx);
|
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, fn.closureContext);
|
||||||
evaluatedArgs.argv.push_back(defaultVal);
|
evaluatedArgs.argv.push_back(defaultVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,13 +162,13 @@ namespace Fig
|
|||||||
if (j < fnParas.posParas.size())
|
if (j < fnParas.posParas.size())
|
||||||
{
|
{
|
||||||
paramName = fnParas.posParas[j].first;
|
paramName = fnParas.posParas[j].first;
|
||||||
paramType = actualType(eval(fnParas.posParas[j].second, ctx));
|
paramType = actualType(eval(fnParas.posParas[j].second, fn.closureContext));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
size_t defParamIndex = j - fnParas.posParas.size();
|
size_t defParamIndex = j - fnParas.posParas.size();
|
||||||
paramName = fnParas.defParas[defParamIndex].first;
|
paramName = fnParas.defParas[defParamIndex].first;
|
||||||
paramType = actualType(eval(fnParas.defParas[defParamIndex].second.first, ctx));
|
paramType = actualType(eval(fnParas.defParas[defParamIndex].second.first, fn.closureContext));
|
||||||
}
|
}
|
||||||
AccessModifier argAm = AccessModifier::Normal;
|
AccessModifier argAm = AccessModifier::Normal;
|
||||||
newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]);
|
newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]);
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ namespace Fig
|
|||||||
};
|
};
|
||||||
|
|
||||||
ContextPtr instanceCtx =
|
ContextPtr instanceCtx =
|
||||||
std::make_shared<Context>(FString(std::format("<StructInstance {}>", structName.toBasicString())), ctx);
|
std::make_shared<Context>(FString(std::format("<StructInstance {}>", structName.toBasicString())), defContext);
|
||||||
/*
|
/*
|
||||||
3 ways of calling constructor
|
3 ways of calling constructor
|
||||||
.1 Person {"Fig", 1, "IDK"};
|
.1 Person {"Fig", 1, "IDK"};
|
||||||
|
|||||||
@@ -8,6 +8,35 @@ namespace Fig
|
|||||||
LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx)
|
LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx)
|
||||||
{
|
{
|
||||||
const FString &name = var->name;
|
const FString &name = var->name;
|
||||||
|
|
||||||
|
// 调试信息
|
||||||
|
// std::cerr << "=== DEBUG evalVarExpr ===" << std::endl;
|
||||||
|
// std::cerr << "Looking for: " << name.toBasicString() << std::endl;
|
||||||
|
// std::cerr << "Context: " << ctx->getScopeName().toBasicString() << std::endl;
|
||||||
|
|
||||||
|
// // 打印上下文
|
||||||
|
// ContextPtr current = ctx;
|
||||||
|
// int depth = 0;
|
||||||
|
// while (current)
|
||||||
|
// {
|
||||||
|
// std::cerr << " [" << depth << "] " << current->getScopeName().toBasicString();
|
||||||
|
// if (current->containsInThisScope(name))
|
||||||
|
// {
|
||||||
|
// std::cerr << " -> FOUND HERE!" << std::endl;
|
||||||
|
// auto slot = current->get(name);
|
||||||
|
// std::cerr << " Type: " << slot->declaredType.toString().toBasicString() << std::endl;
|
||||||
|
// std::cerr << " Value: " << (slot->value ? slot->value->toString().toBasicString() : "null")
|
||||||
|
// << std::endl;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// std::cerr << " -> not found" << std::endl;
|
||||||
|
// }
|
||||||
|
// current = current->parent;
|
||||||
|
// depth++;
|
||||||
|
// }
|
||||||
|
// end
|
||||||
|
|
||||||
if (!ctx->contains(name)) { throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); }
|
if (!ctx->contains(name)) { throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); }
|
||||||
return LvObject(ctx->get(name), ctx);
|
return LvObject(ctx->get(name), ctx);
|
||||||
}
|
}
|
||||||
@@ -18,7 +47,26 @@ namespace Fig
|
|||||||
const FString &member = me->member;
|
const FString &member = me->member;
|
||||||
if (baseVal->getTypeInfo() == ValueType::Module)
|
if (baseVal->getTypeInfo() == ValueType::Module)
|
||||||
{
|
{
|
||||||
|
// std::cerr << "=== DEBUG evalMemberExpr (Module) ===" << std::endl;
|
||||||
|
// std::cerr << "Module object: " << baseVal->toString().toBasicString() << std::endl;
|
||||||
|
|
||||||
const Module &mod = baseVal->as<Module>();
|
const Module &mod = baseVal->as<Module>();
|
||||||
|
// std::cerr << "Module context: " << mod.ctx->getScopeName().toBasicString() << std::endl;
|
||||||
|
// std::cerr << "Looking for member: " << member.toBasicString() << std::endl;
|
||||||
|
|
||||||
|
// if (mod.ctx->contains(member))
|
||||||
|
// {
|
||||||
|
// std::cerr << "Found in module context!" << std::endl;
|
||||||
|
// if (mod.ctx->isVariablePublic(member)) { std::cerr << "And it's public!" << std::endl; }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// std::cerr << "But it's NOT public!" << std::endl;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// std::cerr << "NOT found in module context!" << std::endl;
|
||||||
|
// }
|
||||||
if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member))
|
if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member))
|
||||||
{
|
{
|
||||||
return LvObject(mod.ctx->get(member), ctx);
|
return LvObject(mod.ctx->get(member), ctx);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "Ast/functionParameters.hpp"
|
#include "Ast/functionParameters.hpp"
|
||||||
#include "Core/fig_string.hpp"
|
#include "Core/fig_string.hpp"
|
||||||
#include "Evaluator/Core/StatementResult.hpp"
|
#include "Evaluator/Core/StatementResult.hpp"
|
||||||
|
#include "Evaluator/Value/Type.hpp"
|
||||||
#include "Evaluator/Value/value.hpp"
|
#include "Evaluator/Value/value.hpp"
|
||||||
#include <Evaluator/Value/LvObject.hpp>
|
#include <Evaluator/Value/LvObject.hpp>
|
||||||
#include <Evaluator/evaluator.hpp>
|
#include <Evaluator/evaluator.hpp>
|
||||||
@@ -103,6 +104,20 @@ namespace Fig
|
|||||||
std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()),
|
std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()),
|
||||||
stDef);
|
stDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeInfo type(stDef->name, true); // register type name
|
||||||
|
ContextPtr defContext = std::make_shared<Context>(FString(std::format("<Struct {} at {}:{}>",
|
||||||
|
stDef->name.toBasicString(),
|
||||||
|
stDef->getAAI().line,
|
||||||
|
stDef->getAAI().column)),
|
||||||
|
ctx);
|
||||||
|
ObjectPtr structTypeObj = std::make_shared<Object>(StructType(type, defContext, {}));
|
||||||
|
|
||||||
|
AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||||
|
|
||||||
|
ctx->def(stDef->name, ValueType::StructType, am, structTypeObj); // predef
|
||||||
|
defContext->def(stDef->name, ValueType::StructType, AccessModifier::Const, structTypeObj); // predef to itself, always const
|
||||||
|
|
||||||
std::vector<Field> fields;
|
std::vector<Field> fields;
|
||||||
std::vector<FString> _fieldNames;
|
std::vector<FString> _fieldNames;
|
||||||
for (Ast::StructDefField field : stDef->fields)
|
for (Ast::StructDefField field : stDef->fields)
|
||||||
@@ -124,11 +139,8 @@ namespace Fig
|
|||||||
|
|
||||||
fields.push_back(Field(field.am, field.fieldName, fieldType, field.defaultValueExpr));
|
fields.push_back(Field(field.am, field.fieldName, fieldType, field.defaultValueExpr));
|
||||||
}
|
}
|
||||||
ContextPtr defContext = std::make_shared<Context>(FString(std::format("<Struct {} at {}:{}>",
|
structTypeObj->as<StructType>().fields = fields;
|
||||||
stDef->name.toBasicString(),
|
|
||||||
stDef->getAAI().line,
|
|
||||||
stDef->getAAI().column)),
|
|
||||||
ctx);
|
|
||||||
const Ast::BlockStatement &body = stDef->body;
|
const Ast::BlockStatement &body = stDef->body;
|
||||||
for (auto &st : body->stmts)
|
for (auto &st : body->stmts)
|
||||||
{
|
{
|
||||||
@@ -141,13 +153,6 @@ namespace Fig
|
|||||||
}
|
}
|
||||||
evalStatement(st, defContext); // function def st
|
evalStatement(st, defContext); // function def st
|
||||||
}
|
}
|
||||||
|
|
||||||
AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
|
||||||
TypeInfo type(stDef->name, true); // register type name
|
|
||||||
ctx->def(stDef->name,
|
|
||||||
ValueType::StructType,
|
|
||||||
am,
|
|
||||||
std::make_shared<Object>(StructType(type, defContext, fields)));
|
|
||||||
return StatementResult::normal();
|
return StatementResult::normal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
#include "Ast/astBase.hpp"
|
||||||
|
#include "Core/fig_string.hpp"
|
||||||
|
#include "Evaluator/Core/StatementResult.hpp"
|
||||||
|
#include "Evaluator/Value/value.hpp"
|
||||||
#include <Utils/utils.hpp>
|
#include <Utils/utils.hpp>
|
||||||
#include <Parser/parser.hpp>
|
#include <Parser/parser.hpp>
|
||||||
|
|
||||||
@@ -7,6 +11,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#ifndef SourceInfo
|
#ifndef SourceInfo
|
||||||
#define SourceInfo(ptr) (ptr->sourcePath), (ptr->sourceLines)
|
#define SourceInfo(ptr) (ptr->sourcePath), (ptr->sourceLines)
|
||||||
@@ -34,20 +39,36 @@ namespace Fig
|
|||||||
|
|
||||||
ContextPtr Evaluator::loadModule(const std::filesystem::path &path)
|
ContextPtr Evaluator::loadModule(const std::filesystem::path &path)
|
||||||
{
|
{
|
||||||
|
static std::unordered_map<FString, std::pair<std::vector<FString>, std::vector<Ast::AstBase>>> mod_ast_cache{};
|
||||||
|
|
||||||
FString modSourcePath(path.string());
|
FString modSourcePath(path.string());
|
||||||
|
|
||||||
std::ifstream file(path);
|
std::ifstream file(path);
|
||||||
assert(file.is_open());
|
assert(file.is_open());
|
||||||
|
|
||||||
|
std::vector<Ast::AstBase> asts;
|
||||||
|
|
||||||
|
std::vector<FString> modSourceLines;
|
||||||
|
|
||||||
|
if (mod_ast_cache.contains(modSourcePath))
|
||||||
|
{
|
||||||
|
auto &[_sl, _asts] = mod_ast_cache[modSourcePath];
|
||||||
|
modSourceLines = _sl;
|
||||||
|
asts = _asts;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
std::string source((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
std::string source((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
std::vector<FString> modSourceLines = Utils::splitSource(FString(source));
|
modSourceLines = Utils::splitSource(FString(source));
|
||||||
|
|
||||||
Lexer lexer((FString(source)), modSourcePath, modSourceLines);
|
Lexer lexer((FString(source)), modSourcePath, modSourceLines);
|
||||||
Parser parser(lexer, modSourcePath, modSourceLines);
|
Parser parser(lexer, modSourcePath, modSourceLines);
|
||||||
std::vector<Ast::AstBase> asts;
|
|
||||||
|
|
||||||
asts = parser.parseAll();
|
asts = parser.parseAll();
|
||||||
|
mod_ast_cache[modSourcePath] = {modSourceLines, asts};
|
||||||
|
}
|
||||||
|
|
||||||
Evaluator evaluator;
|
Evaluator evaluator;
|
||||||
evaluator.SetSourcePath(modSourcePath);
|
evaluator.SetSourcePath(modSourcePath);
|
||||||
@@ -77,6 +98,21 @@ namespace Fig
|
|||||||
|
|
||||||
ctx->getImplRegistry().insert(modCtx->getImplRegistry().begin(), modCtx->getImplRegistry().end()); // load impl
|
ctx->getImplRegistry().insert(modCtx->getImplRegistry().begin(), modCtx->getImplRegistry().end()); // load impl
|
||||||
|
|
||||||
|
for (auto &[type, opRecord] : modCtx->getOpRegistry())
|
||||||
|
{
|
||||||
|
if (ctx->getOpRegistry().contains(type))
|
||||||
|
{
|
||||||
|
throw EvaluatorError(
|
||||||
|
u8"DuplicateOperationOverload",
|
||||||
|
std::format("Module `{}` and current context `{}` have conflict operation overload for `{}` object",
|
||||||
|
modCtx->getScopeName().toBasicString(),
|
||||||
|
ctx->getScopeName().toBasicString(),
|
||||||
|
type.toString().toBasicString()),
|
||||||
|
i);
|
||||||
|
}
|
||||||
|
ctx->getOpRegistry()[type] = opRecord;
|
||||||
|
}
|
||||||
|
|
||||||
// std::cerr << modName.toBasicString() << '\n'; DEBUG
|
// std::cerr << modName.toBasicString() << '\n'; DEBUG
|
||||||
|
|
||||||
if (ctx->containsInThisScope(modName))
|
if (ctx->containsInThisScope(modName))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Official Module `std.tester`
|
Official Module `std.test`
|
||||||
Library/std/tester/tester.fig
|
Library/std/test/test.fig
|
||||||
|
|
||||||
Copyright © 2025 PuqiAR. All rights reserved.
|
Copyright © 2025 PuqiAR. All rights reserved.
|
||||||
*/
|
*/
|
||||||
@@ -43,6 +43,17 @@ public struct Time
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// TODO: support `-` operator when Fig supports overload
|
// TODO: support `-` operator when Fig supports overload
|
||||||
|
// supported now! 26-2-2. PuqiAR
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operation for Time
|
||||||
|
{
|
||||||
|
Sub(l: Time, r: Time)
|
||||||
|
{
|
||||||
|
return new Time{
|
||||||
|
l.since(r)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func now() -> Time
|
public func now() -> Time
|
||||||
|
|||||||
Reference in New Issue
Block a user