mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 19:11:09 +01:00
Split interpreter into smaller modules (#662)
- Move Heap and Frame/Scope to their own headers. - Move some functions to more appropriate headers (e.g. CopyValue -> value.h). - Define a separate Bazel rule for each header/cpp pair. - Modify PrintValue to not print the frames of a ContinuationValue. This was necessary to break a dependency cycle between PrintValue, PrintFrame, and Action::Print. 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
def98d1182
commit
d5124256a2
@@ -6,39 +6,19 @@ load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
|
||||
package(default_visibility = ["//executable_semantics:__subpackages__"])
|
||||
|
||||
# TODO: It may be helpful to break this apart.
|
||||
cc_library(
|
||||
name = "interpreter",
|
||||
srcs = [
|
||||
"action.cpp",
|
||||
"interpreter.cpp",
|
||||
"typecheck.cpp",
|
||||
"value.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"action.h",
|
||||
"interpreter.h",
|
||||
"typecheck.h",
|
||||
"value.h",
|
||||
],
|
||||
name = "action",
|
||||
srcs = ["action.cpp"],
|
||||
hdrs = ["action.h"],
|
||||
deps = [
|
||||
":address",
|
||||
":containers",
|
||||
"//common:check",
|
||||
"//executable_semantics:tracing_flag",
|
||||
"//executable_semantics/ast:declaration",
|
||||
":value",
|
||||
"//executable_semantics/ast:expression",
|
||||
"//executable_semantics/ast:function_definition",
|
||||
"//executable_semantics/ast:member",
|
||||
"//executable_semantics/ast:statement",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "field_path",
|
||||
hdrs = ["field_path.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "address",
|
||||
hdrs = ["address.h"],
|
||||
@@ -58,3 +38,78 @@ cc_library(
|
||||
],
|
||||
deps = ["//common:check"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "field_path",
|
||||
hdrs = ["field_path.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "frame",
|
||||
srcs = ["frame.cpp"],
|
||||
hdrs = ["frame.h"],
|
||||
deps = [
|
||||
":action",
|
||||
":address",
|
||||
":containers",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "heap",
|
||||
srcs = ["heap.cpp"],
|
||||
hdrs = ["heap.h"],
|
||||
deps = [
|
||||
":address",
|
||||
":value",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "interpreter",
|
||||
srcs = [
|
||||
"interpreter.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"interpreter.h",
|
||||
],
|
||||
deps = [
|
||||
":action",
|
||||
":address",
|
||||
":containers",
|
||||
":frame",
|
||||
":heap",
|
||||
":value",
|
||||
"//common:check",
|
||||
"//executable_semantics:tracing_flag",
|
||||
"//executable_semantics/ast:declaration",
|
||||
"//executable_semantics/ast:expression",
|
||||
"//executable_semantics/ast:function_definition",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "typecheck",
|
||||
srcs = ["typecheck.cpp"],
|
||||
hdrs = ["typecheck.h"],
|
||||
deps = [
|
||||
":containers",
|
||||
":interpreter",
|
||||
"//executable_semantics:tracing_flag",
|
||||
"//executable_semantics/ast:expression",
|
||||
"//executable_semantics/ast:function_definition",
|
||||
"//executable_semantics/ast:statement",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "value",
|
||||
srcs = ["value.cpp"],
|
||||
hdrs = ["value.h"],
|
||||
deps = [
|
||||
":address",
|
||||
":containers",
|
||||
":field_path",
|
||||
"//executable_semantics/ast:statement",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
@@ -11,9 +13,7 @@
|
||||
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/function_definition.h"
|
||||
#include "executable_semantics/interpreter/interpreter.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "executable_semantics/interpreter/typecheck.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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 <ostream>
|
||||
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
void PrintFrame(Frame* frame, std::ostream& out) {
|
||||
out << frame->name;
|
||||
out << "{";
|
||||
Action::PrintList(frame->todo, out);
|
||||
out << "}";
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 <list>
|
||||
#include <string>
|
||||
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
#include "executable_semantics/interpreter/address.h"
|
||||
#include "executable_semantics/interpreter/dictionary.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
using Env = Dictionary<std::string, Address>;
|
||||
|
||||
struct Scope {
|
||||
Scope(Env values, std::list<std::string> l)
|
||||
: values(values), locals(std::move(l)) {}
|
||||
Env values;
|
||||
std::list<std::string> locals;
|
||||
};
|
||||
|
||||
// A frame represents either a function call or a delimited continuation.
|
||||
struct Frame {
|
||||
// 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<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<Action*> todo;
|
||||
// If this frame is the bottom frame of a continuation, then it stores
|
||||
// the address of the continuation.
|
||||
std::optional<Address> continuation;
|
||||
|
||||
Frame(std::string n, Stack<Scope*> s, Stack<Action*> c)
|
||||
: name(std::move(std::move(n))), scopes(s), todo(c), continuation() {}
|
||||
};
|
||||
|
||||
void PrintFrame(Frame* frame, std::ostream& out);
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_INTERPRETER_FRAME_H_
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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/heap.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
auto Heap::AllocateValue(const Value* v) -> Address {
|
||||
// Putting the following two side effects together in this function
|
||||
// ensures that we don't do anything else in between, which is really bad!
|
||||
// Consider whether to include a copy of the input v in this function
|
||||
// or to leave it up to the caller.
|
||||
CHECK(v != nullptr);
|
||||
Address a(values_.size());
|
||||
values_.push_back(v);
|
||||
alive_.push_back(true);
|
||||
return a;
|
||||
}
|
||||
|
||||
auto Heap::Read(const Address& a, int line_num) -> const Value* {
|
||||
this->CheckAlive(a, line_num);
|
||||
return values_[a.index]->GetField(a.field_path, line_num);
|
||||
}
|
||||
|
||||
auto Heap::Write(const Address& a, const Value* v, int line_num) -> void {
|
||||
CHECK(v != nullptr);
|
||||
this->CheckAlive(a, line_num);
|
||||
values_[a.index] = values_[a.index]->SetField(a.field_path, v, line_num);
|
||||
}
|
||||
|
||||
void Heap::CheckAlive(const Address& address, int line_num) {
|
||||
if (!alive_[address.index]) {
|
||||
std::cerr << line_num << ": undefined behavior: access to dead value ";
|
||||
PrintValue(values_[address.index], std::cerr);
|
||||
std::cerr << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::Deallocate(const Address& address) {
|
||||
CHECK(address.field_path.IsEmpty());
|
||||
if (alive_[address.index]) {
|
||||
alive_[address.index] = false;
|
||||
} else {
|
||||
std::cerr << "runtime error, deallocating an already dead value"
|
||||
<< std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::PrintHeap(std::ostream& out) {
|
||||
for (size_t i = 0; i < values_.size(); ++i) {
|
||||
PrintAddress(Address(i), out);
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
auto Heap::PrintAddress(const Address& a, std::ostream& out) -> void {
|
||||
if (!alive_[a.index]) {
|
||||
out << "!!";
|
||||
}
|
||||
PrintValue(values_[a.index], out);
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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_MEMORY_H_
|
||||
#define EXECUTABLE_SEMANTICS_INTERPRETER_MEMORY_H_
|
||||
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
#include "executable_semantics/interpreter/address.h"
|
||||
#include "executable_semantics/interpreter/value.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// A Heap represents the abstract machine's dynamically allocated memory.
|
||||
class Heap {
|
||||
public:
|
||||
// Constructs an empty Heap.
|
||||
Heap() = default;
|
||||
|
||||
Heap(const Heap&) = delete;
|
||||
Heap& operator=(const Heap&) = delete;
|
||||
|
||||
// Returns the value at the given address in the heap after
|
||||
// checking that it is alive.
|
||||
auto Read(const Address& a, int line_num) -> const Value*;
|
||||
|
||||
// Writes the given value at the address in the heap after
|
||||
// checking that the address is alive.
|
||||
auto Write(const Address& a, const Value* v, int line_num) -> void;
|
||||
|
||||
// Put the given value on the heap and mark it as alive.
|
||||
auto AllocateValue(const Value* v) -> Address;
|
||||
|
||||
// Marks the object at this address, and all of its sub-objects, as dead.
|
||||
auto Deallocate(const Address& address) -> void;
|
||||
|
||||
// Print the value at the given address to the stream `out`.
|
||||
auto PrintAddress(const Address& a, std::ostream& out) -> void;
|
||||
|
||||
// Print all the values on the heap to the stream `out`.
|
||||
auto PrintHeap(std::ostream& out) -> void;
|
||||
|
||||
private:
|
||||
// Signal an error if the address is no longer alive.
|
||||
void CheckAlive(const Address& address, int line_num);
|
||||
|
||||
std::vector<const Value*> values_;
|
||||
std::vector<bool> alive_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_INTERPRETER_MEMORY_H_
|
||||
@@ -15,8 +15,9 @@
|
||||
#include "common/check.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/function_definition.h"
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "executable_semantics/interpreter/typecheck.h"
|
||||
#include "executable_semantics/tracing_flag.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -30,109 +31,6 @@ auto Step() -> void;
|
||||
// Auxiliary Functions
|
||||
//
|
||||
|
||||
auto Heap::AllocateValue(const Value* v) -> Address {
|
||||
// Putting the following two side effects together in this function
|
||||
// ensures that we don't do anything else in between, which is really bad!
|
||||
// Consider whether to include a copy of the input v in this function
|
||||
// or to leave it up to the caller.
|
||||
CHECK(v != nullptr);
|
||||
Address a(values_.size());
|
||||
values_.push_back(v);
|
||||
alive_.push_back(true);
|
||||
return a;
|
||||
}
|
||||
|
||||
auto Heap::Read(const Address& a, int line_num) -> const Value* {
|
||||
this->CheckAlive(a, line_num);
|
||||
return values_[a.index]->GetField(a.field_path, line_num);
|
||||
}
|
||||
|
||||
auto Heap::Write(const Address& a, const Value* v, int line_num) -> void {
|
||||
CHECK(v != nullptr);
|
||||
this->CheckAlive(a, line_num);
|
||||
values_[a.index] = values_[a.index]->SetField(a.field_path, v, line_num);
|
||||
}
|
||||
|
||||
void Heap::CheckAlive(const Address& address, int line_num) {
|
||||
if (!alive_[address.index]) {
|
||||
std::cerr << line_num << ": undefined behavior: access to dead value ";
|
||||
PrintValue(values_[address.index], std::cerr);
|
||||
std::cerr << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
auto CopyVal(const Value* val, int line_num) -> const Value* {
|
||||
switch (val->tag()) {
|
||||
case ValKind::TupleValue: {
|
||||
std::vector<TupleElement> elements;
|
||||
for (const TupleElement& element : val->GetTupleValue().elements) {
|
||||
elements.push_back(
|
||||
{.name = element.name, .value = CopyVal(element.value, line_num)});
|
||||
}
|
||||
return Value::MakeTupleValue(std::move(elements));
|
||||
}
|
||||
case ValKind::AlternativeValue: {
|
||||
const Value* arg = CopyVal(val->GetAlternativeValue().argument, line_num);
|
||||
return Value::MakeAlternativeValue(val->GetAlternativeValue().alt_name,
|
||||
val->GetAlternativeValue().choice_name,
|
||||
arg);
|
||||
}
|
||||
case ValKind::StructValue: {
|
||||
const Value* inits = CopyVal(val->GetStructValue().inits, line_num);
|
||||
return Value::MakeStructValue(val->GetStructValue().type, inits);
|
||||
}
|
||||
case ValKind::IntValue:
|
||||
return Value::MakeIntValue(val->GetIntValue());
|
||||
case ValKind::BoolValue:
|
||||
return Value::MakeBoolValue(val->GetBoolValue());
|
||||
case ValKind::FunctionValue:
|
||||
return Value::MakeFunctionValue(val->GetFunctionValue().name,
|
||||
val->GetFunctionValue().param,
|
||||
val->GetFunctionValue().body);
|
||||
case ValKind::PointerValue:
|
||||
return Value::MakePointerValue(val->GetPointerValue());
|
||||
case ValKind::ContinuationValue:
|
||||
// Copying a continuation is "shallow".
|
||||
return val;
|
||||
case ValKind::FunctionType:
|
||||
return Value::MakeFunctionType(
|
||||
CopyVal(val->GetFunctionType().param, line_num),
|
||||
CopyVal(val->GetFunctionType().ret, line_num));
|
||||
|
||||
case ValKind::PointerType:
|
||||
return Value::MakePointerType(
|
||||
CopyVal(val->GetPointerType().type, line_num));
|
||||
case ValKind::IntType:
|
||||
return Value::MakeIntType();
|
||||
case ValKind::BoolType:
|
||||
return Value::MakeBoolType();
|
||||
case ValKind::TypeType:
|
||||
return Value::MakeTypeType();
|
||||
case ValKind::AutoType:
|
||||
return Value::MakeAutoType();
|
||||
case ValKind::ContinuationType:
|
||||
return Value::MakeContinuationType();
|
||||
case ValKind::StructType:
|
||||
case ValKind::ChoiceType:
|
||||
case ValKind::BindingPlaceholderValue:
|
||||
case ValKind::AlternativeConstructorValue:
|
||||
return val; // no need to copy these because they are immutable?
|
||||
// No, they need to be copied so they don't get killed. -Jeremy
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::Deallocate(const Address& address) {
|
||||
CHECK(address.field_path.IsEmpty());
|
||||
if (alive_[address.index]) {
|
||||
alive_[address.index] = false;
|
||||
} else {
|
||||
std::cerr << "runtime error, deallocating an already dead value"
|
||||
<< std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintEnv(Env values, std::ostream& out) {
|
||||
for (const auto& [name, address] : values) {
|
||||
out << name << ": ";
|
||||
@@ -142,16 +40,9 @@ void PrintEnv(Env values, std::ostream& out) {
|
||||
}
|
||||
|
||||
//
|
||||
// Frame and State Operations
|
||||
// State Operations
|
||||
//
|
||||
|
||||
void PrintFrame(Frame* frame, std::ostream& out) {
|
||||
out << frame->name;
|
||||
out << "{";
|
||||
Action::PrintList(frame->todo, out);
|
||||
out << "}";
|
||||
}
|
||||
|
||||
void PrintStack(Stack<Frame*> ls, std::ostream& out) {
|
||||
if (!ls.IsEmpty()) {
|
||||
PrintFrame(ls.Pop(), out);
|
||||
@@ -162,20 +53,6 @@ void PrintStack(Stack<Frame*> ls, std::ostream& out) {
|
||||
}
|
||||
}
|
||||
|
||||
void Heap::PrintHeap(std::ostream& out) {
|
||||
for (size_t i = 0; i < values_.size(); ++i) {
|
||||
PrintAddress(Address(i), out);
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
auto Heap::PrintAddress(const Address& a, std::ostream& out) -> void {
|
||||
if (!alive_[a.index]) {
|
||||
out << "!!";
|
||||
}
|
||||
PrintValue(values_[a.index], out);
|
||||
}
|
||||
|
||||
auto CurrentEnv(State* state) -> Env {
|
||||
Frame* frame = state->stack.Top();
|
||||
return frame->scopes.Top()->values;
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "executable_semantics/ast/declaration.h"
|
||||
#include "executable_semantics/interpreter/action.h"
|
||||
#include "executable_semantics/interpreter/dictionary.h"
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
#include "executable_semantics/interpreter/heap.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "executable_semantics/interpreter/value.h"
|
||||
|
||||
@@ -19,77 +19,6 @@ namespace Carbon {
|
||||
|
||||
using Env = Dictionary<std::string, Address>;
|
||||
|
||||
/***** Scopes *****/
|
||||
|
||||
struct Scope {
|
||||
Scope(Env values, std::list<std::string> l)
|
||||
: values(values), locals(std::move(l)) {}
|
||||
Env values;
|
||||
std::list<std::string> locals;
|
||||
};
|
||||
|
||||
/***** Frames and State *****/
|
||||
|
||||
// A frame represents either a function call or a delimited continuation.
|
||||
struct Frame {
|
||||
// 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<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<Action*> todo;
|
||||
// If this frame is the bottom frame of a continuation, then it stores
|
||||
// the address of the continuation.
|
||||
std::optional<Address> continuation;
|
||||
|
||||
Frame(std::string n, Stack<Scope*> s, Stack<Action*> c)
|
||||
: name(std::move(std::move(n))), scopes(s), todo(c), continuation() {}
|
||||
};
|
||||
|
||||
// A Heap represents the abstract machine's dynamically allocated memory.
|
||||
class Heap {
|
||||
public:
|
||||
// Constructs an empty Heap.
|
||||
Heap() = default;
|
||||
|
||||
Heap(const Heap&) = delete;
|
||||
Heap& operator=(const Heap&) = delete;
|
||||
|
||||
// Returns the value at the given address in the heap after
|
||||
// checking that it is alive.
|
||||
auto Read(const Address& a, int line_num) -> const Value*;
|
||||
|
||||
// Writes the given value at the address in the heap after
|
||||
// checking that the address is alive.
|
||||
auto Write(const Address& a, const Value* v, int line_num) -> void;
|
||||
|
||||
// Put the given value on the heap and mark it as alive.
|
||||
auto AllocateValue(const Value* v) -> Address;
|
||||
|
||||
// Marks the object at this address, and all of its sub-objects, as dead.
|
||||
auto Deallocate(const Address& address) -> void;
|
||||
|
||||
// Print the value at the given address to the stream `out`.
|
||||
auto PrintAddress(const Address& a, std::ostream& out) -> void;
|
||||
|
||||
// Print all the values on the heap to the stream `out`.
|
||||
auto PrintHeap(std::ostream& out) -> void;
|
||||
|
||||
private:
|
||||
// Signal an error if the address is no longer alive.
|
||||
void CheckAlive(const Address& address, int line_num);
|
||||
|
||||
std::vector<const Value*> values_;
|
||||
std::vector<bool> alive_;
|
||||
};
|
||||
|
||||
struct State {
|
||||
Stack<Frame*> stack;
|
||||
Heap heap;
|
||||
@@ -98,11 +27,8 @@ struct State {
|
||||
extern State* state;
|
||||
|
||||
void InitEnv(const Declaration& d, Env* env);
|
||||
void PrintFrame(Frame* frame, std::ostream& out);
|
||||
void PrintStack(Stack<Frame*> ls, std::ostream& out);
|
||||
void PrintEnv(Env values);
|
||||
auto CopyVal(const Value* val, int line_num) -> const Value*;
|
||||
auto ToInteger(const Value* v) -> int;
|
||||
|
||||
/***** Interpreters *****/
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "executable_semantics/interpreter/interpreter.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -434,16 +433,73 @@ auto PrintValue(const Value* val, std::ostream& out) -> void {
|
||||
out << "choice " << val->GetChoiceType().name;
|
||||
break;
|
||||
case ValKind::ContinuationValue:
|
||||
out << "continuation[[";
|
||||
for (Frame* frame : val->GetContinuationValue().stack) {
|
||||
PrintFrame(frame, out);
|
||||
out << " :: ";
|
||||
}
|
||||
out << "]]";
|
||||
out << "continuation";
|
||||
// TODO: Find a way to print useful information about the continuation
|
||||
// without creating a dependency cycle.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto CopyVal(const Value* val, int line_num) -> const Value* {
|
||||
switch (val->tag()) {
|
||||
case ValKind::TupleValue: {
|
||||
std::vector<TupleElement> elements;
|
||||
for (const TupleElement& element : val->GetTupleValue().elements) {
|
||||
elements.push_back(
|
||||
{.name = element.name, .value = CopyVal(element.value, line_num)});
|
||||
}
|
||||
return Value::MakeTupleValue(std::move(elements));
|
||||
}
|
||||
case ValKind::AlternativeValue: {
|
||||
const Value* arg = CopyVal(val->GetAlternativeValue().argument, line_num);
|
||||
return Value::MakeAlternativeValue(val->GetAlternativeValue().alt_name,
|
||||
val->GetAlternativeValue().choice_name,
|
||||
arg);
|
||||
}
|
||||
case ValKind::StructValue: {
|
||||
const Value* inits = CopyVal(val->GetStructValue().inits, line_num);
|
||||
return Value::MakeStructValue(val->GetStructValue().type, inits);
|
||||
}
|
||||
case ValKind::IntValue:
|
||||
return Value::MakeIntValue(val->GetIntValue());
|
||||
case ValKind::BoolValue:
|
||||
return Value::MakeBoolValue(val->GetBoolValue());
|
||||
case ValKind::FunctionValue:
|
||||
return Value::MakeFunctionValue(val->GetFunctionValue().name,
|
||||
val->GetFunctionValue().param,
|
||||
val->GetFunctionValue().body);
|
||||
case ValKind::PointerValue:
|
||||
return Value::MakePointerValue(val->GetPointerValue());
|
||||
case ValKind::ContinuationValue:
|
||||
// Copying a continuation is "shallow".
|
||||
return val;
|
||||
case ValKind::FunctionType:
|
||||
return Value::MakeFunctionType(
|
||||
CopyVal(val->GetFunctionType().param, line_num),
|
||||
CopyVal(val->GetFunctionType().ret, line_num));
|
||||
|
||||
case ValKind::PointerType:
|
||||
return Value::MakePointerType(
|
||||
CopyVal(val->GetPointerType().type, line_num));
|
||||
case ValKind::IntType:
|
||||
return Value::MakeIntType();
|
||||
case ValKind::BoolType:
|
||||
return Value::MakeBoolType();
|
||||
case ValKind::TypeType:
|
||||
return Value::MakeTypeType();
|
||||
case ValKind::AutoType:
|
||||
return Value::MakeAutoType();
|
||||
case ValKind::ContinuationType:
|
||||
return Value::MakeContinuationType();
|
||||
case ValKind::StructType:
|
||||
case ValKind::ChoiceType:
|
||||
case ValKind::BindingPlaceholderValue:
|
||||
case ValKind::AlternativeConstructorValue:
|
||||
// TODO: These should be copied so that they don't get destructed.
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
auto TypeEqual(const Value* t1, const Value* t2) -> bool {
|
||||
if (t1->tag() != t2->tag()) {
|
||||
return false;
|
||||
|
||||
@@ -240,6 +240,8 @@ struct Value {
|
||||
|
||||
void PrintValue(const Value* val, std::ostream& out);
|
||||
|
||||
auto CopyVal(const Value* val, int line_num) -> const Value*;
|
||||
|
||||
auto TypeEqual(const Value* t1, const Value* t2) -> bool;
|
||||
auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ cc_library(
|
||||
"//executable_semantics/ast:declaration",
|
||||
"//executable_semantics/ast:expression",
|
||||
"//executable_semantics/interpreter",
|
||||
"//executable_semantics/interpreter:typecheck",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user