mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Unify Action and Scope stacks, and eliminate Frame (#880)
Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
Jon Meow
parent
44154c8663
commit
3bec7f8dc0
@@ -17,6 +17,8 @@
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
class FunctionDeclaration;
|
||||
|
||||
class Statement {
|
||||
public:
|
||||
enum class Kind {
|
||||
@@ -167,9 +169,24 @@ class Return : public Statement {
|
||||
auto expression() -> Expression& { return *expression_; }
|
||||
auto is_omitted_expression() const -> bool { return is_omitted_expression_; }
|
||||
|
||||
// The AST node representing the function body this statement returns from.
|
||||
// Can only be called after ResolveControlFlow has visited this node.
|
||||
//
|
||||
// Note that this function does not represent an edge in the tree
|
||||
// structure of the AST: the return value is not a child of this node,
|
||||
// but an ancestor.
|
||||
auto function() const -> const FunctionDeclaration& { return **function_; }
|
||||
|
||||
// Can only be called once, by ResolveControlFlow.
|
||||
void set_function(Nonnull<const FunctionDeclaration*> function) {
|
||||
CHECK(!function_.has_value());
|
||||
function_ = function;
|
||||
}
|
||||
|
||||
private:
|
||||
Nonnull<Expression*> expression_;
|
||||
bool is_omitted_expression_;
|
||||
std::optional<Nonnull<const FunctionDeclaration*>> function_;
|
||||
};
|
||||
|
||||
class Sequence : public Statement {
|
||||
@@ -244,6 +261,23 @@ class Break : public Statement {
|
||||
static auto classof(const Statement* stmt) -> bool {
|
||||
return stmt->kind() == Kind::Break;
|
||||
}
|
||||
|
||||
// The AST node representing the loop this statement breaks out of.
|
||||
// Can only be called after ResolveControlFlow has visited this node.
|
||||
//
|
||||
// Note that this function does not represent an edge in the tree
|
||||
// structure of the AST: the return value is not a child of this node,
|
||||
// but an ancestor.
|
||||
auto loop() const -> const Statement& { return **loop_; }
|
||||
|
||||
// Can only be called once, by ResolveControlFlow.
|
||||
void set_loop(Nonnull<const Statement*> loop) {
|
||||
CHECK(!loop_.has_value());
|
||||
loop_ = loop;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<Nonnull<const Statement*>> loop_;
|
||||
};
|
||||
|
||||
class Continue : public Statement {
|
||||
@@ -254,6 +288,23 @@ class Continue : public Statement {
|
||||
static auto classof(const Statement* stmt) -> bool {
|
||||
return stmt->kind() == Kind::Continue;
|
||||
}
|
||||
|
||||
// The AST node representing the loop this statement continues.
|
||||
// Can only be called after ResolveControlFlow has visited this node.
|
||||
//
|
||||
// Note that this function does not represent an edge in the tree
|
||||
// structure of the AST: the return value is not a child of this node,
|
||||
// but an ancestor.
|
||||
auto loop() const -> const Statement& { return **loop_; }
|
||||
|
||||
// Can only be called once, by ResolveControlFlow.
|
||||
void set_loop(Nonnull<const Statement*> loop) {
|
||||
CHECK(!loop_.has_value());
|
||||
loop_ = loop;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<Nonnull<const Statement*>> loop_;
|
||||
};
|
||||
|
||||
class Match : public Statement {
|
||||
|
||||
@@ -7,15 +7,13 @@ package(default_visibility = ["//executable_semantics:__pkg__"])
|
||||
# These currently have to be a single build rule because of a dependency cycle
|
||||
# in printing.
|
||||
cc_library(
|
||||
name = "action_frame_and_value",
|
||||
name = "action_and_value",
|
||||
srcs = [
|
||||
"action.cpp",
|
||||
"frame.cpp",
|
||||
"value.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"action.h",
|
||||
"frame.h",
|
||||
"value.h",
|
||||
],
|
||||
deps = [
|
||||
@@ -55,6 +53,7 @@ cc_library(
|
||||
hdrs = ["exec_program.h"],
|
||||
deps = [
|
||||
":interpreter",
|
||||
":resolve_control_flow",
|
||||
":type_checker",
|
||||
"//executable_semantics/ast",
|
||||
],
|
||||
@@ -74,7 +73,7 @@ cc_library(
|
||||
srcs = ["heap.cpp"],
|
||||
hdrs = ["heap.h"],
|
||||
deps = [
|
||||
":action_frame_and_value",
|
||||
":action_and_value",
|
||||
":address",
|
||||
"//common:ostream",
|
||||
"@llvm-project//llvm:Support",
|
||||
@@ -90,7 +89,7 @@ cc_library(
|
||||
"interpreter.h",
|
||||
],
|
||||
deps = [
|
||||
":action_frame_and_value",
|
||||
":action_and_value",
|
||||
":address",
|
||||
":heap",
|
||||
"//common:check",
|
||||
@@ -102,6 +101,19 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "resolve_control_flow",
|
||||
srcs = ["resolve_control_flow.cpp"],
|
||||
hdrs = ["resolve_control_flow.h"],
|
||||
deps = [
|
||||
"//common:check",
|
||||
"//executable_semantics/ast",
|
||||
"//executable_semantics/ast:declaration",
|
||||
"//executable_semantics/ast:statement",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "stack",
|
||||
hdrs = ["stack.h"],
|
||||
|
||||
@@ -35,6 +35,8 @@ void Action::Print(llvm::raw_ostream& out) const {
|
||||
case Action::Kind::StatementAction:
|
||||
cast<StatementAction>(*this).statement().PrintDepth(1, out);
|
||||
break;
|
||||
case Action::Kind::ScopeAction:
|
||||
out << "ScopeAction";
|
||||
}
|
||||
out << "<" << pos_ << ">";
|
||||
if (results_.size() > 0) {
|
||||
|
||||
@@ -11,12 +11,25 @@
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/ast/statement.h"
|
||||
#include "executable_semantics/interpreter/dictionary.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "executable_semantics/interpreter/value.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
using Env = Dictionary<std::string, Address>;
|
||||
|
||||
struct Scope {
|
||||
explicit Scope(Env values) : Scope(values, std::vector<std::string>()) {}
|
||||
Scope(Env values, std::vector<std::string> l)
|
||||
: values(values), locals(std::move(l)) {}
|
||||
|
||||
Env values;
|
||||
std::vector<std::string> locals;
|
||||
bool deallocated = false;
|
||||
};
|
||||
|
||||
class Action {
|
||||
public:
|
||||
enum class Kind {
|
||||
@@ -24,6 +37,7 @@ class Action {
|
||||
ExpressionAction,
|
||||
PatternAction,
|
||||
StatementAction,
|
||||
ScopeAction,
|
||||
};
|
||||
|
||||
Action(const Value&) = delete;
|
||||
@@ -32,10 +46,23 @@ class Action {
|
||||
void AddResult(Nonnull<const Value*> result) { results_.push_back(result); }
|
||||
|
||||
void Clear() {
|
||||
CHECK(!scope_.has_value());
|
||||
pos_ = 0;
|
||||
results_.clear();
|
||||
}
|
||||
|
||||
// Associates this action with a new scope, with initial state `scope`.
|
||||
// Values that are local to this scope will be deallocated when this
|
||||
// Action is completed or unwound. Can only be called once on a given
|
||||
// Action.
|
||||
void StartScope(Scope scope) {
|
||||
CHECK(!scope_.has_value());
|
||||
scope_ = std::move(scope);
|
||||
}
|
||||
|
||||
// Returns the scope associated with this Action, if any.
|
||||
auto scope() -> std::optional<Scope>& { return scope_; }
|
||||
|
||||
static void PrintList(const Stack<Nonnull<Action*>>& ls,
|
||||
llvm::raw_ostream& out);
|
||||
|
||||
@@ -69,10 +96,14 @@ class Action {
|
||||
private:
|
||||
int pos_ = 0;
|
||||
std::vector<Nonnull<const Value*>> results_;
|
||||
std::optional<Scope> scope_;
|
||||
|
||||
const Kind kind_;
|
||||
};
|
||||
|
||||
// An Action which implements evaluation of an Expression to produce an
|
||||
// lvalue. The result be expressed as a PointerValue which points to the
|
||||
// Expression's value.
|
||||
class LValAction : public Action {
|
||||
public:
|
||||
explicit LValAction(Nonnull<const Expression*> expression)
|
||||
@@ -82,12 +113,15 @@ class LValAction : public Action {
|
||||
return action->kind() == Kind::LValAction;
|
||||
}
|
||||
|
||||
// The Expression this Action evaluates.
|
||||
auto expression() const -> const Expression& { return *expression_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Expression*> expression_;
|
||||
};
|
||||
|
||||
// An Action which implements evaluation of an Expression to produce an
|
||||
// rvalue. The result is expressed as a Value.
|
||||
class ExpressionAction : public Action {
|
||||
public:
|
||||
explicit ExpressionAction(Nonnull<const Expression*> expression)
|
||||
@@ -97,12 +131,15 @@ class ExpressionAction : public Action {
|
||||
return action->kind() == Kind::ExpressionAction;
|
||||
}
|
||||
|
||||
// The Expression this Action evaluates.
|
||||
auto expression() const -> const Expression& { return *expression_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Expression*> expression_;
|
||||
};
|
||||
|
||||
// An Action which implements evaluation of a Pattern. The result is expressed
|
||||
// as a Value.
|
||||
class PatternAction : public Action {
|
||||
public:
|
||||
explicit PatternAction(Nonnull<const Pattern*> pattern)
|
||||
@@ -112,12 +149,15 @@ class PatternAction : public Action {
|
||||
return action->kind() == Kind::PatternAction;
|
||||
}
|
||||
|
||||
// The Pattern this Action evaluates.
|
||||
auto pattern() const -> const Pattern& { return *pattern_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Pattern*> pattern_;
|
||||
};
|
||||
|
||||
// An Action which implements execution of a Statement. Does not produce a
|
||||
// result.
|
||||
class StatementAction : public Action {
|
||||
public:
|
||||
explicit StatementAction(Nonnull<const Statement*> statement)
|
||||
@@ -127,12 +167,28 @@ class StatementAction : public Action {
|
||||
return action->kind() == Kind::StatementAction;
|
||||
}
|
||||
|
||||
// The Statement this Action executes.
|
||||
auto statement() const -> const Statement& { return *statement_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Statement*> statement_;
|
||||
};
|
||||
|
||||
// Action which does nothing except introduce a new scope into the action
|
||||
// stack. This is useful when a distinct scope doesn't otherwise have an
|
||||
// Action it can naturally be associated with. ScopeActions are not associated
|
||||
// with AST nodes.
|
||||
class ScopeAction : public Action {
|
||||
public:
|
||||
ScopeAction(Scope scope) : Action(Kind::ScopeAction) {
|
||||
StartScope(std::move(scope));
|
||||
}
|
||||
|
||||
static auto classof(const Action* action) -> bool {
|
||||
return action->kind() == Kind::ScopeAction;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_INTERPRETER_ACTION_H_
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/common/arena.h"
|
||||
#include "executable_semantics/interpreter/interpreter.h"
|
||||
#include "executable_semantics/interpreter/resolve_control_flow.h"
|
||||
#include "executable_semantics/interpreter/type_checker.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -42,6 +43,7 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
|
||||
}
|
||||
llvm::outs() << "********** type checking **********\n";
|
||||
}
|
||||
ResolveControlFlow(ast);
|
||||
TypeChecker type_checker(arena, trace);
|
||||
TypeChecker::TypeCheckContext p = type_checker.TopLevel(&ast.declarations);
|
||||
TypeEnv top = p.types;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
void Frame::Print(llvm::raw_ostream& out) const {
|
||||
out << name << "{";
|
||||
Action::PrintList(todo, out);
|
||||
out << "}";
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -1,66 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef EXECUTABLE_SEMANTICS_INTERPRETER_FRAME_H_
|
||||
#define EXECUTABLE_SEMANTICS_INTERPRETER_FRAME_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
#include "executable_semantics/interpreter/address.h"
|
||||
#include "executable_semantics/interpreter/dictionary.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
using Env = Dictionary<std::string, Address>;
|
||||
|
||||
struct Scope {
|
||||
explicit Scope(Env values) : Scope(values, std::vector<std::string>()) {}
|
||||
Scope(Env values, std::vector<std::string> l)
|
||||
: values(values), locals(std::move(l)) {}
|
||||
|
||||
Env values;
|
||||
std::vector<std::string> locals;
|
||||
};
|
||||
|
||||
// A frame represents either a function call or a delimited continuation.
|
||||
struct Frame {
|
||||
Frame(const Frame&) = delete;
|
||||
auto operator=(const Frame&) -> Frame& = delete;
|
||||
|
||||
Frame(std::string n, Stack<Nonnull<Scope*>> s, Stack<Nonnull<Action*>> c)
|
||||
: name(std::move(n)),
|
||||
scopes(std::move(s)),
|
||||
todo(std::move(c)),
|
||||
continuation() {}
|
||||
|
||||
void Print(llvm::raw_ostream& out) const;
|
||||
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
|
||||
|
||||
// The name of the function.
|
||||
std::string name;
|
||||
// If the frame represents a function call, the bottom scope
|
||||
// contains the parameter-argument bindings for this function
|
||||
// call. The rest of the scopes contain local variables defined by
|
||||
// blocks within the function. The scope at the top of the stack is
|
||||
// the current scope and its environment is the one used for looking
|
||||
// up the value associated with a variable.
|
||||
Stack<Nonnull<Scope*>> scopes;
|
||||
// The actions that need to be executed in the future of the
|
||||
// current function call. The top of the stack is the action
|
||||
// that is executed first.
|
||||
Stack<Nonnull<Action*>> todo;
|
||||
// If this frame is the bottom frame of a continuation, then it stores
|
||||
// the address of the continuation.
|
||||
std::optional<Address> continuation;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_INTERPRETER_FRAME_H_
|
||||
@@ -17,13 +17,12 @@
|
||||
#include "executable_semantics/common/arena.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
using llvm::cast;
|
||||
using llvm::dyn_cast;
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -43,11 +42,17 @@ void Interpreter::PrintEnv(Env values, llvm::raw_ostream& out) {
|
||||
// State Operations
|
||||
//
|
||||
|
||||
auto Interpreter::CurrentEnv() -> Env {
|
||||
Nonnull<Frame*> frame = stack_.Top();
|
||||
return frame->scopes.Top()->values;
|
||||
auto Interpreter::CurrentScope() -> Scope& {
|
||||
for (Nonnull<Action*> action : todo_) {
|
||||
if (action->scope().has_value()) {
|
||||
return *action->scope();
|
||||
}
|
||||
}
|
||||
FATAL() << "No current scope";
|
||||
}
|
||||
|
||||
auto Interpreter::CurrentEnv() -> Env { return CurrentScope().values; }
|
||||
|
||||
// Returns the given name from the environment, printing an error if not found.
|
||||
auto Interpreter::GetFromEnv(SourceLocation source_loc, const std::string& name)
|
||||
-> Address {
|
||||
@@ -61,11 +66,11 @@ auto Interpreter::GetFromEnv(SourceLocation source_loc, const std::string& name)
|
||||
void Interpreter::PrintState(llvm::raw_ostream& out) {
|
||||
out << "{\nstack: ";
|
||||
llvm::ListSeparator sep(" :: ");
|
||||
for (const auto& frame : stack_) {
|
||||
out << sep << *frame;
|
||||
for (Nonnull<const Action*> action : todo_) {
|
||||
out << sep << *action;
|
||||
}
|
||||
out << "\nheap: " << heap_;
|
||||
if (!stack_.IsEmpty() && !stack_.Top()->scopes.IsEmpty()) {
|
||||
if (!todo_.IsEmpty()) {
|
||||
out << "\nvalues: ";
|
||||
PrintEnv(CurrentEnv(), out);
|
||||
}
|
||||
@@ -176,19 +181,14 @@ void Interpreter::InitGlobals(llvm::ArrayRef<Nonnull<Declaration*>> fs) {
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::DeallocateScope(Nonnull<Scope*> scope) {
|
||||
for (const auto& l : scope->locals) {
|
||||
std::optional<Address> a = scope->values.Get(l);
|
||||
void Interpreter::DeallocateScope(Scope& scope) {
|
||||
CHECK(!scope.deallocated);
|
||||
for (const auto& l : scope.locals) {
|
||||
std::optional<Address> a = scope.values.Get(l);
|
||||
CHECK(a);
|
||||
heap_.Deallocate(*a);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::DeallocateLocals(Nonnull<Frame*> frame) {
|
||||
while (!frame->scopes.IsEmpty()) {
|
||||
DeallocateScope(frame->scopes.Top());
|
||||
frame->scopes.Pop();
|
||||
}
|
||||
scope.deallocated = true;
|
||||
}
|
||||
|
||||
auto Interpreter::CreateTuple(Nonnull<Action*> act,
|
||||
@@ -374,7 +374,7 @@ void Interpreter::PatternAssignment(Nonnull<const Value*> pat,
|
||||
}
|
||||
|
||||
auto Interpreter::StepLvalue() -> Transition {
|
||||
Nonnull<Action*> act = stack_.Top()->todo.Top();
|
||||
Nonnull<Action*> act = todo_.Top();
|
||||
const Expression& exp = cast<LValAction>(*act).expression();
|
||||
if (trace_) {
|
||||
llvm::outs() << "--- step lvalue " << exp << " (" << exp.source_loc()
|
||||
@@ -524,7 +524,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
}
|
||||
|
||||
auto Interpreter::StepExp() -> Transition {
|
||||
Nonnull<Action*> act = stack_.Top()->todo.Top();
|
||||
Nonnull<Action*> act = todo_.Top();
|
||||
const Expression& exp = cast<ExpressionAction>(*act).expression();
|
||||
if (trace_) {
|
||||
llvm::outs() << "--- step exp " << exp << " (" << exp.source_loc()
|
||||
@@ -659,6 +659,13 @@ auto Interpreter::StepExp() -> Transition {
|
||||
FATAL_RUNTIME_ERROR(exp.source_loc())
|
||||
<< "in call, expected a function, not " << *act->results()[0];
|
||||
}
|
||||
} else if (act->pos() == 3) {
|
||||
if (act->results().size() < 3) {
|
||||
// Control fell through without explicit return.
|
||||
return Done{TupleValue::Empty()};
|
||||
} else {
|
||||
return Done{act->results()[2]};
|
||||
}
|
||||
} else {
|
||||
FATAL() << "in handle_value with Call pos " << act->pos();
|
||||
}
|
||||
@@ -720,7 +727,7 @@ auto Interpreter::StepExp() -> Transition {
|
||||
}
|
||||
|
||||
auto Interpreter::StepPattern() -> Transition {
|
||||
Nonnull<Action*> act = stack_.Top()->todo.Top();
|
||||
Nonnull<Action*> act = todo_.Top();
|
||||
const Pattern& pattern = cast<PatternAction>(*act).pattern();
|
||||
if (trace_) {
|
||||
llvm::outs() << "--- step pattern " << pattern << " ("
|
||||
@@ -772,38 +779,13 @@ auto Interpreter::StepPattern() -> Transition {
|
||||
}
|
||||
}
|
||||
|
||||
static auto IsWhileAct(Nonnull<Action*> act) -> bool {
|
||||
switch (act->kind()) {
|
||||
case Action::Kind::StatementAction:
|
||||
switch (cast<StatementAction>(*act).statement().kind()) {
|
||||
case Statement::Kind::While:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static auto HasLocalScope(Nonnull<Action*> act) -> bool {
|
||||
switch (act->kind()) {
|
||||
case Action::Kind::StatementAction:
|
||||
switch (cast<StatementAction>(*act).statement().kind()) {
|
||||
case Statement::Kind::Block:
|
||||
case Statement::Kind::Match:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
static auto IsRunAction(Nonnull<Action*> action) -> bool {
|
||||
const auto* statement = dyn_cast<StatementAction>(action);
|
||||
return statement != nullptr && llvm::isa<Run>(statement->statement());
|
||||
}
|
||||
|
||||
auto Interpreter::StepStmt() -> Transition {
|
||||
Nonnull<Frame*> frame = stack_.Top();
|
||||
Nonnull<Action*> act = frame->todo.Top();
|
||||
Nonnull<Action*> act = todo_.Top();
|
||||
const Statement& stmt = cast<StatementAction>(*act).statement();
|
||||
if (trace_) {
|
||||
llvm::outs() << "--- step stmt ";
|
||||
@@ -816,13 +798,11 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
if (act->pos() == 0) {
|
||||
// { { (match (e) ...) :: C, E, F} :: S, H}
|
||||
// -> { { e :: (match ([]) ...) :: C, E, F} :: S, H}
|
||||
frame->scopes.Push(arena_->New<Scope>(CurrentEnv()));
|
||||
act->StartScope(Scope(CurrentEnv()));
|
||||
return Spawn{arena_->New<ExpressionAction>(&match_stmt.expression())};
|
||||
} else {
|
||||
int clause_num = act->pos() - 1;
|
||||
if (clause_num >= static_cast<int>(match_stmt.clauses().size())) {
|
||||
DeallocateScope(frame->scopes.Top());
|
||||
frame->scopes.Pop();
|
||||
return Done{};
|
||||
}
|
||||
auto c = match_stmt.clauses()[clause_num];
|
||||
@@ -835,8 +815,8 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
act->set_pos(match_stmt.clauses().size() + 1);
|
||||
|
||||
for (const auto& [name, value] : *matches) {
|
||||
frame->scopes.Top()->values.Set(name, value);
|
||||
frame->scopes.Top()->locals.push_back(name);
|
||||
act->scope()->values.Set(name, value);
|
||||
act->scope()->locals.push_back(name);
|
||||
}
|
||||
return Spawn{arena_->New<StatementAction>(&c.statement())};
|
||||
} else {
|
||||
@@ -868,40 +848,24 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
CHECK(act->pos() == 0);
|
||||
// { { break; :: ... :: (while (e) s) :: C, E, F} :: S, H}
|
||||
// -> { { C, E', F} :: S, H}
|
||||
auto it =
|
||||
std::find_if(frame->todo.begin(), frame->todo.end(), &IsWhileAct);
|
||||
if (it == frame->todo.end()) {
|
||||
FATAL_RUNTIME_ERROR(stmt.source_loc())
|
||||
<< "`break` not inside `while` statement";
|
||||
}
|
||||
++it;
|
||||
return UnwindTo{*it};
|
||||
return UnwindPast{&cast<Break>(stmt).loop()};
|
||||
}
|
||||
case Statement::Kind::Continue: {
|
||||
CHECK(act->pos() == 0);
|
||||
// { { continue; :: ... :: (while (e) s) :: C, E, F} :: S, H}
|
||||
// -> { { (while (e) s) :: C, E', F} :: S, H}
|
||||
auto it =
|
||||
std::find_if(frame->todo.begin(), frame->todo.end(), &IsWhileAct);
|
||||
if (it == frame->todo.end()) {
|
||||
FATAL_RUNTIME_ERROR(stmt.source_loc())
|
||||
<< "`continue` not inside `while` statement";
|
||||
}
|
||||
return UnwindTo{*it};
|
||||
return UnwindTo{&cast<Continue>(stmt).loop()};
|
||||
}
|
||||
case Statement::Kind::Block: {
|
||||
if (act->pos() == 0) {
|
||||
const auto& block = cast<Block>(stmt);
|
||||
const Block& block = cast<Block>(stmt);
|
||||
if (block.statement()) {
|
||||
frame->scopes.Push(arena_->New<Scope>(CurrentEnv()));
|
||||
act->StartScope(Scope(CurrentEnv()));
|
||||
return Spawn{arena_->New<StatementAction>(*block.statement())};
|
||||
} else {
|
||||
return Done{};
|
||||
}
|
||||
} else {
|
||||
Nonnull<Scope*> scope = frame->scopes.Top();
|
||||
DeallocateScope(scope);
|
||||
frame->scopes.Pop(1);
|
||||
return Done{};
|
||||
}
|
||||
}
|
||||
@@ -924,8 +888,9 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
<< stmt.source_loc()
|
||||
<< ": internal error in variable definition, match failed";
|
||||
for (const auto& [name, value] : *matches) {
|
||||
frame->scopes.Top()->values.Set(name, value);
|
||||
frame->scopes.Top()->locals.push_back(name);
|
||||
Scope& current_scope = CurrentScope();
|
||||
current_scope.values.Set(name, value);
|
||||
current_scope.locals.push_back(name);
|
||||
}
|
||||
return Done{};
|
||||
}
|
||||
@@ -994,7 +959,8 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
// -> { {v :: C', E', F'} :: S, H}
|
||||
// TODO(geoffromer): convert the result to the function's return type,
|
||||
// once #880 gives us a way to find that type.
|
||||
return UnwindFunctionCall{act->results()[0]};
|
||||
const FunctionDeclaration& function = cast<Return>(stmt).function();
|
||||
return UnwindPast{*function.body(), act->results()[0]};
|
||||
}
|
||||
case Statement::Kind::Sequence: {
|
||||
// { { (s1,s2) :: C, E, F} :: S, H}
|
||||
@@ -1015,64 +981,50 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
CHECK(act->pos() == 0);
|
||||
// Create a continuation object by creating a frame similar the
|
||||
// way one is created in a function call.
|
||||
auto scopes = Stack<Nonnull<Scope*>>(arena_->New<Scope>(CurrentEnv()));
|
||||
Stack<Nonnull<Action*>> todo;
|
||||
todo.Push(arena_->New<StatementAction>(
|
||||
arena_->New<Return>(arena_, stmt.source_loc())));
|
||||
todo.Push(arena_->New<StatementAction>(&cast<Continuation>(stmt).body()));
|
||||
auto continuation_stack = arena_->New<std::vector<Nonnull<Frame*>>>();
|
||||
auto continuation_frame =
|
||||
arena_->New<Frame>("__continuation", scopes, todo);
|
||||
continuation_stack->push_back(continuation_frame);
|
||||
auto continuation_stack = arena_->New<std::vector<Nonnull<Action*>>>();
|
||||
continuation_stack->push_back(
|
||||
arena_->New<StatementAction>(&cast<Continuation>(stmt).body()));
|
||||
continuation_stack->push_back(
|
||||
arena_->New<ScopeAction>(Scope(CurrentEnv())));
|
||||
Address continuation_address = heap_.AllocateValue(
|
||||
arena_->New<ContinuationValue>(continuation_stack));
|
||||
// Store the continuation's address in the frame.
|
||||
continuation_frame->continuation = continuation_address;
|
||||
// Bind the continuation object to the continuation variable
|
||||
frame->scopes.Top()->values.Set(
|
||||
CurrentScope().values.Set(
|
||||
cast<Continuation>(stmt).continuation_variable(),
|
||||
continuation_address);
|
||||
// Pop the continuation statement.
|
||||
frame->todo.Pop();
|
||||
return ManualTransition{};
|
||||
return Done{};
|
||||
}
|
||||
case Statement::Kind::Run:
|
||||
case Statement::Kind::Run: {
|
||||
auto& run = cast<Run>(stmt);
|
||||
if (act->pos() == 0) {
|
||||
// Evaluate the argument of the run statement.
|
||||
return Spawn{
|
||||
arena_->New<ExpressionAction>(&cast<Run>(stmt).argument())};
|
||||
} else {
|
||||
frame->todo.Pop(1);
|
||||
// Push an expression statement action to ignore the result
|
||||
// value from the continuation.
|
||||
auto ignore_result =
|
||||
arena_->New<StatementAction>(arena_->New<ExpressionStatement>(
|
||||
stmt.source_loc(),
|
||||
arena_->New<TupleLiteral>(stmt.source_loc())));
|
||||
frame->todo.Push(ignore_result);
|
||||
// Push the continuation onto the current stack_.
|
||||
Nonnull<const Value*> arg =
|
||||
Convert(act->results()[0], arena_->New<ContinuationType>());
|
||||
std::vector<Nonnull<Frame*>>& continuation_vector =
|
||||
cast<ContinuationValue>(*arg).stack();
|
||||
return Spawn{arena_->New<ExpressionAction>(&run.argument())};
|
||||
} else if (act->pos() == 1) {
|
||||
// Push the continuation onto the current stack.
|
||||
std::vector<Nonnull<Action*>>& continuation_vector =
|
||||
cast<const ContinuationValue>(*act->results()[0]).stack();
|
||||
while (!continuation_vector.empty()) {
|
||||
stack_.Push(continuation_vector.back());
|
||||
todo_.Push(continuation_vector.back());
|
||||
continuation_vector.pop_back();
|
||||
}
|
||||
act->set_pos(2);
|
||||
return ManualTransition{};
|
||||
} else {
|
||||
return Done{};
|
||||
}
|
||||
}
|
||||
case Statement::Kind::Await:
|
||||
CHECK(act->pos() == 0);
|
||||
// Pause the current continuation
|
||||
frame->todo.Pop();
|
||||
std::vector<Nonnull<Frame*>> paused;
|
||||
do {
|
||||
paused.push_back(stack_.Pop());
|
||||
} while (paused.back()->continuation == std::nullopt);
|
||||
// Update the continuation with the paused stack_.
|
||||
const auto& continuation = cast<ContinuationValue>(
|
||||
*heap_.Read(*paused.back()->continuation, stmt.source_loc()));
|
||||
todo_.Pop();
|
||||
std::vector<Nonnull<Action*>> paused;
|
||||
while (!IsRunAction(todo_.Top())) {
|
||||
paused.push_back(todo_.Pop());
|
||||
}
|
||||
const auto& continuation =
|
||||
cast<const ContinuationValue>(*todo_.Top()->results()[0]);
|
||||
CHECK(continuation.stack().empty());
|
||||
// Update the continuation with the paused stack.
|
||||
continuation.stack() = std::move(paused);
|
||||
return ManualTransition{};
|
||||
}
|
||||
@@ -1084,62 +1036,82 @@ class Interpreter::DoTransition {
|
||||
explicit DoTransition(Interpreter* interpreter) : interpreter(interpreter) {}
|
||||
|
||||
void operator()(const Done& done) {
|
||||
Nonnull<Frame*> frame = interpreter->stack_.Top();
|
||||
if (frame->todo.Top()->kind() != Action::Kind::StatementAction) {
|
||||
CHECK(done.result);
|
||||
frame->todo.Pop();
|
||||
if (frame->todo.IsEmpty()) {
|
||||
interpreter->program_value_ = *done.result;
|
||||
} else {
|
||||
frame->todo.Top()->AddResult(*done.result);
|
||||
}
|
||||
} else {
|
||||
CHECK(!done.result);
|
||||
frame->todo.Pop();
|
||||
Nonnull<Action*> act = interpreter->todo_.Pop();
|
||||
if (act->scope().has_value()) {
|
||||
interpreter->DeallocateScope(*act->scope());
|
||||
}
|
||||
switch (act->kind()) {
|
||||
case Action::Kind::ExpressionAction:
|
||||
case Action::Kind::LValAction:
|
||||
case Action::Kind::PatternAction:
|
||||
CHECK(done.result.has_value());
|
||||
interpreter->todo_.Top()->AddResult(*done.result);
|
||||
break;
|
||||
case Action::Kind::StatementAction:
|
||||
CHECK(!done.result.has_value());
|
||||
break;
|
||||
case Action::Kind::ScopeAction:
|
||||
if (done.result.has_value()) {
|
||||
interpreter->todo_.Top()->AddResult(*done.result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const Spawn& spawn) {
|
||||
Nonnull<Frame*> frame = interpreter->stack_.Top();
|
||||
Nonnull<Action*> action = frame->todo.Top();
|
||||
Nonnull<Action*> action = interpreter->todo_.Top();
|
||||
action->set_pos(action->pos() + 1);
|
||||
frame->todo.Push(spawn.child);
|
||||
interpreter->todo_.Push(spawn.child);
|
||||
}
|
||||
|
||||
void operator()(const Delegate& delegate) {
|
||||
Nonnull<Frame*> frame = interpreter->stack_.Top();
|
||||
frame->todo.Pop();
|
||||
frame->todo.Push(delegate.delegate);
|
||||
Nonnull<Action*> act = interpreter->todo_.Pop();
|
||||
if (act->scope().has_value()) {
|
||||
delegate.delegate->StartScope(*act->scope());
|
||||
}
|
||||
interpreter->todo_.Push(delegate.delegate);
|
||||
}
|
||||
|
||||
void operator()(const RunAgain&) {
|
||||
Nonnull<Action*> action = interpreter->stack_.Top()->todo.Top();
|
||||
Nonnull<Action*> action = interpreter->todo_.Top();
|
||||
action->set_pos(action->pos() + 1);
|
||||
}
|
||||
|
||||
void operator()(const UnwindTo& unwind_to) {
|
||||
Nonnull<Frame*> frame = interpreter->stack_.Top();
|
||||
while (frame->todo.Top() != unwind_to.new_top) {
|
||||
if (HasLocalScope(frame->todo.Top())) {
|
||||
interpreter->DeallocateScope(frame->scopes.Top());
|
||||
frame->scopes.Pop();
|
||||
while (true) {
|
||||
if (const auto* statement_action =
|
||||
dyn_cast<StatementAction>(interpreter->todo_.Top());
|
||||
statement_action != nullptr &&
|
||||
&statement_action->statement() == unwind_to.ast_node) {
|
||||
break;
|
||||
}
|
||||
Nonnull<Action*> action = interpreter->todo_.Pop();
|
||||
if (action->scope().has_value()) {
|
||||
interpreter->DeallocateScope(*action->scope());
|
||||
}
|
||||
frame->todo.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const UnwindFunctionCall& unwind) {
|
||||
interpreter->DeallocateLocals(interpreter->stack_.Top());
|
||||
interpreter->stack_.Pop();
|
||||
if (interpreter->stack_.Top()->todo.IsEmpty()) {
|
||||
interpreter->program_value_ = unwind.return_val;
|
||||
} else {
|
||||
interpreter->stack_.Top()->todo.Top()->AddResult(unwind.return_val);
|
||||
void operator()(const UnwindPast& unwind_past) {
|
||||
while (true) {
|
||||
Nonnull<Action*> action = interpreter->todo_.Pop();
|
||||
if (action->scope().has_value()) {
|
||||
interpreter->DeallocateScope(*action->scope());
|
||||
}
|
||||
if (const auto* statement_action = dyn_cast<StatementAction>(action);
|
||||
statement_action != nullptr &&
|
||||
&statement_action->statement() == unwind_past.ast_node) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unwind_past.result.has_value()) {
|
||||
interpreter->todo_.Top()->AddResult(*unwind_past.result);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const CallFunction& call) {
|
||||
interpreter->stack_.Top()->todo.Pop();
|
||||
Nonnull<Action*> action = interpreter->todo_.Top();
|
||||
action->set_pos(action->pos() + 1);
|
||||
Nonnull<const Value*> converted_args = interpreter->Convert(
|
||||
call.args, &call.function->param_pattern().static_type());
|
||||
std::optional<Env> matches =
|
||||
@@ -1148,20 +1120,16 @@ class Interpreter::DoTransition {
|
||||
CHECK(matches.has_value())
|
||||
<< "internal error in call_function, pattern match failed";
|
||||
// Create the new frame and push it on the stack
|
||||
Env values = interpreter->globals_;
|
||||
std::vector<std::string> params;
|
||||
Scope new_scope(interpreter->globals_);
|
||||
for (const auto& [name, value] : *matches) {
|
||||
values.Set(name, value);
|
||||
params.push_back(name);
|
||||
new_scope.values.Set(name, value);
|
||||
new_scope.locals.push_back(name);
|
||||
}
|
||||
auto scopes =
|
||||
Stack<Nonnull<Scope*>>(interpreter->arena_->New<Scope>(values, params));
|
||||
interpreter->todo_.Push(
|
||||
interpreter->arena_->New<ScopeAction>(std::move(new_scope)));
|
||||
CHECK(call.function->body()) << "Calling a function that's missing a body";
|
||||
auto todo = Stack<Nonnull<Action*>>(
|
||||
interpreter->todo_.Push(
|
||||
interpreter->arena_->New<StatementAction>(*call.function->body()));
|
||||
auto frame =
|
||||
interpreter->arena_->New<Frame>(call.function->name(), scopes, todo);
|
||||
interpreter->stack_.Push(frame);
|
||||
}
|
||||
|
||||
void operator()(const ManualTransition&) {}
|
||||
@@ -1172,14 +1140,7 @@ class Interpreter::DoTransition {
|
||||
|
||||
// State transition.
|
||||
void Interpreter::Step() {
|
||||
Nonnull<Frame*> frame = stack_.Top();
|
||||
if (frame->todo.IsEmpty()) {
|
||||
std::visit(DoTransition(this),
|
||||
Transition{UnwindFunctionCall{TupleValue::Empty()}});
|
||||
return;
|
||||
}
|
||||
|
||||
Nonnull<Action*> act = frame->todo.Top();
|
||||
Nonnull<Action*> act = todo_.Top();
|
||||
switch (act->kind()) {
|
||||
case Action::Kind::LValAction:
|
||||
std::visit(DoTransition(this), StepLvalue());
|
||||
@@ -1193,75 +1154,63 @@ void Interpreter::Step() {
|
||||
case Action::Kind::StatementAction:
|
||||
std::visit(DoTransition(this), StepStmt());
|
||||
break;
|
||||
case Action::Kind::ScopeAction:
|
||||
if (act->results().empty()) {
|
||||
std::visit(DoTransition(this), Transition{Done{}});
|
||||
} else {
|
||||
CHECK(act->results().size() == 1);
|
||||
std::visit(DoTransition(this), Transition{Done{act->results()[0]}});
|
||||
}
|
||||
} // switch
|
||||
}
|
||||
|
||||
auto Interpreter::ExecuteAction(Nonnull<Action*> action, Env values,
|
||||
bool trace_steps) -> Nonnull<const Value*> {
|
||||
todo_ = {};
|
||||
todo_.Push(arena_->New<ScopeAction>(Scope(values)));
|
||||
todo_.Push(action);
|
||||
|
||||
while (todo_.Count() > 1) {
|
||||
Step();
|
||||
if (trace_steps) {
|
||||
PrintState(llvm::outs());
|
||||
}
|
||||
}
|
||||
CHECK(todo_.Top()->results().size() == 1);
|
||||
return todo_.Top()->results()[0];
|
||||
}
|
||||
|
||||
auto Interpreter::InterpProgram(llvm::ArrayRef<Nonnull<Declaration*>> fs,
|
||||
Nonnull<const Expression*> call_main) -> int {
|
||||
// Check that the interpreter is in a clean state.
|
||||
CHECK(globals_.IsEmpty());
|
||||
CHECK(stack_.IsEmpty());
|
||||
CHECK(program_value_ == std::nullopt);
|
||||
CHECK(todo_.IsEmpty());
|
||||
|
||||
if (trace_) {
|
||||
llvm::outs() << "********** initializing globals **********\n";
|
||||
}
|
||||
InitGlobals(fs);
|
||||
|
||||
auto todo = Stack<Nonnull<Action*>>(arena_->New<ExpressionAction>(call_main));
|
||||
auto scopes = Stack<Nonnull<Scope*>>(arena_->New<Scope>(globals_));
|
||||
stack_ = Stack<Nonnull<Frame*>>(arena_->New<Frame>("top", scopes, todo));
|
||||
|
||||
if (trace_) {
|
||||
llvm::outs() << "********** calling main function **********\n";
|
||||
PrintState(llvm::outs());
|
||||
}
|
||||
|
||||
while (stack_.Count() > 1 || !stack_.Top()->todo.IsEmpty()) {
|
||||
if (!stack_.Top()->todo.IsEmpty()) {
|
||||
CHECK(stack_.Top()->todo.Top()->kind() != Action::Kind::PatternAction)
|
||||
<< "Pattern evaluation must happen before run-time.";
|
||||
}
|
||||
Step();
|
||||
if (trace_) {
|
||||
PrintState(llvm::outs());
|
||||
}
|
||||
}
|
||||
return cast<IntValue>(**program_value_).value();
|
||||
return cast<IntValue>(*ExecuteAction(arena_->New<ExpressionAction>(call_main),
|
||||
globals_, trace_))
|
||||
.value();
|
||||
}
|
||||
|
||||
auto Interpreter::InterpExp(Env values, Nonnull<const Expression*> e)
|
||||
-> Nonnull<const Value*> {
|
||||
CHECK(program_value_ == std::nullopt);
|
||||
auto program_value_guard =
|
||||
llvm::make_scope_exit([&] { program_value_ = std::nullopt; });
|
||||
auto todo = Stack<Nonnull<Action*>>(arena_->New<ExpressionAction>(e));
|
||||
auto scopes = Stack<Nonnull<Scope*>>(arena_->New<Scope>(values));
|
||||
stack_ =
|
||||
Stack<Nonnull<Frame*>>(arena_->New<Frame>("InterpExp", scopes, todo));
|
||||
|
||||
while (stack_.Count() > 1 || !stack_.Top()->todo.IsEmpty()) {
|
||||
Step();
|
||||
}
|
||||
CHECK(program_value_ != std::nullopt);
|
||||
return *program_value_;
|
||||
return ExecuteAction(arena_->New<ExpressionAction>(e), values,
|
||||
/*trace_steps=*/false);
|
||||
}
|
||||
|
||||
auto Interpreter::InterpPattern(Env values, Nonnull<const Pattern*> p)
|
||||
-> Nonnull<const Value*> {
|
||||
CHECK(program_value_ == std::nullopt);
|
||||
auto program_value_guard =
|
||||
llvm::make_scope_exit([&] { program_value_ = std::nullopt; });
|
||||
auto todo = Stack<Nonnull<Action*>>(arena_->New<PatternAction>(p));
|
||||
auto scopes = Stack<Nonnull<Scope*>>(arena_->New<Scope>(values));
|
||||
stack_ =
|
||||
Stack<Nonnull<Frame*>>(arena_->New<Frame>("InterpPattern", scopes, todo));
|
||||
|
||||
while (stack_.Count() > 1 || !stack_.Top()->todo.IsEmpty()) {
|
||||
Step();
|
||||
}
|
||||
CHECK(program_value_ != std::nullopt);
|
||||
return *program_value_;
|
||||
return ExecuteAction(arena_->New<PatternAction>(p), values,
|
||||
/*trace_steps=*/false);
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "executable_semantics/ast/declaration.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
#include "executable_semantics/interpreter/heap.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "executable_semantics/interpreter/value.h"
|
||||
@@ -21,8 +21,6 @@
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
using Env = Dictionary<std::string, Address>;
|
||||
|
||||
class Interpreter {
|
||||
public:
|
||||
explicit Interpreter(Nonnull<Arena*> arena, bool trace)
|
||||
@@ -85,16 +83,19 @@ class Interpreter {
|
||||
// and increments its position counter.
|
||||
struct RunAgain {};
|
||||
|
||||
// Transition type which unwinds the `todo` and `scopes` stacks until it
|
||||
// reaches a specified Action lower in the stack.
|
||||
// Transition type which unwinds the `todo` stack until it reaches the
|
||||
// StatementAction associated with `ast_node`. Execution then resumes with
|
||||
// that StatementAction.
|
||||
struct UnwindTo {
|
||||
const Nonnull<Action*> new_top;
|
||||
Nonnull<const Statement*> ast_node;
|
||||
};
|
||||
|
||||
// Transition type which unwinds the entire current stack frame, and returns
|
||||
// a specified value to the caller.
|
||||
struct UnwindFunctionCall {
|
||||
Nonnull<const Value*> return_val;
|
||||
// Transition type which unwinds the `todo` stack down to and including the
|
||||
// StatementAction associated with `ast_node`. If `result` is set, it will be
|
||||
// treated as the result of that StatementAction.
|
||||
struct UnwindPast {
|
||||
Nonnull<const Statement*> ast_node;
|
||||
std::optional<Nonnull<const Value*>> result;
|
||||
};
|
||||
|
||||
// Transition type which removes the current action from the top of the todo
|
||||
@@ -112,12 +113,12 @@ class Interpreter {
|
||||
// uses of this type should be replaced with meaningful transitions.
|
||||
struct ManualTransition {};
|
||||
|
||||
using Transition =
|
||||
std::variant<Done, Spawn, Delegate, RunAgain, UnwindTo,
|
||||
UnwindFunctionCall, CallFunction, ManualTransition>;
|
||||
using Transition = std::variant<Done, Spawn, Delegate, RunAgain, UnwindTo,
|
||||
UnwindPast, CallFunction, ManualTransition>;
|
||||
|
||||
// Visitor which implements the behavior associated with each transition type.
|
||||
class DoTransition;
|
||||
friend class DoTransition;
|
||||
|
||||
void Step();
|
||||
|
||||
@@ -131,12 +132,12 @@ class Interpreter {
|
||||
auto StepStmt() -> Transition;
|
||||
|
||||
void InitGlobals(llvm::ArrayRef<Nonnull<Declaration*>> fs);
|
||||
auto CurrentScope() -> Scope&;
|
||||
auto CurrentEnv() -> Env;
|
||||
auto GetFromEnv(SourceLocation source_loc, const std::string& name)
|
||||
-> Address;
|
||||
|
||||
void DeallocateScope(Nonnull<Scope*> scope);
|
||||
void DeallocateLocals(Nonnull<Frame*> frame);
|
||||
void DeallocateScope(Scope& scope);
|
||||
|
||||
auto CreateTuple(Nonnull<Action*> act, Nonnull<const Expression*> exp)
|
||||
-> Nonnull<const Value*>;
|
||||
@@ -157,14 +158,22 @@ class Interpreter {
|
||||
|
||||
void PrintState(llvm::raw_ostream& out);
|
||||
|
||||
// Runs `action` in a scope consisting of `values`, and returns the result.
|
||||
// `action` must produce a result. In other words, it must not be a
|
||||
// StatementAction or ScopeAction.
|
||||
//
|
||||
// TODO: consider whether to use this->trace_ rather than a separate
|
||||
// trace_steps parameter.
|
||||
auto ExecuteAction(Nonnull<Action*> action, Env values, bool trace_steps)
|
||||
-> Nonnull<const Value*>;
|
||||
|
||||
Nonnull<Arena*> arena_;
|
||||
|
||||
// Globally-defined entities, such as functions, structs, or choices.
|
||||
Env globals_;
|
||||
|
||||
Stack<Nonnull<Frame*>> stack_;
|
||||
Stack<Nonnull<Action*>> todo_;
|
||||
Heap heap_;
|
||||
std::optional<Nonnull<const Value*>> program_value_;
|
||||
|
||||
bool trace_;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "executable_semantics/interpreter/resolve_control_flow.h"
|
||||
|
||||
#include "executable_semantics/ast/declaration.h"
|
||||
#include "executable_semantics/ast/statement.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
using llvm::cast;
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// Resolves control-flow edges in the AST rooted at `statement`. `return`
|
||||
// statements will resolve to `*function`, and `break` and `continue`
|
||||
// statements will resolve to `*loop`. If either parameter is nullopt, that
|
||||
// indicates a context where the corresponding statements are not permitted.
|
||||
static void ResolveControlFlow(
|
||||
Nonnull<Statement*> statement,
|
||||
std::optional<Nonnull<const FunctionDeclaration*>> function,
|
||||
std::optional<Nonnull<const Statement*>> loop) {
|
||||
switch (statement->kind()) {
|
||||
case Statement::Kind::Return:
|
||||
if (!function.has_value()) {
|
||||
FATAL_COMPILATION_ERROR(statement->source_loc())
|
||||
<< "return is not within a function body";
|
||||
}
|
||||
cast<Return>(*statement).set_function(*function);
|
||||
return;
|
||||
case Statement::Kind::Break:
|
||||
if (!loop.has_value()) {
|
||||
FATAL_COMPILATION_ERROR(statement->source_loc())
|
||||
<< "break is not within a loop body";
|
||||
}
|
||||
cast<Break>(*statement).set_loop(*loop);
|
||||
return;
|
||||
case Statement::Kind::Continue:
|
||||
if (!loop.has_value()) {
|
||||
FATAL_COMPILATION_ERROR(statement->source_loc())
|
||||
<< "continue is not within a loop body";
|
||||
}
|
||||
cast<Continue>(*statement).set_loop(*loop);
|
||||
return;
|
||||
case Statement::Kind::If: {
|
||||
auto& if_stmt = cast<If>(*statement);
|
||||
ResolveControlFlow(&if_stmt.then_statement(), function, loop);
|
||||
if (if_stmt.else_statement().has_value()) {
|
||||
ResolveControlFlow(*if_stmt.else_statement(), function, loop);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Statement::Kind::Sequence: {
|
||||
auto& seq = cast<Sequence>(*statement);
|
||||
ResolveControlFlow(&seq.statement(), function, loop);
|
||||
if (seq.next().has_value()) {
|
||||
ResolveControlFlow(*seq.next(), function, loop);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Statement::Kind::Block: {
|
||||
auto& block = cast<Block>(*statement);
|
||||
if (block.statement().has_value()) {
|
||||
ResolveControlFlow(*block.statement(), function, loop);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Statement::Kind::While:
|
||||
ResolveControlFlow(&cast<While>(*statement).body(), function, statement);
|
||||
return;
|
||||
case Statement::Kind::Match: {
|
||||
auto& match = cast<Match>(*statement);
|
||||
for (Match::Clause& clause : match.clauses()) {
|
||||
ResolveControlFlow(&clause.statement(), function, loop);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case Statement::Kind::Continuation:
|
||||
ResolveControlFlow(&cast<Continuation>(*statement).body(), std::nullopt,
|
||||
std::nullopt);
|
||||
return;
|
||||
case Statement::Kind::ExpressionStatement:
|
||||
case Statement::Kind::Assign:
|
||||
case Statement::Kind::VariableDefinition:
|
||||
case Statement::Kind::Run:
|
||||
case Statement::Kind::Await:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveControlFlow(AST& ast) {
|
||||
for (auto declaration : ast.declarations) {
|
||||
if (declaration->kind() != Declaration::Kind::FunctionDeclaration) {
|
||||
continue;
|
||||
}
|
||||
auto& function = cast<FunctionDeclaration>(*declaration);
|
||||
if (function.body().has_value()) {
|
||||
ResolveControlFlow(*function.body(), &function, std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,19 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef EXECUTABLE_SEMANTICS_INTERPRETER_RESOLVE_CONTROL_FLOW_H_
|
||||
#define EXECUTABLE_SEMANTICS_INTERPRETER_RESOLVE_CONTROL_FLOW_H_
|
||||
|
||||
#include "executable_semantics/ast/ast.h"
|
||||
#include "executable_semantics/common/nonnull.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// Resolves non-local control-flow edges, such as `break` and `return`, in the
|
||||
// given AST.
|
||||
void ResolveControlFlow(AST& ast);
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_INTERPRETER_RESOLVE_CONTROL_FLOW_H_
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "common/check.h"
|
||||
#include "executable_semantics/common/arena.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
@@ -274,8 +274,9 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
case Value::Kind::ContinuationValue: {
|
||||
out << "{";
|
||||
llvm::ListSeparator sep(" :: ");
|
||||
for (Nonnull<Frame*> frame : cast<ContinuationValue>(*this).stack()) {
|
||||
out << sep << *frame;
|
||||
for (Nonnull<const Action*> action :
|
||||
cast<ContinuationValue>(*this).stack()) {
|
||||
out << sep << *action;
|
||||
}
|
||||
out << "}";
|
||||
break;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
class Action;
|
||||
|
||||
// Abstract base class of all AST nodes representing values.
|
||||
//
|
||||
// Value and its derived classes support LLVM-style RTTI, including
|
||||
@@ -106,8 +108,6 @@ struct StructElement {
|
||||
Nonnull<const Value*> value;
|
||||
};
|
||||
|
||||
struct Frame; // Used by continuation.
|
||||
|
||||
// An integer value.
|
||||
class IntValue : public Value {
|
||||
public:
|
||||
@@ -489,21 +489,21 @@ class VariableType : public Value {
|
||||
// fragment, which is exposed by `Stack()`.
|
||||
class ContinuationValue : public Value {
|
||||
public:
|
||||
explicit ContinuationValue(Nonnull<std::vector<Nonnull<Frame*>>*> stack)
|
||||
explicit ContinuationValue(Nonnull<std::vector<Nonnull<Action*>>*> stack)
|
||||
: Value(Kind::ContinuationValue), stack_(stack) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::ContinuationValue;
|
||||
}
|
||||
|
||||
// The call stack of the suspended continuation, starting with the top
|
||||
// frame (the reverse of the usual order). Note that this provides mutable
|
||||
// The todo stack of the suspended continuation, starting with the top
|
||||
// Action (the reverse of the usual order). Note that this provides mutable
|
||||
// access, even when *this is const, because of the reference-like semantics
|
||||
// of ContinuationValue.
|
||||
auto stack() const -> std::vector<Nonnull<Frame*>>& { return *stack_; }
|
||||
auto stack() const -> std::vector<Nonnull<Action*>>& { return *stack_; }
|
||||
|
||||
private:
|
||||
Nonnull<std::vector<Nonnull<Frame*>>*> stack_;
|
||||
Nonnull<std::vector<Nonnull<Action*>>*> stack_;
|
||||
};
|
||||
|
||||
// The String type.
|
||||
|
||||
Reference in New Issue
Block a user