Skip to content

Commit

Permalink
WIP Blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
jonnyarnold committed Sep 25, 2017
1 parent a8752d2 commit 386699f
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 65 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ obj/vm.o: src/vm/vm.cpp src/vm/vm.h src/number.h

# The specs are built with Catch,
# a cool C++ testing framework.
VM_SPECS=obj/vm/bool.spec.o obj/vm/number.spec.o obj/vm/load.spec.o obj/vm/call.spec.o obj/vm/eq.spec.o obj/vm/when.spec.o
VM_SPECS=obj/vm/bool.spec.o obj/vm/number.spec.o obj/vm/load.spec.o obj/vm/call.spec.o obj/vm/eq.spec.o obj/vm/when.spec.o obj/vm/block.spec.o
E2E_SPECS=obj/e2e/bool.spec.o obj/e2e/language.spec.o obj/e2e/number.spec.o obj/e2e/value.spec.o obj/e2e/def.spec.o obj/e2e/block.spec.o

tmp/spec: spec/spec.cpp $(VM_SPECS) $(E2E_SPECS) tmp/cli.o obj/exec.o obj/parser.o tmp/parse.o obj/codegen.o obj/bytecode.o obj/vm.o tmp/lex.cpp
Expand Down
10 changes: 5 additions & 5 deletions spec/e2e/block.spec.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#include "spec/spec.h"

// TEST_CASE("Block set/get") {
// REQUIRE(resultOf("x = {}; x") == "{}");
// REQUIRE(resultOf("x = { a = 1 }; x.a") == "1");
// }
TEST_CASE("Block set/get") {
REQUIRE(resultOf("x = {}; x")->toString() == "{}");
REQUIRE(resultOf("x = { a = 1 }; x.a")->asNumber() == fn::Number(0, 1));
}

// TEST_CASE("Assignment outside of block") {
// REQUIRE(resultOf("x = {}; x.y = 1; x.y") == "1");
// REQUIRE(resultOf("x = {}; x.y = 1; x.y")->asNumber() == fn::Number(0, 1));
// }

