diff --git a/Makefile b/Makefile index d7dab71..6b8451d 100644 --- a/Makefile +++ b/Makefile @@ -1,149 +1,96 @@ -# TODO: This is getting a little unruly. -# Time to move to something a little more automated! +# Builds the fn language. -.PHONY: spec -all: bin/fn spec +### Variables: +# The compilation command. +COMPILE=g++ -std=c++11 -Wno-deprecated-register -I. +COMPILE_BIN=$(COMPILE) -L. +COMPILE_OBJ=$(COMPILE) -c -CPP_FLAGS = -g -std=c++11 -Wno-deprecated-register -INCLUDES = -I. -# Find all .cpp files in src/ and strip the front. -CPP_FILES = `find ./src -name "*.cpp" | sed 's/\.\/src\///g'` -OBJ_FILES = $(CPP_FILES:.cpp=.o) - -OBJ_DIR = obj -OBJ_FILENAMES = lex.o parse.o parser.o value.o def.o list.o world.o ast.o machine.o number.o string.o exec.o -MAIN_OBJ_FILENAMES = $(OBJ_FILENAMES) main.o -MAIN_OBJS = $(patsubst %,$(OBJ_DIR)/%,$(MAIN_OBJ_FILENAMES)) - -SPEC_OBJ_FILENAMES = $(OBJ_FILENAMES) spec.o language.spec.o world.spec.o bool.spec.o def.spec.o list.spec.o number.spec.o block.spec.o string.spec.o value.spec.o -SPEC_OBJS = $(patsubst %,$(OBJ_DIR)/%,$(SPEC_OBJ_FILENAMES)) - -### MAIN ### - -bin/fn: $(MAIN_OBJS) - g++ $(CPP_FLAGS) -o $@ $(MAIN_OBJS) - -tmp/lex.cpp: src/parser/flex.cpp - flex -o tmp/lex.cpp --header-file=tmp/lex.h src/parser/flex.cpp - -$(OBJ_DIR)/lex.o: tmp/lex.cpp tmp/parse.cpp - g++ -c -o $@ tmp/lex.cpp $(CPP_FLAGS) $(INCLUDES) - -tmp/parse.cpp: src/parser/bison.cpp - bison --report=state -o tmp/parse.cpp --defines=tmp/parse.h src/parser/bison.cpp - -$(OBJ_DIR)/parse.o: tmp/parse.cpp tmp/lex.cpp - g++ -c -o $@ tmp/parse.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/parser.o: src/parser.cpp - g++ -c -o $@ src/parser.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/value.o: src/interpreter/objects/value.cpp - g++ -c -o $@ src/interpreter/objects/value.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/block.o: src/interpreter/objects/block.cpp - g++ -c -o $@ src/interpreter/objects/block.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/def.o: src/interpreter/objects/def.cpp - g++ -c -o $@ src/interpreter/objects/def.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/list.o: src/interpreter/objects/list.cpp - g++ -c -o $@ src/interpreter/objects/list.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/world.o: src/interpreter/world.cpp - g++ -c -o $@ src/interpreter/world.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/number.o: src/interpreter/objects/number.cpp - g++ -c -o $@ src/interpreter/objects/number.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/string.o: src/interpreter/objects/string.cpp - g++ -c -o $@ src/interpreter/objects/string.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/machine.o: src/interpreter/machine.cpp - g++ -c -o $@ src/interpreter/machine.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/ast.o: src/ast.cpp - g++ -c -o $@ src/ast.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/cli.o: src/cli.cpp - g++ -c -o $@ src/cli.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/main.o: src/main.cpp - g++ -c -o $@ src/main.cpp $(CPP_FLAGS) $(INCLUDES) - -$(OBJ_DIR)/exec.o: src/exec.cpp - g++ -c -o $@ src/exec.cpp $(CPP_FLAGS) $(INCLUDES) +### Makefile Tasks: +# `all` is the default, and will build in development mode: +all: test +# `test` will compile fn with debugging symbols and build the specs. +test: COMPILE+=-g +test: bin/fn tmp/spec + ./tmp/spec -### SPECS ### +# TODO: Production compilation -spec: tmp/spec - ./tmp/spec +# `clean` removes all build artifacts and temporary files. +clean: + rm -rf tmp/* obj/* bin/* -tmp/spec: $(SPEC_OBJS) - g++ $(CPP_FLAGS) -o $@ $(SPEC_OBJS) +# There are two `watch` tasks, depending on the environment. +# (Sorry Windows, nothing for you here.) +watch-mac: + fswatch -or src spec | xargs -I{} make spec +watch-linux: + while inotifywait -qq -e close_write -r .; do make; done -$(OBJ_DIR)/spec.o: spec/spec.cpp - g++ -c -o $@ spec/spec.cpp $(CPP_FLAGS) $(INCLUDES) -$(OBJ_DIR)/language.spec.o: spec/e2e/language.spec.cpp - g++ -c -o $@ spec/e2e/language.spec.cpp $(CPP_FLAGS) $(INCLUDES) -$(OBJ_DIR)/world.spec.o: spec/e2e/world.spec.cpp - g++ -c -o $@ spec/e2e/world.spec.cpp $(CPP_FLAGS) $(INCLUDES) +# The fn language binary is a Command Line Interface (CLI) +# that executes Fn code. +# +# TODO: Tidy this up! Can't we get some clever partial compilation somewhere? +bin/fn: src/main.cpp tmp/cli.o obj/exec.o obj/exec.o obj/parser.o tmp/parse.o obj/codegen.o obj/vm.o tmp/lex.cpp + $(COMPILE_BIN) -o $@ $^ -$(OBJ_DIR)/bool.spec.o: spec/e2e/bool.spec.cpp - g++ -c -o $@ spec/e2e/bool.spec.cpp $(CPP_FLAGS) $(INCLUDES) +# The Command Line Interface is built as an object to avoid +# a double-definition of main() in the specs. +tmp/cli.o: src/cli.cpp src/cli.h obj/exec.o + $(COMPILE_OBJ) -o $@ src/cli.cpp -$(OBJ_DIR)/def.spec.o: spec/e2e/def.spec.cpp - g++ -c -o $@ spec/e2e/def.spec.cpp $(CPP_FLAGS) $(INCLUDES) +# To execute Fn code, we do it in three steps: +# +# - parser: convert Fn code to an AST. +# - codegen: convert an AST into VM instructions. +# - vm: execute VM instructions. +obj/exec.o: src/exec.cpp src/exec.h obj/parser.o obj/codegen.o obj/vm.o + $(COMPILE_OBJ) -o $@ src/exec.cpp -$(OBJ_DIR)/list.spec.o: spec/e2e/list.spec.cpp - g++ -c -o $@ spec/e2e/list.spec.cpp $(CPP_FLAGS) $(INCLUDES) -$(OBJ_DIR)/number.spec.o: spec/e2e/number.spec.cpp - g++ -c -o $@ spec/e2e/number.spec.cpp $(CPP_FLAGS) $(INCLUDES) -$(OBJ_DIR)/block.spec.o: spec/e2e/block.spec.cpp - g++ -c -o $@ spec/e2e/block.spec.cpp $(CPP_FLAGS) $(INCLUDES) +# The parser uses Flex and Bison to build a parser; +# we tack a C++ interface onto it. +obj/parser.o: src/parser/parser.cpp src/parser/parser.h tmp/parse.o + $(COMPILE_OBJ) -o $@ src/parser/parser.cpp -$(OBJ_DIR)/string.spec.o: spec/e2e/string.spec.cpp - g++ -c -o $@ spec/e2e/string.spec.cpp $(CPP_FLAGS) $(INCLUDES) +tmp/parse.o: tmp/parse.cpp tmp/parse.h tmp/lex.cpp tmp/lex.h + $(COMPILE_OBJ) -o $@ tmp/parse.cpp -$(OBJ_DIR)/value.spec.o: spec/e2e/value.spec.cpp - g++ -c -o $@ spec/e2e/value.spec.cpp $(CPP_FLAGS) $(INCLUDES) +tmp/lex.cpp tmp/lex.h: src/parser/flex.cpp + flex -o tmp/lex.cpp --header-file=tmp/lex.h $^ +tmp/parse.cpp tmp/parse.h: src/parser/bison.cpp + bison --report=state -o tmp/parse.cpp --defines=tmp/parse.h $^ -### DEV TOOLS ### -clean: - rm -rf tmp/* obj/* bin/* +# The Code Generator converts the AST +# into VM instructions. +obj/codegen.o: src/codegen/codegen.cpp src/codegen/codegen.h + $(COMPILE_OBJ) -o $@ src/codegen/codegen.cpp -watch-mac: # Mac-specific! - fswatch -or src spec | xargs -I{} make spec -watch-linux: - while inotifywait -qq -e close_write -r .; do make; done +# The VM is homegrown, baby! +obj/vm.o: src/vm/vm.cpp src/vm/vm.h src/vm/number.h + $(COMPILE_OBJ) -o $@ src/vm/vm.cpp -### VM STUFF ### -obj/vm.o: src/vm/vm.cpp src/vm/vm.h src/vm/number.h - g++ -c -o $@ src/vm/vm.cpp $(CPP_FLAGS) $(INCLUDES) +# The specs are built with Catch, +# a cool C++ testing framework. +tmp/spec: spec/spec.cpp spec/spec.h obj/bool.spec.o obj/number.spec.o obj/vm.o + $(COMPILE) -o $@ $^ obj/bool.spec.o: spec/spec.cpp spec/spec.h spec/vm/bool.spec.cpp - g++ -c -o $@ spec/vm/bool.spec.cpp $(CPP_FLAGS) $(INCLUDES) + $(COMPILE_OBJ) -o $@ spec/vm/bool.spec.cpp obj/number.spec.o: spec/spec.cpp spec/spec.h spec/vm/number.spec.cpp - g++ -c -o $@ spec/vm/number.spec.cpp $(CPP_FLAGS) $(INCLUDES) - -tmp/vm_spec: obj/vm.o obj/bool.spec.o obj/number.spec.o obj/spec.o - g++ $(CPP_FLAGS) -o $@ $^ - -vm: tmp/vm_spec - ./tmp/vm_spec + $(COMPILE_OBJ) -o $@ spec/vm/number.spec.cpp diff --git a/fn b/fn deleted file mode 120000 index 39cddee..0000000 --- a/fn +++ /dev/null @@ -1 +0,0 @@ -bin/fn \ No newline at end of file diff --git a/spec/spec.h b/spec/spec.h index 0a8960a..eb75083 100644 --- a/spec/spec.h +++ b/spec/spec.h @@ -1,5 +1,4 @@ -#ifndef HELPERS -#define HELPERS +#pragma once #include "vendor/catch.h" // Needed by all specs. // #include "src/errors.h" @@ -9,5 +8,3 @@ // // Expect a code listing to fail. // bool failure(const char code[]); - -#endif diff --git a/spec/vm/bool.spec.cpp b/spec/vm/bool.spec.cpp index 4ca9d6a..dfbe77d 100644 --- a/spec/vm/bool.spec.cpp +++ b/spec/vm/bool.spec.cpp @@ -1,136 +1,138 @@ #include "spec/spec.h" #include "src/vm/vm.h" +using namespace fn; + TEST_CASE("BOOL false") { - fnByte instructions[1] = {FN_OP_FALSE}; + bytecode::CodeByte instructions[1] = {FN_OP_FALSE}; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)1); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)1); REQUIRE(result.asBool == false); } TEST_CASE("BOOL true") { - fnByte instructions[1] = {FN_OP_TRUE}; + bytecode::CodeByte instructions[1] = {FN_OP_TRUE}; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)1); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)1); REQUIRE(result.asBool == true); } TEST_CASE("AND true true") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_TRUE, FN_OP_TRUE, FN_OP_AND, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == true); } TEST_CASE("AND true false") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_TRUE, FN_OP_FALSE, FN_OP_AND, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == false); } TEST_CASE("AND false true") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_FALSE, FN_OP_TRUE, FN_OP_AND, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == false); } TEST_CASE("AND false false") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_FALSE, FN_OP_FALSE, FN_OP_AND, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == false); } TEST_CASE("OR true true") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_TRUE, FN_OP_TRUE, FN_OP_OR, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == true); } TEST_CASE("OR true false") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_TRUE, FN_OP_FALSE, FN_OP_OR, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == true); } TEST_CASE("OR false true") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_FALSE, FN_OP_TRUE, FN_OP_OR, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == true); } TEST_CASE("OR false false") { - fnByte instructions[5] = { + bytecode::CodeByte instructions[5] = { FN_OP_FALSE, FN_OP_FALSE, FN_OP_OR, 0, 1 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)5); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)5); REQUIRE(result.asBool == false); } TEST_CASE("NOT true") { - fnByte instructions[3] = { + bytecode::CodeByte instructions[3] = { FN_OP_TRUE, FN_OP_NOT, 0 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)3); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)3); REQUIRE(result.asBool == false); } TEST_CASE("NOT false") { - fnByte instructions[3] = { + bytecode::CodeByte instructions[3] = { FN_OP_FALSE, FN_OP_NOT, 0 }; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)3); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)3); REQUIRE(result.asBool == true); } diff --git a/spec/vm/number.spec.cpp b/spec/vm/number.spec.cpp index d9b26e6..9a31a01 100644 --- a/spec/vm/number.spec.cpp +++ b/spec/vm/number.spec.cpp @@ -1,410 +1,412 @@ #include "spec/spec.h" #include "src/vm/vm.h" +using namespace fn; + TEST_CASE("NUMBER 1") { - fnByte instructions[8]; + bytecode::CodeByte instructions[8]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)1; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)8); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)8); REQUIRE(result.asNumber.coefficient == 1); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("NUMBER 10") { - fnByte instructions[8]; + bytecode::CodeByte instructions[8]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)1; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)8); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)8); REQUIRE(result.asNumber.coefficient == 1); REQUIRE(result.asNumber.exponent == 1); } TEST_CASE("NUMBER -1") { - fnByte instructions[8]; + bytecode::CodeByte instructions[8]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)-1; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)-1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)8); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)8); REQUIRE(result.asNumber.coefficient == -1); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("NUMBER 0.1") { - fnByte instructions[8]; + bytecode::CodeByte instructions[8]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)-1; - instructions[2] = (fnCoefficient)1; + instructions[1] = (vm::number::Exponent)-1; + instructions[2] = (vm::number::Coefficient)1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)8); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)8); REQUIRE(result.asNumber.coefficient == 1); REQUIRE(result.asNumber.exponent == -1); } TEST_CASE("NUMBER -0.1") { - fnByte instructions[8]; + bytecode::CodeByte instructions[8]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)-1; - instructions[2] = (fnCoefficient)-1; + instructions[1] = (vm::number::Exponent)-1; + instructions[2] = (vm::number::Coefficient)-1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)8); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)8); REQUIRE(result.asNumber.coefficient == -1); REQUIRE(result.asNumber.exponent == -1); } TEST_CASE("MULTIPLY 2 2") { - fnByte instructions[11]; + bytecode::CodeByte instructions[11]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_MULTIPLY; instructions[9] = 0; instructions[10] = 0; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)11); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)11); REQUIRE(result.asNumber.coefficient == 4); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("MULTIPLY 2 -2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)-2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)-2; instructions[16] = FN_OP_MULTIPLY; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == -4); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("MULTIPLY 20 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_MULTIPLY; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 4); REQUIRE(result.asNumber.exponent == 1); } TEST_CASE("MULTIPLY 0.2 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)-1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)-1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_MULTIPLY; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 4); REQUIRE(result.asNumber.exponent == -1); } TEST_CASE("MULTIPLY -20 -0.2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)-2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)-2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)-1; - instructions[10] = (fnCoefficient)-2; + instructions[9] = (vm::number::Exponent)-1; + instructions[10] = (vm::number::Coefficient)-2; instructions[16] = FN_OP_MULTIPLY; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 4); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("DIVIDE 2 2") { - fnByte instructions[11]; + bytecode::CodeByte instructions[11]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_DIVIDE; instructions[9] = 0; instructions[10] = 0; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)11); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)11); REQUIRE(result.asNumber.coefficient == 1); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("DIVIDE 2 -2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)-2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)-2; instructions[16] = FN_OP_DIVIDE; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == -1); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("DIVIDE 20 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_DIVIDE; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 1); REQUIRE(result.asNumber.exponent == 1); } TEST_CASE("DIVIDE 0.2 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)-1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)-1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_DIVIDE; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 1); REQUIRE(result.asNumber.exponent == -1); } TEST_CASE("DIVIDE -20 -0.2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)-2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)-2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)-1; - instructions[10] = (fnCoefficient)-2; + instructions[9] = (vm::number::Exponent)-1; + instructions[10] = (vm::number::Coefficient)-2; instructions[16] = FN_OP_DIVIDE; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 1); REQUIRE(result.asNumber.exponent == 2); } TEST_CASE("ADD 2 2") { - fnByte instructions[11]; + bytecode::CodeByte instructions[11]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_ADD; instructions[9] = 0; instructions[10] = 0; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)11); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)11); REQUIRE(result.asNumber.coefficient == 4); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("ADD 20 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_ADD; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 22); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("ADD 20 0.2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)-1; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)-1; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_ADD; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 202); REQUIRE(result.asNumber.exponent == -1); } TEST_CASE("ADD -2 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)-2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)-2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_ADD; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 0); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("ADD -20 -0.2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)-2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)-2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)-1; - instructions[10] = (fnCoefficient)-2; + instructions[9] = (vm::number::Exponent)-1; + instructions[10] = (vm::number::Coefficient)-2; instructions[16] = FN_OP_ADD; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == -202); REQUIRE(result.asNumber.exponent == -1); } TEST_CASE("SUBTRACT 2 2") { - fnByte instructions[11]; + bytecode::CodeByte instructions[11]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_SUBTRACT; instructions[9] = 0; instructions[10] = 0; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)11); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)11); REQUIRE(result.asNumber.coefficient == 0); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("SUBTRACT 20 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_SUBTRACT; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 18); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("SUBTRACT 20 0.2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)-1; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)-1; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_SUBTRACT; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == 198); REQUIRE(result.asNumber.exponent == -1); } TEST_CASE("SUBTRACT -2 2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)0; - instructions[2] = (fnCoefficient)-2; + instructions[1] = (vm::number::Exponent)0; + instructions[2] = (vm::number::Coefficient)-2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)0; - instructions[10] = (fnCoefficient)2; + instructions[9] = (vm::number::Exponent)0; + instructions[10] = (vm::number::Coefficient)2; instructions[16] = FN_OP_SUBTRACT; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == -4); REQUIRE(result.asNumber.exponent == 0); } TEST_CASE("SUBTRACT -20 -0.2") { - fnByte instructions[19]; + bytecode::CodeByte instructions[19]; instructions[0] = FN_OP_NUMBER; - instructions[1] = (fnExponent)1; - instructions[2] = (fnCoefficient)-2; + instructions[1] = (vm::number::Exponent)1; + instructions[2] = (vm::number::Coefficient)-2; instructions[8] = FN_OP_NUMBER; - instructions[9] = (fnExponent)-1; - instructions[10] = (fnCoefficient)-2; + instructions[9] = (vm::number::Exponent)-1; + instructions[10] = (vm::number::Coefficient)-2; instructions[16] = FN_OP_SUBTRACT; instructions[17] = 0; instructions[18] = 1; - fnVM vm = fnVM(); - fnVMValue result = vm.run(instructions, (size_t)19); + VM vm = VM(); + vm::Value result = vm.run(instructions, (size_t)19); REQUIRE(result.asNumber.coefficient == -198); REQUIRE(result.asNumber.exponent == -1); } diff --git a/src/ast.h b/src/ast.h index 7bf820c..4894e2f 100644 --- a/src/ast.h +++ b/src/ast.h @@ -1,234 +1,283 @@ -#ifndef FN_AST -#define FN_AST - -#include -#include - -// FORWARD DECLARATIONS -// See fn.runtime.h -class fnMachine; -class fnValue; - -class astStatement { -public: - virtual ~astStatement() {}; - virtual fnValue* execute(fnMachine*) = 0; - virtual std::string asString(int indent) = 0; - std::string asString() { - return this->asString(0); - } -}; - -class astValue : public astStatement { -public: - virtual ~astValue() {}; - virtual fnValue* execute(fnMachine*) = 0; - virtual std::string asString(int indent) = 0; -}; - -class astReference : public astValue { -public: - virtual ~astReference() {}; - virtual fnValue* execute(fnMachine*) = 0; - virtual std::string asString(int indent) = 0; -}; - -class astId : public astReference { -public: - std::string* name; - astId(std::string* name) { this->name = name; } - ~astId() { delete this->name; } - fnValue* execute(fnMachine*) override; - - virtual std::string asString(int indent) override { - return *this->name; - } -}; - -class astDeref : public astReference { -public: - astValue* parent; - astValue* child; - astDeref(astValue* parent, astValue* child) { this->parent = parent; this->child = child; } - ~astDeref() { delete this->child; delete this->parent; } - fnValue* execute(fnMachine*) override; - - virtual std::string asString(int indent) override { - return "(DEREF parent:" + this->parent->asString(indent) + " child:" + this->child->asString(indent) + ")"; - } -}; - -class astBlock : public astValue { - std::vector statements; - -public: - astBlock(std::vector statements) { this->statements = statements; } - astBlock() {} - - ~astBlock() { - this->statements.clear(); - } - - virtual fnValue* execute(fnMachine*) override; - fnValue* executeStatements(fnMachine*); - - int size() { return statements.size(); } - - virtual std::string asString(int indent) override { - std::string result = "(BLOCK [\n"; - - for(auto statement: this->statements) { - result += std::string(indent + 2, ' ') + statement->asString(indent + 2) + "\n"; - } - - result += std::string(indent, ' ') + "])"; - - return result; - } -}; - -class astAssignment : public astStatement { -public: - astReference* key; - astValue* value; - astAssignment(astReference* key, astValue* value) { this->key = key; this->value = value; } - ~astAssignment() { delete this->value; delete this->key; } - virtual fnValue* execute(fnMachine*) override; - - virtual std::string asString(int indent) override { - return "(ASSIGNMENT id:" + this->key->asString(indent) + " value:" + this->value->asString(indent) + ")"; - } -}; - -class astInt : public astValue { - int value; - -public: - astInt(int value) { this->value = value; } - virtual fnValue* execute(fnMachine*) override; - - virtual std::string asString(int indent) override { - return "(INT " + std::to_string(this->value) + ")"; - } -}; - -class astDouble : public astValue { - double value; - -public: - astDouble(double value) { this->value = value; } - virtual fnValue* execute(fnMachine*) override; +// An Abstract Syntax Tree is used +// to represent a Fn program in a tree format; +// this provides the right ordering when +// generating code. +// +// This is the data structure that links the parser +// and code generator. + +#pragma once + +#include // std::string +#include // std::vector + +// #include "src/bytecode.h" // CodeByte +// #include "src/codegen.h" // Generator + +namespace fn { namespace ast { + + // Base class for all statements. + class Statement { + public: + //virtual bytecode::CodeByte* codegen(codegen::Generator*) = 0; + virtual std::string asString(int indent) = 0; + std::string asString() { return this->asString(0); } + }; + + // Base class for all statements + // that refer to values. + class Value : public Statement { + public: + //virtual bytecode::CodeByte* codegen(codegen::Generator*) = 0; + virtual std::string asString(int indent) = 0; + }; + + // Base class for all statements + // that alias defined values. + class Reference : public Value { + public: + //virtual bytecode::CodeByte* codegen(codegen::Generator*) = 0; + virtual std::string asString(int indent) = 0; + }; + + // Represents a variable name. + class Id : public Reference { + public: + std::string name; + Id(std::string name) { this->name = name; } + ~Id() = default; + + //bytecode::CodeByte* codegen(codegen::Generator*) override; + + std::string asString(int indent) override { + return "(ID " + this->name + ")"; + } + }; + + // Represents a nested variable reference. + // (The variable exists within another value.) + class Deref : public Reference { + public: + Value* parent; + Value* child; + Deref(Value* parent, Value* child) { + this->parent = parent; + this->child = child; + } + ~Deref() { delete this->child; delete this->parent; } + + //bytecode::CodeByte* codegen(codegen::Generator*) override; + + std::string asString(int indent) override { + return "(DEREF parent:" + + this->parent->asString(indent) + + " child:" + + this->child->asString(indent) + ")"; + } + }; + + // Represents the definition of a variable. + class Assignment : public Statement { + public: + Reference* key; + Value* value; + Assignment(Reference* key, Value* value) { + this->key = key; + this->value = value; + } + ~Assignment() { delete this->value; delete this->key; } + + //bytecode::CodeByte* codegen(codegen::Generator*) override; + + virtual std::string asString(int indent) override { + return "(ASSIGNMENT id:" + this->key->asString(indent) + " value:" + this->value->asString(indent) + ")"; + } + }; + + // 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 Block : public Value { + std::vector statements; + + public: + Block(std::vector statements) { this->statements = statements; } + Block() { this->statements = std::vector(); } + ~Block() { this->statements.clear(); } + + //bytecode::CodeByte* codegen(codegen::Generator*) override; + //bytecode::CodeByte* executeStatements(codegen::Generator*); + + std::string asString(int indent) override { + std::string result = "(BLOCK [\n"; + + for(auto statement: this->statements) { + result += std::string(indent + 2, ' ') + + statement->asString(indent + 2) + + "\n"; + } - virtual std::string asString(int indent) override { - return "(DOUBLE " + std::to_string(this->value) + ")"; - } -}; + result += std::string(indent, ' ') + "])"; -class astString : public astValue { - std::string* value; + return result; + } + }; + + // Represents a boolean value. + class Bool : public Value { + bool value; -public: - astString(std::string* value) { this->value = value; } - ~astString() { delete this->value; } - virtual fnValue* execute(fnMachine*) override; + public: + Bool(bool value) { this->value = value; } + ~Bool() = default; - virtual std::string asString(int indent) override { - return "(STRING " + *this->value + ")"; - } -}; + //bytecode::CodeByte* codegen(codegen::Generator*) override; -class astBool : public astValue { - bool value; + std::string asString(int indent) override { + return "(BOOL " + std::to_string(this->value) + ")"; + } + }; -public: - astBool(bool value) { this->value = value; } - virtual fnValue* execute(fnMachine*) override; + // Represents a numeric value. + class Number : public Value { + double value; - virtual std::string asString(int indent) override { - return "(BOOL " + std::to_string(this->value) + ")"; - } -}; + public: + Number(double value) { this->value = value; } + ~Number() = default; -class astFnCall : public astValue { - astReference* target; - std::vector* args; + //bytecode::CodeByte* codegen(codegen::Generator*) override; -public: - astFnCall(astReference* target, std::vector* args) { this->target = target; this->args = args; } - ~astFnCall() { delete this->args; delete this->target; } - virtual fnValue* execute(fnMachine*) override; + std::string asString(int indent) override { + return "(NUMBER " + std::to_string(this->value) + ")"; + } + }; - virtual std::string asString(int indent) override { - std::string result = "(CALL target:" + this->target->asString(indent) + " args:[\n"; + // Represents a string of characters. + class String : public Value { + std::string value; - for(auto arg: *this->args) { - result += std::string(indent + 2, ' ') + arg->asString(indent + 2) + "\n"; + public: + String(std::string value) { this->value = value; } + ~String() = default; + + //bytecode::CodeByte* codegen(codegen::Generator*) override; + + std::string asString(int indent) override { + return "(STRING " + this->value + ")"; } + }; - result += std::string(indent, ' ') + "])"; + // Represents a call (request to execute) to a function. + class Call : public Value { + Reference* target; + std::vector args; - return result; - } -}; + public: + Call(Reference* target, std::vector args) { + this->target = target; + this->args = args; + } + ~Call() { this->args.clear(); delete this->target; } + + //bytecode::CodeByte* codegen(codegen::Generator*) override; -class astFnDef : public astValue { - std::vector* params; - astBlock* body; + std::string asString(int indent) override { + std::string result = "(CALL target:" + + this->target->asString(indent) + + " args:[\n"; -public: - astFnDef(std::vector* params, astBlock* body) { this->params = params; this->body = body; } - ~astFnDef() { delete this->body; delete this->params; } - virtual fnValue* execute(fnMachine*) override; + for(auto arg: this->args) { + result += std::string(indent + 2, ' ') + arg->asString(indent + 2) + "\n"; + } - virtual std::string asString(int indent) override { - std::string result = "(DEF params:[\n"; + result += std::string(indent, ' ') + "])"; - for(auto param: *this->params) { - result += std::string(indent + 2, ' ') + param + "\n"; + return result; } + }; - result += std::string(indent, ' ') + "] body:" + this->body->asString(indent) + ")"; + // Represents a function definition (sometimes known as a prototype). + class Def : public Value { + std::vector params; + Block* body; - return result; - } -}; + public: + Def(std::vector params, Block* body) { + this->params = params; + this->body = body; + } + ~Def() { delete this->body; } -class astCondition : public astStatement { - astValue* test; - astBlock* body; + //bytecode::CodeByte* codegen(codegen::Generator*) override; -public: - astCondition(astValue* test, astBlock* body) { this->test = test; this->body = body; } - ~astCondition() { delete this->body; delete this->test; } - virtual fnValue* execute(fnMachine*) override; + virtual std::string asString(int indent) override { + std::string result = "(DEF params:[\n"; - virtual std::string asString(int indent) override { - return "(COND test:" + - this->test->asString(indent) + - " body:" + - this->body->asString(indent) + ")"; - } -}; + for(auto param: this->params) { + result += std::string(indent + 2, ' ') + param + "\n"; + } -class astConditional : public astValue { - std::vector* conditions; + result += std::string(indent, ' ') + + "] body:" + + this->body->asString(indent) + + ")"; -public: - astConditional(std::vector* conditions) { this->conditions = conditions; } - ~astConditional() { delete this->conditions; } - fnValue* execute(fnMachine*) override; + return result; + } + }; + + // Used within Conditionals; represents a test + // and the code to execute if the test passes. + class Condition : public Statement { + Value* test; + Block* body; + + public: + Condition(Value* test, Block* body) { + this->test = test; + this->body = body; + } + ~Condition() { delete this->body; delete this->test; } + + //bytecode::CodeByte* codegen(codegen::Generator*) override; + + std::string asString(int indent) override { + return "(COND test:" + + this->test->asString(indent) + + " body:" + + this->body->asString(indent) + ")"; + } + }; - virtual std::string asString(int indent) override { - std::string result = "(WHEN [\n"; + // Represents a collection of Conditions + // (conditionally executed code paths). + class Conditional : public Value { + std::vector conditions; - for(auto condition: *this->conditions) { - result += std::string(indent + 2, ' ') + condition->asString(indent + 2) + "\n"; + public: + Conditional(std::vector conditions) { + this->conditions = conditions; } + ~Conditional() { this->conditions.clear(); } + + //bytecode::CodeByte* codegen(codegen::Generator*) override; - result += std::string(indent, ' ') + "])"; + std::string asString(int indent) override { + std::string result = "(WHEN [\n"; - return result; - } -}; + for(auto condition: this->conditions) { + result += std::string(indent + 2, ' ') + + condition->asString(indent + 2) + + "\n"; + } + + result += std::string(indent, ' ') + "])"; + + return result; + } + }; -#endif +}} diff --git a/src/bytecode.h b/src/bytecode.h new file mode 100644 index 0000000..febb563 --- /dev/null +++ b/src/bytecode.h @@ -0,0 +1,33 @@ +// Fn Bytecode is the intermediate representation of Fn programs. +// It is generated by the code generator and executed by the +// virtual machine. + +#pragma once + +namespace fn { namespace bytecode { + + // The smallest unit of instruction. + // (Note that instructions are at least this size, + // but are usually more.) + typedef char CodeByte; + + // The first byte of any instruction is an OpCode. + // It tells the VM the operation to perform. + typedef CodeByte OpCode; + #define FN_OP_FALSE (fn::bytecode::OpCode)(0) + #define FN_OP_TRUE (fn::bytecode::OpCode)(1) + #define FN_OP_AND (fn::bytecode::OpCode)(2) + #define FN_OP_OR (fn::bytecode::OpCode)(3) + #define FN_OP_NOT (fn::bytecode::OpCode)(4) + + #define FN_OP_NUMBER (fn::bytecode::OpCode)(5) + #define FN_OP_MULTIPLY (fn::bytecode::OpCode)(6) + #define FN_OP_DIVIDE (fn::bytecode::OpCode)(7) + #define FN_OP_ADD (fn::bytecode::OpCode)(8) + #define FN_OP_SUBTRACT (fn::bytecode::OpCode)(9) + + #define FN_OP_STRING (fn::bytecode::OpCode)(10) + + // TODO: Instruction generators + +}} diff --git a/src/cli.cpp b/src/cli.cpp index cb7b64f..9fd8654 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -1,47 +1,38 @@ -#include -#include -#include "vendor/cxxopts.h" +// The Command Line Interface for fn, +// utilising cxxopts. -#include "src/parser.h" -#include "src/interpreter/machine.h" +#include // std::cout +#include // std::vector +#include "vendor/cxxopts.h" // cxxopts -#include "src/exec.h" -#include "src/errors.h" +#include "src/exec.h" // fn::exec + +using namespace fn; int run(const char fileName[], bool debug) { - try { - exec(fileName, debug); - return 0; - } catch (FnError e) { - std::cout << e.what(); - return -1; - } + exec(fileName, debug); + return 0; } int repl(bool debug) { - fnParser* parser = new fnParser(debug); - fnMachine* context = new fnMachine(debug); + Execution context = Execution(debug); std::string currentLine; - fnValue* lastReturnValue; + vm::Value lastReturnValue; // Start the REPL loop std::cout << "fn REPL\nCTRL+C to exit\n"; while(true) { + // Read std::cout << "> "; std::getline(std::cin, currentLine); - // Parse - astBlock* command = parser->parseCode(currentLine); + // Eval + lastReturnValue = context.exec(currentLine); - // If we used astBlock::execute, we'd run our code in - // an isolated block, which we don't want to do in the REPL. - // So we run it manually! - lastReturnValue = command->executeStatements(context); - - std::cout << lastReturnValue->asString() << std::endl; + // Print + std::cout << lastReturnValue.toString() << std::endl; } - } int parseCli(int argc, char* argv[]) diff --git a/src/cli.h b/src/cli.h index dbefadf..aae9960 100644 --- a/src/cli.h +++ b/src/cli.h @@ -1,8 +1,4 @@ -#ifndef FN_CLI -#define FN_CLI - -#include "src/ast.h" +// The Command Line Interface for fn, +// utilising cxxopts. int parseCli(int argc, char* argv[]); - -#endif diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp new file mode 100644 index 0000000..4613aa7 --- /dev/null +++ b/src/codegen/codegen.cpp @@ -0,0 +1,5 @@ +#include "src/codegen/codegen.h" + +void fn::CodeGenerator::digest(ast::Statement* code) { + // TODO! +}; diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h new file mode 100644 index 0000000..a49dec2 --- /dev/null +++ b/src/codegen/codegen.h @@ -0,0 +1,26 @@ +// The Fn Code Generator converts an AST into a set of instructions. + +#pragma once + +#include "src/ast.h" +#include "src/bytecode.h" + +namespace fn { + + // A Generator is used to generate instructions + // from a Fn AST. + class CodeGenerator { + bool debug; + + public: + bytecode::CodeByte* instructions; + unsigned int instructionByteCount; + + CodeGenerator(bool debug) { this->debug = debug; } + CodeGenerator() : CodeGenerator(false) {} + + // Return the instructions for the given statement. + void digest(ast::Statement* code); + }; + +} diff --git a/src/errors.h b/src/errors.h deleted file mode 100644 index 129cc16..0000000 --- a/src/errors.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include - -// Denotes a location in the code. -class CodeLocation { -public: - std::string fileName; - int line; - - CodeLocation(std::string fileName, int line) { - this->fileName = fileName; - this->line = line; - } - - std::string asString() { - return this->fileName; - } -}; - -// All expected exceptions from Fn -// should inherit from this type. -class FnError : public std::exception { -public: - std::string errorText; - CodeLocation* location; - - FnError(std::string errorText, CodeLocation* location) { - this->errorText = errorText; - this->location = location; - } - - FnError(std::string errorText) { - this->errorText = errorText; - } - - // std::string getMessage() const { - // std::string result = this->errorText + "\n"; - // if (this->location != NULL) { - // result += "@ " + this->location->asString() + "\n"; - // } - - // return result; - // } - - virtual const char* what() const throw() { - std::string result = this->errorText + "\n"; - if (this->location != NULL) { - result += "@ " + this->location->asString() + "\n"; - } - - return result.c_str(); - } - -}; - -// Called for issues when executing. -class FnRuntimeError : public FnError { - using FnError::FnError; -}; diff --git a/src/exec.cpp b/src/exec.cpp index 86a818a..2312d82 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -1,16 +1,30 @@ -#include "src/parser.h" -#include "src/interpreter/machine.h" +#include "src/exec.h" -fnValue* exec(const char fileName[], bool debug) { - fnParser* parser = new fnParser(debug); - astBlock* program = parser->parseFile(fileName); +namespace fn { - fnMachine* context = new fnMachine(debug); - fnValue* result = program->execute(context); + vm::Value exec(const char fileName[], bool debug) { + // Code -> AST + Parser parser = fn::Parser(debug); + ast::Block* program = parser.parseFile(fileName); + + // AST -> Instructions + CodeGenerator generator = fn::CodeGenerator(debug); + generator.digest(program); + + // Instructions -> Result + VM vm = fn::VM(debug); + vm::Value result = vm.run(generator.instructions, generator.instructionByteCount); + + + delete program; + return result; + } + + vm::Value Execution::exec(std::string code) { + ast::Block* program = parser.parseCode(code); + generator.digest(program); + return vm.run(generator.instructions, generator.instructionByteCount); + } - delete context; - delete program; - delete parser; - - return result; } + diff --git a/src/exec.h b/src/exec.h index 25c65d7..1942831 100644 --- a/src/exec.h +++ b/src/exec.h @@ -1,3 +1,32 @@ +// exec() and Execution combine the steps +// of processing Fn code to provide an easy interface +// for executing Fn code. + #pragma once -fnValue* exec(const char fileName[], bool debug); +#include "src/parser/parser.h" +#include "src/codegen/codegen.h" +#include "src/vm/vm.h" + +namespace fn { + vm::Value exec(const char fileName[], bool debug); + + class Execution { + bool debug; + Parser parser; + CodeGenerator generator; + VM vm; + + public: + Execution(bool debug) { + this->debug = debug; + + this->parser = Parser(debug); + this->generator = CodeGenerator(debug); + this->vm = VM(debug); + } + + vm::Value exec(std::string code); + }; +} + diff --git a/src/interpreter/machine.cpp b/src/interpreter/machine.cpp deleted file mode 100644 index cdda6d8..0000000 --- a/src/interpreter/machine.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include - -#include "src/interpreter/machine.h" - -#define DEBUG(msg) if (this->debug) { std::cout << msg << std::endl; } - -fnValue* fnMachine::pushNewScope(fnValue* parent) { - fnValue* scope = new fnValue(parent); - - this->scopeStack->push(scope); - - DEBUG("PUSH_NEW_SCOPE(parent: " << parent << ") = " << scope); - - return scope; -} - -void fnMachine::pushScopeByValue(fnValue* scope) { - this->scopeStack->push(scope); - - DEBUG("PUSH_SCOPE(" << scope->asString() << ")"); -} - -fnValue* fnMachine::currentScope() { - - // This guard should never happen. - // We always have a fnWorld on the stack to start with, - // and all other operations push and pop their own blocks - // onto the top of the stack. - if(this->scopeStack->empty()) { - std::cout << "!!! Scope stack is empty!"; - return NULL; - } - - return this->scopeStack->top(); -} - -void fnMachine::popScope() { - fnValue* poppedScope = this->scopeStack->top(); - this->scopeStack->pop(); - - DEBUG("POP_SCOPE(" << poppedScope->asString(0) << ")"); -} - -void fnMachine::setValue(std::string* name, fnValue* value) { - this->currentScope()->set(name, value); - DEBUG("SET_VALUE(name: " << *name << ", value: " << value->asString() << ")"); -} - -fnValue* fnMachine::getValueByName(std::string* name) { - fnValue* value = this->currentScope()->get(name); - - DEBUG("GET_VALUE(name: " << *name << ") = " << value->asString()); - - return value; -} - -fnValue* fnMachine::callByValue(fnValue* def, std::vector args) { - DEBUG("START_CALL(" << def->asString(0) << ")") - fnValue* value = def->call(this, args); - DEBUG("END_CALL(" << def->asString(0) << ")") - - return value; -} diff --git a/src/interpreter/machine.h b/src/interpreter/machine.h deleted file mode 100644 index efb830e..0000000 --- a/src/interpreter/machine.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include -#include - -#include "src/interpreter/world.h" -#include "src/interpreter/objects/value.h" - -// A class holding contextual information about an execution. -class fnMachine { -public: - std::stack* scopeStack; - bool debug; - - fnMachine(bool debug) { - this->debug = debug; - this->scopeStack = new std::stack(); - this->scopeStack->push(new fnWorld()); - - if(this->debug) { std::cout << "CREATE_MACHINE()" << std::endl; } - } - - fnMachine() : fnMachine(false) {} - - ~fnMachine() { - while (!this->scopeStack->empty()) { - fnValue* scope = this->scopeStack->top(); - delete scope; - this->scopeStack->pop(); - } - - delete this->scopeStack; - } - - // === - // BLOCKS - // === - - // Creates a new block on the top of the block stack. - fnValue* pushNewScope(fnValue*); - fnValue* pushNewScope() { return this->pushNewScope(this->currentScope()); } - - // Gets a value by name in the current block - // and pushes it onto the top of the block stack. - void pushScopeByValue(fnValue*); - - // Gets the block at the top of the block stack. - fnValue* currentScope(); - - // Get the top of the scope stack as a given type. - template - T currentScope(); - - // Removes the block at the top of the block stack. - void popScope(); - - - - // === - // VALUES - // === - - // Sets a value in the current block. - void setValue(std::string* name, fnValue* value); - - // Gets a value from the current block by name. - fnValue* getValueByName(std::string*); - - // Gets a value from the current block - // and calls it with the given arguments. - fnValue* callByValue(fnValue* def, std::vector args); - - void printState() { - std::stack printStack = *this->scopeStack; - - std::cout << "MACHINE_STACK = List(\n"; - - while(!printStack.empty()) { - fnValue* scope = printStack.top(); - - std::cout << " " << scope->asString(2) << ",\n"; - - printStack.pop(); - } - - std::cout << ")\n"; - } -}; diff --git a/src/interpreter/objects/bool.h b/src/interpreter/objects/bool.h deleted file mode 100644 index a421d20..0000000 --- a/src/interpreter/objects/bool.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/constant.h" - -// Represents a bool. -class fnBool : public fnConstant { - using fnConstant::fnConstant; - - virtual std::string asString(int indentationLevel) override { - return this->value ? "true" : "false"; - } - - virtual bool asBool() override { return this->value; } -}; diff --git a/src/interpreter/objects/constant.h b/src/interpreter/objects/constant.h deleted file mode 100644 index e7cd7c4..0000000 --- a/src/interpreter/objects/constant.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" - -// Represents any constant value in Fn. -// The type T is the C-type that the value -// belongs to. -template -class fnConstant : public fnValue { -public: - - // The C-value of the constant. - T value; - - fnConstant(T value) : fnValue(NULL) { this->value = value; } - - virtual std::size_t hash() override { - return (std::size_t)this->value; - } - - virtual std::string asString(int indentationLevel) override = 0; -}; diff --git a/src/interpreter/objects/def.cpp b/src/interpreter/objects/def.cpp deleted file mode 100644 index ff0762e..0000000 --- a/src/interpreter/objects/def.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "src/interpreter/objects/def.h" - -#include "src/interpreter/machine.h" - -fnValue* fnDef::call(fnMachine* context, std::vector args) { - fnValue* scope = context->pushNewScope(this->parent); - - // Set the arguments in the FnDef scope. - for(int i = 0; i < this->params->size(); i++) { - std::string paramName = (*this->params)[i]; - fnValue* paramValue = args[i]; - - context->setValue(¶mName, paramValue); - } - - // Execute the FnDef instructions. - fnValue* result = this->execute(context, args); - - context->popScope(); - - return result; -} - -std::string fnDef::asString(int indentationLevel) { - std::string result = "("; - for(auto param: (*this->params)) { - result += param; - if(param != this->params->back()) { - result += ", "; - } - } - - result += ") { ... }"; - - return result; -} - -std::string fnFnDef::asString(int indentationLevel) { - std::string result = "("; - for(auto param: (*this->params)) { - result += param; - if(param != this->params->back()) { - result += ", "; - } - } - - result += ") "; - result += this->block->asString(indentationLevel); - - return result; -} - diff --git a/src/interpreter/objects/def.h b/src/interpreter/objects/def.h deleted file mode 100644 index d628023..0000000 --- a/src/interpreter/objects/def.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "src/interpreter/objects/value.h" -#include "src/ast.h" - -// Forward declaration. -class fnMachine; - -// A vector of strings. -typedef std::vector Strings; - -// The style of function used by Fn. -typedef std::function)> fnFunc; - -// Represents a function definition. -class fnDef : public fnValue { -protected: - // The names of parameters of this definition. - Strings* params; - - // Bare function definition. - // See fnValue for more details. - fnDef() : fnValue() {} - -public: - fnDef(fnValue* parent, Strings* params) : fnValue(parent) { - this->params = params; - } - - ~fnDef() { - delete this->params; - } - - // Call is used by the machine - // to set up the arguments and - // then pass to execute(). - fnValue* call(fnMachine*, std::vector) override; - - // The operation encapsulated by this object. - virtual fnValue* execute(fnMachine*, std::vector) = 0; - - // Functions are only equal by reference. - virtual std::size_t hash() override { - return (std::size_t)(this); - } - - virtual std::string asString(int indentationLevel) override; -}; - -// Some functions need to be bare (do not have locals) -// to avoid an infinite loop on initialization. -// Check out fnValue for more info. -class bareFnDef : public fnDef { -protected: - bareFnDef(fnValue* parent, Strings* params) : fnDef() { - this->parent = parent; - this->params = params; - } - - ~bareFnDef() { - delete this->params; - } -}; - -// Denotes a fnDef whose implementation is a fn block. -class fnFnDef : public fnDef { - // The block of instructions to execute when called. - astBlock* block; - -public: - fnFnDef(astBlock* block, fnValue* parent, Strings* params) : fnDef(parent, params) { - this->block = block; - } - - ~fnFnDef() { - delete this->block; - } - - virtual fnValue* execute(fnMachine* context, std::vector values) override { - return this->block->execute(context); - } - - virtual std::string asString(int indentationLevel) override; -}; diff --git a/src/interpreter/objects/defs/and.h b/src/interpreter/objects/defs/and.h deleted file mode 100644 index d75de6b..0000000 --- a/src/interpreter/objects/defs/and.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/bool.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/machine.h" - -// Object equality comparison. -class fnAnd : public bareFnDef { - fnValue* execute(fnMachine* context, std::vector values) { - bool result = - this->parent->asBool() && - values[0]->asBool(); - - return new fnBool(result); - } - -public: - fnAnd(fnValue* parent) : bareFnDef( - parent, - new std::vector{"other"} - ) {}; -}; diff --git a/src/interpreter/objects/defs/eq.h b/src/interpreter/objects/defs/eq.h deleted file mode 100644 index bce1d6a..0000000 --- a/src/interpreter/objects/defs/eq.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/bool.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/machine.h" - -// Object equality comparison. -class fnEq : public bareFnDef { - fnValue* execute(fnMachine* context, std::vector values) { - bool result = this->parent->hash() == values[0]->hash(); - return new fnBool(result); - } - -public: - fnEq(fnValue* parent) : bareFnDef( - parent, - new std::vector{"other"} - ) {}; -}; diff --git a/src/interpreter/objects/defs/include.h b/src/interpreter/objects/defs/include.h deleted file mode 100644 index 8d31dbc..0000000 --- a/src/interpreter/objects/defs/include.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/bool.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/machine.h" - -// Includes the contents of a value -// into the current value. -// -// As with traditional assignment, -// if a value has already been defined, -// an error will occur. -class fnInclude : public bareFnDef { - fnValue* execute(fnMachine* context, std::vector values) { - // Look at each of the locals in the given block - // and import them into the parent. - for (auto local : values[0]->locals) { - // TODO: Fix this violation of type safety. - std::string* key = const_cast(&local.first); - fnValue* value = local.second; - - this->parent->set(key, value); - } - } - -public: - fnInclude(fnValue* parent) : bareFnDef( - parent, - new std::vector{"value"} - ) {}; -}; diff --git a/src/interpreter/objects/defs/load.h b/src/interpreter/objects/defs/load.h deleted file mode 100644 index 3cf9790..0000000 --- a/src/interpreter/objects/defs/load.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/bool.h" -#include "src/interpreter/objects/string.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/machine.h" - -#include "src/exec.h" -#include "src/errors.h" - -// Loads the result of executing the file at the given path -// into a value for use. -// -// Files are loaded in a sandboxed manner: -// no variables will be given to the file for execution. -class fnLoad : public bareFnDef { - fnValue* execute(fnMachine* context, std::vector values) { - - fnString* path = dynamic_cast(values[0]); - if (path == NULL) { - throw FnRuntimeError("load called with non-string argument " + values[0]->asString()); - } - - return exec(path->value->c_str(), context->debug); - } - -public: - fnLoad() : bareFnDef( - NULL, - new std::vector{"path"} - ) {}; -}; diff --git a/src/interpreter/objects/defs/or.h b/src/interpreter/objects/defs/or.h deleted file mode 100644 index 85bf73d..0000000 --- a/src/interpreter/objects/defs/or.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/bool.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/machine.h" - -// Object equality comparison. -class fnOr : public bareFnDef { - fnValue* execute(fnMachine* context, std::vector values) { - bool result = - this->parent->asBool() || - values[0]->asBool(); - - return new fnBool(result); - } - -public: - fnOr(fnValue* parent) : bareFnDef( - parent, - new std::vector{"other"} - ) {}; -}; diff --git a/src/interpreter/objects/list.cpp b/src/interpreter/objects/list.cpp deleted file mode 100644 index c78ed6f..0000000 --- a/src/interpreter/objects/list.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "src/interpreter/objects/list.h" -#include "src/errors.h" - -#include - -fnValue* fnMap::execute(fnMachine* context, std::vector values) { - fnDef* def = dynamic_cast(values[0]); - if (def == NULL) { - throw FnRuntimeError( - "Non-def " + values[0]->asString() + " given to List.map" - ); - } - - std::vector* results = new std::vector(); - for(auto value: this->list->values) { - fnValue* result = context->callByValue(def, std::vector { value }); - results->push_back(result); - } - - - return new fnList(*results); -} \ No newline at end of file diff --git a/src/interpreter/objects/list.h b/src/interpreter/objects/list.h deleted file mode 100644 index edf6c19..0000000 --- a/src/interpreter/objects/list.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "src/interpreter/machine.h" -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/number.h" - -// Number of spaces between indent levels when converting to string. -const int INDENT_SIZE = 2; - -class fnList; - -class fnMap : public fnDef { - fnList* list; - fnValue* execute(fnMachine* context, std::vector values); - -public: - fnMap(fnList* parentList) : fnDef(parent, new std::vector{"fn"}) { - this->list = parentList; - } -}; - -// Represents an array of values. -class fnList : public fnValue { -public: - std::vector values; - - fnList(std::vector values) : fnValue(NULL) { - this->values = values; - - this->locals = ValueDict{ - {"map", new fnMap(this)}, - }; - } - - virtual std::size_t hash() override { - std::size_t workingHash = 0; - - for(auto value: values) { - std::size_t valueHash = value->hash(); - - // From http://en.cppreference.com/w/cpp/utility/hash - workingHash = workingHash ^ (valueHash << 1); - } - - return workingHash; - } - - // The default call to fnList returns - // the element at the given index. - fnValue* call(fnMachine* context, std::vector values) override { - int index = static_cast(values[0])->asInt(); - return this->values[index]; - } - - virtual std::string asString(int indentationLevel) override { - std::string result = "List("; - for(auto value: this->values) { - result += value->asString(); - if(value != this->values.back()) { - result += ", "; - } - } - - result += ")"; - - return result; - } -}; diff --git a/src/interpreter/objects/number.cpp b/src/interpreter/objects/number.cpp deleted file mode 100644 index ae8b333..0000000 --- a/src/interpreter/objects/number.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "src/interpreter/objects/number.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/objects/value.h" - -typedef std::unordered_map ValueDict; - -// Adds two integers together. -class fnAdd : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - double sum = - static_cast(this->parent)->asDouble() + - static_cast(values[0])->asDouble(); - - return new fnNumber(sum); - } - -public: - fnAdd(fnValue* parent) : fnDef( - parent, - new std::vector{"other"} - ) {}; -}; - -// // Subtracts two integers. -class fnSubtract : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - double diff = - static_cast(this->parent)->asDouble() - - static_cast(values[0])->asDouble(); - - return new fnNumber(diff); - } - -public: - fnSubtract(fnValue* parent) : fnDef( - parent, - new std::vector{"other"} - ) {}; -}; - -class fnMultiply : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - double product = - static_cast(this->parent)->asDouble() * - static_cast(values[0])->asDouble(); - - return new fnNumber(product); - } - -public: - fnMultiply(fnValue* parent) : fnDef( - parent, - new std::vector{"other"} - ) {}; -}; - -class fnDivide : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - double quotient = - static_cast(this->parent)->asDouble() / - static_cast(values[0])->asDouble(); - - return new fnNumber(quotient); - } - -public: - fnDivide(fnValue* parent) : fnDef( - parent, - new std::vector{"other"} - ) {}; -}; - -class fnMoreThan : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - bool moreThan = - static_cast(this->parent)->asDouble() > - static_cast(values[0])->asDouble(); - - return new fnBool(moreThan); - } - -public: - fnMoreThan(fnValue* parent) : fnDef( - parent, - new std::vector{"other"} - ) {}; -}; - -class fnLessThan : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - bool lessThan = - static_cast(this->parent)->asDouble() < - static_cast(values[0])->asDouble(); - - return new fnBool(lessThan); - } - -public: - fnLessThan(fnValue* parent) : fnDef( - parent, - new std::vector{"other"} - ) {}; -}; - -fnNumber::fnNumber(int i) : fnConstant(new Number(i)) { - ValueDict methods = ValueDict{ - {"+", new fnAdd(this)}, - {"-", new fnSubtract(this)}, - {"*", new fnMultiply(this)}, - {"/", new fnDivide(this)}, - {"moreThan", new fnMoreThan(this)}, - {"lessThan", new fnLessThan(this)}, - }; - - this->locals.insert(methods.begin(), methods.end()); -} - -fnNumber::fnNumber(double d) : fnConstant(new Number(d)) { - ValueDict methods = ValueDict{ - {"+", new fnAdd(this)}, - {"-", new fnSubtract(this)}, - {"*", new fnMultiply(this)}, - {"/", new fnDivide(this)}, - {"moreThan", new fnMoreThan(this)}, - {"lessThan", new fnLessThan(this)}, - }; - - this->locals.insert(methods.begin(), methods.end()); -} - -fnNumber::~fnNumber() { - delete this->value; -} diff --git a/src/interpreter/objects/number.h b/src/interpreter/objects/number.h deleted file mode 100644 index c99f5f2..0000000 --- a/src/interpreter/objects/number.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include - -#include "src/interpreter/objects/constant.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/objects/bool.h" -#include "src/errors.h" - -// Generic number class, incorporating integers and doubles. -class Number { - - // Denotes the active union member. - enum{INT, DOUBLE} active; - - // Stores the value. - union { - int i; - double d; - } value; - -public: - - Number(int i) { this->active = INT; this->value.i = i; } - Number(double d) { - // We check if the double could be an int; - // if it can, we store it as an int instead. - if (roundf(d) == d) { - this->active = INT; - this->value.i = (int)d; - } else { - this->active = DOUBLE; - this->value.d = d; - } - } - - // Returns the string - std::string asString() { - if(this->active == INT) { return std::to_string(this->value.i); } - else if(this->active == DOUBLE) { return std::to_string(this->value.d); } - } - - // Returns the stored value as a size_t. - // Useful for hashing. - std::size_t asSizeT() { - if(this->active == INT) { - return std::hash()(this->value.i); - } else if(this->active == DOUBLE) { - return std::hash()(this->value.d); - } - } - - double asDouble() { - if(this->active == INT) { return (double)this->value.i; } - else if(this->active == DOUBLE) { return this->value.d; } - } - - int asInt() { - if(this->active == INT) { return this->value.i; } - else { - throw FnRuntimeError("Attempted conversion from double to integer."); - } - } -}; - -// Represents a number (either integer or double). -class fnNumber : public fnConstant { -public: - fnNumber(int i); - fnNumber(double d); - - ~fnNumber(); - - virtual std::size_t hash() override { - return this->value->asSizeT(); - } - - virtual std::string asString(int indentationLevel) override { - return this->value->asString(); - } - - double asDouble() { return this->value->asDouble(); } - int asInt() { return this->value->asInt(); } -}; diff --git a/src/interpreter/objects/string.cpp b/src/interpreter/objects/string.cpp deleted file mode 100644 index 8732dcf..0000000 --- a/src/interpreter/objects/string.cpp +++ /dev/null @@ -1,16 +0,0 @@ - -#include "src/interpreter/objects/string.h" -#include "src/interpreter/objects/number.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/objects/value.h" - -typedef std::unordered_map ValueDict; - -fnString::fnString(std::string* value) : fnConstant(value) { - ValueDict methods = ValueDict{ - {"length", new fnNumber((int)value->length())}, - }; - - this->locals.insert(methods.begin(), methods.end()); -} - diff --git a/src/interpreter/objects/string.h b/src/interpreter/objects/string.h deleted file mode 100644 index 2ecd5da..0000000 --- a/src/interpreter/objects/string.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/constant.h" - -// Represents a string. -class fnString : public fnConstant { - -public: - fnString(std::string*); - - virtual std::size_t hash() override { - return std::hash()(*this->value); - } - - virtual std::string asString(int indentationLevel) override { - return *this->value; - } -}; diff --git a/src/interpreter/objects/value.cpp b/src/interpreter/objects/value.cpp deleted file mode 100644 index 8575cb9..0000000 --- a/src/interpreter/objects/value.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/defs/eq.h" -#include "src/interpreter/objects/defs/and.h" -#include "src/interpreter/objects/defs/or.h" -#include "src/interpreter/objects/defs/include.h" -#include "src/interpreter/objects/defs/load.h" -#include "src/interpreter/machine.h" -#include "src/errors.h" - -#include - -// Number of spaces between indent levels when converting to string. -const int INDENT_SIZE = 2; - -fnValue::fnValue(fnValue* parent) { - this->parent = parent; - - this->builtins = ValueDict{ - {"eq", new fnEq(this)}, - {"and", new fnAnd(this)}, - {"or", new fnOr(this)}, - {"include", new fnInclude(this)}, - {"load", new fnLoad()}, - }; - - this->locals = ValueDict{}; -} - -fnValue::~fnValue() { - // Don't delete the parent! - this->locals.clear(); - this->builtins.clear(); -} - -fnValue* fnValue::get(std::string* name) { - fnValue* value = this->locals[(*name)]; - - if (value == NULL) { - // Try the builtins - value = this->builtins[(*name)]; - } - - if (value == NULL) { - if(this->parent != NULL) { return this->parent->get(name); } - else { - throw FnRuntimeError(*name + " is not defined in " + this->asString(0)); - } - } - - return value; -} - -void fnValue::set(std::string* name, fnValue* value) { - if(this->locals.find(*name) != this->locals.end()) { - throw FnRuntimeError(*name + " is already defined in " + this->asString(0)); - } - - this->locals[*name] = value; -} - -std::size_t fnValue::hash() { - std::size_t workingHash = (std::size_t)parent; - - for(auto local: locals) { - std::size_t keyHash = std::hash()(local.first); - - // TODO: Why is this happening?! - if (local.second == NULL) { - std::cout << "WARNING: Undefined attribute " << local.first << " in " << this->asString(0) << std::endl; - continue; - } - - std::size_t valueHash = local.second->hash(); - - // From http://en.cppreference.com/w/cpp/utility/hash - workingHash = workingHash ^ (keyHash << 1); - workingHash = workingHash ^ (valueHash << 1); - } - - return workingHash; -} - -std::string fnValue::asString(int indentationLevel) { - std::string result = "{"; - - bool endBraceOnNewLine = false; - for(auto local: locals) { - if(local.second != NULL) { - result += "\n" + std::string(indentationLevel+INDENT_SIZE, ' ') + local.first + " = " + local.second->asString(indentationLevel + INDENT_SIZE); - endBraceOnNewLine = true; - } - } - - if (endBraceOnNewLine) { result += "\n" + std::string(indentationLevel, ' '); } - result += "}"; - return result; -} diff --git a/src/interpreter/objects/value.h b/src/interpreter/objects/value.h deleted file mode 100644 index 5bf3384..0000000 --- a/src/interpreter/objects/value.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include -#include - -// A few forward declarations... -class fnValue; -class fnDef; -class fnMachine; - -// Shorthand for a dictionary of values. -typedef std::unordered_map ValueDict; - -// Base class for all Fn values. -class fnValue { -protected: - - // Stores builtin attributes that can be overwritten. - ValueDict builtins; - - - // Super-special constructor. - // This is used for fnDefs attached to fnValue, - // otherwise an infinite loop occurs because - // fnDef's constructor calls fnValue's constructor, - // which calls a new fnDef... - // - // So this is bare. It has no locals or parent. - fnValue() { - this->parent = NULL; - this->locals = ValueDict{}; - this->builtins = ValueDict{}; - } - -public: - - // Stores attributes that are nested within the value. - ValueDict locals; - - // If a value has a parent, the parent will be searched for a value - // if this value does not have it in its locals. - fnValue* parent; - - fnValue(fnValue* parent); - ~fnValue(); - - // Return a value for comparison. - virtual std::size_t hash(); - - // Returns a string representing the value. - virtual std::string asString(int indentationLevel = 0); - - // In fn, all values (except for false) are true. - virtual bool asBool() { return true; } - - // Get a local value. - fnValue* get(std::string* name); - - // Set a value to the locals of the current value. - void set(std::string* name, fnValue* value); - - // Called when the value is invoked like a function. - // - // By default, call() will get the value specified in the - // first argument. - virtual fnValue* call(fnMachine* context, std::vector values) { - std::string name = values[0]->asString(); - return this->get(&name); - }; - -}; diff --git a/src/interpreter/runtime.h b/src/interpreter/runtime.h deleted file mode 100644 index 2b567e6..0000000 --- a/src/interpreter/runtime.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/constant.h" -#include "src/interpreter/objects/bool.h" -#include "src/interpreter/objects/number.h" -#include "src/interpreter/objects/string.h" -#include "src/interpreter/objects/def.h" -#include "src/interpreter/objects/list.h" diff --git a/src/interpreter/world.cpp b/src/interpreter/world.cpp deleted file mode 100644 index 1128d29..0000000 --- a/src/interpreter/world.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include - -#include "src/interpreter/runtime.h" -#include "src/interpreter/objects/defs/include.h" -#include "src/interpreter/objects/defs/load.h" - -class fnPrint : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - std::string asString = values[0]->asString(); - std::cout << asString << "\n"; - return new fnString(&asString); - } - -public: - fnPrint() : fnDef( - NULL, - new std::vector{"out"} - ) {}; -}; - -class fnNot : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - bool result = !(values[0]->asBool()); - return new fnBool(result); - } - -public: - fnNot() : fnDef( - NULL, - new std::vector{"obj"} - ) {}; -}; - -class fnListConstructor : public fnDef { - fnValue* execute(fnMachine* context, std::vector values) { - return new fnList(values); - } - -public: - fnListConstructor() : fnDef( - NULL, - new std::vector{"...items"} - ) {}; -}; - -fnWorld::fnWorld() : fnValue() { - this->locals = ValueDict{ - {"print", new fnPrint()}, - {"not", new fnNot()}, - {"List", new fnListConstructor()}, - {"include", new fnInclude(this)}, - {"load", new fnLoad()}, - }; -} - -fnWorld::~fnWorld() { - this->locals.clear(); -} diff --git a/src/interpreter/world.h b/src/interpreter/world.h deleted file mode 100644 index 335e504..0000000 --- a/src/interpreter/world.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "src/interpreter/objects/value.h" -#include "src/interpreter/objects/def.h" - -// The top-level block. -class fnWorld : public fnValue { -public: - fnWorld(); - ~fnWorld(); -}; diff --git a/src/main.cpp b/src/main.cpp index ec84e99..cca666f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,3 @@ -#include "src/cli.cpp" +#include "src/cli.h" -int main(int argc, char **argv) -{ - return parseCli(argc, argv); -} +int main(int argc, char **argv) { return parseCli(argc, argv); } diff --git a/src/parser.h b/src/parser.h deleted file mode 100644 index 56d189f..0000000 --- a/src/parser.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "src/ast.h" - -// Required extern declarations -// linking to Flex/Bison. -extern FILE* yyin; -extern int yyparse(); -typedef struct yy_buffer_state * YY_BUFFER_STATE; -extern int yyparse(); -extern YY_BUFFER_STATE yy_scan_string(const char* str); -extern void yy_delete_buffer(YY_BUFFER_STATE buffer); -extern astBlock* programBlock; - -// Converts fn code into an Abstract Syntax Tree (AST). -class fnParser { - bool debug; - -public: - fnParser(bool debug) { - this->debug = debug; - } - - fnParser() : fnParser(false) {} - - // Return the AST for the given file. - astBlock* parseFile(const char* filename); - - // Return the AST for the given code. - astBlock* parseCode(std::string code); -}; diff --git a/src/parser/bison.cpp b/src/parser/bison.cpp index bf5152f..230fe25 100644 --- a/src/parser/bison.cpp +++ b/src/parser/bison.cpp @@ -1,11 +1,10 @@ %{ - #include - #include - #include + #include // std::string + #include // std::cout + #include // std::reverse #include "tmp/lex.h" #include "src/ast.h" - #include "src/errors.h" // stuff from flex that bison needs to know about: extern int yylex(); @@ -14,7 +13,8 @@ void yyerror(const char *s); // Here we go! - astBlock* programBlock; + using namespace fn; + fn::ast::Block* programBlock; %} /* Represents the many different ways we can access our data */ @@ -22,34 +22,32 @@ // Literals std::string *v_string; double v_double; - int v_int; bool v_bool; - // AST - astBlock* v_block; - astStatement* v_statement; - astReference* v_reference; - astId* v_id; - astDeref* v_deref; - astValue* v_value; - astConditional* v_conditional; - astCondition* v_condition; - astAssignment* v_assignment; - astFnCall* v_fncall; - astFnDef* v_fndef; + // AST elements + fn::ast::Block* v_block; + fn::ast::Statement* v_statement; + fn::ast::Reference* v_reference; + fn::ast::Id* v_id; + fn::ast::Deref* v_deref; + fn::ast::Value* v_value; + fn::ast::Conditional* v_conditional; + fn::ast::Condition* v_condition; + fn::ast::Assignment* v_assignment; + fn::ast::Call* v_call; + fn::ast::Def* v_def; // Intermediaries - std::vector* v_statements; + std::vector* v_statements; std::vector* v_strings; - std::vector* v_values; - std::vector* v_ids; - std::vector* v_conditions; + std::vector* v_values; + std::vector* v_ids; + std::vector* v_conditions; } // Terminal symbols. %token TSTRING TINFIX TID %token TDOUBLE -%token TINT %token TBOOL // Non-terminal symbols. @@ -66,8 +64,8 @@ %type conditions %type condition %type assignment -%type functionDef -%type functionCall infixOperation +%type functionDef +%type functionCall infixOperation // The starting rule. %start program @@ -86,10 +84,10 @@ %% program: - statements { programBlock = new astBlock(*$1); } + statements { programBlock = new fn::ast::Block(*$1); } statements: - /* empty */ { $$ = new std::vector(); } + /* empty */ { $$ = new std::vector(); } | statements statement { ($1)->push_back($2); $$ = $1; } | statements statement ';' { ($1)->push_back($2); $$ = $1; } ; @@ -100,7 +98,7 @@ ; assignment: - reference '=' value { $$ = new astAssignment($1, $3); } + reference '=' value { $$ = new fn::ast::Assignment($1, $3); } ; value: @@ -118,17 +116,16 @@ ; literal: - TINT { $$ = new astInt($1); } -| TDOUBLE { $$ = new astDouble($1); } -| TSTRING { $$ = new astString($1); } -| TBOOL { $$ = new astBool($1); } + TBOOL { $$ = new fn::ast::Bool($1); } +| TDOUBLE { $$ = new fn::ast::Number($1); } +| TSTRING { $$ = new fn::ast::String(*$1); } ; infixOperation: value TINFIX value { - $$ = new astFnCall( - new astDeref($1, new astId($2)), - new std::vector{$3}); + $$ = new fn::ast::Call( + new fn::ast::Deref($1, new fn::ast::Id(*$2)), + std::vector{$3}); } reference: @@ -138,12 +135,12 @@ ; identifier: - TID { $$ = new astId($1); } -| TINFIX { $$ = new astId($1); } + TID { $$ = new fn::ast::Id(*$1); } +| TINFIX { $$ = new fn::ast::Id(*$1); } ; deref: - reference '.' reference { $$ = new astDeref($1, $3); } + reference '.' reference { $$ = new fn::ast::Deref($1, $3); } functionCall: reference '(' args ')' { @@ -151,12 +148,12 @@ // args are parsed in reverse order... std::reverse(($3)->begin(), ($3)->end()); - $$ = new astFnCall($1, $3); + $$ = new fn::ast::Call($1, *$3); } args: - /* empty */ { $$ = new std::vector{}; } -| value { $$ = new std::vector{$1}; } + /* empty */ { $$ = new std::vector{}; } +| value { $$ = new std::vector{$1}; } | value ',' args { ($3)->push_back($1); $$ = $3; } functionDef: @@ -165,7 +162,7 @@ // params are parsed in reverse order... std::reverse(($3)->begin(), ($3)->end()); - $$ = new astFnDef($3, $5); + $$ = new fn::ast::Def(*$3, $5); } params: @@ -174,33 +171,24 @@ | TID ',' params { std::string* str = new std::string(*$1); ($3)->push_back(*str); $$ = $3; } block: - '{' statements '}' { $$ = new astBlock(*$2); } + '{' statements '}' { $$ = new fn::ast::Block(*$2); } conditional: - TWHEN '{' conditions '}' { $$ = new astConditional($3); } + TWHEN '{' conditions '}' { $$ = new fn::ast::Conditional(*$3); } conditions: - /* empty */ { $$ = new std::vector(); } + /* empty */ { $$ = new std::vector(); } | conditions condition { ($1)->push_back($2); $$ = $1; } condition: - test block { $$ = new astCondition($1, $2); } + test block { $$ = new fn::ast::Condition($1, $2); } test: - TBOOL { $$ = new astBool($1); } + TBOOL { $$ = new fn::ast::Bool($1); } | infixOperation | reference | functionCall %% -class FnParseError : public FnError { - using FnError::FnError; -}; - -void yyerror(char const* s) { - throw FnParseError( - s, - new CodeLocation("???", line) - ); -} +void yyerror(char const* s) { throw "Parse error"; } diff --git a/src/parser/flex.cpp b/src/parser/flex.cpp index 3b26b8f..5951316 100644 --- a/src/parser/flex.cpp +++ b/src/parser/flex.cpp @@ -1,6 +1,6 @@ %{ - #include - #include + #include // std::string + #include // std::cout #include "src/ast.h" #include "tmp/parse.h" @@ -18,8 +18,7 @@ \n { line++; } -[0-9]+\.[0-9]* { yylval.v_double = atof(yytext); return TDOUBLE; } -[0-9]+ { yylval.v_int = atoi(yytext); return TINT; } +[0-9]+(\.[0-9]*)? { yylval.v_double = atof(yytext); return TDOUBLE; } \"[^\"]*\" { std::string* workingString = new std::string(yytext); diff --git a/src/parser.cpp b/src/parser/parser.cpp similarity index 71% rename from src/parser.cpp rename to src/parser/parser.cpp index 7acdb42..e281f21 100644 --- a/src/parser.cpp +++ b/src/parser/parser.cpp @@ -1,8 +1,10 @@ -#include "parser.h" +#include "src/parser/parser.h" #include -astBlock* fnParser::parseFile(const char* filename) { +using namespace fn; + +ast::Block* Parser::parseFile(const char* filename) { yyin = fopen(filename,"r"); yyparse(); fclose(yyin); @@ -12,7 +14,7 @@ astBlock* fnParser::parseFile(const char* filename) { return programBlock; } -astBlock* fnParser::parseCode(std::string code) { +ast::Block* Parser::parseCode(std::string code) { YY_BUFFER_STATE buffer = yy_scan_string(code.c_str()); yyparse(); yy_delete_buffer(buffer); diff --git a/src/parser/parser.h b/src/parser/parser.h new file mode 100644 index 0000000..87b91ac --- /dev/null +++ b/src/parser/parser.h @@ -0,0 +1,34 @@ +// The parser provides a C++-friendly interface +// to the C-based Flex/Bison parser. + +#pragma once + +#include "src/ast.h" + +// These extern declarations are required +// by Flex/Bison. +extern FILE* yyin; +extern int yyparse(); +typedef struct yy_buffer_state * YY_BUFFER_STATE; +extern int yyparse(); +extern YY_BUFFER_STATE yy_scan_string(const char* str); +extern void yy_delete_buffer(YY_BUFFER_STATE buffer); +extern fn::ast::Block* programBlock; + +namespace fn { + + class Parser { + bool debug; + + public: + Parser(bool debug) { this->debug = debug; } + Parser() : Parser(false) {} + + // Return the AST for the given file. + ast::Block* parseFile(const char* filename); + + // Return the AST for the given code. + ast::Block* parseCode(std::string code); + }; + +} diff --git a/src/vm/number.h b/src/vm/number.h index 00a03e8..8c834c9 100644 --- a/src/vm/number.h +++ b/src/vm/number.h @@ -7,85 +7,94 @@ #include // pow, abs, ... -// Types for the exponent and coefficient of a Number. -typedef signed char fnExponent; -typedef signed long long int fnCoefficient; - -class fnVMNumber { -public: - fnExponent exponent; - fnCoefficient coefficient; - - fnVMNumber(fnExponent exponent, fnCoefficient coefficient) { - this->exponent = exponent; - this->coefficient = coefficient; +namespace fn { namespace vm { + + namespace number { + // Types for the exponent and coefficient of a Number. + typedef signed char Exponent; + typedef signed long long int Coefficient; } - fnVMNumber() = default; + class Number { + public: + number::Exponent exponent; + number::Coefficient coefficient; - friend fnVMNumber operator*(fnVMNumber lhs, fnVMNumber rhs) { - fnVMNumber result; - - result.exponent = lhs.exponent + rhs.exponent; - result.coefficient = lhs.coefficient * rhs.coefficient; + Number(number::Exponent exponent, number::Coefficient coefficient) { + this->exponent = exponent; + this->coefficient = coefficient; + } - // TODO: Detect overflow and correct + Number() = default; - return result; - } + friend Number operator*(Number lhs, Number rhs) { + Number result; + + result.exponent = lhs.exponent + rhs.exponent; + result.coefficient = lhs.coefficient * rhs.coefficient; - friend fnVMNumber operator/(fnVMNumber lhs, fnVMNumber rhs) { - fnVMNumber result; - - result.exponent = lhs.exponent - rhs.exponent; - result.coefficient = lhs.coefficient / rhs.coefficient; + // TODO: Detect overflow and correct - // TODO: Divide by 0? + return result; + } - return result; - } + friend Number operator/(Number lhs, Number rhs) { + Number result; + + result.exponent = lhs.exponent - rhs.exponent; + result.coefficient = lhs.coefficient / rhs.coefficient; + + // TODO: Divide by 0? - friend fnVMNumber operator+(fnVMNumber lhs, fnVMNumber rhs) { - fnVMNumber result; + return result; + } - // Get both numbers to the same base. - fnExponent exponentDifference = lhs.exponent - rhs.exponent; - fnCoefficient coefficientMultiplier = (int)pow(10, abs(exponentDifference)); + friend Number operator+(Number lhs, Number rhs) { + Number result; - if (exponentDifference < 0) { - rhs.coefficient *= coefficientMultiplier; - result.exponent = lhs.exponent; - } else { - lhs.coefficient *= coefficientMultiplier; - result.exponent = rhs.exponent; + // Get both numbers to the same base. + number::Exponent exponentDifference = lhs.exponent - rhs.exponent; + number::Coefficient coefficientMultiplier = (int)pow(10, abs(exponentDifference)); + + if (exponentDifference < 0) { + rhs.coefficient *= coefficientMultiplier; + result.exponent = lhs.exponent; + } else { + lhs.coefficient *= coefficientMultiplier; + result.exponent = rhs.exponent; + } + + result.coefficient = lhs.coefficient + rhs.coefficient; + + // TODO: Detect overflow and correct + + return result; } - - result.coefficient = lhs.coefficient + rhs.coefficient; - // TODO: Detect overflow and correct + friend Number operator-(Number lhs, Number rhs) { + Number result; - return result; - } + // Get both numbers to the same base. + number::Exponent exponentDifference = lhs.exponent - rhs.exponent; + number::Coefficient coefficientMultiplier = (int)pow(10, abs(exponentDifference)); - friend fnVMNumber operator-(fnVMNumber lhs, fnVMNumber rhs) { - fnVMNumber result; + if (exponentDifference < 0) { + rhs.coefficient *= coefficientMultiplier; + result.exponent = lhs.exponent; + } else { + lhs.coefficient *= coefficientMultiplier; + result.exponent = rhs.exponent; + } + + result.coefficient = lhs.coefficient - rhs.coefficient; - // Get both numbers to the same base. - fnExponent exponentDifference = lhs.exponent - rhs.exponent; - fnCoefficient coefficientMultiplier = (int)pow(10, abs(exponentDifference)); + // TODO: Detect overflow and correct - if (exponentDifference < 0) { - rhs.coefficient *= coefficientMultiplier; - result.exponent = lhs.exponent; - } else { - lhs.coefficient *= coefficientMultiplier; - result.exponent = rhs.exponent; + return result; } - - result.coefficient = lhs.coefficient - rhs.coefficient; + }; + + +}} - // TODO: Detect overflow and correct - return result; - } -}; \ No newline at end of file diff --git a/src/vm/value.h b/src/vm/value.h new file mode 100644 index 0000000..71fd151 --- /dev/null +++ b/src/vm/value.h @@ -0,0 +1,20 @@ +// Union of all possible VM values. + +#pragma once + +#include // std::string +#include "src/vm/number.h" // Number + +namespace fn { namespace vm { + + // fnVMValue is a union capable of storing any value. + typedef union _Value { + bool asBool; + Number asNumber; + + std::string toString() { + return "???"; + } + } Value; + +}} diff --git a/src/vm/vm.cpp b/src/vm/vm.cpp index dc7d504..7759d10 100644 --- a/src/vm/vm.cpp +++ b/src/vm/vm.cpp @@ -3,23 +3,25 @@ #include // std::cout #include // std::bitset +using namespace fn; + #define DEBUG(msg) if (this->debug) { std::cout << msg << std::endl; } -fnVM::fnVM(bool debug) { +VM::VM(bool debug) { this->debug = debug; - this->values = std::vector(); + this->values = std::vector(); } -fnVM::~fnVM() { +VM::~VM() { this->values.clear(); } -fnVMValue fnVM::run(fnByte instructions[], size_t num_bytes) { +vm::Value VM::run(bytecode::CodeByte instructions[], size_t num_bytes) { size_t counter = 0; - fnVMValue* returnValue = NULL; + vm::Value* returnValue = NULL; while(counter < num_bytes) { - fnByte opcode = instructions[counter]; + bytecode::CodeByte opcode = instructions[counter]; switch(opcode) { @@ -79,7 +81,7 @@ fnVMValue fnVM::run(fnByte instructions[], size_t num_bytes) { } // Get a value by index. -fnVMValue* fnVM::value(fnByte index) { +vm::Value* VM::value(bytecode::CodeByte index) { return this->values[index]; } @@ -88,13 +90,13 @@ fnVMValue* fnVM::value(fnByte index) { // (1 byte) // // Allocates a new boolean constant. -fnVMValue* fnVM::declareBool(fnByte value) { +vm::Value* VM::declareBool(bytecode::CodeByte value) { return this->declareBool((bool)value); } -fnVMValue* fnVM::declareBool(bool value) { +vm::Value* VM::declareBool(bool value) { DEBUG("DECLARE_BOOL(" << value << ")"); - fnVMValue* b = new fnVMValue; + vm::Value* b = new vm::Value; b->asBool = value; this->values.push_back(b); @@ -105,24 +107,24 @@ fnVMValue* fnVM::declareBool(bool value) { // (8 bytes) // // Allocates a new numeric constant. -fnVMValue* fnVM::declareNumber(fnByte value[]) { - fnExponent exponent = (fnExponent)value[1]; - fnCoefficient coefficient = (fnCoefficient)value[2]; +vm::Value* VM::declareNumber(bytecode::CodeByte value[]) { + vm::number::Exponent exponent = (vm::number::Exponent)value[1]; + vm::number::Coefficient coefficient = (vm::number::Coefficient)value[2]; DEBUG("DECLARE_NUMBER(" << (int)coefficient << " * 10^" << (int)exponent << ")"); - fnVMValue* n = new fnVMValue; - n->asNumber = fnVMNumber(exponent, coefficient); + vm::Value* n = new vm::Value; + n->asNumber = vm::Number(exponent, coefficient); this->values.push_back(n); return n; } -fnVMValue* fnVM::declareNumber(fnVMNumber value) { +vm::Value* VM::declareNumber(vm::Number value) { DEBUG("DECLARE_NUMBER(" << (int)value.coefficient << " * 10^" << (int)value.exponent << ")"); - fnVMValue* n = new fnVMValue; + vm::Value* n = new vm::Value; n->asNumber = value; this->values.push_back(n); @@ -134,7 +136,7 @@ fnVMValue* fnVM::declareNumber(fnVMNumber value) { // // Returns true if both arguments are true, // false otherwise. -fnVMValue* fnVM::fnAnd(fnByte value[]) { +vm::Value* VM::fnAnd(bytecode::CodeByte value[]) { bool first = this->value(value[1])->asBool; if (!first) { @@ -157,7 +159,7 @@ fnVMValue* fnVM::fnAnd(fnByte value[]) { // // Returns true if either arguments are true, // false otherwise. -fnVMValue* fnVM::fnOr(fnByte value[]) { +vm::Value* VM::fnOr(bytecode::CodeByte value[]) { bool first = this->value(value[1])->asBool; if (first) { DEBUG("OR(true, ???)"); @@ -179,7 +181,7 @@ fnVMValue* fnVM::fnOr(fnByte value[]) { // // Returns true if the argument is false // and vice versa. -fnVMValue* fnVM::fnNot(fnByte value[]) { +vm::Value* VM::fnNot(bytecode::CodeByte value[]) { bool arg = this->value(value[1])->asBool; DEBUG("NOT(" << arg << ")"); @@ -194,13 +196,13 @@ fnVMValue* fnVM::fnNot(fnByte value[]) { // (3 bytes) // // Returns the product of two numeric values. -fnVMValue* fnVM::fnMultiply(fnByte value[]) { - fnVMNumber first = this->value(value[1])->asNumber; - fnVMNumber second = this->value(value[2])->asNumber; +vm::Value* VM::fnMultiply(bytecode::CodeByte value[]) { + vm::Number first = this->value(value[1])->asNumber; + vm::Number second = this->value(value[2])->asNumber; DEBUG("MULTIPLY(" << (int)first.coefficient << " * 10^" << (int)first.exponent << ", " << (int)second.coefficient << " * 10^" << (int)second.exponent << ")"); - fnVMNumber product = first * second; + vm::Number product = first * second; return this->declareNumber(product); } @@ -209,13 +211,13 @@ fnVMValue* fnVM::fnMultiply(fnByte value[]) { // (3 bytes) // // Returns the fraction of two numeric values. -fnVMValue* fnVM::fnDivide(fnByte value[]) { - fnVMNumber first = this->value(value[1])->asNumber; - fnVMNumber second = this->value(value[2])->asNumber; +vm::Value* VM::fnDivide(bytecode::CodeByte value[]) { + vm::Number first = this->value(value[1])->asNumber; + vm::Number second = this->value(value[2])->asNumber; DEBUG("DIVIDE(" << (int)first.coefficient << " * 10^" << (int)first.exponent << ", " << (int)second.coefficient << " * 10^" << (int)second.exponent << ")"); - fnVMNumber fraction = first / second; + vm::Number fraction = first / second; return this->declareNumber(fraction); } @@ -224,13 +226,13 @@ fnVMValue* fnVM::fnDivide(fnByte value[]) { // (3 bytes) // // Returns the sum of two numeric values. -fnVMValue* fnVM::fnAdd(fnByte value[]) { - fnVMNumber first = this->value(value[1])->asNumber; - fnVMNumber second = this->value(value[2])->asNumber; +vm::Value* VM::fnAdd(bytecode::CodeByte value[]) { + vm::Number first = this->value(value[1])->asNumber; + vm::Number second = this->value(value[2])->asNumber; DEBUG("ADD(" << (int)first.coefficient << " * 10^" << (int)first.exponent << ", " << (int)second.coefficient << " * 10^" << (int)second.exponent << ")"); - fnVMNumber sum = first + second; + vm::Number sum = first + second; return this->declareNumber(sum); } @@ -239,13 +241,13 @@ fnVMValue* fnVM::fnAdd(fnByte value[]) { // (3 bytes) // // Returns the difference of two numeric values. -fnVMValue* fnVM::fnSubtract(fnByte value[]) { - fnVMNumber first = this->value(value[1])->asNumber; - fnVMNumber second = this->value(value[2])->asNumber; +vm::Value* VM::fnSubtract(bytecode::CodeByte value[]) { + vm::Number first = this->value(value[1])->asNumber; + vm::Number second = this->value(value[2])->asNumber; DEBUG("SUBTRACT(" << (int)first.coefficient << " * 10^" << (int)first.exponent << ", " << (int)second.coefficient << " * 10^" << (int)second.exponent << ")"); - fnVMNumber difference = first - second; + vm::Number difference = first - second; return this->declareNumber(difference); } diff --git a/src/vm/vm.h b/src/vm/vm.h index b5a4f91..f8be396 100644 --- a/src/vm/vm.h +++ b/src/vm/vm.h @@ -7,69 +7,48 @@ #include "stdlib.h" // size_t #include // std::vector -#include "src/vm/number.h" // fnVMNumber - -// The smallest unit of instruction. -// (Note that instructions are at least this size, -// but are usually more.) -typedef char fnByte; - -// Opcodes are the first byte of any instruction, -// and denote the operation that the VM should perform. -typedef fnByte fnOp; -#define FN_OP_FALSE (fnOp)(0) -#define FN_OP_TRUE (fnOp)(1) -#define FN_OP_AND (fnOp)(2) -#define FN_OP_OR (fnOp)(3) -#define FN_OP_NOT (fnOp)(4) - -#define FN_OP_NUMBER (fnOp)(5) -#define FN_OP_MULTIPLY (fnOp)(6) -#define FN_OP_DIVIDE (fnOp)(7) -#define FN_OP_ADD (fnOp)(8) -#define FN_OP_SUBTRACT (fnOp)(9) - -#define FN_OP_STRING (fnOp)(10) - - -// TODO: Helper methods for defining instructions. - -// fnVMValue is a union capable of storing any value. -typedef union _fnVMValue { - bool asBool; - fnVMNumber asNumber; -} fnVMValue; - -// fnVM is the virtual machine that instructions are run in. -class fnVM { -public: - - fnVM(bool debug); - fnVM() : fnVM(false) {}; - ~fnVM(); - - // Executes a number of instructions. - // Returns the pointer to the return value. - fnVMValue run(fnByte instructions[], size_t num_bytes); - -protected: - bool debug; - - std::vector values; - fnVMValue* value(fnByte index); - - fnVMValue* declareBool(fnByte value); - fnVMValue* declareBool(bool value); - - fnVMValue* fnAnd(fnByte value[]); - fnVMValue* fnOr(fnByte value[]); - fnVMValue* fnNot(fnByte value[]); - - fnVMValue* declareNumber(fnByte value[]); - fnVMValue* declareNumber(fnVMNumber value); - - fnVMValue* fnMultiply(fnByte value[]); - fnVMValue* fnDivide(fnByte value[]); - fnVMValue* fnAdd(fnByte value[]); - fnVMValue* fnSubtract(fnByte value[]); -}; +#include "src/bytecode.h" // CodeByte +#include "src/vm/value.h" // vm::Value +#include "src/vm/number.h" // vm::Number + +namespace fn { + + // VM is the virtual machine that instructions are run in. + class VM { + public: + + VM(bool debug); + VM() : VM(false) {}; + ~VM(); + + // Executes a number of instructions. + // Returns the pointer to the return value. + vm::Value run(bytecode::CodeByte instructions[], size_t num_bytes); + + protected: + bool debug; + + std::vector values; + vm::Value* value(bytecode::CodeByte index); + + vm::Value* declareBool(bytecode::CodeByte value); + vm::Value* declareBool(bool value); + + vm::Value* fnAnd(bytecode::CodeByte value[]); + vm::Value* fnOr(bytecode::CodeByte value[]); + vm::Value* fnNot(bytecode::CodeByte value[]); + + vm::Value* declareNumber(bytecode::CodeByte value[]); + vm::Value* declareNumber(vm::Number value); + + vm::Value* fnMultiply(bytecode::CodeByte value[]); + vm::Value* fnDivide(bytecode::CodeByte value[]); + vm::Value* fnAdd(bytecode::CodeByte value[]); + vm::Value* fnSubtract(bytecode::CodeByte value[]); + }; + +} + + + +