From 3e29ebe2a302a337976e9bffa05a1c49a704463b Mon Sep 17 00:00:00 2001 From: Brenton Bostick Date: Mon, 5 Dec 2016 23:15:27 -0500 Subject: [PATCH] import code --- .gitignore | 29 +- README.md | 234 +++++++- examples/sqrt.2p | 26 + src/CMakeLists.txt | 19 + src/TuppenceConfig.h.in | 4 + src/test/CMakeLists.txt | 70 +++ src/test/Eval.test.cpp | 155 ++++++ src/test/FiniteWord.test.cpp | 81 +++ src/test/Parser.test.cpp | 58 ++ src/test/RationalWord.test.cpp | 187 +++++++ src/test/Value.test.cpp | 22 + src/tuppence/CMakeLists.txt | 52 ++ src/tuppence/Eval.cpp | 953 +++++++++++++++++++++++++++++++++ src/tuppence/Eval.h | 42 ++ src/tuppence/FiniteWord.cpp | 643 ++++++++++++++++++++++ src/tuppence/FiniteWord.h | 152 ++++++ src/tuppence/Lexer.cpp | 249 +++++++++ src/tuppence/Lexer.h | 75 +++ src/tuppence/Logger.cpp | 30 ++ src/tuppence/Logger.h | 24 + src/tuppence/Main.cpp | 155 ++++++ src/tuppence/Parser.cpp | 699 ++++++++++++++++++++++++ src/tuppence/Parser.h | 448 ++++++++++++++++ src/tuppence/RationalWord.cpp | 731 +++++++++++++++++++++++++ src/tuppence/RationalWord.h | 152 ++++++ src/tuppence/Value.cpp | 80 +++ src/tuppence/Value.h | 120 +++++ src/tuppence/ValueList.cpp | 219 ++++++++ src/tuppence/ValueList.h | 67 +++ 29 files changed, 5747 insertions(+), 29 deletions(-) create mode 100644 examples/sqrt.2p create mode 100644 src/CMakeLists.txt create mode 100644 src/TuppenceConfig.h.in create mode 100644 src/test/CMakeLists.txt create mode 100644 src/test/Eval.test.cpp create mode 100644 src/test/FiniteWord.test.cpp create mode 100644 src/test/Parser.test.cpp create mode 100644 src/test/RationalWord.test.cpp create mode 100644 src/test/Value.test.cpp create mode 100644 src/tuppence/CMakeLists.txt create mode 100644 src/tuppence/Eval.cpp create mode 100644 src/tuppence/Eval.h create mode 100644 src/tuppence/FiniteWord.cpp create mode 100644 src/tuppence/FiniteWord.h create mode 100644 src/tuppence/Lexer.cpp create mode 100644 src/tuppence/Lexer.h create mode 100644 src/tuppence/Logger.cpp create mode 100644 src/tuppence/Logger.h create mode 100644 src/tuppence/Main.cpp create mode 100644 src/tuppence/Parser.cpp create mode 100644 src/tuppence/Parser.h create mode 100644 src/tuppence/RationalWord.cpp create mode 100644 src/tuppence/RationalWord.h create mode 100644 src/tuppence/Value.cpp create mode 100644 src/tuppence/Value.h create mode 100644 src/tuppence/ValueList.cpp create mode 100644 src/tuppence/ValueList.h diff --git a/.gitignore b/.gitignore index 4581ef2..c869ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,4 @@ -# Compiled Object files -*.slo -*.lo -*.o -*.obj -# Precompiled Headers -*.gch -*.pch +build -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app +.DS_Store diff --git a/README.md b/README.md index cb29b01..c9c3b61 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,232 @@ -# tuppence -Tuppence Programming Language +# The Tuppence Programming Language + +A language for 2-adic arithmetic and bit-twiddling. + +Tuppence is an interpreted language. + +``` +>./tuppence +Tuppence 0.1 +>>> +``` + +You can type expressions and evaluate them by hitting Enter. + +``` +>>> 1 + 1 +2 +``` + +Tuppence has exact rational arithmetic. + +``` +>>> 1/3 + 1/5 +8/15 +``` + +Integers and rationals are represented using **Rational Words**. The Rational Word data type in Tuppence is modeled after the Quote notation for p-adic numbers popularized by Hehner and Horspool [Quote notation](https://en.wikipedia.org/wiki/Quote_notation). + +The 2-adic expansion of 1/3 is `...01010101010101011`, with the pattern extending infinitely to the left. + +This is similar to the binary real expansion of 1/3, which is `0.0101010101010101...`, with the pattern extending infinitely to the right. + +In the 2-adic expansion of 1/3, there is a single `1` bit that is not part of the repeating `01` pattern. The initial non-repeating part is called the `transient` and the repeating part is called the `period`. + +``` +>>> x = 1/3 +>>> x.period +`01` +>>> x.transient +`1` +``` + +The period and transient are represented as **Finite Words**. Finite Words are binary strings delimited by `` ` `` (backtick) characters. You can do arithmetic and bitwise operations with Finite Words. + +``` +>>> `1110000000` | `0000000111` +`1110000111` +``` + +Arithmetic and bitwise operations must be done on Finite Words of all the same size. + +You can create a Rational Word from a Finite Word. + +``` +>>> ...`1` +-1 +``` + +The `...` operator extends its operand infinitely to the left. Here, you can see that an infinite string of `1` bits is equal to -1. If you are familiar with 2's complement representation of negative numbers, then you have seen how a string of all `1` bits can represent -1 for 32-bit or 64-bit words. The idea of 2's complement notation comes naturally from 2-adic arithmetic. The 2-adic representation of -1 is an infinite string of `1` bits. + +You can concatenate Finite Words with other Finite Words. + +``` +>>> `11` # `000` +`11000` +``` + +And you can concatenate Rational Words with Finite Words. + +``` +>>> ...`01` # `1` +1/3 +``` + + +## Philospophy + +Tuppence might seem strange to someone unfamiliar with 2-adic numbers. For example, there are no real numbers in Tuppence. And dividing numbers by 2 is an error. + +Indexing occurs right-to-left. Evaluation of lists occurs right-to-left. + +``` +>>> (print(1), print(2), print(3)) +3 +2 +1 +(``, ``, ``) +``` + +Where it makes sense, information is processed right-to-left. Exceptions include the spelling of English keywords and functions of the language ( would `tnirp` be acceptable for a function name? ), function application ( `(1)print` would be too strange) and operators like `/` and `-` ( Writing `3\1` would be too strange for representing the fraction one-third. ) + +Traditionally, when adding 32-bit or 64-bit integers, the carry bit is lost. Tuppence propagates the carry bit for later use. + +``` +>>> `1` + `1` +(`1`, `0`) +``` + +Similarly with multiplication, when multiplying 2 n-bit words, the result is a list with 2 n-bit elements. + +## Language + +Values in Tuppence are immutable. + +`%%` is the residue operator. It allows you to extract a finite number of bits from a rational number. + +``` +>>> 1/3 %% 20 +`10101010101010101011` +``` + +#### Arithmetic operations + +`+` and `-` return Lists with 2 elements. The first element of the result has the same bit width as the operands. The second element is the carry (or borrow) and is the size needed to represent the maximum possibly carry (or borrow). + +`*` returns a List with 2 elements. Both elements are the same width as the operands, and both elements together represent the complete product of the operands. + +`/` returns a List with 2 elements, i.e., (Remainder, Quotient) + +#### Bitwise operations + +The operators `&`, `|`, and `^` represent the traditional bitwise operators for `and`, `or`, and `xor`, respectively. The notation was popularized by the C programming language. + +## Evaluation rules + +Operations like `+` and `*` return a list of values. + +``` +>>> `1` + `1` +(`1`, `0`) +``` + +But, if lists are an intermediate result of evaluation, they are usually concatenated together. + +``` +>>> (`1` + `1`) * `11` +(`01`, `10`) +``` +The intermediate result of `` `1` + `1` `` is `` (`1`, `0`) ``, but it is concatenated to `` `10` `` before being multiplied with `` `11` ``. + + +### Assignment + +A list of identifiers can be a LHS and it will bind the corresponding values on the RHS list. Any extra elements on the RHS are not assigned to a variable. This allowed writing the code ``x = `1` + `1` `` to work as expected, i.e., `x` is assigned `0`. But you can also write ``(c, x) = `1` + `1` `` if you wish to also keep track of the carry. + + + +### Examples + +It is a property of 2-adic numbers that they must start with `001` in order to have a square root. Let's find the square root of 17, which is `10001` in binary. + +``` +; sqrt.2p +; Brenton Bostick +; Square Root of 17 in Tuppence +; +; initial guess of 1 +guess = 1 +; Newton's Method +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) + +; print guess as integer with 100 bits of precision +print(0 # (guess %% 100)) + +; print result as integer with 100 bits of precision +result = guess * guess +print(0 # (result %% 100)) +``` + +And if we run the interpreter with this file: + +``` +./tuppence.exe sqrt.2p +9 +49/9 +1889/441 +3437249/833049 +Warning: Loop limit exceeded in RationalWord divide. Returning truncated result. Loop limit is: 1000 +3526299271737049901142489188064762452771618484675801833747781042574412567139404581266443855599402418586554728091093092889940241552756633570817371360792679954999464735144547873671925392914561568751203486508447435984639721081511658824706088313577043771350752808234845312735541844167512127491844406685816091329/833049 +Warning: Loop limit exceeded in RationalWord divide. Returning truncated result. Loop limit is: 1000 +1990313625524614549424364567010171681910560013052581941814155397945006558578945406256220214929938179709836283596602702628927511002443863124929448389577151524995890102899552055223683766691867151573000720401828645206417256180613135585469867360297289228689905093033409343704317280587576819335923647992097436353/833049 +1049862217672007560153128969961 +Warning: Loop limit exceeded in RationalWord multiply. Returning truncated result. Loop limit is: 1000 +17 +``` + +The warnings indicate that it took more than 1000 iterations to get an answer inside of the code for dividing and multiplying. The guesses that are printed have been truncated to 1000 digits of precision. It is especially easy to generate fractions with enormous numerators and denominators in Tuppence and I will be working on methods for managing this complexity. + +So we can see in terms of 100-bit unsigned integers, 1049862217672007560153128969961 is the square root of 17. + +Pretty cool, huh? + +## Why the name *Tuppence*? + +Tuppence is British slang for "two pence" Two pence. You frequently hear the coin referred to as "2p". I was looking for a name that captured the sense that p-adic numbers, for p=2, were the foundation of the language. + + + + +## Building Instructions + +Tuppence uses CMake to generate project files. + +Make sure to have a build of LLVM. Follow the instructions for building LLVM with CMake: http://llvm.org/docs/CMake.html + +Open CMake and set the source code location to tuppence/src. It is recommended to set the build directory to tuppence/build. + +Set the CMake entries as follows: + +Name | Value +------- | ------- +LLVM_DIR | your-llvm-dir/build/lib/cmake/llvm +GTEST_INCLUDE_DIR | your-llvm-dir/llvm/utils/unittest/googletest/include +GTEST_LIBRARY | your-llvm-dir/build/Release/lib/gtest.lib +GTEST_LIBRARY_DEBUG | your-llvm-dir/build/Debug/lib/gtest.lib +GTEST_MAIN_LIBRARY | your-llvm-dir/build/Release/lib/gtest_main.lib +GTEST_MAIN_LIBRARY_DEBUG | your-llvm-dir/build/Debug/lib/gtest_main.lib + +Then Configure and Generate. The tuppence project is the interpreter and the test project builds an executable named runUnitTests for running the GTest tests for Tuppence. + +## Future work + +Actual LLVM codegen, Jupyter kernel, Finite Field arithmetic, crit-bit trees, BDDs (binary decision diagrams). diff --git a/examples/sqrt.2p b/examples/sqrt.2p new file mode 100644 index 0000000..82b4a27 --- /dev/null +++ b/examples/sqrt.2p @@ -0,0 +1,26 @@ +; sqrt.2p +; Brenton Bostick +; Square Root of 17 in Tuppence +; +; initial guess of 1 +guess = 1 +; Newton's Method +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) +guess = (guess + 17/guess) >> 1 +print(guess) + +; print guess as integer with 100 bits of precision +print(0 # (guess %% 100)) + +; print result as integer with 100 bits of precision +result = guess * guess +print(0 # (result %% 100)) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..6f3e578 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required (VERSION 2.8.8) + +project(Tuppence) + +# The version number. +set(Tuppence_VERSION_MAJOR 0) +set(Tuppence_VERSION_MINOR 1) +set(Tuppence_LOOP_LIMIT 1000) + + +# for making sure that gtest and gtest_main are copied into the same build dir as tuppence and runUnitTests +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +set(CMAKE_MACOSX_RPATH ON) + +add_subdirectory(tuppence) +add_subdirectory(test) diff --git a/src/TuppenceConfig.h.in b/src/TuppenceConfig.h.in new file mode 100644 index 0000000..667a67b --- /dev/null +++ b/src/TuppenceConfig.h.in @@ -0,0 +1,4 @@ +// the configured options and settings for Tuppence +#define Tuppence_VERSION_MAJOR @Tuppence_VERSION_MAJOR@ +#define Tuppence_VERSION_MINOR @Tuppence_VERSION_MINOR@ +#define Tuppence_LOOP_LIMIT @Tuppence_LOOP_LIMIT@ diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt new file mode 100644 index 0000000..d3dbcd2 --- /dev/null +++ b/src/test/CMakeLists.txt @@ -0,0 +1,70 @@ +project(runUnitTests) + +find_package(LLVM REQUIRED CONFIG) + +# Find the libraries that correspond to the LLVM components +# that we wish to use +llvm_map_components_to_libnames(llvm_libs + Core + ExecutionEngine + RuntimeDyld + native) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file ( + ../TuppenceConfig.h.in + "${CMAKE_CURRENT_BINARY_DIR}/TuppenceConfig.h" + ) + +# add the binary tree to the search path for include files +# so that we will find TuppenceConfig.h +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + +include_directories(${LLVM_INCLUDE_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) + +enable_testing() +find_package(GTest REQUIRED) +include_directories(${GTEST_INCLUDE_DIRS}) + +include_directories("../tuppence/") + +# Add test cpp file +add_executable(runUnitTests + Eval.test.cpp + FiniteWord.test.cpp + Parser.test.cpp + RationalWord.test.cpp + Value.test.cpp + + ../tuppence/Eval.h + ../tuppence/Eval.cpp + ../tuppence/FiniteWord.h + ../tuppence/FiniteWord.cpp + ../tuppence/Lexer.h + ../tuppence/Lexer.cpp + ../tuppence/Logger.h + ../tuppence/Logger.cpp + ../tuppence/Parser.h + ../tuppence/Parser.cpp + ../tuppence/RationalWord.h + ../tuppence/RationalWord.cpp + ../tuppence/Value.h + ../tuppence/Value.cpp + ../tuppence/ValueList.h + ../tuppence/ValueList.cpp + ${CMAKE_CURRENT_BINARY_DIR}/TuppenceConfig.h +) + +# Link test executable against gtest & gtest_main +target_link_libraries(runUnitTests ${GTEST_BOTH_LIBRARIES}) +target_link_libraries(runUnitTests ${llvm_libs}) + +add_test( + NAME runUnitTests + COMMAND runUnitTests +) + +set_property(TARGET runUnitTests PROPERTY CXX_STANDARD 11) +set_property(TARGET runUnitTests PROPERTY CXX_STANDARD_REQUIRED ON) diff --git a/src/test/Eval.test.cpp b/src/test/Eval.test.cpp new file mode 100644 index 0000000..ce3b5ba --- /dev/null +++ b/src/test/Eval.test.cpp @@ -0,0 +1,155 @@ +//===------ Eval.test.cpp -------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Parser.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/StringRef.h" + +#include "gtest/gtest.h" + +using namespace tuppence; + +TEST(Eval, Test1) { + + { + std::stringstream ss("`11011`\n"); + Parser P(ss); + P.readNextToken(); + auto Parsed = P.ParseTopLevelExpression(); + auto Expr = llvm::dyn_cast(Parsed.get()); + ASSERT_TRUE(Expr != nullptr); + auto Evaled = Expr->eval(); + auto Val = llvm::dyn_cast(Evaled.get()); + ASSERT_TRUE(Val != nullptr); + + EXPECT_EQ("`11011`", Val->representation()); + } + + { + std::stringstream ss("27\n"); + Parser P(ss); + P.readNextToken(); + auto Parsed = P.ParseTopLevelExpression(); + auto Expr = llvm::dyn_cast(Parsed.get()); + ASSERT_TRUE(Expr != nullptr); + auto Evaled = Expr->eval(); + auto Val = llvm::dyn_cast(Evaled.get()); + ASSERT_TRUE(Val != nullptr); + + EXPECT_EQ("0'11011", Val->representation()); + + auto Nine = std::make_shared(RationalWord::FactoryString("9")); + + auto Res = Val->residue(*Nine->getTransient().getRawData()); + EXPECT_EQ("`000011011`", Res.representation()); + } + + { + std::stringstream ss("0#`11011`\n"); + Parser P(ss); + P.readNextToken(); + auto E = P.ParseTopLevelExpression(); + ASSERT_TRUE(E != nullptr); + auto Expr = llvm::dyn_cast(E.get()); + ASSERT_TRUE(Expr != nullptr); + EXPECT_EQ(Expr->getOp(), '#'); + auto Evaled = Expr->eval(); + ASSERT_TRUE(Evaled != nullptr); + auto Val = llvm::dyn_cast(Evaled.get()); + ASSERT_TRUE(Val != nullptr); + + EXPECT_EQ("0'11011", Val->representation()); + } + + // Eval code + { + std::stringstream ss("17 / ``\n"); + Parser P(ss); + P.readNextToken(); + auto E = P.ParseTopLevelExpression(); + ASSERT_TRUE(E != nullptr); + auto Expr = llvm::dyn_cast(E.get()); + ASSERT_TRUE(Expr != nullptr); + + auto LHS = Expr->getLHS(); + ASSERT_TRUE(LHS != nullptr); + auto RationalWordLHS = llvm::dyn_cast(LHS.get()); + ASSERT_TRUE(RationalWordLHS != nullptr); + + auto RHS = Expr->getRHS(); + ASSERT_TRUE(RHS != nullptr); + auto FiniteWordRHS = llvm::dyn_cast(RHS.get()); + ASSERT_TRUE(FiniteWordRHS != nullptr); + + auto Evaled = Expr->eval(); + ASSERT_TRUE(Evaled == nullptr); + } + + // Test Eval code + { + std::stringstream ss("17 - ``\n"); + Parser P(ss); + P.readNextToken(); + auto E = P.ParseTopLevelExpression(); + ASSERT_TRUE(E != nullptr); + auto Expr = llvm::dyn_cast(E.get()); + ASSERT_TRUE(Expr != nullptr); + + auto LHS = Expr->getLHS(); + ASSERT_TRUE(LHS != nullptr); + auto RationalWordLHS = llvm::dyn_cast(LHS.get()); + ASSERT_TRUE(RationalWordLHS != nullptr); + + auto RHS = Expr->getRHS(); + ASSERT_TRUE(RHS != nullptr); + auto FiniteWordRHS = llvm::dyn_cast(RHS.get()); + ASSERT_TRUE(FiniteWordRHS != nullptr); + + auto Evaled = Expr->eval(); + ASSERT_TRUE(Evaled == nullptr); + } + + // Test Eval code + { + std::stringstream ss("`` / ``\n"); + Parser P(ss); + P.readNextToken(); + auto E = P.ParseTopLevelExpression(); + ASSERT_TRUE(E != nullptr); + auto Expr = llvm::dyn_cast(E.get()); + ASSERT_TRUE(Expr != nullptr); + + auto LHS = Expr->getLHS(); + ASSERT_TRUE(LHS != nullptr); + auto FiniteWordLHS = llvm::dyn_cast(LHS.get()); + ASSERT_TRUE(FiniteWordLHS != nullptr); + + auto RHS = Expr->getRHS(); + ASSERT_TRUE(RHS != nullptr); + auto FiniteWordRHS = llvm::dyn_cast(RHS.get()); + ASSERT_TRUE(FiniteWordRHS != nullptr); + + auto Evaled = Expr->eval(); + ASSERT_TRUE(Evaled == nullptr); + } +} + +TEST(Eval, ErrorTest1) { + + { + std::stringstream ss("() # 2\n"); + Parser P(ss); + P.readNextToken(); + auto Parsed = P.ParseTopLevelExpression(); + ASSERT_TRUE(Parsed != nullptr); + auto Evaled = Parsed->eval(); + ASSERT_TRUE(Evaled == nullptr); + } +} diff --git a/src/test/FiniteWord.test.cpp b/src/test/FiniteWord.test.cpp new file mode 100644 index 0000000..004e9e8 --- /dev/null +++ b/src/test/FiniteWord.test.cpp @@ -0,0 +1,81 @@ +//===------ FiniteWord.test.cpp -------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "FiniteWord.h" + +#include "gtest/gtest.h" + +using namespace tuppence; + +class FiniteWordTest : public ::testing::Test { +protected: + + FiniteWordTest() {} + + virtual ~FiniteWordTest() {} + + static void SetUpTestCase() {} + + virtual void SetUp() {} + + virtual void TearDown() {} +}; + +TEST_F(FiniteWordTest, unequal) { + std::vector Vals; + Vals.push_back(finiteword::ZERO_1BIT); + Vals.push_back(finiteword::ZERO_1BIT); + Vals.push_back(finiteword::ONE_1BIT); + EXPECT_EQ(finiteword::ZERO_1BIT, FiniteWord::notequal(Vals)); + + + Vals.clear(); + Vals.push_back(finiteword::ONE_1BIT); + Vals.push_back(finiteword::ZERO_1BIT); + Vals.push_back(finiteword::ZERO_1BIT); + EXPECT_EQ(finiteword::ZERO_1BIT, FiniteWord::notequal(Vals)); +} + +TEST_F(FiniteWordTest, concatenate) { + + auto Res = finiteword::ONE_1BIT.concatenate(finiteword::ONE_1BIT); + + EXPECT_EQ("11", Res.bits()); + + EXPECT_EQ("`11`", Res.string()); +} + +TEST_F(FiniteWordTest, period) { + + EXPECT_TRUE(FiniteWord::isPeriodCompressed(FiniteWord::FactoryString("101"))); + + EXPECT_FALSE(FiniteWord::isPeriodCompressed(FiniteWord::FactoryString("1010"))); + + auto A = FiniteWord::FactoryString("1010"); + FiniteWord::compressPeriod(A); + EXPECT_EQ(FiniteWord::FactoryString("10"), A); + +} + +TEST_F(FiniteWordTest, FactoryRepsWord) { + + auto Pattern = FiniteWord::FactoryString("1110"); + + auto A = FiniteWord::FactoryRepsWord(1, Pattern); + EXPECT_EQ(FiniteWord::FactoryString("1110"), A); + + auto B = FiniteWord::FactoryRepsWord(2, Pattern); + EXPECT_EQ(FiniteWord::FactoryString("11101110"), B); + + auto C = FiniteWord::FactoryRepsWord(3, Pattern); + EXPECT_EQ(FiniteWord::FactoryString("111011101110"), C); + + auto D = FiniteWord::FactoryRepsWord(10, Pattern); + EXPECT_EQ(FiniteWord::FactoryString("1110111011101110111011101110111011101110"), D); +} diff --git a/src/test/Parser.test.cpp b/src/test/Parser.test.cpp new file mode 100644 index 0000000..e94d289 --- /dev/null +++ b/src/test/Parser.test.cpp @@ -0,0 +1,58 @@ +//===------ Parser.test.cpp -----------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Parser.h" + +#include "gtest/gtest.h" + +using namespace tuppence; + +TEST(Parser, Test1) { + std::stringstream ss; + ss << "1+1\n"; + Parser P(ss); + P.readNextToken(); + auto Parsed = P.ParseTopLevelExpression(); + auto Expr = llvm::dyn_cast(Parsed.get()); + EXPECT_TRUE(Expr != nullptr); +} + +TEST(Parser, Test2) { + std::stringstream ss; + ss << "() # 2\n"; + Parser P(ss); + P.readNextToken(); + auto Parsed = P.ParseTopLevelExpression(); + auto Expr = llvm::dyn_cast(Parsed.get()); + EXPECT_TRUE(Expr != nullptr); +} + +TEST(Parser, Test3) { + std::stringstream ss; + ss << "3 --\n"; + Parser P(ss); + P.readNextToken(); + auto Parsed = P.ParseTopLevelExpression(); + EXPECT_TRUE(Parsed == nullptr); +} + +TEST(Parser, Test4) { + std::stringstream ss; + ss << "01\n"; + Parser P(ss); + P.readNextToken(); + auto Parsed = P.ParseTopLevelExpression(); + auto Expr = llvm::dyn_cast(Parsed.get()); + EXPECT_TRUE(Expr != nullptr); + auto Val = Expr->eval(); + auto RationalWordVal = llvm::dyn_cast(Val.get()); + EXPECT_TRUE(RationalWordVal != nullptr); + + EXPECT_EQ("1", RationalWordVal->decimal()); +} diff --git a/src/test/RationalWord.test.cpp b/src/test/RationalWord.test.cpp new file mode 100644 index 0000000..8d3eb22 --- /dev/null +++ b/src/test/RationalWord.test.cpp @@ -0,0 +1,187 @@ +//===------ RationalWord.test.cpp -----------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "RationalWord.h" + +#include "gtest/gtest.h" + +namespace tuppence { + namespace rationalword { + + RationalWord TWO; + RationalWord THREE; + RationalWord FOUR; + RationalWord FIVE; + RationalWord SEVEN; + RationalWord TEN; + RationalWord SEVENTEEN; + RationalWord NINETEEN; + RationalWord TWENTYTHREE; + RationalWord THIRTYTHREE; + RationalWord TWOHUNDREDNINETEEN; + RationalWord THREEHUNDREDTHIRTYTHREE; + RationalWord TENTHOUSANDONE; + + RationalWord SEVENTEEN_OVER_SEVEN; + RationalWord FIVE_OVER_SEVENTEEN; + RationalWord NINETEEN_OVER_SEVEN; + RationalWord FIVE_OVER_NINETEEN; + RationalWord TWENTYTHREE_OVER_THREE; + RationalWord FIVE_OVER_TWENTRYTHREE; + RationalWord TWENTYTHREE_OVER_SEVEN; + RationalWord ONE_OVER_TWOHUNDREDNINETEEN; + RationalWord ONE_OVER_TENTHOUSANDONE; + + } +} + +using namespace tuppence; + +class RationalWordTest : public ::testing::Test { +protected: + + RationalWordTest() {} + + virtual ~RationalWordTest() {} + + static void SetUpTestCase() { + + rationalword::TWO = RationalWord::FactoryString("2"); + rationalword::THREE = RationalWord::FactoryString("3"); + rationalword::FOUR = RationalWord::FactoryString("4"); + rationalword::FIVE = RationalWord::FactoryString("5"); + rationalword::SEVEN = RationalWord::FactoryString("7"); + rationalword::TEN = RationalWord::FactoryString("10"); + rationalword::SEVENTEEN = RationalWord::FactoryString("17"); + rationalword::NINETEEN = RationalWord::FactoryString("19"); + rationalword::TWENTYTHREE = RationalWord::FactoryString("23"); + rationalword::THIRTYTHREE = RationalWord::FactoryString("33"); + rationalword::TWOHUNDREDNINETEEN = RationalWord::FactoryString("219"); + rationalword::THREEHUNDREDTHIRTYTHREE = RationalWord::FactoryString("333"); + rationalword::TENTHOUSANDONE = RationalWord::FactoryString("10001"); + + rationalword::SEVENTEEN_OVER_SEVEN = rationalword::SEVENTEEN / rationalword::SEVEN; + rationalword::FIVE_OVER_SEVENTEEN = rationalword::FIVE / rationalword::SEVENTEEN; + rationalword::NINETEEN_OVER_SEVEN = rationalword::NINETEEN / rationalword::SEVEN; + rationalword::FIVE_OVER_NINETEEN = rationalword::FIVE / rationalword::NINETEEN; + rationalword::TWENTYTHREE_OVER_THREE = rationalword::TWENTYTHREE / rationalword::THREE; + rationalword::FIVE_OVER_TWENTRYTHREE = rationalword::FIVE / rationalword::TWENTYTHREE; + rationalword::TWENTYTHREE_OVER_SEVEN = rationalword::TWENTYTHREE / rationalword::SEVEN; + rationalword::ONE_OVER_TWOHUNDREDNINETEEN = rationalword::ONE / rationalword::TWOHUNDREDNINETEEN; + rationalword::ONE_OVER_TENTHOUSANDONE = rationalword::ONE / rationalword::TENTHOUSANDONE; + + } + + virtual void SetUp() {} + + virtual void TearDown() {} +}; + +TEST_F(RationalWordTest, multiply) { + + EXPECT_EQ("-3", (rationalword::MINUS_ONE * rationalword::THREE).decimal()); + + EXPECT_EQ("5/7", (rationalword::FIVE_OVER_SEVENTEEN * rationalword::SEVENTEEN_OVER_SEVEN).decimal()); + + EXPECT_EQ("5/7", (rationalword::SEVENTEEN_OVER_SEVEN * rationalword::FIVE_OVER_SEVENTEEN).decimal()); + + EXPECT_EQ("5/7", (rationalword::NINETEEN_OVER_SEVEN * rationalword::FIVE_OVER_NINETEEN).decimal()); + + EXPECT_EQ("5/7", (rationalword::FIVE_OVER_NINETEEN * rationalword::NINETEEN_OVER_SEVEN).decimal()); + + EXPECT_EQ("5/3", (rationalword::TWENTYTHREE_OVER_THREE * rationalword::FIVE_OVER_TWENTRYTHREE).decimal()); + + EXPECT_EQ("5/7", (rationalword::TWENTYTHREE_OVER_SEVEN * rationalword::FIVE_OVER_TWENTRYTHREE).decimal()); + + EXPECT_EQ("5/7", (rationalword::FIVE_OVER_TWENTRYTHREE * rationalword::TWENTYTHREE_OVER_SEVEN).decimal()); + + EXPECT_EQ("1", (rationalword::ONE_OVER_TWOHUNDREDNINETEEN * rationalword::TWOHUNDREDNINETEEN).decimal()); + + EXPECT_EQ("1", (rationalword::TWOHUNDREDNINETEEN * rationalword::ONE_OVER_TWOHUNDREDNINETEEN).decimal()); + + EXPECT_EQ("1", (rationalword::ONE * rationalword::ONE).decimal()); + + EXPECT_EQ("2", (rationalword::TWO * rationalword::ONE).decimal()); + + EXPECT_EQ("3", (rationalword::THREE * rationalword::ONE).decimal()); + + EXPECT_EQ("4", (rationalword::FOUR * rationalword::ONE).decimal()); + + EXPECT_EQ("5", (rationalword::FIVE * rationalword::ONE).decimal()); + + EXPECT_EQ("10", (rationalword::TEN * rationalword::ONE).decimal()); + + EXPECT_EQ("33", (rationalword::THIRTYTHREE * rationalword::ONE).decimal()); + + EXPECT_EQ("219", (rationalword::TWOHUNDREDNINETEEN * rationalword::ONE).decimal()); + + EXPECT_EQ("333", (rationalword::THREEHUNDREDTHIRTYTHREE * rationalword::ONE).decimal()); +} + +TEST_F(RationalWordTest, divide) { + + EXPECT_EQ("17/7", rationalword::SEVENTEEN_OVER_SEVEN.decimal()); + + EXPECT_EQ("5/17", rationalword::FIVE_OVER_SEVENTEEN.decimal()); + + EXPECT_EQ("19/7", rationalword::NINETEEN_OVER_SEVEN.decimal()); + + EXPECT_EQ("5/19", rationalword::FIVE_OVER_NINETEEN.decimal()); + + EXPECT_EQ("23/3", rationalword::TWENTYTHREE_OVER_THREE.decimal()); + + EXPECT_EQ("5/23", rationalword::FIVE_OVER_TWENTRYTHREE.decimal()); + + EXPECT_EQ("23/7", rationalword::TWENTYTHREE_OVER_SEVEN.decimal()); + + EXPECT_EQ("1/219", rationalword::ONE_OVER_TWOHUNDREDNINETEEN.decimal()); + + EXPECT_EQ("1/10001", rationalword::ONE_OVER_TENTHOUSANDONE.decimal()); +} + +TEST_F(RationalWordTest, unequal) { + + auto ZERO_1BIT = FiniteWord::FactoryBool((bool)0); + + std::vector Vals; + Vals.push_back(rationalword::ONE); + Vals.push_back(rationalword::ONE); + Vals.push_back(rationalword::TWO); + EXPECT_EQ(ZERO_1BIT, RationalWord::notequal(Vals)); + + + Vals.clear(); + Vals.push_back(rationalword::TWO); + Vals.push_back(rationalword::ONE); + Vals.push_back(rationalword::ONE); + EXPECT_EQ(ZERO_1BIT, RationalWord::notequal(Vals)); +} + +TEST_F(RationalWordTest, subtract) { + + EXPECT_EQ(rationalword::ONE, rationalword::TWO - rationalword::ONE); + + EXPECT_EQ(rationalword::TWO, rationalword::THREE - rationalword::ONE); + + EXPECT_EQ(rationalword::TWO, rationalword::SEVEN - rationalword::FIVE); +} + +TEST_F(RationalWordTest, windupTransient) { + + FiniteWord period = FiniteWord::FactoryString("11011"); + FiniteWord transient = FiniteWord::FactoryString("11011"); + + auto Res = RationalWord::FactoryPeriodTransient(period, transient); + period = Res.getPeriod(); + transient = Res.getTransient(); + + EXPECT_EQ(FiniteWord::FactoryString("11011"), period); + EXPECT_EQ(FiniteWord::FactoryString(""), transient); + +} diff --git a/src/test/Value.test.cpp b/src/test/Value.test.cpp new file mode 100644 index 0000000..46e2cf3 --- /dev/null +++ b/src/test/Value.test.cpp @@ -0,0 +1,22 @@ +//===------ Value.test.cpp ------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Value.h" + +#include "gtest/gtest.h" + +using namespace tuppence; + +TEST(Value, bitLength) { + EXPECT_EQ(1, bitLength(1)); + EXPECT_EQ(2, bitLength(3)); + EXPECT_EQ(3, bitLength(7)); + EXPECT_EQ(4, bitLength(15)); + EXPECT_EQ(8, bitLength(255)); +} diff --git a/src/tuppence/CMakeLists.txt b/src/tuppence/CMakeLists.txt new file mode 100644 index 0000000..39d6260 --- /dev/null +++ b/src/tuppence/CMakeLists.txt @@ -0,0 +1,52 @@ +project(tuppence) + +find_package(LLVM REQUIRED CONFIG) + +# Find the libraries that correspond to the LLVM components +# that we wish to use +llvm_map_components_to_libnames(llvm_libs + Core + ExecutionEngine + RuntimeDyld + native) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file ( + ../TuppenceConfig.h.in + "${CMAKE_CURRENT_BINARY_DIR}/TuppenceConfig.h" + ) + +# add the binary tree to the search path for include files +# so that we will find TuppenceConfig.h +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + +include_directories(${LLVM_INCLUDE_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) + +# add the interpreter +add_executable(tuppence + Eval.h + Eval.cpp + FiniteWord.h + FiniteWord.cpp + Lexer.h + Lexer.cpp + Logger.h + Logger.cpp + Main.cpp + Parser.h + Parser.cpp + RationalWord.h + RationalWord.cpp + Value.h + Value.cpp + ValueList.h + ValueList.cpp + ${CMAKE_CURRENT_BINARY_DIR}/TuppenceConfig.h) + +# Link against LLVM libraries +target_link_libraries(tuppence ${llvm_libs}) + +set_property(TARGET tuppence PROPERTY CXX_STANDARD 11) +set_property(TARGET tuppence PROPERTY CXX_STANDARD_REQUIRED ON) diff --git a/src/tuppence/Eval.cpp b/src/tuppence/Eval.cpp new file mode 100644 index 0000000..5c2c506 --- /dev/null +++ b/src/tuppence/Eval.cpp @@ -0,0 +1,953 @@ +//===------ Eval.cpp ------------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Eval.h" + +#include "Logger.h" +#include "Parser.h" + +#include "llvm/Support/ErrorHandling.h" + +#include + +namespace tuppence { + + namespace eval { + + const FiniteWord EMPTY = FiniteWord::FactoryEmpty(); + const FiniteWord ZERO_1BIT = FiniteWord::FactoryBool((bool)0); + const FiniteWord ONE_1BIT = FiniteWord::FactoryBool((bool)1); + + const Symbol SYMBOL_INFINITY = Symbol::Symbol("infinity"); + const BuiltinFunction BUILTIN_PRINT = BuiltinFunction::BuiltinFunction(&tuppence::eval::print); + const BuiltinFunction BUILTIN_EXIT = BuiltinFunction::BuiltinFunction(&tuppence::eval::exit); + const BuiltinFunction BUILTIN_RATIONALIZE = BuiltinFunction::BuiltinFunction(&tuppence::eval::rationalize); + + } + +} + +using namespace tuppence; + +std::map> eval::NamedValues; + +const std::shared_ptr ExpressionListExprAST::eval() const { + std::vector> Vals; + for (auto& Expr : Exprs) { + auto Val = Expr->eval(); + if (!Val) { + return nullptr; + } + // flatten out all child value lists + if (auto ValList = llvm::dyn_cast(Val.get())) { + for (auto& V : *ValList) { + Vals.push_back(V); + } + } + else { + Vals.push_back(Val); + } + } + return std::make_shared(Vals); +} + +/// Last part of sequence is returned +const std::shared_ptr ExpressionSequenceExprAST::eval() const { + if (Exprs.empty()) { + return std::make_shared(eval::EMPTY); + } + auto Iter = Exprs.begin(); + for (; Iter != --Exprs.end(); Iter++) { + auto Expr = *Iter; + auto Val = Expr->eval(); + if (!Val) { + return nullptr; + } + } + + auto Last = *Iter; + auto LastVal = Last->eval(); + if (!LastVal) { + return nullptr; + } + return LastVal; +} + +const std::shared_ptr RationalWordExprAST::eval() const { + return std::make_shared(Val); +} + +const std::shared_ptr FiniteWordExprAST::eval() const { + return std::make_shared(Val); +} + +const std::shared_ptr IdentifierExprAST::eval() const { + auto V = eval::NamedValues[Name]; + if (!V) { + return LogError("Unknown variable name: " + Name); + } + return V; +} + +const std::shared_ptr UnaryExprAST::eval() const { + auto OperandValue = Operand->eval(); + if (!OperandValue) { + return nullptr; + } + + // For arithmetic, bitwise, and structural operations, concatenate all lists together + if (auto ListOperand = llvm::dyn_cast(OperandValue.get())) { + OperandValue = ListOperand->concatenate(); + if (!OperandValue) { + return nullptr; + } + } + + switch (Opcode) { + case '-': { + if (auto FiniteWordOperand = llvm::dyn_cast(OperandValue.get())) { + return std::make_shared(-(*FiniteWordOperand)); + } + else if (auto RationalWordOperand = llvm::dyn_cast(OperandValue.get())) { + return std::make_shared(-(*RationalWordOperand)); + } + return LogError("Invalid type for operator -: " + stringFromValueKind(OperandValue->getKind())); + } + case tok_ddd: { + if (auto FiniteWordOperand = llvm::dyn_cast(OperandValue.get())) { + if (FiniteWordOperand->getSize() == 0) { + return std::make_shared(); + } + else { + return std::make_shared(RationalWord::FactoryPeriodTransient(*FiniteWordOperand, eval::EMPTY)); + } + } + return LogError("Invalid type for operator ...: " + stringFromValueKind(OperandValue->getKind())); + } + case '~': { + if (auto FiniteWordOperand = llvm::dyn_cast(OperandValue.get())) { + return std::make_shared(FiniteWord::bitwise_not(*FiniteWordOperand)); + } + else if (auto RationalWordOperand = llvm::dyn_cast(OperandValue.get())) { + return std::make_shared(RationalWord::bitwise_not(*RationalWordOperand)); + } + return LogError("Invalid type for operator ~: " + stringFromValueKind(OperandValue->getKind())); + } + default: + return LogError("Unary operator not found: " + stringFromToken(Opcode)); + } +} + +const std::shared_ptr BinaryExprAST::eval() const { + // Special case '=' because we don't want to emit the LHS as an expression. + if (Op == '=') { + if (auto IdentifierLHS = llvm::dyn_cast(LHS.get())) { + // eval the RHS. + + auto Val = RHS->eval(); + if (!Val) { + return nullptr; + } + + if (auto ListVal = llvm::dyn_cast(Val.get())) { + + eval::NamedValues[IdentifierLHS->getName()] = (*ListVal)[0]; + + return std::make_shared(); + } + else { + // regular, non-ValueList + eval::NamedValues[IdentifierLHS->getName()] = Val; + + return std::make_shared(); + } + } + else if (auto ListLHS = llvm::dyn_cast(LHS.get())) { + + auto Val = RHS->eval(); + if (!Val) { + return nullptr; + } + + if (auto ListVal = llvm::dyn_cast(Val.get())) { + + auto Exprs = ListLHS->getExprs(); + + if (Exprs.size() <= ListVal->size()) { + + for (size_t i = 0; i < Exprs.size(); i++) { + auto Var = Exprs[i]; + if (auto V = llvm::dyn_cast(Var.get())) { + eval::NamedValues[V->getName()] = (*ListVal)[i]; + } + else { + return LogError("Expected identifer: " + Var->string()); + } + } + + return std::make_shared(); + } + else { + return LogError("Too many identifiers for assignment"); + } + } + else { + return LogError("List expected on RHS: " + RHS->string()); + } + } + else { + return LogError("Invalid LHS for =: " + LHS->string()); + } + } + + auto L = LHS->eval(); + if (!L) { + return nullptr; + } + // if L is a list, then concatenate all together + if (auto ListL = llvm::dyn_cast(L.get())) { + L = ListL->concatenate(); + if (!L) { + return nullptr; + } + } + + // handle method calls first + if (Op == '.') { + if (auto IdentifierRHS = llvm::dyn_cast(RHS.get())) { + auto Name = IdentifierRHS->getName(); + if (auto FiniteWordL = llvm::dyn_cast(L.get())) { + if (Name == "size") { + return std::make_shared(RationalWord::FactorySizeT(FiniteWordL->getSize())); + } + else { + return LogError("Unrecognized method for FiniteWord: " + Name); + } + } + else if (auto RationalWordL = llvm::dyn_cast(L.get())) { + if (Name == "period") { + return std::make_shared(RationalWordL->getPeriod()); + } else if (Name == "transient") { + return std::make_shared(RationalWordL->getTransient()); + } + else if (Name == "size") { + auto Infinity = eval::NamedValues["infinity"]; + return Infinity; + } + else if (Name == "numerator") { + auto Numerator = RationalWordL->getNumerator(); + return std::make_shared(Numerator); + } + else if (Name == "denominator") { + auto Denominator = RationalWordL->getDenominator(); + return std::make_shared(Denominator); + } + else { + return LogError("Unrecognized method for RationalWord: " + Name); + } + } else { + return LogError("Invalid LHS for .: " + L->string()); + } + } + else { + return LogError("Invalid RHS for .: " + RHS->string()); + } + } + + auto R = RHS->eval(); + if (!R) { + return nullptr; + } + + // For arithmetic and structural operations, concatenate all lists together + if (auto ListR = llvm::dyn_cast(R.get())) { + R = ListR->concatenate(); + if (!R) { + return nullptr; + } + } + + switch (Op) { + case tok_pp: { + if (auto RationalWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + if (!RationalWordR->isNonNegativeInteger()) { + return LogError("Expected non-negative integer on RHS of %%: " + R->string()); + } + auto i = RationalWordR->getUInt64Value(); + return std::make_shared(RationalWordL->residue(i)); + } else if (auto SymbolR = llvm::dyn_cast(R.get())) { + if (*SymbolR == eval::SYMBOL_INFINITY) { + return L; + } + } + } + else if (auto FiniteWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + if (!RationalWordR->isNonNegativeInteger()) { + return LogError("Expected non-negative integer on RHS of %%: " + R->string()); + } + auto i = RationalWordR->getUInt64Value(); + if (i > FiniteWordL->getSize()) { + return LogError("Requested residue is too large: " + L->string() + " " + R->string()); + } + return std::make_shared(FiniteWordL->residue(i)); + } + else if (auto SymbolR = llvm::dyn_cast(R.get())) { + if (*SymbolR == eval::SYMBOL_INFINITY) { + return L; + } + } + } + return LogError("Incorrect types for %%: " + L->string() + " " + R->string()); + } + case tok_gtgt: { + if (auto RationalWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + if (!RationalWordR->isNonNegativeInteger()) { + return LogError("Expected non-negative integer on RHS of >>: " + R->string()); + } + auto i = RationalWordR->getUInt64Value(); + return std::make_shared(RationalWordL->shiftRight(i)); + } + } + else if (auto FiniteWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + if (!RationalWordR->isNonNegativeInteger()) { + return LogError("Expected non-negative integer on RHS of >>: " + R->string()); + } + auto i = RationalWordR->getUInt64Value(); + if (i > FiniteWordL->getSize()) { + return LogError("Requested shift is too large: " + L->string() + " " + R->string()); + } + return std::make_shared(FiniteWordL->shiftRight(i)); + } + } + return LogError("Incorrect types for >>: " + L->string() + " " + R->string()); + } + case tok_gtp: { + if (auto RationalWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + if (!RationalWordR->isNonNegativeInteger()) { + return LogError("Expected non-negative integer on RHS of >%: " + R->string()); + } + auto i = RationalWordR->getUInt64Value(); + RationalWord Hi; + FiniteWord Lo; + RationalWordL->shiftRightResidue(i, Hi, Lo); + std::vector> Vec = { std::make_shared(Lo), std::make_shared(Hi) }; + return std::make_shared(Vec); + } + } + else if (auto FiniteWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + if (!RationalWordR->isNonNegativeInteger()) { + return LogError("Expected non-negative integer on RHS of >%: " + R->string()); + } + auto i = RationalWordR->getUInt64Value(); + if (i > FiniteWordL->getSize()) { + return LogError("Requested shift is too large: " + L->string() + " " + R->string()); + } + FiniteWord Hi; + FiniteWord Lo; + FiniteWordL->shiftRightResidue(i, Hi, Lo); + std::vector> Vec = { std::make_shared(Lo), std::make_shared(Hi) }; + return std::make_shared(Vec); + } + } + return LogError("Incorrect types for >%: " + L->string() + " " + R->string()); + } + case '-': { + if (auto FiniteWordL = llvm::dyn_cast(L.get())) { + if (auto FiniteWordR = llvm::dyn_cast(R.get())) { + if (FiniteWordL->getSize() != FiniteWordR->getSize()) { + return LogError("Expected same sizes for -: " + L->string() + " " + R->string()); + } + if (FiniteWordL->getSize() == 0) { + return LogError("Expected positive sizes for -: " + L->string() + " " + R->string()); + } + FiniteWord Lo; + FiniteWord Hi; + FiniteWord::minus(*FiniteWordL, *FiniteWordR, Hi, Lo); + std::vector> Vals = { std::make_shared(Lo), std::make_shared(Hi) }; + return std::make_shared(Vals); + } + else { + return LogError("Invalid types for -: " + L->string() + " " + R->string()); + } + } + else if (auto RationalWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + return std::make_shared(RationalWord::minus(*RationalWordL, *RationalWordR)); + } + else { + return LogError("Invalid types for -: " + L->string() + " " + R->string()); + } + } + else { + return LogError("Invalid types for -: " + L->string() + " " + R->string()); + } + } + case '/': { + if (auto FiniteWordL = llvm::dyn_cast(L.get())) { + if (auto FiniteWordR = llvm::dyn_cast(R.get())) { + if (FiniteWordL->getSize() != FiniteWordR->getSize()) { + return LogError("Expected same sizes for /: " + L->string() + " " + R->string()); + } + if (FiniteWordL->getSize() == 0) { + return LogError("Expected positive sizes for /: " + L->string() + " " + R->string()); + } + FiniteWord Remainder; + FiniteWord Quotient; + FiniteWord::divide(*FiniteWordL, *FiniteWordR, Remainder, Quotient); + std::vector> Vals = { std::make_shared(Quotient), std::make_shared(Remainder) }; + return std::make_shared(Vals); + } + else { + return LogError("Invalid types for /: " + L->string() + " " + R->string()); + } + } + else if (auto RationalWordL = llvm::dyn_cast(L.get())) { + if (auto RationalWordR = llvm::dyn_cast(R.get())) { + if (RationalWordR->residue(1) == eval::ZERO_1BIT) { + return LogError("Divisor cannot have 0 first bit for /: " + R->string()); + } + return std::make_shared(RationalWord::divide(*RationalWordL, *RationalWordR)); + } + else { + return LogError("Invalid types for /: " + L->string() + " " + R->string()); + } + } + else { + return LogError("Invalid types for /: " + L->string() + " " + R->string()); + } + } + default: + return LogError("Binary operator not found: " + stringFromToken(Op)); + } +} + +const std::shared_ptr InfixExprAST::eval() const { + + std::vector> Vals; + for (auto& Expr : Args) { + auto Val = Expr->eval(); + if (!Val) { + return nullptr; + } + Vals.push_back(Val); + } + + if (Op == tok_eqeq || Op == tok_beq) { + // == and != operators are special, if any word values are present, then only consider + // the first element of any lists + // if all arguments are lists, then compare element-wise + // functions are not comparable to anything + + // first determine if any are FiniteWords, and the necessary size + auto finiteWordIsPresent(false); + auto rationalWordIsPresent(false); + size_t finiteWordSize(0); + size_t valueListSize(0); + for (auto& Val : Vals) { + if (auto FiniteWordVal = llvm::dyn_cast(Val.get())) { + finiteWordIsPresent = true; + // use first size that is encountered + finiteWordSize = FiniteWordVal->getSize(); + } + else if (/*auto RationalWordVal = */llvm::dyn_cast(Val.get())) { + rationalWordIsPresent = true; + } + else if (auto ValueListVal = llvm::dyn_cast(Val.get())) { + valueListSize = ValueListVal->size(); + } + } + + // if FiniteWords are present, then check sizes are correct + // ensure only FiniteWords and RationalWords are present + if (finiteWordIsPresent) { + std::vector FiniteWordVals; + + for (auto& Val : Vals) { + if (auto FiniteWordVal = llvm::dyn_cast(Val.get())) { + if (FiniteWordVal->getSize() != finiteWordSize) { + return LogError("Incorrect size for +:" + Val->string()); + } + FiniteWordVals.push_back(*FiniteWordVal); + } + else if (auto ValueListVal = llvm::dyn_cast(Val.get())) { + auto First = (*ValueListVal)[0]; + if (auto FiniteWordVal = llvm::dyn_cast(First.get())) { + if (FiniteWordVal->getSize() != finiteWordSize) { + return LogError("Incorrect size for +:" + First->string()); + } + FiniteWordVals.push_back(*FiniteWordVal); + } + } + else { + return LogError("Invalid type for +:" + Val->string()); + } + } + + switch (Op) { + case tok_eqeq: return std::make_shared(FiniteWord::equal(FiniteWordVals)); + case tok_beq: return std::make_shared(FiniteWord::notequal(FiniteWordVals)); + default: + llvm_unreachable("fallthrough"); + } + } + else if (rationalWordIsPresent) { + std::vector RationalWordVals; + + // ensure only RationalWords are present + for (auto& Val : Vals) { + if (auto RationalWordVal = llvm::dyn_cast(Val.get())) { + RationalWordVals.push_back(*RationalWordVal); + } + else if (auto ValueListVal = llvm::dyn_cast(Val.get())) { + auto First = ValueListVal[0]; + if (auto RationalWordVal = llvm::dyn_cast(&First)) { + RationalWordVals.push_back(*RationalWordVal); + } + } + else { + return LogError("Invalid type for +:" + Val->string()); + } + } + + switch (Op) { + case tok_eqeq: return std::make_shared(RationalWord::equal(RationalWordVals)); + case tok_beq: return std::make_shared(RationalWord::notequal(RationalWordVals)); + default: + llvm_unreachable("fallthrough"); + } + } + else { + // assume comparing lists element-wise + std::vector ValueListVals; + + // ensure only ValueLists are present + for (auto& Val : Vals) { + if (auto ValueListVal = llvm::dyn_cast(Val.get())) { + if (ValueListVal->size() != valueListSize) { + return LogError("Incorrect list size for +:" + Val->string()); + } + if (!ValueListVal->isComparable()) { + return LogError("List contains non-comparable object: " + Val->string()); + } + ValueListVals.push_back(*ValueListVal); + } + else { + return LogError("Invalid type for +:" + Val->string()); + } + } + + switch (Op) { + case tok_eqeq: return std::make_shared(ValueList::equal(ValueListVals)); + case tok_beq: return std::make_shared(ValueList::notequal(ValueListVals)); + default: + llvm_unreachable("fallthrough"); + } + } + } + + // For arithmetic and bitwise operations, concatenate all lists together + { + std::vector> FlattenedVals; + for (auto i = 0; i < Vals.size(); i++) { + auto Val = Vals[i]; + if (auto ListVal = llvm::dyn_cast(Val.get())) { + auto FlatVal = ListVal->concatenate(); + if (!FlatVal) { + return nullptr; + } + FlattenedVals.push_back(FlatVal); + } + else { + FlattenedVals.push_back(Val); + } + } + Vals = FlattenedVals; + } + + if (Op == '#') { + return ValueList(Vals).concatenate(); + } + + // Now, only remaining operators are those that cast to either FiniteWord or RationalWord + + if (Op == '+' || + Op == '*' || + Op == '|' || + Op == '&' || + Op == '^') { + // first determine if any are FiniteWords, and the necessary size + auto finiteWordIsPresent(false); + auto rationalWordIsPresent(false); + size_t finiteWordSize(0); + for (auto& Val : Vals) { + if (auto FiniteWordVal = llvm::dyn_cast(Val.get())) { + finiteWordIsPresent = true; + // use first size that is encountered + finiteWordSize = FiniteWordVal->getSize(); + } + else if (/*auto RationalWordVal = */llvm::dyn_cast(Val.get())) { + rationalWordIsPresent = true; + } + } + + // if FiniteWords are present, then check sizes are correct + // ensure only FiniteWords and RationalWords are present + if (finiteWordIsPresent) { + std::vector FiniteWordVals; + + for (auto& Val : Vals) { + if (auto FiniteWordVal = llvm::dyn_cast(Val.get())) { + if (FiniteWordVal->getSize() != finiteWordSize) { + return LogError("Invalid size: " + Val->string()); + } + FiniteWordVals.push_back(*FiniteWordVal); + } + else { + return LogError("Invalid type: " + Val->string()); + } + } + + switch (Op) { + case '+': { + FiniteWord Lo; + FiniteWord Hi; + FiniteWord::plus(FiniteWordVals, Hi, Lo); + std::vector> Vals = { std::make_shared(Lo), std::make_shared(Hi) }; + return std::make_shared(Vals); + } + case '*': { + FiniteWord Lo; + FiniteWord Hi; + FiniteWord::times(FiniteWordVals, Hi, Lo); + std::vector> Vals = { std::make_shared(Lo), std::make_shared(Hi) }; + return std::make_shared(Vals); + } + case '|': return std::make_shared(FiniteWord::bitwise_or(FiniteWordVals)); + case '&': return std::make_shared(FiniteWord::bitwise_and(FiniteWordVals)); + case '^': return std::make_shared(FiniteWord::bitwise_xor(FiniteWordVals)); + default: + llvm_unreachable("fallthrough"); + } + } + else if (rationalWordIsPresent) { + std::vector RationalWordVals; + + // ensure only RationalWords are present + for (auto& Val : Vals) { + if (auto RationalWordVal = llvm::dyn_cast(Val.get())) { + RationalWordVals.push_back(*RationalWordVal); + } + else { + return LogError("Invalid type: " + Val->string()); + } + } + + switch (Op) { + case '+': return std::make_shared(RationalWord::plus(RationalWordVals)); + case '*': return std::make_shared(RationalWord::times(RationalWordVals)); + case '|': return std::make_shared(RationalWord::bitwise_or(RationalWordVals)); + case '&': return std::make_shared(RationalWord::bitwise_and(RationalWordVals)); + case '^': return std::make_shared(RationalWord::bitwise_xor(RationalWordVals)); + default: + llvm_unreachable("fallthrough"); + } + } + else { + return LogError("Cannot operate on values"); + } + } + + return LogError("Infix operator not found: " + stringFromToken(Op)); +} + +const std::shared_ptr CallExprAST::eval() const { + + std::vector> ArgsV; + auto Args = getArgs()->getExprs(); + for (auto& Arg : Args) { + auto V = Arg->eval(); + if (!V) { + return nullptr; + } + + ArgsV.push_back(V); + } + + // Look up the name in the global module table. + auto BultinCallee = llvm::dyn_cast(eval::NamedValues[Callee->getName()].get()); + if (!BultinCallee) { + auto UserCalee = llvm::dyn_cast(eval::NamedValues[Callee->getName()].get()); + if (!UserCalee) { + return LogError("Unknown function referenced:" + Callee->getName()); + } + else { + return UserCalee->call(ArgsV); + } + } + else { + return BultinCallee->call(ArgsV); + } +} + +const std::shared_ptr IfExprAST::eval() const { + auto CondV = Cond->eval(); + if (!CondV) { + return nullptr; + } + + // if Cond is a list, then concatenate all values + if (auto ListCond = llvm::dyn_cast(CondV.get())) { + CondV = ListCond->concatenate(); + if (!CondV) { + return nullptr; + } + } + + auto Test = llvm::dyn_cast(CondV.get()); + if (!Test) { + return LogError("Test is not a FiniteWord: " + Test->string()); + } + + if (Test->getSize() != 1) { + return LogError("Test is not size 1: " + Test->string()); + } + + if (*Test == eval::ONE_1BIT) { + return Then->eval(); + } + else { + return Else->eval(); + } +} + +const std::shared_ptr ForExprAST::eval() const { + + auto StartV = Start->eval(); + if (!StartV) { + return nullptr; + } + + std::shared_ptr Res = std::make_shared(); + + while (1) { + auto EV = End->eval(); + if (!EV) { + return nullptr; + } + + auto EndV = llvm::dyn_cast(EV.get()); + if (!EndV) { + return LogError("End is not a FiniteWord: " + EndV->string()); + } + + if (EndV->getSize() != 1) { + return LogError("End is not size 1: " + EndV->string()); + } + + if (*EndV == eval::ZERO_1BIT) { + break; + } + + if (!(Res = Body->eval())) { + return nullptr; + } + + if (!(Step->eval())) { + return nullptr; + } + } + + return std::make_shared(); +} + +const std::shared_ptr WhileExprAST::eval() const { + while (1) { + + auto TestV = Test->eval(); + if (!TestV) { + return nullptr; + } + + if (auto FiniteWordTest = llvm::dyn_cast(TestV.get())) { + if (FiniteWordTest->getSize() == 1) { + if (*FiniteWordTest == eval::ZERO_1BIT) { + break; + } + } + else { + return LogError("Test is not size 1: " + FiniteWordTest->string()); + } + } + else { + return LogError("Test is not a FiniteWord: " + TestV->string()); + } + + auto Res = Body->eval(); + if (!Res) { + return nullptr; + } + } + + return std::make_shared(); +} + +const std::shared_ptr VarExprAST::eval() const { + std::vector> Values; + + for (auto& Val : Vals) { + auto ValEvaled = Val->eval(); + if (!ValEvaled) { + return nullptr; + } + + // flatten out ValueList + if (auto ValList = llvm::dyn_cast(ValEvaled.get())) { + for (auto& V : *ValList) { + Values.push_back(V); + } + } + else { + Values.push_back(ValEvaled); + } + } + + if (Vars.size() > Values.size()) { + return LogError("Wrong # of identifiers and values"); + } + + // Push bindings + std::vector> OldBindings; + for (size_t i = 0, e = Vars.size(); i != e; ++i) { + auto VarName = Vars[i]; + auto Init = Values[i]; + OldBindings.push_back(eval::NamedValues[VarName->getName()]); + eval::NamedValues[VarName->getName()] = Init; + } + + auto BodyVal = Body->eval(); + if (!BodyVal) { + return nullptr; + } + + // Pop bindings + for (size_t i = 0, e = Vars.size(); i != e; ++i) { + auto VarName = Vars[i]; + eval::NamedValues[VarName->getName()] = OldBindings[i]; + } + + return BodyVal; +} + +const std::shared_ptr DefinitionAST::eval() const { + + auto F = std::make_shared(Proto, Body); + + eval::NamedValues[Proto->getName()->getName()] = F; + + return std::make_shared(); +} + +const std::shared_ptr UserFunction::call(std::vector> Args) const { + if (Proto->getParams().size() != Args.size()) { + return LogError("Incorrect # arguments passed"); + } + + // Push all bindings + std::vector> OldBindings; + for (size_t i = 0, e = Args.size(); i != e; ++i) { + auto VarName = Proto->getParams()[i]; + auto Val = Args[i]; + OldBindings.push_back(eval::NamedValues[VarName->getName()]); + eval::NamedValues[VarName->getName()] = Val; + } + + auto Res = Body->eval(); + if (!Res) { + return nullptr; + } + + // Pop bindings back + for (size_t i = 0, e = Args.size(); i != e; ++i) { + auto VarName = Proto->getParams()[i]; + eval::NamedValues[VarName->getName()] = OldBindings[i]; + } + + return Res; +} + +const std::shared_ptr BuiltinFunction::call(std::vector> Args) const { + + auto Res = (*FunctionPointer)(Args); + if (!Res) { + return nullptr; + } + + return Res; +} + +// +// functions +// + +// printing is reversed from normal, i.e., printing is done right-to-left +const std::shared_ptr eval::print(std::vector> Args) { + for (auto Iter = Args.rbegin(); Iter != Args.rend(); Iter++) { + std::cout << (*Iter)->string() << "\n"; + } + + return std::make_shared(); +} + +const std::shared_ptr eval::exit(std::vector> Args) { + if (Args.size() != 0) { + return LogError("Incorrect # arguments passed"); + } + + std::exit(EXIT_SUCCESS); + return nullptr; +} + +const std::shared_ptr eval::rationalize(std::vector> Args) { + if (Args.size() != 1) { + return LogError("Incorrect # arguments passed"); + } + + auto Val = Args[0]; + + if (auto FiniteWordVal = llvm::dyn_cast(Val.get())) { + if (FiniteWordVal->getSize() == 0) { + return LogError("Cannot rationalize empty word"); + } + + FiniteWord Hi; + FiniteWord Lo; + FiniteWordVal->shiftRightResidue(FiniteWordVal->getSize() - 1, Hi, Lo); + auto Best = RationalWord::FactoryPeriodTransient(Hi, Lo); + + for (auto i = 0; i < FiniteWordVal->getSize() - 1; i++) { + FiniteWordVal->shiftRightResidue(i, Hi, Lo); + auto Res = RationalWord::FactoryPeriodTransient(Hi, Lo); + if (Res.getPeriod().getSize() + Res.getTransient().getSize() < Best.getPeriod().getSize() + Best.getTransient().getSize()) { + Best = Res; + } + } + + return std::make_shared(Best); + + } + else { + return LogError("Expected FiniteWord: " + Val->string()); + } +} diff --git a/src/tuppence/Eval.h b/src/tuppence/Eval.h new file mode 100644 index 0000000..8d66d21 --- /dev/null +++ b/src/tuppence/Eval.h @@ -0,0 +1,42 @@ +//===------ Eval.h --------------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_EVAL_H +#define TUPPENCE_EVAL_H + +#include "Value.h" +#include "ValueList.h" + +#include +#include + +namespace tuppence { + + namespace eval { + + extern std::map> NamedValues; + + // + // Functions + // + + const std::shared_ptr print(std::vector> Args); + const std::shared_ptr exit(std::vector> Args); + + /// Convert a FiniteWord into a RationalWord + const std::shared_ptr rationalize(std::vector> Args); + + extern const Symbol SYMBOL_INFINITY; + extern const BuiltinFunction BUILTIN_PRINT; + extern const BuiltinFunction BUILTIN_EXIT; + extern const BuiltinFunction BUILTIN_RATIONALIZE; + } +} + +#endif diff --git a/src/tuppence/FiniteWord.cpp b/src/tuppence/FiniteWord.cpp new file mode 100644 index 0000000..deddf3a --- /dev/null +++ b/src/tuppence/FiniteWord.cpp @@ -0,0 +1,643 @@ +//===------ FiniteWord.cpp ------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "FiniteWord.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/StringRef.h" + +#include +#include + +namespace tuppence { + + namespace finiteword { + + const FiniteWord EMPTY = FiniteWord::FactoryEmpty(); + const FiniteWord ZERO_1BIT = FiniteWord::FactoryBool((bool)0); + const FiniteWord ONE_1BIT = FiniteWord::FactoryBool((bool)1); + + } + +} + +using namespace tuppence; + +/// Default constructor +FiniteWord::FiniteWord() : + Value(VK_FiniteWord), size(0) {} + +FiniteWord::FiniteWord(std::shared_ptr APIntVal) : + Value(VK_FiniteWord), size(APIntVal->getBitWidth()), Val(APIntVal) {} + +const FiniteWord FiniteWord::FactoryEmpty() { + return FiniteWord(); +} + +const FiniteWord FiniteWord::FactoryString(std::string BinaryString) { + auto size = BinaryString.size(); + if (size == 0) { + return finiteword::EMPTY; + } + + llvm::StringRef text(BinaryString); + auto Val = llvm::APInt(static_cast(size), text, 2); + return FactoryAPInt(std::make_shared(Val)); +} + +const FiniteWord FiniteWord::FactoryDecimalString(std::string DecimalString) { + assert(!DecimalString.empty() && "string is empty"); + assert((DecimalString.size() == 1 || DecimalString[0] != '0') && "string is padded with 0"); + llvm::StringRef text(DecimalString); + auto needed = llvm::APInt::getBitsNeeded(text, 10); + auto Val = llvm::APInt(needed, text, 10); + return FactoryAPInt(std::make_shared(Val)); +} + +const FiniteWord FiniteWord::FactoryRepsWord(size_t Repetitions, FiniteWord Pattern) { + auto size = Repetitions * Pattern.size; + if (size == 0) { + return finiteword::EMPTY; + } + + // optimizations for all 0s and all 1s + { + auto pop = Pattern.Val->countPopulation(); + if (pop == 0) { + // all 0s + auto Accumulate = llvm::APInt(static_cast(size), (uint64_t)0, false); + Accumulate.clearAllBits(); + return FiniteWord::FactoryAPInt(std::make_shared(Accumulate)); + } + if (pop == Pattern.size) { + // all 1s + auto Accumulate = llvm::APInt(static_cast(size), (uint64_t)0, false); + Accumulate.setAllBits(); + return FiniteWord::FactoryAPInt(std::make_shared(Accumulate)); + } + } + + auto Width = bitLength(Repetitions); + auto FiniteWordReps = llvm::APInt(static_cast(Width), Repetitions, false); + auto ReversedReps = FiniteWordReps.reverseBits(); + assert(ReversedReps[0] == 1 && "ReversedReps[0] must be 1"); + ReversedReps = ReversedReps.lshr(1); + auto PatternExtended = Pattern.Val->zextOrSelf(static_cast(size)); + auto Accumulate = PatternExtended; + auto AccumulatedSize = Pattern.size; + while (AccumulatedSize < size) { + // double the reps + auto Original = Accumulate; + Accumulate <<= static_cast(AccumulatedSize); + Accumulate |= Original; + AccumulatedSize *= 2; + if (ReversedReps[0] == 1) { + // add 1 rep + Accumulate <<= static_cast(Pattern.size); + Accumulate |= PatternExtended; + AccumulatedSize += Pattern.size; + } + ReversedReps = ReversedReps.lshr(1); + } + assert(AccumulatedSize == size && "Accumulated size is not expected size"); + assert(ReversedReps.getActiveBits() == 0 && "ReversedReps is not properly processed"); + return FiniteWord::FactoryAPInt(std::make_shared(Accumulate)); +} + +const FiniteWord FiniteWord::FactoryBool(bool Bit) { + auto Val = llvm::APInt(1, (uint64_t)Bit, false); + return FiniteWord(std::make_shared(Val)); +} + +const FiniteWord FiniteWord::FactoryAPInt(std::shared_ptr Value) { + return FiniteWord(Value); +} + +const FiniteWord FiniteWord::FactorySizeT(size_t Val) { + auto A = llvm::APInt(64, Val); + return FiniteWord::FactoryAPInt(std::make_shared(A)); +} + +/// Copy constructor +FiniteWord::FiniteWord(const FiniteWord& other) : + Value(VK_FiniteWord), size(other.size) { + if (size > 0) { + Val = other.Val; + } +} + +/// Copy assignment operator +FiniteWord& FiniteWord::operator=(const FiniteWord& other) { + size = other.size; + if (size > 0) { + Val = other.Val; + } + return *this; +} + +/// Move constructor +FiniteWord::FiniteWord(FiniteWord&& other) : + Value(VK_FiniteWord), size(other.size) { + if (size > 0) { + Val = std::move(other.Val); + } +} + +/// Move assignment operator +FiniteWord& FiniteWord::operator=(FiniteWord&& other) { + size = other.size; + if (size > 0) { + Val = std::move(other.Val); + } + return *this; +} + +const size_t FiniteWord::getSize() const { + return size; +} + +const std::string FiniteWord::representation() const { + std::stringstream ss; + ss << "`"; + ss << bits(); + ss << "`"; + return ss.str(); +} + +const std::string FiniteWord::bits() const { + std::stringstream ss; + for (auto i = 0; i < size; i++) { + auto index = size - 1 - i; + auto bit = (*Val)[static_cast(index)]; + ss << bit; + } + return ss.str(); +} + +const std::string FiniteWord::decimal() const { + if (size == 0) { + return "0"; + } else { + return Val->toString(10, false); + } +} + +const std::string FiniteWord::decimalSigned() const { + if (size == 0) { + return "0"; + } + else { + return Val->toString(10, true); + } +} + +const std::string FiniteWord::string() const { + return representation(); +} + +const bool FiniteWord::get(size_t index) const { + assert(index < size && "Index is too large"); + return (*Val)[static_cast(index)]; +} + +const bool FiniteWord::operator==(const FiniteWord other) const { + assert(size == other.size && "Sizes not equal"); + if (size == 0) { + return true; + } + return *Val == *other.Val; +} + +const bool FiniteWord::operator!=(const FiniteWord other) const { + assert(size == other.size && "Sizes not equal"); + if (size == 0) { + return false; + } + return *Val != *other.Val; +} + +const FiniteWord FiniteWord::rotateRight(size_t amount) const { + auto Rotated = Val->rotr(static_cast(amount)); + return FactoryAPInt(std::make_shared(Rotated)); +} + +const FiniteWord FiniteWord::rotateLeft(size_t amount) const { + auto Rotated = Val->rotl(static_cast(amount)); + return FactoryAPInt(std::make_shared(Rotated)); +} + +const FiniteWord FiniteWord::reverse() const { + auto Reversed = Val->reverseBits(); + return FactoryAPInt(std::make_shared(Reversed)); +} + +// +// bitwise operations +// + +const FiniteWord FiniteWord::operator~() const { + if (size == 0) { + return finiteword::EMPTY; + } + auto Not = ~*Val; + return FactoryAPInt(std::make_shared(Not)); +} + +const FiniteWord FiniteWord::operator^(FiniteWord other) const { + assert(size == other.size && "Sizes not equal"); + if (size == 0) { + return finiteword::EMPTY; + } + auto Xor = *Val ^ *other.Val; + return FactoryAPInt(std::make_shared(Xor)); +} + +const FiniteWord FiniteWord::operator&(FiniteWord other) const { + assert(size == other.size && "Sizes not equal"); + if (size == 0) { + return finiteword::EMPTY; + } + auto And = *Val & *other.Val; + return FactoryAPInt(std::make_shared(And)); +} + +const FiniteWord FiniteWord::operator|(FiniteWord other) const { + assert(size == other.size && "Sizes not equal"); + if (size == 0) { + return finiteword::EMPTY; + } + auto Or = *Val | *other.Val; + return FactoryAPInt(std::make_shared(Or)); +} + +const uint64_t* FiniteWord::getRawData() const { + return Val->getRawData(); +} + +// +// Structural Operations +// + +const FiniteWord FiniteWord::equal(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto First = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + if (*Iter != First) { + return finiteword::ZERO_1BIT; + } + } + return finiteword::ONE_1BIT; +} + +const FiniteWord FiniteWord::notequal(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + for (auto Iter = Vals.begin(); Iter != Vals.end(); ++Iter) { + auto First = *Iter; + auto Iter2 = Iter; + Iter2++; + for (; Iter2 != Vals.end(); Iter2++) { + auto Second = *Iter2; + // maintain intuitive order + if (Second == First) { + return finiteword::ZERO_1BIT; + } + } + } + return finiteword::ONE_1BIT; +} + +const bool FiniteWord::ugt(FiniteWord other) const { + assert(size == other.size && "Sizes not equal"); + return Val->ugt(*other.Val); +} + +const FiniteWord FiniteWord::residue(size_t i) const { + assert(i <= size && "Index too large"); + if (i == size) { + return *this; + } + if (i == 0) { + return finiteword::EMPTY; + } + auto Trunc = Val->trunc(static_cast(i)); + return FactoryAPInt(std::make_shared(Trunc)); +} + +const FiniteWord FiniteWord::shiftRight(size_t i) const { + assert(i <= size && "Index too large"); + if (i == 0) { + return *this; + } + if (i == size) { + return FiniteWord::FactoryEmpty(); + } + auto Shifted = Val->lshr(static_cast(i)).trunc(static_cast(size - i)); + return FiniteWord::FactoryAPInt(std::make_shared(Shifted)); +} + +// this could be passed in as Hi or Lo, so make sure to make +// a copy first +void FiniteWord::shiftRightResidue(size_t i, FiniteWord& Hi, FiniteWord& Lo) const { + assert(i <= size && "Index too large"); + auto Tmp = *this; + Hi = Tmp.shiftRight(i); + Lo = Tmp.residue(i); +} + +const FiniteWord FiniteWord::concatenate(FiniteWord R) const { + if (size == 0) { + return R; + } + if (R.size == 0) { + return *this; + } + auto Res = Val->zextOrSelf(static_cast(size + R.size)); + auto B = R.Val->zextOrSelf(static_cast(size + R.size)); + Res <<= static_cast(R.size); + Res |= B; + return FiniteWord::FactoryAPInt(std::make_shared(Res)); +} + +/// copied from ScalarEvolution.cpp +const FiniteWord FiniteWord::gcd(FiniteWord A, FiniteWord B) { + + auto AVal = *A.Val; + auto BVal = *B.Val; + + auto ABW = AVal.getBitWidth(); + auto BBW = BVal.getBitWidth(); + + if (ABW > BBW) { + BVal = BVal.zext(ABW); + } + else if (ABW < BBW) { + AVal = AVal.zext(BBW); + } + + auto Ret = llvm::APIntOps::GreatestCommonDivisor(AVal, BVal); + return FiniteWord::FactoryAPInt(std::make_shared(Ret)); +} + +void FiniteWord::compressPeriod(FiniteWord& period) { + auto periodSize = period.getSize(); + auto sq = (size_t)sqrt(periodSize); + for (auto testDivisor = 1; testDivisor <= sq; testDivisor++) { + if (periodSize % testDivisor == 0) { + + // first use testDivisor + if (periodSize > 1) { + if (period.Val->isSplat(testDivisor)) { + auto modWord = period.residue(testDivisor); + period = modWord; + + // recurse on compressedPeriod, but it is now smaller + compressPeriod(period); + return; + } + } + + // then use use periodSize / testDivisor + if (testDivisor > 1) { + if (period.Val->isSplat(static_cast(periodSize / testDivisor))) { + auto modWord = period.residue(periodSize / testDivisor); + period = modWord; + + // recurse on compressedPeriod, but it is now smaller + compressPeriod(period); + return; + } + } + } + } +} + +const bool FiniteWord::isPeriodCompressed(const FiniteWord period) { + assert(period.getSize() > 0 && "Period size is 0"); + + auto periodSize = period.getSize(); + auto sq = (size_t)sqrt(periodSize); + for (auto testDivisor = 1; testDivisor <= sq; testDivisor++) { + if (periodSize % testDivisor == 0) { + + // first use testDivisor + if (periodSize > 1) { + if (period.Val->isSplat(testDivisor)) { + return false; + } + } + + // then use periodSize / testDivisor + if (testDivisor > 1) { + if (period.Val->isSplat(static_cast(periodSize / testDivisor))) { + return false; + } + } + } + } + + return true; +} + +void FiniteWord::multiplyPeriod(FiniteWord& period, size_t factor) { + period = FiniteWord::FactoryRepsWord(factor, period); +} + +// +// Bitwise operations +// + +const FiniteWord FiniteWord::bitwise_not(FiniteWord L) { + return ~L; +} + +const FiniteWord FiniteWord::bitwise_or(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter | Val; + } + return Val; +} + +const FiniteWord FiniteWord::bitwise_and(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter & Val; + } + return Val; +} + +const FiniteWord FiniteWord::bitwise_xor(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter ^ Val; + } + return Val; +} + +// +// Arithmetic Operations +// + +const FiniteWord FiniteWord::operator-() const { + assert(size > 0 && "size is 0"); + auto Not = -*Val; + return FactoryAPInt(std::make_shared(Not)); +} + +const FiniteWord FiniteWord::operator+(FiniteWord other) const { + assert(size > 0 && "size is 0"); + assert(size == other.size && "Sizes not equal"); + auto Added = *Val + *other.Val; + return FactoryAPInt(std::make_shared(Added)); +} + +const FiniteWord FiniteWord::operator-(FiniteWord other) const { + assert(size > 0 && "size is 0"); + assert(size == other.size && "Sizes not equal"); + auto Minused = *Val - *other.Val; + return FactoryAPInt(std::make_shared(Minused)); +} + +const FiniteWord FiniteWord::operator*(FiniteWord other) const { + assert(size > 0 && "size is 0"); + assert(size == other.size && "Sizes not equal"); + auto Multiplied = *Val * *other.Val; + return FactoryAPInt(std::make_shared(Multiplied)); +} + +const FiniteWord FiniteWord::operator/(FiniteWord other) const { + assert(size > 0 && "size is 0"); + assert(size == other.size && "Sizes not equal"); + auto Divided = Val->udiv(*other.Val); + return FactoryAPInt(std::make_shared(Divided)); +} + +const FiniteWord FiniteWord::operator%(FiniteWord other) const { + assert(size > 0 && "size is 0"); + assert(size == other.size && "Sizes not equal"); + auto Modded = Val->urem(*other.Val); + return FactoryAPInt(std::make_shared(Modded)); +} + +void FiniteWord::plus(std::vector Vals, FiniteWord& Hi, FiniteWord& Lo) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + size_t OldWidth = Iter->getSize(); + if (OldWidth == 0) { + // Still return 1 carry bit, even if the words are empty + Hi = finiteword::ZERO_1BIT; + Lo = finiteword::EMPTY; + return; + } + + auto CarryWidth = bitLength(Vals.size() - 1); + + size_t NewWidth = OldWidth + CarryWidth; + auto APVal = Iter->Val->zext(static_cast(NewWidth)); + Iter++; + + for (; Iter != Vals.end(); ++Iter) { + assert(Iter->getSize() == OldWidth && "Sizes not equal"); + // maintain intuitive order + APVal = Iter->Val->zext(static_cast(NewWidth)) + APVal; + } + + auto HiAPInt = APVal.lshr(static_cast(OldWidth)).trunc(static_cast(CarryWidth)); + Hi = FactoryAPInt(std::make_shared(HiAPInt)); + auto LoAPInt = APVal.trunc(static_cast(OldWidth)); + Lo = FactoryAPInt(std::make_shared(LoAPInt)); +} + +void FiniteWord::minus(FiniteWord L, FiniteWord R, FiniteWord& Hi, FiniteWord& Lo) { + assert(L.size == R.size && "Sizes not equal"); + auto OldWidth = L.size; + if (OldWidth == 0) { + // Still return 1 carry bit, even if the words are empty + Hi = finiteword::ZERO_1BIT; + Lo = finiteword::EMPTY; + return; + } + + auto BorrowWidth = 1; + + size_t NewWidth = OldWidth + BorrowWidth; + auto NewLVal = L.Val->zext(static_cast(NewWidth)); + auto NewRVal = R.Val->zext(static_cast(NewWidth)); + + auto NewVal = NewLVal - NewRVal; + + auto HiAPInt = NewVal.lshr(static_cast(OldWidth)).trunc(static_cast(BorrowWidth)); + Hi = FactoryAPInt(std::make_shared(HiAPInt)); + auto LoAPInt = NewVal.trunc(static_cast(OldWidth)); + Lo = FactoryAPInt(std::make_shared(LoAPInt)); +} + +void FiniteWord::times(std::vector Vals, FiniteWord& Hi, FiniteWord& Lo) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + size_t OldWidth = Iter->getSize(); + if (OldWidth == 0) { + // Still return 1 Hi bit, even if the words are empty + Hi = finiteword::ZERO_1BIT; + Lo = finiteword::EMPTY; + return; + } + + size_t NewWidth = OldWidth * Vals.size(); + auto APVal = Iter->Val->zext(static_cast(NewWidth)); + Iter++; + + for (; Iter != Vals.end(); ++Iter) { + assert(Iter->getSize() == OldWidth && "Sizes not equal"); + // maintain intuitive order + APVal = Iter->Val->zext(static_cast(NewWidth)) * APVal; + } + auto HiAPInt = APVal.lshr(static_cast(OldWidth)).trunc(static_cast(OldWidth * (Vals.size() - 1))); + Hi = FactoryAPInt(std::make_shared(HiAPInt)); + auto LoAPInt = APVal.trunc(static_cast(OldWidth)); + Lo = FactoryAPInt(std::make_shared(LoAPInt)); +} + +void FiniteWord::divide(FiniteWord Dividend, FiniteWord Divisor, FiniteWord& Remainder, FiniteWord& Quotient) { + assert(Dividend.size == Divisor.size && "Sizes not equal"); + auto OldWidth = Dividend.size; + if (OldWidth == 0) { + Quotient = finiteword::EMPTY; + Remainder = finiteword::EMPTY; + return; + } + llvm::APInt APQuotient; + llvm::APInt APRemainder; + llvm::APInt::udivrem(*Dividend.Val, *Divisor.Val, APQuotient, APRemainder); + Quotient = FactoryAPInt(std::make_shared(APQuotient)); + Remainder = FactoryAPInt(std::make_shared(APRemainder)); +} + +// +// GTest +// + +void tuppence::PrintTo(const FiniteWord& bar, ::std::ostream* os) { + *os << bar.string(); +} diff --git a/src/tuppence/FiniteWord.h b/src/tuppence/FiniteWord.h new file mode 100644 index 0000000..747cc1f --- /dev/null +++ b/src/tuppence/FiniteWord.h @@ -0,0 +1,152 @@ +//===------ FiniteWord.h - FiniteWord class definition --------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_FINITEWORD_H +#define TUPPENCE_FINITEWORD_H + +#include "Value.h" + +#include +#include +#include + +// Forward declare APInt (and use pointers to APInt) to prevent any dependencies on APInt creeping out of FiniteWord +namespace llvm { + class APInt; +} + +namespace tuppence { + + class FiniteWord : public Value { + size_t size; + std::shared_ptr Val; + + FiniteWord(std::shared_ptr); + static const FiniteWord FactoryAPInt(std::shared_ptr); + + public: + static const FiniteWord FactoryEmpty(); + static const FiniteWord FactoryString(std::string BinaryString); + static const FiniteWord FactoryDecimalString(std::string DecimalString); + static const FiniteWord FactoryRepsWord(size_t RepetitionCount, FiniteWord Pattern); + static const FiniteWord FactoryBool(bool Bit); + static const FiniteWord FactorySizeT(size_t Val); + + /// Default constructor + FiniteWord(); + + /// Copy constructor + FiniteWord(const FiniteWord& other); + + /// Copy assignment operator + FiniteWord& operator=(const FiniteWord& other); + + /// Move constructor + FiniteWord(FiniteWord&& other); + + /// Move assignment operator + FiniteWord& operator=(FiniteWord&& other); + + const size_t getSize() const; + + const std::string string() const override; + + const std::string representation() const; + + const std::string bits() const; + + const std::string decimal() const; + const std::string decimalSigned() const; + + const bool get(size_t index) const; + + const FiniteWord rotateRight(size_t amount) const; + const FiniteWord rotateLeft(size_t amount) const; + const FiniteWord reverse() const; + + const uint64_t* getRawData() const; + + static const FiniteWord gcd(FiniteWord a, FiniteWord b); + + // + // structural operations + // + + const bool operator==(FiniteWord other) const; + const bool operator!=(FiniteWord other) const; + + /// Unsigned Greater Than + const bool ugt(FiniteWord other) const; + + static const FiniteWord equal(std::vector ToTest); + static const FiniteWord notequal(std::vector ToTest); + + const FiniteWord residue(size_t i) const; + const FiniteWord shiftRight(size_t i) const; + void shiftRightResidue(size_t i, FiniteWord& Hi, FiniteWord& Lo) const; + const FiniteWord concatenate(FiniteWord other) const; + + static void compressPeriod(FiniteWord& period); + static void multiplyPeriod(FiniteWord& period, size_t factor); + static const bool isPeriodCompressed(FiniteWord period); + + // + // bitwise operations + // + + const FiniteWord operator~() const; + const FiniteWord operator^(FiniteWord other) const; + const FiniteWord operator&(FiniteWord other) const; + const FiniteWord operator|(FiniteWord other) const; + + static const FiniteWord bitwise_not(FiniteWord other); + static const FiniteWord bitwise_or(std::vector); + static const FiniteWord bitwise_and(std::vector); + static const FiniteWord bitwise_xor(std::vector); + + // + // arithmetic operations + // + + const FiniteWord operator-() const; + const FiniteWord operator+(FiniteWord other) const; + const FiniteWord operator-(FiniteWord other) const; + const FiniteWord operator*(FiniteWord other) const; + const FiniteWord operator/(FiniteWord other) const; + const FiniteWord operator%(FiniteWord other) const; + + static void plus(std::vector Values, FiniteWord& Carry, FiniteWord& Result); + static void minus(FiniteWord Minuend, FiniteWord Subtrahend, FiniteWord& Borrow, FiniteWord& Result); + static void times(std::vector Values, FiniteWord& Hi, FiniteWord& Lo); + static void divide(FiniteWord Dividend, FiniteWord Divisor, FiniteWord& Remainder, FiniteWord& Quotient); + + // + // LLVM RTTI + // + static bool classof(const Value *S) { + return S->getKind() == VK_FiniteWord; + } + }; + + namespace finiteword { + + extern const FiniteWord EMPTY; + extern const FiniteWord ZERO_1BIT; + extern const FiniteWord ONE_1BIT; + + } + + // + // GTest + // + void PrintTo(const FiniteWord& bar, ::std::ostream* os); + +} + +#endif diff --git a/src/tuppence/Lexer.cpp b/src/tuppence/Lexer.cpp new file mode 100644 index 0000000..a157613 --- /dev/null +++ b/src/tuppence/Lexer.cpp @@ -0,0 +1,249 @@ +//===------ Lexer.cpp -----------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Lexer.h" + +#include "Logger.h" + +#include "llvm/Support/ErrorHandling.h" + +#include + +using namespace tuppence; + +Lexer::Lexer(std::istream &In) : + In(In), LastChar('\0') {} + +const std::string tuppence::stringFromToken(char Op) { + switch (Op) { + case tok_error: return "tok_error"; + case tok_eqeq: return "=="; + case tok_pp: return "%%"; + case tok_gtgt: return ">>"; + case '\n': return "\\n"; + case EOF: return "EOF"; + case tok_ddd: return "..."; + case tok_for: return "tok_for"; + case tok_beq: return "!="; + case tok_identifier: return "tok_identifier"; + default: + if (Op < 0) { + llvm_unreachable("Add this token"); + } + return std::string(1, static_cast(Op)); + } +} + +const std::string Lexer::currentState() const { + switch (CurTok) { + case tok_identifier: return IdentifierStr; + case tok_rationalword: return RationalWordVal->string(); + case tok_finiteword: return FiniteWordVal->string(); + default: return stringFromToken(CurTok); + } +} + +int Lexer::nextChar() { + if (Line.empty()) { + std::getline(In, Line); + if (!In.good()) { + return EOF; + } + Line = Line + '\n'; + } + auto first = Line.at(0); + Line.erase(0, 1); + return first; +} + +char Lexer::gettok() { + if (LastChar == '\0') { + LastChar = nextChar(); + } + + // Skip any whitespace. + while (LastChar == ' ' || LastChar == '\t') { + LastChar = nextChar(); + } + + if (isalpha(LastChar) || LastChar == '_') { + auto Str = std::string(); + Str += LastChar; + while (isalnum((LastChar = nextChar())) || (LastChar == '_')) { + Str += LastChar; + } + if (Str == "define") { + return tok_define; + } + if (Str == "if") { + return tok_if; + } + if (Str == "else") { + return tok_else; + } + if (Str == "for") { + return tok_for; + } + if (Str == "while") { + return tok_while; + } + if (Str == "var") { + return tok_var; + } + + IdentifierStr = Str; + return tok_identifier; + } + + if (isdigit(LastChar)) { + std::string RationalWordStr; + if (LastChar == '0') { + // eat any 0 padding + while (LastChar == '0') { + LastChar = nextChar(); + } + if (!isdigit(LastChar)) { + // 0 + RationalWordStr = "0"; + RationalWordVal = std::make_shared(RationalWord::FactoryString(RationalWordStr)); + return tok_rationalword; + } + } + // non-0 + do { + RationalWordStr += LastChar; + LastChar = nextChar(); + } while (isdigit(LastChar)); + + RationalWordVal = std::make_shared(RationalWord::FactoryString(RationalWordStr)); + return tok_rationalword; + } + + if (LastChar == '`') { + std::string FiniteWordStr; + LastChar = nextChar(); + while (LastChar == ' ') { + LastChar = nextChar(); + } + while (LastChar == '0' || LastChar == '1') { + FiniteWordStr += LastChar; + LastChar = nextChar(); + } + if (LastChar == '`') { + LastChar = nextChar(); + FiniteWordVal = std::make_shared(FiniteWord::FactoryString(FiniteWordStr)); + return tok_finiteword; + } + else { + return LogLexerError("Could not lex FiniteWord, problem with " + stringFromToken(LastChar)); + } + } + + if (LastChar == '=') { + LastChar = nextChar(); + if (LastChar == '=') { + LastChar = nextChar(); + return tok_eqeq; + } + else { + return '='; + } + } + + if (LastChar == '%') { + LastChar = nextChar(); + if (LastChar == '%') { + LastChar = nextChar(); + return tok_pp; + } + else { + return '%'; + } + } + + if (LastChar == '>') { + LastChar = nextChar(); + if (LastChar == '>') { + LastChar = nextChar(); + return tok_gtgt; + } + else if (LastChar == '%') { + LastChar = nextChar(); + return tok_gtp; + } + else { + return '>'; + } + } + + if (LastChar == '.') { + LastChar = nextChar(); + if (LastChar == '.') { + LastChar = nextChar(); + if (LastChar == '.') { + LastChar = nextChar(); + return tok_ddd; + } + else { + return LogLexerError("Could not lex: .."); + } + } + else { + return '.'; + } + } + + if (LastChar == '!') { + LastChar = nextChar(); + if (LastChar == '=') { + LastChar = nextChar(); + return tok_beq; + } + else { + return '!'; + } + } + + if (LastChar == ';') { + // Comment until end of line. + do + LastChar = nextChar(); + while (LastChar != EOF && LastChar != '\n'); + + if (LastChar != EOF) + return gettok(); + } + + if (LastChar == '\n') { + //reset LastChar back to "fresh" + LastChar = '\0'; + return '\n'; + } + + // Check for end of file. Don't eat the EOF. + if (LastChar == EOF) { + return tok_eof; + } + + // Otherwise, just return the character as its ascii value. + { + int ThisChar = LastChar; + LastChar = nextChar(); + return ThisChar; + } +} + +void Lexer::readNextToken() { + CurTok = gettok(); +} + +void Lexer::throwAwayLine() { + Line.clear(); + LastChar = '\0'; +} diff --git a/src/tuppence/Lexer.h b/src/tuppence/Lexer.h new file mode 100644 index 0000000..c149f16 --- /dev/null +++ b/src/tuppence/Lexer.h @@ -0,0 +1,75 @@ +//===------ Lexer.h -------------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_LEXER_H +#define TUPPENCE_LEXER_H + +#include "FiniteWord.h" +#include "RationalWord.h" + +#include +#include + +namespace tuppence { + + enum Token { + tok_eof = -1, + tok_error = -2, + + // commands + tok_define = -10, + + // primary + tok_identifier = -20, + tok_rationalword = -21, + tok_finiteword = -22, + + // control + tok_if = -30, + tok_else = -32, + tok_for = -33, + tok_while = -35, + + // operators + tok_eqeq = -42, + tok_pp = -43, + tok_gtgt = -44, + tok_gtp = -48, + tok_ddd = -50, + tok_beq = -51, + + // var definition + tok_var = -60 + }; + + const std::string stringFromToken(char); + + class Lexer { + std::istream &In; + char LastChar; + int nextChar(); + std::string Line; + char gettok(); + + public: + Lexer(std::istream &); + + std::string IdentifierStr; + std::shared_ptr RationalWordVal; + std::shared_ptr FiniteWordVal; + + const std::string currentState() const; + + char CurTok; + void readNextToken(); + void throwAwayLine(); + }; +} + +#endif diff --git a/src/tuppence/Logger.cpp b/src/tuppence/Logger.cpp new file mode 100644 index 0000000..b5c0ccf --- /dev/null +++ b/src/tuppence/Logger.cpp @@ -0,0 +1,30 @@ +//===------ Logger.cpp ----------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Logger.h" + +#include "Lexer.h" + +#include + +using namespace tuppence; + +const std::nullptr_t tuppence::LogError(std::string Str) { + std::cerr << "Error: " << Str << "\n"; + return nullptr; +} + +const char tuppence::LogLexerError(std::string Str) { + std::cerr << "Syntax Error: " << Str << "\n"; + return tok_error; +} + +void tuppence::LogWarning(std::string Str) { + std::cerr << "Warning: " << Str << "\n"; +} diff --git a/src/tuppence/Logger.h b/src/tuppence/Logger.h new file mode 100644 index 0000000..a5a1da9 --- /dev/null +++ b/src/tuppence/Logger.h @@ -0,0 +1,24 @@ +//===------ Logger.h ------------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_LOGGER_H +#define TUPPENCE_LOGGER_H + +#include + +namespace tuppence { + + const std::nullptr_t LogError(std::string Str); + + const char LogLexerError(std::string); + + void LogWarning(std::string Str); +} + +#endif diff --git a/src/tuppence/Main.cpp b/src/tuppence/Main.cpp new file mode 100644 index 0000000..bfec218 --- /dev/null +++ b/src/tuppence/Main.cpp @@ -0,0 +1,155 @@ +#include "Eval.h" +#include "Parser.h" +#include "TuppenceConfig.h" + +#include "llvm/Support/ErrorHandling.h" + +#include +#include +#include + +using namespace tuppence; + +class Interpreter { + Parser Parse; + +public: + Interpreter(std::istream& in) : + Parse(Parser(in)) { + + eval::NamedValues["infinity"] = std::make_shared(eval::SYMBOL_INFINITY); + eval::NamedValues["print"] = std::make_shared(eval::BUILTIN_PRINT); + eval::NamedValues["exit"] = std::make_shared(eval::BUILTIN_EXIT); + eval::NamedValues["rationalize"] = std::make_shared(eval::BUILTIN_RATIONALIZE); + + }; + + void HandleDefinition(bool interactive) { + if (auto FnAST = Parse.ParseDefinition()) { + auto FnIR = FnAST->eval(); + if (!FnIR) { + return; + } + } + else { + Parse.throwAwayLine(); + } + } + + void HandleTopLevelExpression(bool interactive) { + if (auto EAST = Parse.ParseTopLevelExpression()) { + auto Res = EAST->eval(); + if (!Res) { + return; + } + + if (interactive) { + if (auto FiniteWordRes = llvm::dyn_cast(Res.get())) { + if (FiniteWordRes->getSize() == 0) { + // Do not print empty + } + else { + std::cout << Res->string() << "\n"; + } + } + else { + std::cout << Res->string() << "\n"; + } + } + } + else { + Parse.throwAwayLine(); + } + } + + // top ::= definition | external | expression | ';' + void MainLoop(bool interactive) { + while (1) { + if (interactive) { + std::cout << ">>> "; + } + Parse.readNextToken(); + auto tok = Parse.getCurrentToken(); + switch (tok) { + case tok_identifier: + case tok_rationalword: + case tok_finiteword: + case tok_if: + case tok_for: + case tok_while: + case tok_var: + case '(': + case '{': + case '-': + case tok_ddd: + case '~': + HandleTopLevelExpression(interactive); + break; + case tok_define: + HandleDefinition(interactive); + break; + case tok_eof: + case '\n': + break; + case tok_error: + Parse.throwAwayLine(); + break; + default: + std::cerr << "Unhandled token: " << tok << "\n"; + Parse.throwAwayLine(); + break; + } + + if (Parse.getCurrentToken() == tok_eof) { + std::exit(EXIT_SUCCESS); + } + } + } +}; + +int main(int argc, char *argv[]) { + + // set cout to be unbuffered + std::cout.setf(std::ios_base::unitbuf); + + if (argc > 1) { + auto fileName = argv[1]; + + std::ifstream infile; + infile.open(fileName); + if (!infile.is_open()) { + std::cerr << "file not good"; + std::exit(EXIT_FAILURE); + } + if (!infile.good()) { + std::cerr << "file not good"; + std::exit(EXIT_FAILURE); + } + + std::string entireFile; + std::string line; + while (std::getline(infile, line)) + { + entireFile += line; + entireFile += "\n"; + } + entireFile += "\n"; + std::istringstream iss(entireFile); + + auto Inter = Interpreter(iss); + Inter.MainLoop(false); + + std::exit(EXIT_SUCCESS); + return 0; + } + else { + std::cout << "Tuppence " << Tuppence_VERSION_MAJOR << "." << Tuppence_VERSION_MINOR << "\n"; + + auto Inter = Interpreter(std::cin); + + // Run the main "interpreter loop" now. + Inter.MainLoop(true); + std::exit(EXIT_SUCCESS); + return 0; + } +} diff --git a/src/tuppence/Parser.cpp b/src/tuppence/Parser.cpp new file mode 100644 index 0000000..8982b51 --- /dev/null +++ b/src/tuppence/Parser.cpp @@ -0,0 +1,699 @@ +//===------ Parser.cpp ----------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Parser.h" + +#include "Logger.h" + +#include "llvm/Support/ErrorHandling.h" + +#include + +using namespace tuppence; + +Parser::Parser(std::istream &In) : + Lex(Lexer(In)) { + // 1 is lowest precedence. + BinopPrecedence['='] = 3; + BinopPrecedence[tok_pp] = 4; + + BinopPrecedence['|'] = 7; + BinopPrecedence['^'] = 8; + BinopPrecedence['&'] = 9; + BinopPrecedence[tok_eqeq] = 10; + BinopPrecedence[tok_beq] = 10; + BinopPrecedence[tok_gtgt] = 19; + BinopPrecedence[tok_gtp] = 19; + BinopPrecedence['+'] = 20; + BinopPrecedence['-'] = 20; + BinopPrecedence['*'] = 40; + BinopPrecedence['/'] = 40; + BinopPrecedence['#'] = 50; + BinopPrecedence['.'] = 60; + // highest. +} + +int Parser::GetTokPrecedence() { + + // return -1 if an error + // \n is not an error, so return some negative and not -1. + if (Lex.CurTok == '\n' || + Lex.CurTok == ')' || + Lex.CurTok == '}' || + Lex.CurTok == tok_else || + Lex.CurTok == ',' + ) { + return -2; + } + + // Make sure it's a declared binop. + int TokPrec = BinopPrecedence[Lex.CurTok]; + if (TokPrec == 0) { + return -1; + } + return TokPrec; +} + +const std::shared_ptr Parser::ParseRationalWordExpr() { + assert(Lex.CurTok == tok_rationalword && "Expected tok_rationalword"); + auto Result = std::make_shared(*Lex.RationalWordVal); + Lex.readNextToken(); // consume the number + return Result; +} + +const std::shared_ptr Parser::ParseFiniteWordExpr() { + assert(Lex.CurTok == tok_finiteword && "Expected tok_finiteword"); + auto Result = std::make_shared(*Lex.FiniteWordVal); + Lex.readNextToken(); // consume the ` + return Result; +} + +const std::shared_ptr Parser::ParseParenExpr() { + assert(Lex.CurTok == '(' && "Expected ("); + Lex.readNextToken(); // eat (. + + std::vector> Exprs; + if (Lex.CurTok != ')') { + while (1) { + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + auto E = ParseExpression(); + if (!E) { + return nullptr; + } + // parsing is reversed from normal + Exprs.insert(Exprs.begin(), E); + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok == ',') { + Lex.readNextToken(); // eat ',' + continue; + } + else if (Lex.CurTok == ')') { + break; + } + else { + return LogError("unexpected token in Paren expr: " + stringFromToken(Lex.CurTok)); + } + } + } + if (Lex.CurTok != ')') { + return LogError("expected ')'"); + } + Lex.readNextToken(); // eat ). + + return std::make_shared(Exprs); +} + +// expressions separated by \n +const std::shared_ptr Parser::ParseCurlyExpr() { + assert(Lex.CurTok == '{' && "Expected {"); + Lex.readNextToken(); // eat {. + + std::vector> Exprs; + if (Lex.CurTok != '}') { + while (1) { + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + auto E = ParseExpression(); + if (!E) { + return nullptr; + } + Exprs.push_back(E); + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok == '}') { + break; + } + } + } + if (Lex.CurTok != '}') { + return LogError("expected '}'"); + } + Lex.readNextToken(); // eat ). + + return std::make_shared(Exprs); +} + +// variable | +// function(args) +const std::shared_ptr Parser::ParseIdentifierExpr() { + assert(Lex.CurTok == tok_identifier && "Expected tok_identifier"); + auto Identifier = Lex.IdentifierStr; + Lex.readNextToken(); // consume the identifier + + auto IdentifierExpr = std::make_shared(Identifier); + + if (Lex.CurTok != '(') { + // simple variable ref + return IdentifierExpr; + } + + // function call + const auto Args = ParseParenExpr(); + if (!Args) { + return nullptr; + } + + return std::make_shared(IdentifierExpr, Args); +} + +/// ifexpr ::= 'if' expression 'then' expression 'else' expression +const std::shared_ptr Parser::ParseIfExpr() { + assert(Lex.CurTok == tok_if && "Expected tok_if"); + Lex.readNextToken(); // eat the if. + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + // condition. + if (Lex.CurTok != '(') { + return LogError("Expected ("); + } + auto Cond = ParseParenExpr(); + if (!Cond) { + return nullptr; + } + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '{') { + return LogError("Expected {"); + } + auto Then = ParseCurlyExpr(); + if (!Then) { + return nullptr; + } + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != tok_else) { + return LogError("expected else"); + } + Lex.readNextToken(); + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '{') { + return LogError("Expected {"); + } + auto Else = ParseCurlyExpr(); + if (!Else) { + return nullptr; + } + + return std::make_shared(Cond, Then, Else); +} + +/// forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression +const std::shared_ptr Parser::ParseForExpr() { + assert(Lex.CurTok == tok_for && "Expected tok_for"); + Lex.readNextToken(); // eat the for. + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '(') { + return LogError("Expected ("); + } + auto Parts = ParseParenExpr(); + if (!Parts) { + return nullptr; + } + + auto Exprs = Parts->getExprs(); + if (Exprs.size() == 3) { + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '{') { + return LogError("Expected {"); + } + auto Body = ParseCurlyExpr(); + if (!Body) { + return nullptr; + } + + auto Start = Exprs[2]; + auto End = Exprs[1]; + auto Step = Exprs[0]; + + return std::make_shared(Start, End, Step, Body); + } + else { + return LogError("expected 3 parts"); + } +} + +/// forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression +const std::shared_ptr Parser::ParseWhileExpr() { + assert(Lex.CurTok == tok_while && "Expected tok_while"); + Lex.readNextToken(); // eat the while. + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '(') { + return LogError("Expected ("); + } + auto Parts = ParseParenExpr(); + if (!Parts) { + return nullptr; + } + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '{') { + return LogError("Expected {"); + } + auto Body = ParseCurlyExpr(); + if (!Body) { + return nullptr; + } + + auto Exprs = Parts->getExprs(); + if (Exprs.size() == 1) { + auto Test = Exprs[0]; + return std::make_shared(Test, Body); + } + else { + return LogError("Expected 1 part"); + } +} + +/// varexpr ::= 'var' identifier ('=' expression)? +// (',' identifier ('=' expression)?)* 'in' expression +const std::shared_ptr Parser::ParseVarExpr() { + assert(Lex.CurTok == tok_var && "Expected tok_var"); + Lex.readNextToken(); // eat the var + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '(') { + return LogError("Expected ("); + } + auto Idents = ParseParenExpr(); + if (!Idents) { + return nullptr; + } + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '=') { + return LogError("expected ="); + } + Lex.readNextToken(); // eat the = + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '(') { + return LogError("Expected ("); + } + auto Vals = ParseParenExpr(); + if (!Vals) { + return nullptr; + } + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + if (Lex.CurTok != '{') { + return LogError("Expected {"); + } + auto Body = ParseCurlyExpr(); + if (!Body) { + return nullptr; + } + + std::vector> Identifiers; + for (auto Ident : Idents->getExprs()) { + if (auto Var = llvm::dyn_cast(Ident.get())) { + Identifiers.push_back(std::make_shared(*Var)); + } + else { + return LogError("Identifier expected in variable list"); + } + } + auto Values = Vals->getExprs(); + return std::make_shared(Identifiers, Values, Body); +} + +const std::shared_ptr Parser::ParsePrimary() { + switch (Lex.CurTok) { + case tok_identifier: + return ParseIdentifierExpr(); + case tok_rationalword: + return ParseRationalWordExpr(); + case tok_finiteword: + return ParseFiniteWordExpr(); + case '(': + return ParseParenExpr(); + case '{': + return ParseCurlyExpr(); + case tok_if: + return ParseIfExpr(); + case tok_for: + return ParseForExpr(); + case tok_while: + return ParseWhileExpr(); + case tok_var: + return ParseVarExpr(); + case tok_error: + return nullptr; + default: + llvm_unreachable("Unexpected token"); + } +} + +/// unary +/// ::= primary +/// ::= '!' unary +const std::shared_ptr Parser::ParseUnary() { + switch (Lex.CurTok) { + // If the current token is not an operator, it must be a primary expr. + case tok_identifier: + case tok_rationalword: + case tok_finiteword: + case tok_if: + case tok_for: + case tok_while: + case tok_var: + case '(': + case '{': { + auto Res = ParsePrimary(); + return Res; + } + case '-': + case tok_ddd: + case '~': { + // If this is a unary operator, read it. + auto Opc = Lex.CurTok; + Lex.readNextToken(); + auto Operand = ParseUnary(); + if (!Operand) { + return nullptr; + } + return std::make_shared(Opc, Operand); + } + case tok_eof: + return LogError("Unexpected EOF"); + case '\n': + return LogError("Unexpected newline"); + case '#': + case '+': + case '*': + return LogError(stringFromToken(Lex.CurTok) + " is an infix operator"); + case '&': + case '|': + case '^': + case tok_gtgt: + case tok_gtp: + case tok_pp: + return LogError(stringFromToken(Lex.CurTok) + " is a binary operator"); + case tok_error: + return nullptr; + default: + if (Lex.CurTok < 0) { + llvm_unreachable("Add token"); + } + return LogError("Unrecognized token: " + stringFromToken(Lex.CurTok)); + } +} + +const std::shared_ptr Parser::ParseBinOpRHS(int ExprPrec, std::shared_ptr LHS) { + // If this is a binop, find its precedence. + while (1) { + if (Lex.CurTok == tok_error) { + return nullptr; + } + int TokPrec = GetTokPrecedence(); + if (TokPrec == -1) { + return LogError("bad operator: " + Lex.currentState()); + } + // If this is a binop that binds at least as tightly as the current binop, + // consume it, otherwise we are done. + if (TokPrec < ExprPrec) { + return LHS; + } + + // Okay, we know this is a binop. + auto BinOp = Lex.CurTok; + Lex.readNextToken(); // eat binop + + while (Lex.CurTok == '\n') { + Lex.readNextToken(); + } + + // Parse the primary expression after the binary operator. + auto RHS = ParseUnary(); + if (!RHS) { + return nullptr; + } + + // If BinOp binds less tightly with RHS than the operator after RHS, let + // the pending operator take RHS as its LHS. + if (Lex.CurTok == tok_error) { + return nullptr; + } + int NextPrec = GetTokPrecedence(); + if (NextPrec == -1) { + return LogError("bad operator: " + Lex.currentState()); + } + if (TokPrec < NextPrec) { + RHS = ParseBinOpRHS(TokPrec + 1, RHS); + if (!RHS) { + return nullptr; + } + } + + // Merge LHS/RHS. + switch (BinOp) { + //case ',': + case '#': + case '+': + case '*': + case tok_eqeq: + case tok_beq: + case '&': + case '|': + case '^': { + std::vector> Args; + Args.push_back(RHS); + if (auto SameOpLHS = llvm::dyn_cast(LHS.get())) { + if (SameOpLHS->getOp() == BinOp) { + auto SameOpLHSArgs = SameOpLHS->getArgs(); + for (auto& Arg : SameOpLHSArgs) { + Args.push_back(Arg); + } + } + else { + Args.push_back(LHS); + } + } + else { + Args.push_back(LHS); + } + + LHS = std::make_shared(BinOp, Args); + break; + } + default: + LHS = std::make_shared(BinOp, LHS, RHS); + break; + } + } +} + +/// expression +/// ::= unary binoprhs +/// +const std::shared_ptr Parser::ParseExpression() { + auto LHS = ParseUnary(); + if (!LHS) { + return nullptr; + } + + auto BinOpRHS = ParseBinOpRHS(0, LHS); + if (!BinOpRHS) { + return nullptr; + } + + return BinOpRHS; +} + +/// prototype +const std::shared_ptr Parser::ParsePrototype() { + assert(Lex.CurTok == tok_identifier && "Expected tok_identifier"); + auto Identifier = Lex.IdentifierStr; + Lex.readNextToken(); // consume the identifier + + auto IdentifierExpr = std::make_shared(Identifier); + + if (Lex.CurTok != '(') { + return LogError("Expected ("); + } + auto Params = ParseParenExpr(); + if (!Params) { + return nullptr; + } + + std::vector> Parameters; + for (auto& Param : Params->getExprs()) { + if (auto P = llvm::dyn_cast(Param.get())) { + Parameters.push_back(std::make_shared(*P)); + } + else { + return LogError("Expected identifier in prototype"); + } + } + + return std::make_shared(IdentifierExpr, Parameters); +} + +/// definition ::= 'def' prototype expression +const std::shared_ptr Parser::ParseDefinition() { + assert(Lex.CurTok == tok_define && "Expected tok_define"); + Lex.readNextToken(); // eat def. + + auto Proto = ParsePrototype(); + if (!Proto) { + return nullptr; + } + + if (Lex.CurTok != '{') { + return LogError("Expected { in function definition"); + } + auto E = ParseCurlyExpr(); + if (!E) { + return nullptr; + } + + if (Lex.CurTok != '\n' && Lex.CurTok != EOF) { + return LogError("Did not consume all input"); + } + + return std::make_shared(Proto, E); +} + +const std::shared_ptr Parser::ParseTopLevelExpression() { + auto Expr = ParseExpression(); + if (!Expr) { + return nullptr; + } + + if (Lex.CurTok != '\n' && Lex.CurTok != EOF) { + return LogError("Did not consume all input"); + } + + return Expr; +} + +void Parser::throwAwayLine() { + Lex.throwAwayLine(); +} + +void Parser::readNextToken() { + Lex.readNextToken(); +} + +const char Parser::getCurrentToken() { + return Lex.CurTok; +} + +const std::string ForExprAST::string() const { + return "ForExprAST"; +} + +const std::string VarExprAST::string() const { + return "VarExprAST"; +} + +const std::string CallExprAST::string() const { + return "CallExprAST"; +} + +const std::string InfixExprAST::string() const { + return "InfixExprAST"; +} + +const std::string WhileExprAST::string() const { + return "WhileExprAST"; +} + +const std::string UnaryExprAST::string() const { + return "UnaryExprAST"; +} + +const std::string BinaryExprAST::string() const { + return "BinaryExprAST"; +} + +const std::string FiniteWordExprAST::string() const { + return Val.string(); +} + +const std::string RationalWordExprAST::string() const { + return Val.string(); +} + +const std::string IdentifierExprAST::string() const { + return "IdentifierExprAST"; +} + +const std::string ExpressionListExprAST::string() const { + return "ExpressionListExprAST"; +} + +const std::string ExpressionSequenceExprAST::string() const { + return "ExpressionSequenceExprAST"; +} + +const std::string IfExprAST::string() const { + return "IfExprAST"; +} + +const std::string PrototypeAST::string() const { + return "PrototypeAST"; +} + +const std::string DefinitionAST::string() const { + return "DefinitionAST"; +} diff --git a/src/tuppence/Parser.h b/src/tuppence/Parser.h new file mode 100644 index 0000000..664e3fb --- /dev/null +++ b/src/tuppence/Parser.h @@ -0,0 +1,448 @@ +//===------ Parser.h ------------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_PARSER_H +#define TUPPENCE_PARSER_H + +#include "Lexer.h" +#include "FiniteWord.h" +#include "RationalWord.h" + +#include "llvm/Support/Casting.h" + +#include +#include +#include + +namespace tuppence { + + class AST { + public: + /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) + enum ASTKind { + AK_IdentifierAST, + AK_PrototypeAST, + AK_DefinitionAST, + AK_ExpressionListExpr, + AK_ExpressionSequenceExpr, + AK_RationalWordExpr, + AK_FiniteWordExpr, + AK_IdentifierExpr, + AK_UnaryExpr, + AK_BinaryExpr, + AK_InfixExpr, + AK_CallExpr, + AK_IfExpr, + AK_ForExpr, + AK_WhileExpr, + AK_VarExpr + }; + private: + const ASTKind Kind; + public: + AST(ASTKind K) : Kind(K) {} + + virtual ~AST() {} + + virtual const std::string string() const = 0; + + const ASTKind getKind() const { return Kind; } + }; + + /// ExprAST - Base class for all expression nodes. + class ExprAST : public AST { + + public: + ExprAST(ASTKind Kind) : AST(Kind) {} + + virtual const std::shared_ptr eval() const = 0; + }; + + // (expr, expr, expr...) + class ExpressionListExprAST : public ExprAST { + const std::vector> Exprs; + + public: + ExpressionListExprAST(std::vector> Exprs) : + ExprAST(AK_ExpressionListExpr), Exprs(Exprs) {} + + const std::shared_ptr eval() const override; + + const std::vector> getExprs() const { return Exprs; } + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_ExpressionListExpr; + } + }; + + // expressions separated by \n + class ExpressionSequenceExprAST : public ExprAST { + const std::vector> Exprs; + + public: + ExpressionSequenceExprAST(std::vector> Exprs) : + ExprAST(AK_ExpressionSequenceExpr), Exprs(Exprs) {} + + const std::shared_ptr eval() const override; + + const std::vector> getExprs() const { return Exprs; } + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_ExpressionSequenceExpr; + } + }; + + /// NumberExprAST - Expression class for numeric literals like "1.0". + class RationalWordExprAST : public ExprAST { + const RationalWord Val; + + public: + RationalWordExprAST(RationalWord Val) : + ExprAST(AK_RationalWordExpr), Val(Val) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_RationalWordExpr; + } + }; + + class FiniteWordExprAST : public ExprAST { + const FiniteWord Val; + public: + FiniteWordExprAST(FiniteWord Val) : + ExprAST(AK_FiniteWordExpr), Val(Val) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_FiniteWordExpr; + } + }; + + /// VariableExprAST - Expression class for referencing a variable, like "a". + class IdentifierExprAST : public ExprAST { + const std::string Name; + + public: + IdentifierExprAST(std::string Name) : + ExprAST(AK_IdentifierExpr), Name(Name) {} + + std::string getName() const { return Name; } + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_IdentifierExpr; + } + }; + + /// UnaryExprAST - Expression class for a unary operator. + class UnaryExprAST : public ExprAST { + const char Opcode; + const std::shared_ptr Operand; + + public: + UnaryExprAST(char Opcode, const std::shared_ptr Operand) : + ExprAST(AK_UnaryExpr), Opcode(Opcode), Operand(Operand) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + }; + + /// BinaryExprAST - Expression class for a binary operator. + class BinaryExprAST : public ExprAST { + const char Op; + const std::shared_ptr LHS; + const std::shared_ptr RHS; + + public: + BinaryExprAST(char Op, const std::shared_ptr LHS, const std::shared_ptr RHS) : + ExprAST(AK_BinaryExpr), Op(Op), LHS(LHS), RHS(RHS) {} + + const std::shared_ptr getLHS() const { + return LHS; + } + + const std::shared_ptr getRHS() const { + return RHS; + } + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_BinaryExpr; + } + }; + + /// InfixExprAST - Expression class for a binary operator. + class InfixExprAST : public ExprAST { + const char Op; + const std::vector> Args; + + public: + InfixExprAST(char Op, std::vector> Args) : + ExprAST(AK_InfixExpr), Op(Op), Args(Args) {} + + char getOp() const { return Op; } + + const std::vector> getArgs() const { return Args; }; + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_InfixExpr; + } + }; + + /// CallExprAST - Expression class for function calls. + class CallExprAST : public ExprAST { + const std::shared_ptr Callee; + const std::shared_ptr Args; + + public: + CallExprAST(std::shared_ptr Callee, const std::shared_ptr Args) : + ExprAST(AK_CallExpr), Callee(Callee), Args(Args) {} + + const std::shared_ptr eval() const override; + + const std::shared_ptr getArgs() const { return Args; } + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_CallExpr; + } + }; + + /// IfExprAST - Expression class for if/then/else. + class IfExprAST : public ExprAST { + const std::shared_ptr Cond; + const std::shared_ptr Then; + const std::shared_ptr Else; + + public: + IfExprAST( + const std::shared_ptr Cond, + const std::shared_ptr Then, + const std::shared_ptr Else) : + ExprAST(AK_IfExpr), Cond(Cond), Then(Then), Else(Else) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_IfExpr; + } + }; + + /// ForExprAST - Expression class for for/in. + class ForExprAST : public ExprAST { + const std::shared_ptr Start; + const std::shared_ptr End; + const std::shared_ptr Step; + const std::shared_ptr Body; + + public: + ForExprAST( + const std::shared_ptr Start, + const std::shared_ptr End, + const std::shared_ptr Step, + const std::shared_ptr Body) : + ExprAST(AK_ForExpr), Start(Start), End(End), Step(Step), Body(Body) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_ForExpr; + } + }; + + /// ForExprAST - Expression class for for/in. + class WhileExprAST : public ExprAST { + const std::shared_ptr Test; + const std::shared_ptr Body; + + public: + WhileExprAST(std::shared_ptr Test, std::shared_ptr Body) : + ExprAST(AK_WhileExpr), Test(Test), Body(Body) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_WhileExpr; + } + }; + + /// VarExprAST - Expression class for var/in + class VarExprAST : public ExprAST { + const std::vector> Vars; + const std::vector> Vals; + const std::shared_ptr Body; + + public: + VarExprAST( + std::vector> Vars, + std::vector> Vals, + const std::shared_ptr Body) : + ExprAST(AK_VarExpr), Vars(Vars), Vals(Vals), Body(Body) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + const std::vector> getVars() const { return Vars; } + + const std::vector> getVals() const { return Vals; } + + const std::shared_ptr getBody() const { return Body; } + + static bool classof(const AST *S) { + return S->getKind() == AK_VarExpr; + } + }; + + /// PrototypeAST - This class represents the "prototype" for a function, + /// which captures its name, and its argument names (thus implicitly the number + /// of arguments the function takes), as well as if it is an operator. + class PrototypeAST : public AST { + const std::shared_ptr Name; + const std::vector> Params; + + public: + PrototypeAST(const std::shared_ptr Name, std::vector> Params) : + AST(AK_PrototypeAST), Name(Name), Params(Params) {} + + const std::shared_ptr getName() const { return Name; } + + const std::vector> getParams() const { return Params; } + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_PrototypeAST; + } + }; + + /// FunctionAST - This class represents a function definition itself. + class DefinitionAST : public ExprAST { + const std::shared_ptr Proto; + const std::shared_ptr Body; + + public: + DefinitionAST(const std::shared_ptr Proto, const std::shared_ptr Body) : + ExprAST(AK_DefinitionAST), Proto(Proto), Body(Body) {} + + const std::shared_ptr eval() const override; + + const std::string string() const override; + + static bool classof(const AST *S) { + return S->getKind() == AK_DefinitionAST; + } + }; + + class Parser { + /// BinopPrecedence - This holds the precedence for each binary operator that is + /// defined. + std::map BinopPrecedence; + Lexer Lex; + + int GetTokPrecedence(); + + /// numberexpr ::= number + const std::shared_ptr ParseRationalWordExpr(); + + const std::shared_ptr ParseFiniteWordExpr(); + + /// parenexpr ::= '(' expression ')' + const std::shared_ptr ParseParenExpr(); + + const std::shared_ptr ParseCurlyExpr(); + + /// identifierexpr + /// ::= identifier + /// ::= identifier '(' expression* ')' + const std::shared_ptr ParseIdentifierExpr(); + + /// ifexpr ::= 'if' expression 'then' expression 'else' expression + const std::shared_ptr ParseIfExpr(); + + /// forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression + const std::shared_ptr ParseForExpr(); + + const std::shared_ptr ParseWhileExpr(); + + /// varexpr ::= 'var' identifier ('=' expression)? + // (',' identifier ('=' expression)?)* 'in' expression + const std::shared_ptr ParseVarExpr(); + + /// primary + /// ::= identifierexpr + /// ::= numberexpr + /// ::= parenexpr + const std::shared_ptr ParsePrimary(); + + /// unary + /// ::= primary + /// ::= '!' unary + const std::shared_ptr ParseUnary(); + + /// binoprhs + /// ::= ('+' primary)* + const std::shared_ptr ParseBinOpRHS(int ExprPrec, std::shared_ptr LHS); + + /// expression + /// ::= primary binoprhs + /// + const std::shared_ptr ParseExpression(); + + public: + Parser(std::istream &); + + /// expression + /// ::= primary binoprhs + /// + const std::shared_ptr ParseTopLevelExpression(); + + /// definition ::= 'def' prototype expression + const std::shared_ptr ParseDefinition(); + + /// prototype + /// ::= id '(' id* ')' + const std::shared_ptr ParsePrototype(); + + void throwAwayLine(); + void readNextToken(); + const char getCurrentToken(); + + }; +} + +#endif diff --git a/src/tuppence/RationalWord.cpp b/src/tuppence/RationalWord.cpp new file mode 100644 index 0000000..5b638a7 --- /dev/null +++ b/src/tuppence/RationalWord.cpp @@ -0,0 +1,731 @@ +//===------ RationalWord.cpp ----------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "RationalWord.h" + +#include "Logger.h" +#include "TuppenceConfig.h" + +#include +#include +#include + +namespace tuppence { + + namespace rationalword { + + const FiniteWord EMPTY = FiniteWord::FactoryEmpty(); + const FiniteWord ZERO_1BIT = FiniteWord::FactoryBool((bool)0); + const FiniteWord ONE_1BIT = FiniteWord::FactoryBool((bool)1); + + const RationalWord ZERO = RationalWord::FactoryPeriodTransient(ZERO_1BIT, EMPTY); + const RationalWord ONE = RationalWord::FactoryPeriodTransient(ZERO_1BIT, ONE_1BIT); + const RationalWord MINUS_ONE = RationalWord::FactoryPeriodTransient(ONE_1BIT, EMPTY); + + } + +} + +using namespace tuppence; + +// Default constructor +RationalWord::RationalWord() : + Value(VK_RationalWord), period(rationalword::ZERO_1BIT), transient() {} + +RationalWord::RationalWord(FiniteWord Period, FiniteWord Transient) : + Value(VK_RationalWord), period(Period), transient(Transient) { + assert(isReduced(period, transient) && "not reduced!"); +} + +const RationalWord RationalWord::FactoryPeriodTransient(FiniteWord period, FiniteWord transient) { + reduce(period, transient); + return RationalWord(period, transient); +} + +const RationalWord RationalWord::FactoryString(std::string StrVal) { + assert(!StrVal.empty() && "string is empty"); + assert((StrVal.size() == 1 || StrVal[0] != '0') && "string is padded with 0"); + auto Transient = FiniteWord::FactoryDecimalString(StrVal); + return RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, Transient); +} + +const RationalWord RationalWord::FactorySizeT(size_t Val) { + auto Transient = FiniteWord::FactorySizeT(Val); + return RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, Transient); +} + +// Copy constructor +RationalWord::RationalWord(const RationalWord& other) : + Value(VK_RationalWord), period(other.period), transient(other.transient) {} + +// Copy assignment operator +RationalWord& RationalWord::operator=(const RationalWord& rhs) { + period = rhs.period; + transient = rhs.transient; + return *this; +} + +// Move constructor +RationalWord::RationalWord(RationalWord&& other) : + Value(VK_RationalWord), period(other.period), transient(other.transient) { + // nothing to clear from other +} + +// Move assignment operator +RationalWord& RationalWord::operator=(RationalWord&& other) { + period = other.period; + transient = other.transient; + // nothing to clear from other + return *this; +} + +const std::string RationalWord::representation() const { + auto PeriodStr = period.bits(); + auto TransientStr = transient.bits(); + + std::stringstream ss; + ss << PeriodStr; + ss << "'"; + ss << TransientStr; + return ss.str(); +} + +void RationalWord::calculateFraction(RationalWord& Numerator, RationalWord& Denominator) const { + if (transient.getSize() > 0) { + auto z = (rationalword::ONE_1BIT.concatenate(FiniteWord::FactoryRepsWord(transient.getSize(), rationalword::ZERO_1BIT))); + auto xExtended = rationalword::ZERO_1BIT.concatenate(FiniteWord::FactoryRepsWord(transient.getSize(), rationalword::ZERO_1BIT)).concatenate(period); + auto zExtended = FiniteWord::FactoryRepsWord(period.getSize(), rationalword::ZERO_1BIT).concatenate(z); + auto xz = xExtended * zExtended; + if (period.getSize() > 1) { + auto w = FiniteWord::FactoryRepsWord(period.getSize(), rationalword::ONE_1BIT); + auto wExtended = rationalword::ZERO_1BIT.concatenate(FiniteWord::FactoryRepsWord(transient.getSize(), rationalword::ZERO_1BIT)).concatenate(w); + auto yExtended = rationalword::ZERO_1BIT.concatenate(FiniteWord::FactoryRepsWord(period.getSize(), rationalword::ZERO_1BIT)).concatenate(transient); + auto wy = wExtended * yExtended; + if (wy.ugt(xz)) { + auto wy_xz = wy - xz; + auto divisor = FiniteWord::gcd(wy_xz, wExtended); + auto wy_xzReduced = wy_xz / divisor; + auto wReduced = wExtended / divisor; + //return wy_xzReduced.decimal() + "/" + wReduced.decimal(); + + Numerator = RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, wy_xzReduced); + Denominator = RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, wReduced); + } + else { + auto xz_wy = xz - wy; + auto divisor = FiniteWord::gcd(xz_wy, wExtended); + auto xz_wyReduced = xz_wy / divisor; + auto wReduced = wExtended / divisor; + //return "-" + xz_wyReduced.decimal() + "/" + wReduced.decimal(); + + Numerator = -RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, xz_wyReduced); + Denominator = RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, wReduced); + } + } + else { + auto yExtended = rationalword::ZERO_1BIT.concatenate(FiniteWord::FactoryRepsWord(period.getSize(), rationalword::ZERO_1BIT)).concatenate(transient); + if (yExtended.ugt(xz)) { + //return (yExtended - xz).decimal(); + + Numerator = RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, (yExtended - xz)); + Denominator = rationalword::ONE; + } + else { + //return "-" + (xz - yExtended).decimal(); + + Numerator = -RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, (xz - yExtended)); + Denominator = rationalword::ONE; + } + } + } + else { + if (period.getSize() > 1) { + auto w = FiniteWord::FactoryRepsWord(period.getSize(), rationalword::ONE_1BIT); + + auto divisor = FiniteWord::gcd(period, w); + auto xReduced = period / divisor; + auto wReduced = w / divisor; + + //return "-" + xReduced.decimal() + "/" + wReduced.decimal(); + + Numerator = -RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, xReduced); + Denominator = RationalWord::FactoryPeriodTransient(rationalword::ZERO_1BIT, wReduced); + } + else if (period == rationalword::ONE_1BIT) { + Numerator = rationalword::MINUS_ONE; + Denominator = rationalword::ONE; + } + else { + Numerator = rationalword::ZERO; + Denominator = rationalword::ONE; + } + } +} + +const std::string RationalWord::decimal() const { + if (isNegativeInteger()) { + return rationalword::ONE_1BIT.concatenate(transient).decimalSigned(); + } + if (isNonNegativeInteger()) { + return transient.decimal(); + } + RationalWord Numerator; + RationalWord Denominator; + calculateFraction(Numerator, Denominator); + return Numerator.decimal() + "/" + Denominator.decimal(); +} + +const std::string RationalWord::string() const { + return decimal(); +} + +const FiniteWord RationalWord::getPeriod() const { + return period; +} + +const FiniteWord RationalWord::getTransient() const { + return transient; +} + +const bool RationalWord::isNonNegativeInteger() const { + return period.getSize() == 1 && period == rationalword::ZERO_1BIT; +} + +const bool RationalWord::isNegativeInteger() const { + return period.getSize() == 1 && period == rationalword::ONE_1BIT; +} + +const uint64_t RationalWord::getUInt64Value() const { + return *transient.getRawData(); +} + +const RationalWord RationalWord::getNumerator() const { + RationalWord Numerator; + RationalWord Denominator; + calculateFraction(Numerator, Denominator); + return Numerator; +} + +const RationalWord RationalWord::getDenominator() const { + RationalWord Numerator; + RationalWord Denominator; + calculateFraction(Numerator, Denominator); + return Denominator; +} + +void RationalWord::windupTransient(FiniteWord& period, FiniteWord& transient) { + assert(period.getSize() > 0 && "Period size is 0"); + while (1) { + auto min = std::min(period.getSize(), transient.getSize()); + auto common = 0; + for (auto i = 0; i < min; i++) { + if (period.get(period.getSize() - 1 - i) == transient.get(transient.getSize() - 1 - i)) { + common++; + } + else { + break; + } + } + + if (common == 0) { + break; + } + + // rotate period to the left + period = period.rotateLeft(common); + + //remove last bit of transient; + transient = transient.residue(transient.getSize() - common); + } +} + +void RationalWord::reduce(FiniteWord& period, FiniteWord& transient) { + assert(period.getSize() > 0 && "Period size is 0"); + FiniteWord::compressPeriod(period); + windupTransient(period, transient); +} + +const bool RationalWord::isReduced(FiniteWord period, FiniteWord transient) { + assert(period.getSize() > 0 && "Period size is 0"); + // transient is wound up? + if (transient.getSize() > 0) { + auto lastPeriodBit = period.get(period.getSize() - 1); + auto lastTransientBit = transient.get(transient.getSize() - 1); + if (lastPeriodBit == lastTransientBit) { + return false; + } + } + + if (!FiniteWord::isPeriodCompressed(period)) { + return false; + } + + return true; +} + +// extend transient to be size extent, unwind period to accomodate +void RationalWord::extendTransient(FiniteWord& period, FiniteWord& transient, size_t extent) { + assert(extent >= transient.getSize() && "Extent is too small"); + auto diff = extent - transient.getSize(); + + if (diff == 0) { + return; + } + + auto fullRepetitions = diff / period.getSize(); + auto leftOver = diff % period.getSize(); + + auto NewTransient = period.residue(leftOver); + NewTransient = NewTransient.concatenate(FiniteWord::FactoryRepsWord(fullRepetitions, period)); + NewTransient = NewTransient.concatenate(transient); + + transient = NewTransient; + period = period.rotateRight(diff); +} + +void RationalWord::alignPeriodAndTransient( + FiniteWord& PeriodL, FiniteWord& TransientL, + FiniteWord& PeriodR, FiniteWord& TransientR) { + + // Extend transients to match + if (TransientL.getSize() > TransientR.getSize()) { + extendTransient(PeriodR, TransientR, TransientL.getSize()); + } + else if (TransientL.getSize() < TransientR.getSize()) { + extendTransient(PeriodL, TransientL, TransientR.getSize()); + } + + // Multiply Periods + auto LCM = lcm(PeriodL.getSize(), PeriodR.getSize()); + auto ToMultiplyL = LCM / PeriodL.getSize(); + auto ToMultiplyR = LCM / PeriodR.getSize(); + FiniteWord::multiplyPeriod(PeriodL, ToMultiplyL); + FiniteWord::multiplyPeriod(PeriodR, ToMultiplyR); +} + +const RationalWord RationalWord::operator~() const { + return RationalWord::FactoryPeriodTransient(~period, ~transient); +} + +const RationalWord RationalWord::operator|(RationalWord other) const { + auto PeriodA = period; + auto PeriodB = other.period; + auto TransientA = transient; + auto TransientB = other.transient; + + alignPeriodAndTransient(PeriodA, TransientA, PeriodB, TransientB); + + auto Period = PeriodA | PeriodB; + auto Transient = TransientA | TransientB; + + return RationalWord::FactoryPeriodTransient(Period, Transient); +} + +const RationalWord RationalWord::operator&(RationalWord other) const { + auto PeriodA = period; + auto PeriodB = other.period; + auto TransientA = transient; + auto TransientB = other.transient; + + alignPeriodAndTransient(PeriodA, TransientA, PeriodB, TransientB); + + auto Period = PeriodA & PeriodB; + auto Transient = TransientA & TransientB; + + return RationalWord::FactoryPeriodTransient(Period, Transient); +} + +const RationalWord RationalWord::operator^(RationalWord other) const { + auto PeriodA = period; + auto PeriodB = other.period; + auto TransientA = transient; + auto TransientB = other.transient; + + alignPeriodAndTransient(PeriodA, TransientA, PeriodB, TransientB); + + auto Period = PeriodA ^ PeriodB; + auto Transient = TransientA ^ TransientB; + + return RationalWord::FactoryPeriodTransient(Period, Transient); +} + +// +// Structural Operations +// + +const bool RationalWord::operator==(RationalWord other) const { + return period.getSize() == other.period.getSize() && transient.getSize() == other.transient.getSize() && + period == other.period && transient == other.transient; +} + +const bool RationalWord::operator!=(RationalWord other) const { + return period.getSize() != other.period.getSize() || transient.getSize() != other.transient.getSize() || + period != other.period || transient != other.transient; +} + +const FiniteWord RationalWord::equal(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto First = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + if (*Iter != First) { + return rationalword::ZERO_1BIT; + } + } + return rationalword::ONE_1BIT; +} + +const FiniteWord RationalWord::notequal(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + for (auto Iter = Vals.begin(); Iter != Vals.end(); ++Iter) { + auto First = *Iter; + auto Iter2 = Iter; + Iter2++; + for (; Iter2 != Vals.end(); Iter2++) { + auto Second = *Iter2; + // maintain intuitive order + if (Second == First) { + return rationalword::ZERO_1BIT; + } + } + } + return rationalword::ONE_1BIT; +} + +const FiniteWord RationalWord::residue(size_t i) const { + if (i <= transient.getSize()) { + return transient.residue(i); + } + + auto forPeriod = i - transient.getSize(); + auto fullRepetitions = forPeriod / period.getSize(); + auto leftOver = forPeriod % period.getSize(); + + return period.residue(leftOver).concatenate(FiniteWord::FactoryRepsWord(fullRepetitions, period)).concatenate(transient); +} + +const RationalWord RationalWord::shiftRight(size_t i) const { + if (i <= transient.getSize()) { + auto Shifted = transient.shiftRight(i); + return RationalWord::FactoryPeriodTransient(period, Shifted); + } + + auto forPeriod = i - transient.getSize(); + auto leftOver = forPeriod % period.getSize(); + + auto NewPeriod = period.rotateRight(leftOver); + FiniteWord NewTransient; + return RationalWord::FactoryPeriodTransient(NewPeriod, NewTransient); +} + +// It is possible to pass this is as Hi or Lo, so make sure +// to make a copy first. +void RationalWord::shiftRightResidue(size_t i, RationalWord& Hi, FiniteWord& Lo) const { + auto Tmp = *this; + Hi = Tmp.shiftRight(i); + Lo = Tmp.residue(i); +} + +const RationalWord RationalWord::concatenate(FiniteWord R) const { + auto T = transient.concatenate(R); + return RationalWord::FactoryPeriodTransient(period, T); +} + +// +// Bitwise operations +// + +const RationalWord RationalWord::bitwise_not(RationalWord L) { + return ~L; +} + +const RationalWord RationalWord::bitwise_or(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter | Val; + } + return Val; +} + +const RationalWord RationalWord::bitwise_and(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter & Val; + } + return Val; +} + +const RationalWord RationalWord::bitwise_xor(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter ^ Val; + } + return Val; +} + +// +// Arithmetic Operations +// + +const RationalWord RationalWord::operator-() const { + static auto ONE = RationalWord::FactoryString("1"); + return ~(*this) + ONE; +} + +const RationalWord RationalWord::operator+(RationalWord other) const { + + auto PeriodA = period; + auto PeriodB = other.period; + auto TransientA = transient; + auto TransientB = other.transient; + + alignPeriodAndTransient(PeriodA, TransientA, PeriodB, TransientB); + + FiniteWord TransientOutput; + FiniteWord TransientCarry; + + // maintain intuitive order + std::vector TransientVals = { TransientB, TransientA }; + FiniteWord::plus(TransientVals, TransientCarry, TransientOutput); + assert(TransientCarry.getSize() == 1 && "Adding 2 FiniteWords did not give a Carry of size 1"); + + FiniteWord PeriodOutput; + FiniteWord PeriodCarry(TransientCarry); + FiniteWord PreviousPeriodCarry; + std::vector PeriodVals; + + FiniteWord TransientAccumulate(TransientOutput); + + do { + PreviousPeriodCarry = PeriodCarry; + + auto CarryExtended = FiniteWord::FactoryRepsWord(PeriodA.getSize() - 1, rationalword::ZERO_1BIT).concatenate(PeriodCarry); + + PeriodVals.clear(); + // maintain intuitive order + PeriodVals.push_back(PeriodB); + PeriodVals.push_back(PeriodA); + PeriodVals.push_back(CarryExtended); + FiniteWord::plus(PeriodVals, PeriodCarry, PeriodOutput); + assert(PeriodCarry.getSize() == 2 && "Adding 3 FiniteWords did not give a Carry of size 2"); + PeriodCarry = PeriodCarry.residue(1); + + TransientAccumulate = PeriodOutput.concatenate(TransientAccumulate); + + } while (PeriodCarry != PreviousPeriodCarry); + + return RationalWord::FactoryPeriodTransient(PeriodOutput, TransientAccumulate); +} + +const RationalWord RationalWord::operator-(RationalWord other) const { + auto PeriodA = period; + auto PeriodB = ~other.period; + auto TransientA = transient; + auto TransientB = ~other.transient; + + alignPeriodAndTransient(PeriodA, TransientA, PeriodB, TransientB); + + FiniteWord TransientOutput; + FiniteWord TransientBorrow; + + auto BorrowExtended = FiniteWord::FactoryRepsWord(TransientA.getSize() - 1, rationalword::ZERO_1BIT).concatenate(rationalword::ONE_1BIT); + std::vector TransientVals = { TransientA, TransientB, BorrowExtended }; + FiniteWord::plus(TransientVals, TransientBorrow, TransientOutput); + assert(TransientBorrow.getSize() == 2 && "Adding 3 FiniteWords did not give a Carry of size 2"); + TransientBorrow = TransientBorrow.residue(1); + + FiniteWord PeriodOutput; + FiniteWord PeriodBorrow(TransientBorrow); + FiniteWord PreviousPeriodBorrow; + std::vector PeriodVals; + + FiniteWord TransientAccumulate(TransientOutput); + + do { + PreviousPeriodBorrow = PeriodBorrow; + + auto BorrowExtended = FiniteWord::FactoryRepsWord(PeriodA.getSize() - 1, rationalword::ZERO_1BIT).concatenate(PeriodBorrow); + + PeriodVals.clear(); + PeriodVals.push_back(PeriodA); + PeriodVals.push_back(PeriodB); + PeriodVals.push_back(BorrowExtended); + FiniteWord::plus(PeriodVals, PeriodBorrow, PeriodOutput); + assert(PeriodBorrow.getSize() == 2 && "Adding 3 FiniteWords did not give a Carry of size 2"); + PeriodBorrow = PeriodBorrow.residue(1); + + TransientAccumulate = PeriodOutput.concatenate(TransientAccumulate); + + } while (PeriodBorrow != PreviousPeriodBorrow); + + return RationalWord::FactoryPeriodTransient(PeriodOutput, TransientAccumulate); +} + +RationalWord finiteMultiply(RationalWord A, FiniteWord B) { + auto BReversed = B.reverse(); + RationalWord Accumulate; + FiniteWord Bit; + while (BReversed.getSize() > 0) { + Accumulate = Accumulate.concatenate(rationalword::ZERO_1BIT); + BReversed.shiftRightResidue(1, BReversed, Bit); + if (Bit == rationalword::ONE_1BIT) { + Accumulate = Accumulate + A; + } + } + return Accumulate; +} + +const RationalWord RationalWord::operator*(RationalWord other) const { + + auto A = *this; + auto B = other; + + auto BTransientProduct = finiteMultiply(A, B.getTransient()); + auto BPeriodProduct = finiteMultiply(A, B.getPeriod()); + + auto BTransientSize = B.getTransient().getSize(); + auto BPeriodSize = B.getPeriod().getSize(); + + FiniteWord Period; + FiniteWord Transient; + + RationalWord Partial; + BTransientProduct.shiftRightResidue(BTransientSize, Partial, Transient); + + std::vector PeriodPartials; + + while (1) { + Partial = BPeriodProduct + Partial; + + auto PeriodStart = std::find(PeriodPartials.begin(), PeriodPartials.end(), Partial); + if (PeriodStart != PeriodPartials.end()) { + for (auto TransientIter = PeriodPartials.begin(); TransientIter < PeriodStart; TransientIter++) { + Transient = TransientIter->residue(BPeriodSize).concatenate(Transient); + } + for (auto PeriodIter = PeriodStart; PeriodIter < PeriodPartials.end(); PeriodIter++) { + Period = PeriodIter->residue(BPeriodSize).concatenate(Period); + } + + break; + } + + PeriodPartials.push_back(Partial); + if (BTransientSize + PeriodPartials.size() >= Tuppence_LOOP_LIMIT) { + LogWarning("Loop limit exceeded in RationalWord multiply. Returning truncated result. Loop limit is: " + std::to_string(Tuppence_LOOP_LIMIT)); + for (auto TransientIter = PeriodPartials.begin(); TransientIter < PeriodPartials.end(); TransientIter++) { + Transient = TransientIter->residue(1).concatenate(Transient); + } + return FactoryPeriodTransient(rationalword::ZERO_1BIT, Transient); + } + + Partial = Partial.shiftRight(BPeriodSize); + } + + return FactoryPeriodTransient(Period, Transient); +} + +const RationalWord RationalWord::operator/(RationalWord other) const { + assert(other.residue(1) == rationalword::ONE_1BIT && "Divisor must be odd!"); + + const auto A = *this; + const auto B = other; + + auto Partial = A; + + FiniteWord Transient; + FiniteWord Period; + + std::vector Partials; + + Partials.push_back(Partial); + + while (1) { + if (Partial.residue(1) == rationalword::ONE_1BIT) { + Partial = Partial - B; + } + Partial = Partial.shiftRight(1); + + auto PeriodStart = std::find(Partials.begin(), Partials.end(), Partial); + if (PeriodStart != Partials.end()) { + for (auto TransientIter = Partials.begin(); TransientIter < PeriodStart; TransientIter++) { + Transient = TransientIter->residue(1).concatenate(Transient); + } + for (auto PeriodIter = PeriodStart; PeriodIter < Partials.end(); PeriodIter++) { + Period = PeriodIter->residue(1).concatenate(Period); + } + + break; + } + + Partials.push_back(Partial); + if (Partials.size() >= Tuppence_LOOP_LIMIT) { + LogWarning("Loop limit exceeded in RationalWord divide. Returning truncated result. Loop limit is: " + std::to_string(Tuppence_LOOP_LIMIT)); + for (auto TransientIter = Partials.begin(); TransientIter < Partials.end(); TransientIter++) { + Transient = TransientIter->residue(1).concatenate(Transient); + } + return FactoryPeriodTransient(rationalword::ZERO_1BIT, Transient); + } + } + + return FactoryPeriodTransient(Period, Transient); +} + +const RationalWord RationalWord::plus(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter + Val; + } + return Val; +} + +const RationalWord RationalWord::minus(RationalWord L, RationalWord R) { + return L - R; +} + +const RationalWord RationalWord::times(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto Val = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + Val = *Iter * Val; + } + return Val; +} + +const RationalWord RationalWord::divide(RationalWord L, RationalWord R) { + return L / R; +} + +// +// GTest +// + +void tuppence::PrintTo(const RationalWord& bar, ::std::ostream* os) { + *os << bar.string(); +} diff --git a/src/tuppence/RationalWord.h b/src/tuppence/RationalWord.h new file mode 100644 index 0000000..a641bba --- /dev/null +++ b/src/tuppence/RationalWord.h @@ -0,0 +1,152 @@ +//===------ RationalWord.h - RationalWord class definition ----------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_RATIONALWORD_H +#define TUPPENCE_RATIONALWORD_H + +#include "FiniteWord.h" +#include "Value.h" + +#include +#include + +namespace tuppence { + + class RationalWord : public Value { + FiniteWord period; + FiniteWord transient; + + RationalWord(FiniteWord Period, FiniteWord Transient); + + void calculateFraction(RationalWord& Numerator, RationalWord& Denominator) const; + + // + // modify period and transient before creating RationalWord + // + static void reduce(FiniteWord& period, FiniteWord& transient); + + static const bool isReduced(FiniteWord period, FiniteWord transient); + + // extend transient to be size extent + static void extendTransient(FiniteWord& period, FiniteWord& transient, size_t extent); + + static void alignPeriodAndTransient( + FiniteWord& periodL, FiniteWord& transientL, + FiniteWord& periodR, FiniteWord& transientR); + + static void windupTransient(FiniteWord& period, FiniteWord& transient); + + + public: + static const RationalWord FactoryPeriodTransient(FiniteWord period, FiniteWord transient); + static const RationalWord FactoryString(std::string); + static const RationalWord FactorySizeT(size_t); + + /// Default constructor + RationalWord(); + + /// Copy constructor + RationalWord(const RationalWord& other); + + /// Copy assignment operator + RationalWord& operator=(const RationalWord& rhs); + + /// Move constructor + RationalWord(RationalWord&& other); + + /// Move assignment operator + RationalWord& operator= (RationalWord&& other); + + const FiniteWord getPeriod() const; + const FiniteWord getTransient() const; + + const std::string string() const override; + + const std::string representation() const; + + const std::string decimal() const; + + const bool isNonNegativeInteger() const; + + const bool isNegativeInteger() const; + + const RationalWord getNumerator() const; + + const RationalWord getDenominator() const; + + const uint64_t getUInt64Value() const; + + // + // structural operations + // + + const bool operator==(RationalWord) const; + const bool operator!=(RationalWord) const; + + static const FiniteWord equal(std::vector); + static const FiniteWord notequal(std::vector); + + const FiniteWord residue(size_t i) const; + const RationalWord shiftRight(size_t i) const; + void shiftRightResidue(size_t i, RationalWord& Hi, FiniteWord& Lo) const; + + const RationalWord concatenate(FiniteWord) const; + + // + // bitwise operations + // + + const RationalWord operator~() const; + const RationalWord operator|(RationalWord) const; + const RationalWord operator&(RationalWord) const; + const RationalWord operator^(RationalWord) const; + + static const RationalWord bitwise_not(RationalWord); + static const RationalWord bitwise_or(std::vector); + static const RationalWord bitwise_and(std::vector); + static const RationalWord bitwise_xor(std::vector); + + // + // arithmetic operations + // + + const RationalWord operator-() const; + const RationalWord operator+(RationalWord) const; + const RationalWord operator-(RationalWord) const; + const RationalWord operator*(RationalWord) const; + const RationalWord operator/(RationalWord) const; + + static const RationalWord plus(std::vector); + static const RationalWord minus(RationalWord, RationalWord); + static const RationalWord times(std::vector); + static const RationalWord divide(RationalWord, RationalWord); + + // + // LLVM RTTI + // + static bool classof(const Value *S) { + return S->getKind() == VK_RationalWord; + } + }; + + namespace rationalword { + + extern const RationalWord ZERO; + extern const RationalWord ONE; + extern const RationalWord MINUS_ONE; + + } + + // + // GTest + // + void PrintTo(const RationalWord& bar, ::std::ostream* os); +} + +#endif diff --git a/src/tuppence/Value.cpp b/src/tuppence/Value.cpp new file mode 100644 index 0000000..4bc278e --- /dev/null +++ b/src/tuppence/Value.cpp @@ -0,0 +1,80 @@ +//===------ Value.cpp ------------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "Value.h" + +#include + +using namespace tuppence; + +const std::string tuppence::stringFromValueKind(Value::ValueKind Kind) { + switch (Kind) { + case Value::ValueKind::VK_BuiltinFunction: return "BuiltinFunction"; + case Value::ValueKind::VK_FiniteWord: return "FiniteWord"; + case Value::ValueKind::VK_RationalWord: return "RationalWord"; + case Value::ValueKind::VK_UserFunction: return "UserFunction"; + case Value::ValueKind::VK_ValueList: return "ValueList"; + default: { + assert(false && "ADD THIS KIND"); + return "ASSERT"; + } + } +} + +const std::string BuiltinFunction::string() const { + return ""; +} + +const std::string UserFunction::string() const { + return ""; +} + +const bool Symbol::operator==(const Symbol other) const { + return Name == other.Name; +} + +const std::string Symbol::string() const { + return Name; +} + +// +// Math +// + +const size_t tuppence::gcd(size_t a, size_t b) { + while (1) { + if (a == 0) { + return b; + } + b %= a; + if (b == 0) { + return a; + } + a %= b; + } +} + +const size_t tuppence::lcm(size_t a, size_t b) { + auto temp = gcd(a, b); + if (temp) { + return a / temp * b; + } + else { + return (size_t)0; + } +} + +const size_t tuppence::bitLength(size_t n) { + auto r = 0; + while (n > 0) { + r++; + n = n >> 1; + } + return r; +} diff --git a/src/tuppence/Value.h b/src/tuppence/Value.h new file mode 100644 index 0000000..6785704 --- /dev/null +++ b/src/tuppence/Value.h @@ -0,0 +1,120 @@ +//===------ Value.h -------------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_VALUE_H +#define TUPPENCE_VALUE_H + +#include +#include +#include +#include + +namespace tuppence { + + class ExprAST; + class PrototypeAST; + + /// Value - Base class for all values + class Value { + public: + /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) + enum ValueKind { + VK_BuiltinFunction, + VK_FiniteWord, + VK_RationalWord, + VK_Symbol, + VK_UserFunction, + VK_ValueList + }; + private: + const ValueKind Kind; + public: + Value(ValueKind K) : Kind(K) {} + + virtual ~Value() {} + + virtual const std::string string() const = 0; + + const ValueKind getKind() const { return Kind; } + }; + + const std::string stringFromValueKind(Value::ValueKind); + + class UserFunction : public Value { + const std::shared_ptr Proto; + const std::shared_ptr Body; + + public: + UserFunction(const std::shared_ptr Proto, const std::shared_ptr Body) : + Value(VK_UserFunction), Proto(Proto), Body(Body) {} + + const std::shared_ptr call(std::vector> Args) const; + + const std::string string() const override; + + // + // LLVM RTTI + // + static bool classof(const Value *S) { + return S->getKind() == VK_UserFunction; + } + }; + + class BuiltinFunction : public Value { + const std::shared_ptr(*FunctionPointer)(std::vector>); + + public: + BuiltinFunction(const std::shared_ptr(*FunctionPointer)(std::vector>)) : + Value(VK_BuiltinFunction), FunctionPointer(FunctionPointer) {} + + const std::shared_ptr call(std::vector> Args) const; + + const std::string string() const override; + + // + // LLVM RTTI + // + static bool classof(const Value *S) { + return S->getKind() == VK_BuiltinFunction; + } + }; + + class Symbol : public Value { + const std::string Name; + + public: + Symbol(std::string Name) : + Value(VK_Symbol), Name(Name) {} + + const std::string string() const override; + + const std::string getName() const { return Name; } + + const bool operator==(Symbol) const; + + // + // LLVM RTTI + // + + static bool classof(const Value *S) { + return S->getKind() == VK_Symbol; + } + }; + + // + // math + // + const size_t gcd(size_t a, size_t b); + + const size_t lcm(size_t a, size_t b); + + const size_t bitLength(size_t); +} + +#endif diff --git a/src/tuppence/ValueList.cpp b/src/tuppence/ValueList.cpp new file mode 100644 index 0000000..4f7b416 --- /dev/null +++ b/src/tuppence/ValueList.cpp @@ -0,0 +1,219 @@ +//===------ ValueList.cpp -------------------------------------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#include "ValueList.h" + +#include "Logger.h" +#include "RationalWord.h" + +#include "llvm/Support/Casting.h" + +#include + +namespace tuppence { + + namespace valuelist { + + const FiniteWord EMPTY = FiniteWord::FactoryEmpty(); + const FiniteWord ZERO_1BIT = FiniteWord::FactoryBool((bool)0); + const FiniteWord ONE_1BIT = FiniteWord::FactoryBool((bool)1); + + } + +} + +using namespace tuppence; + +const std::string ValueList::string() const { + std::ostringstream strs; + // iterate reversed for printing + strs << "("; + for (auto iter = Vals.rbegin(); iter != Vals.rend(); ++iter) { + strs << (*iter)->string(); + if (std::next(iter) != Vals.rend()) { + strs << ", "; + } + } + strs << ")"; + std::string str = strs.str(); + return str; +} + +// +// Structural Operations +// + +const bool ValueList::isComparable() const { + for (auto& Val : Vals) { + if (/*auto FiniteWordVal = */llvm::dyn_cast(Val.get())) { + + } + else if (/*auto RationalWordVal = */llvm::dyn_cast(Val.get())) { + + } + else if (auto ValueListVal = llvm::dyn_cast(Val.get())) { + if (!ValueListVal->isComparable()) { + return false; + } + } + else { + // some other, non-comparable element + return false; + } + } + return true; +} + +const bool ValueList::operator==(const ValueList other) const { + auto size = Vals.size(); + auto otherSize = other.Vals.size(); + assert(size == otherSize && "Sizes not equal"); + + for (auto i = 0; i < size; i++) { + auto Val = Vals[i]; + auto OtherVal = other.Vals[i]; + if (auto FiniteWordVal = llvm::dyn_cast(Val.get())) { + if (auto OtherFiniteWordVal = llvm::dyn_cast(OtherVal.get())) { + if (*FiniteWordVal != *OtherFiniteWordVal) { + return false; + } + } + else { + assert(false && "do better type checking"); + return false; + } + } + else if (auto RationalWordVal = llvm::dyn_cast(Val.get())) { + if (auto OtherRationalWordVal = llvm::dyn_cast(OtherVal.get())) { + if (*RationalWordVal != *OtherRationalWordVal) { + return false; + } + } + else { + assert(false && "do better type checking"); + return false; + } + } + else if (auto ValueListVal = llvm::dyn_cast(Val.get())) { + if (auto OtherValueListVal = llvm::dyn_cast(OtherVal.get())) { + if (*ValueListVal != *OtherValueListVal) { + return false; + } + } + else { + assert(false && "do better type checking"); + return false; + } + } + } + + return true; +} + +const bool ValueList::operator!=(const ValueList other) const { + auto size = Vals.size(); + auto otherSize = other.Vals.size(); + assert(size == otherSize && "Sizes not equal"); + + for (auto i = 0; i < size; i++) { + auto Val = Vals[i]; + auto OtherVal = other.Vals[i]; + if (auto FiniteWordVal = llvm::dyn_cast(Val.get())) { + if (auto OtherFiniteWordVal = llvm::dyn_cast(OtherVal.get())) { + if (*FiniteWordVal == *OtherFiniteWordVal) { + return false; + } + } + else { + assert(false && "do better type checking"); + return false; + } + } + else if (auto RationalWordVal = llvm::dyn_cast(Val.get())) { + if (auto OtherRationalWordVal = llvm::dyn_cast(OtherVal.get())) { + if (*RationalWordVal == *OtherRationalWordVal) { + return false; + } + } + else { + assert(false && "do better type checking"); + return false; + } + } + else if (auto ValueListVal = llvm::dyn_cast(Val.get())) { + if (auto OtherValueListVal = llvm::dyn_cast(OtherVal.get())) { + if (*ValueListVal == *OtherValueListVal) { + return false; + } + } + else { + assert(false && "do better type checking"); + return false; + } + } + } + + return true; +} + +const FiniteWord ValueList::equal(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto First = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + if (*Iter != First) { + return valuelist::ZERO_1BIT; + } + } + return valuelist::ONE_1BIT; +} + +const FiniteWord ValueList::notequal(std::vector Vals) { + assert(Vals.size() > 1 && "Vals does not contain more than one element"); + auto Iter = Vals.begin(); + auto First = *Iter; + Iter++; + for (; Iter != Vals.end(); ++Iter) { + // maintain intuitive order + if (*Iter == First) { + return valuelist::ZERO_1BIT; + } + } + return valuelist::ONE_1BIT; +} + +const std::shared_ptr ValueList::concatenate() const { + if (Vals.size() == 0) { + return LogError("Cannot concatenate 0 elements"); + } + auto AccumulateFinite = std::make_shared(); + auto Iter = Vals.begin(); + for (; Iter != --Vals.end(); ++Iter) { + auto V = *Iter; + if (auto FiniteWordIter = llvm::dyn_cast(V.get())) { + AccumulateFinite = std::make_shared(FiniteWordIter->concatenate(*AccumulateFinite)); + } + else { + return LogError("Expected FiniteWord: " + V->string()); + } + } + + auto Last = *Iter; + if (auto FiniteWordLast = llvm::dyn_cast(Last.get())) { + return std::make_shared(FiniteWordLast->concatenate(*AccumulateFinite)); + } + else if (auto RationalWordLast = llvm::dyn_cast(Last.get())) { + return std::make_shared(RationalWordLast->concatenate(*AccumulateFinite)); + } + else { + return LogError("Expected FiniteWord or RationalWord: " + Last->string()); + } +} diff --git a/src/tuppence/ValueList.h b/src/tuppence/ValueList.h new file mode 100644 index 0000000..3157fb9 --- /dev/null +++ b/src/tuppence/ValueList.h @@ -0,0 +1,67 @@ +//===------ ValueList.h - ValueList class definition ----------------------===// +// +// The Tuppence Programming Language +// +// This file is distributed under the MIT Open Source License. +// See LICENSE for details. +// +//===----------------------------------------------------------------------===// + +#ifndef TUPPENCE_VALUELIST_H +#define TUPPENCE_VALUELIST_H + +#include "FiniteWord.h" +#include "Value.h" + +#include + +namespace tuppence { + + class ValueList : public Value { + const std::vector> Vals; + + public: + ValueList(std::vector> Vals) : + Value(VK_ValueList), Vals(Vals) {} + + const std::string string() const override; + + typedef std::vector>::const_iterator const_iterator; + + const_iterator begin() const { return Vals.begin(); } + const_iterator end() const { return Vals.end(); } + + const std::shared_ptr operator[](size_t i) const { + return Vals[i]; + } + + const size_t size() const { + return Vals.size(); + } + + // + // structural operations + // + + /// This list only contains comparable elements + const bool isComparable() const; + + const bool operator==(ValueList) const; + const bool operator!=(ValueList) const; + + static const FiniteWord equal(std::vector); + static const FiniteWord notequal(std::vector); + + const std::shared_ptr concatenate() const; + + // + // LLVM RTTI + // + + static bool classof(const Value *S) { + return S->getKind() == VK_ValueList; + } + }; +} + +#endif