// TEST_CASE("Nested blocks") {
Expand Down
4 changes: 2 additions & 2 deletions spec/e2e/def.spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ TEST_CASE("Fn set/call") {
// }

// x.y(1)
// )") == "2");
// )")->asNumber() == fn::Number(0, 2));
// }

// TEST_CASE("Fn call within block") {
Expand All @@ -31,7 +31,7 @@ TEST_CASE("Fn set/call") {
// }

// (x(1)).foo(1)
// )") == "2");
// )")->asNumber() == fn::Number(0, 2));
// }

// There was a time when we weren't passing params
Expand Down
34 changes: 34 additions & 0 deletions spec/vm/block.spec.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "spec/spec.h"

TEST_CASE("NEW_FRAME COMPRESS returns to original frame") {
bytecode::CodeBlob instructions = bytecode::CodeBlob{
bytecode::iTrue(),
bytecode::iNewFrame(),
bytecode::iCompress()
};

REQUIRE(resultOf(instructions)->asBool() == true);
}

TEST_CASE("NEW_FRAME COMPRESS EXPAND uses new frame") {
bytecode::CodeBlob instructions = bytecode::CodeBlob{
bytecode::iNewFrame(),
bytecode::iTrue(),
bytecode::iCompress(),
bytecode::iExpand()
};

REQUIRE(resultOf(instructions)->asBool() == true);
}

TEST_CASE("Dereference") {
bytecode::CodeBlob instructions = bytecode::CodeBlob{
bytecode::iNewFrame(),
bytecode::iTrue(),
bytecode::iCompress(),
bytecode::iExpand(),
bytecode::iReturnLast()
};

REQUIRE(resultOf(instructions)->asBool() == true);
}
30 changes: 30 additions & 0 deletions src/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,36 @@ namespace fn { namespace ast {
}
};

// Represents a collection of statements which,
// when executed, return a value.
//
// They can be thought of as zero-arity, immediately executed
// function closures, if you're feeling masochistic.
class BlockValue : public Value {
public:
std::vector<Statement*> statements;

BlockValue(std::vector<Statement*> statements) { this->statements = statements; }
BlockValue() { this->statements = std::vector<Statement*>(); }
~BlockValue() {
for(auto statement : this->statements) { delete statement; }
}

std::string asString(int indent) override {
std::string result = "(BLOCKVALUE [\n";

for(auto statement: this->statements) {
result += std::string(indent + 2, ' ') +
statement->asString(indent + 2) +
"\n";
}

result += std::string(indent, ' ') + "])";

return result;
}
};

// Represents a boolean value.
class Bool : public Value {
public:
Expand Down
4 changes: 4 additions & 0 deletions src/bytecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,8 @@ namespace fn { namespace bytecode {

CodeBlob iJumpIfLastFalse(InstructionIndex jump) { return CodeBlob{FN_OP_FALSE_JUMP, jump}; }

CodeBlob iNewFrame() { return CodeBlob{FN_OP_NEW_FRAME}; }
CodeBlob iCompress() { return CodeBlob{FN_OP_COMPRESS}; }
CodeBlob iExpand() { return CodeBlob{FN_OP_EXPAND}; }

}}
8 changes: 8 additions & 0 deletions src/bytecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ namespace fn { namespace bytecode {

#define FN_OP_FALSE_JUMP (fn::bytecode::OpCode)(60)

#define FN_OP_NEW_FRAME (fn::bytecode::OpCode)(70)
#define FN_OP_COMPRESS (fn::bytecode::OpCode)(71)
#define FN_OP_EXPAND (fn::bytecode::OpCode)(72)


// Instruction references are given by this type.
// TODO: Expand to 32-bit.
Expand Down Expand Up @@ -142,4 +146,8 @@ namespace fn { namespace bytecode {

CodeBlob iJumpIfLastFalse(InstructionIndex jump);

CodeBlob iNewFrame();
CodeBlob iCompress();
CodeBlob iExpand();

}}
25 changes: 15 additions & 10 deletions src/codegen/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ bytecode::CodeBlob CodeGenerator::digest(ast::Statement* statement) {
try_digest_as(ast::Deref);
try_digest_as(ast::Assignment);
try_digest_as(ast::Block);
try_digest_as(ast::BlockValue);
try_digest_as(ast::Bool);
try_digest_as(ast::Number);
try_digest_as(ast::String);
Expand All @@ -37,13 +38,11 @@ bytecode::CodeBlob CodeGenerator::digest(ast::Id* id) {
}

bytecode::CodeBlob CodeGenerator::digest(ast::Deref* deref) {
// bytecode::CodeBlob blob = this->digest(deref->parent);
// TODO: Push scope!
// blob.append(this->digest(deref->child));
// TODO: Pop scope!
// return blob;

return this->digest(deref->child);
bytecode::CodeBlob blob = this->digest(deref->parent);
blob.append(bytecode::iExpand());
blob.append(this->digest(deref->child));
blob.append(bytecode::iReturnLast());
return blob;
}

bytecode::CodeBlob CodeGenerator::digest(ast::Assignment* assignment) {
Expand Down Expand Up @@ -72,13 +71,19 @@ bytecode::CodeBlob CodeGenerator::digest(ast::Assignment* assignment) {

bytecode::CodeBlob CodeGenerator::digest(ast::Block* block) {
bytecode::CodeBlob blockBlob = bytecode::CodeBlob();

// TODO: Push new scope?
for (auto statement : block->statements) {
blockBlob.append(this->digest(statement));
}
// TODO: Pop scope into variable?
return blockBlob;
}

bytecode::CodeBlob CodeGenerator::digest(ast::BlockValue* block) {
bytecode::CodeBlob blockBlob = bytecode::CodeBlob();
blockBlob.append(bytecode::iNewFrame());
for (auto statement : block->statements) {
blockBlob.append(this->digest(statement));
}
blockBlob.append(bytecode::iCompress());
return blockBlob;
}

Expand Down
1 change: 1 addition & 0 deletions src/codegen/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace fn {
bytecode::CodeBlob digest(ast::Deref* deref);
bytecode::CodeBlob digest(ast::Assignment* assignment);
bytecode::CodeBlob digest(ast::Block* block);
bytecode::CodeBlob digest(ast::BlockValue* block);
bytecode::CodeBlob digest(ast::Bool* b);
bytecode::CodeBlob digest(ast::Number* n);
bytecode::CodeBlob digest(ast::String* s);
Expand Down
10 changes: 9 additions & 1 deletion src/parser/bison.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

// AST elements
fn::ast::Block* v_block;
fn::ast::BlockValue* v_blockvalue;
fn::ast::Statement* v_statement;
fn::ast::Reference* v_reference;
fn::ast::Id* v_id;
Expand All @@ -49,6 +50,7 @@

// Non-terminal symbols.
%type <v_block> program block
%type <v_blockvalue> blockvalue
%type <v_statements> statements
%type <v_statement> statement
%type <v_reference> reference
Expand Down Expand Up @@ -106,7 +108,7 @@
| infixOperation
| reference
| functionCall
| block
| blockvalue
| functionDef
| conditional
;
Expand Down Expand Up @@ -180,6 +182,12 @@
delete $2;
}

blockvalue:
'{' statements '}' {
$$ = new fn::ast::BlockValue(*$2);
delete $2;
}

conditional:
TWHEN '{' conditions '}' {
$$ = new fn::ast::Conditional(*$3);
Expand Down
54 changes: 54 additions & 0 deletions src/vm/callframe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// The Fn VM is a basic, hand-made virtual machine.
// Using a handful of VM instructions,
// this VM is capable of running any Fn program.

#pragma once

#include <vector> // std::vector
#include <unordered_map> // std::unordered_map

#define INITIAL_VALUE_MAP_CAPACITY 4
#define INITIAL_SYMBOL_TABLE_CAPACITY 16

namespace fn {
namespace vm {
typedef std::vector<vm::Value*> ValueMap;
typedef std::unordered_map<bytecode::NameHash, vm::Value*> SymbolTable;

class CallFrame {
public:
// Denotes the place the program counter will be set to
// when RETURN_LAST is hit.
bytecode::InstructionIndex returnCounter;

// The values defined in the given frame.
vm::ValueMap values;

// The symbols defined in the given frame.
vm::SymbolTable symbols;

CallFrame(bytecode::InstructionIndex returnCounter) : CallFrame() {
this->returnCounter = returnCounter;
}

CallFrame() {
// The [] operator returns 0 if a value
// is not found. To make sure that's not
// confused with the first element, we push
// a NULL value to the 0 position.
this->values = vm::ValueMap();
this->values.reserve(INITIAL_VALUE_MAP_CAPACITY);
this->values.push_back(NULL);

this->symbols = vm::SymbolTable();
this->symbols.reserve(INITIAL_SYMBOL_TABLE_CAPACITY);
}

~CallFrame() {
for(auto value : this->values) {
if (value != NULL) { delete value; }
}
}
};
}
}
19 changes: 19 additions & 0 deletions src/vm/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

namespace fn { namespace vm {

// Forward declaration
class CallFrame;

class Value {
public:
virtual ~Value() {};
Expand All @@ -17,6 +20,7 @@ namespace fn { namespace vm {
virtual bytecode::InstructionIndex getCallCounterPos() { throw "Not callable!"; }
virtual bool isDef() { return false; }
virtual Def asDef() { throw "Not a Def!"; }
virtual CallFrame* asCallFrame() { throw "Not a CallFrame!"; }
virtual bool eq(Value* other) = 0;
virtual std::string toString() = 0;
};
Expand Down Expand Up @@ -77,4 +81,19 @@ namespace fn { namespace vm {
}
};

class CallFrameValue : public Value {
protected:
CallFrame* value;
public:
CallFrameValue(CallFrame* value) : value(value) {}
~CallFrameValue() = default;
CallFrame* asCallFrame() override { return this->value; }
std::string toString() override {
return "{}";
}
bool eq(Value* other) override {
return false;
}
};

}}
Loading

0 comments on commit 386699f

Please sign in to comment.