Skip to content

Commit

Permalink
support construct function by cpp user
Browse files Browse the repository at this point in the history
  • Loading branch information
chloro-pn committed Jul 16, 2024
1 parent a05b51f commit c4f443d
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 1 deletion.
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@
* [done] 内置函数放置在wamon包名称中
* 支持using语句以省略外部包的命名空间前缀 - 重构ParseIdentifier,默认情况下不填充本包名称, 然后在语义分析阶段利用using信息依次在本包和using包中查找,找到则重写id。
* [done] 标准库以包的形式提供
* [done] 支持简化的函数调用形式@
* [done] 支持简化的函数调用形式@
* 重构符号查找规则
36 changes: 36 additions & 0 deletions include/wamon/executable_block_stmt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <wamon/interpreter.h>
#include <wamon/variable.h>
#include <wamon/variable_void.h>

#include <functional>

#include "wamon/ast.h"

namespace wamon {

class ExecutableBlockStmt : public BlockStmt {
public:
using executable_type = std::function<std::shared_ptr<Variable>(Interpreter&)>;

ExecutableBlockStmt() = default;

std::string GetStmtName() override { return "executable_block_stmt"; }

ExecuteResult Execute(Interpreter& ip) override {
if (executable_) {
return ExecuteResult::Return(executable_(ip));
}
return ExecuteResult::Return(GetVoidVariable());
}

void SetExecutable(executable_type&& e) { executable_ = std::move(e); }

void SetExecutable(const executable_type& e) { executable_ = e; }

private:
executable_type executable_;
};

} // namespace wamon
2 changes: 2 additions & 0 deletions include/wamon/function_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class FunctionDef {

bool IsDeclaration() const { return block_stmt_ == nullptr; }

bool IsExecutableBlockStmt() const;

private:
std::string name_;
std::vector<ParameterListItem> param_list_;
Expand Down
5 changes: 5 additions & 0 deletions src/function_def.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#include "wamon/function_def.h"

#include "wamon/ast.h"
#include "wamon/executable_block_stmt.h"

namespace wamon {

void FunctionDef::SetBlockStmt(std::unique_ptr<BlockStmt>&& block_stmt) { block_stmt_ = std::move(block_stmt); }

bool FunctionDef::IsExecutableBlockStmt() const {
return dynamic_cast<ExecutableBlockStmt*>(block_stmt_.get()) != nullptr;
}

} // namespace wamon
7 changes: 7 additions & 0 deletions src/type_checker.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include "wamon/type_checker.h"

#include <wamon/ast.h>

#include <cassert>
#include <cstddef>
#include <string>
#include <vector>

Expand Down Expand Up @@ -122,6 +125,10 @@ void TypeChecker::CheckFunctions() {
if (capture_ids.empty() == false) {
continue;
}
// 跳过用户实现BlockStmt的函数
if (each.second->IsExecutableBlockStmt()) {
continue;
}
// 首先进行上下文相关的检测、表达式类型检测、语句合法性检测
auto func_context = std::make_unique<Context>(each.second->name_);
static_analyzer_.RegisterFuncParamsToContext(each.second->param_list_, func_context.get());
Expand Down
77 changes: 77 additions & 0 deletions test/executable_block_stmt_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "wamon/executable_block_stmt.h"

#include <wamon/ast.h>
#include <wamon/package_unit.h>
#include <wamon/type_checker.h>
#include <wamon/variable_int.h>
#include <wamon/variable_string.h>

#include <memory>
#include <string>

#include "gtest/gtest.h"
#include "wamon/function_def.h"
#include "wamon/interpreter.h"
#include "wamon/parser.h"
#include "wamon/scanner.h"
#include "wamon/type.h"
#include "wamon/variable.h"

using namespace wamon;

std::shared_ptr<FunctionDef> generate_func_for_test() {
std::shared_ptr<FunctionDef> fd(new FunctionDef("my_fn"));
fd->AddParamList({
.name = "main$param_a",
.type = TypeFactory<int>::Get(),
.is_ref = false,
});

fd->AddParamList({
.name = "main$param_ref",
.type = TypeFactory<std::string>::Get(),
.is_ref = true,
});

fd->SetReturnType(TypeFactory<int>::Get());

auto executable_block = std::make_unique<ExecutableBlockStmt>();
executable_block->SetExecutable([](Interpreter& ip) -> std::shared_ptr<Variable> {
auto va = ip.FindVariableById("main$param_a");
auto vref = ip.FindVariableById("main$param_ref");
va = ip.CallFunctionByName("main$update_value_from_script", {va});
int ret = AsIntVariable(va)->GetValue();
AsStringVariable(vref)->SetValue("changed");
return std::make_shared<IntVariable>(ret * 2, Variable::ValueCategory::RValue, "");
});

fd->SetBlockStmt(std::move(executable_block));
return fd;
}

TEST(executable_block_stmt, basic) {
std::string script = R"(
package main;
func update_value_from_script(int a) -> int {
return a + 10;
}
)";

Scanner scan;
auto tokens = scan.Scan(script);
auto pu = Parse(tokens);
pu.AddFuncDef(generate_func_for_test());
pu = MergePackageUnits(std::move(pu));
auto va = std::make_shared<IntVariable>(1, Variable::ValueCategory::RValue, "");
auto vb = std::make_shared<StringVariable>("hello", Variable::ValueCategory::LValue, "");
TypeChecker tc(pu);
std::string reason;
bool succ = tc.CheckAll(reason);
EXPECT_EQ(succ, true) << reason;

Interpreter ip(pu);
auto ret = ip.CallFunctionByName("main$my_fn", {va, vb});
EXPECT_EQ(AsIntVariable((ret))->GetValue(), 22);
EXPECT_EQ(AsStringVariable(vb)->GetValue(), "changed");
}

0 comments on commit c4f443d

Please sign in to comment.