diff --git a/executable_semantics/interpreter/BUILD b/executable_semantics/interpreter/BUILD index 376392c143e3..59014046e2c4 100644 --- a/executable_semantics/interpreter/BUILD +++ b/executable_semantics/interpreter/BUILD @@ -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", + ], +) diff --git a/executable_semantics/interpreter/action.cpp b/executable_semantics/interpreter/action.cpp index 9ee6d9c2fd5f..1e9105f5436b 100644 --- a/executable_semantics/interpreter/action.cpp +++ b/executable_semantics/interpreter/action.cpp @@ -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 #include #include @@ -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 { diff --git a/executable_semantics/interpreter/frame.cpp b/executable_semantics/interpreter/frame.cpp new file mode 100644 index 000000000000..3a9e665e5cf8 --- /dev/null +++ b/executable_semantics/interpreter/frame.cpp @@ -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 + +#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 diff --git a/executable_semantics/interpreter/frame.h b/executable_semantics/interpreter/frame.h new file mode 100644 index 000000000000..abd132f12669 --- /dev/null +++ b/executable_semantics/interpreter/frame.h @@ -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 +#include + +#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; + +struct Scope { + Scope(Env values, std::list l) + : values(values), locals(std::move(l)) {} + Env values; + std::list 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 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 todo; + // If this frame is the bottom frame of a continuation, then it stores + // the address of the continuation. + std::optional
continuation; + + Frame(std::string n, Stack s, Stack 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_ diff --git a/executable_semantics/interpreter/heap.cpp b/executable_semantics/interpreter/heap.cpp new file mode 100644 index 000000000000..95bb6389f340 --- /dev/null +++ b/executable_semantics/interpreter/heap.cpp @@ -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 diff --git a/executable_semantics/interpreter/heap.h b/executable_semantics/interpreter/heap.h new file mode 100644 index 000000000000..a32cddcd4e0f --- /dev/null +++ b/executable_semantics/interpreter/heap.h @@ -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 +#include + +#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 values_; + std::vector alive_; +}; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_INTERPRETER_MEMORY_H_ diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 271c8dffb70e..5c5d265836e6 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -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 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 ls, std::ostream& out) { if (!ls.IsEmpty()) { PrintFrame(ls.Pop(), out); @@ -162,20 +53,6 @@ void PrintStack(Stack 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; diff --git a/executable_semantics/interpreter/interpreter.h b/executable_semantics/interpreter/interpreter.h index 79f0a4f60fa5..047b1b70b108 100644 --- a/executable_semantics/interpreter/interpreter.h +++ b/executable_semantics/interpreter/interpreter.h @@ -10,8 +10,8 @@ #include #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; -/***** Scopes *****/ - -struct Scope { - Scope(Env values, std::list l) - : values(values), locals(std::move(l)) {} - Env values; - std::list 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 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 todo; - // If this frame is the bottom frame of a continuation, then it stores - // the address of the continuation. - std::optional
continuation; - - Frame(std::string n, Stack s, Stack 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 values_; - std::vector alive_; -}; - struct State { Stack 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 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 *****/ diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index 303501a037ae..55aef9e65403 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -8,7 +8,6 @@ #include #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 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; diff --git a/executable_semantics/interpreter/value.h b/executable_semantics/interpreter/value.h index 66c9a899ebfa..c1a642484fcc 100644 --- a/executable_semantics/interpreter/value.h +++ b/executable_semantics/interpreter/value.h @@ -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; diff --git a/executable_semantics/syntax/BUILD b/executable_semantics/syntax/BUILD index 31fda0c45bfa..6a1b35520389 100644 --- a/executable_semantics/syntax/BUILD +++ b/executable_semantics/syntax/BUILD @@ -33,6 +33,7 @@ cc_library( "//executable_semantics/ast:declaration", "//executable_semantics/ast:expression", "//executable_semantics/interpreter", + "//executable_semantics/interpreter:typecheck", ], )