|
|
@@ -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
|