From 592dc8aaefa075ebf9ca48785b4acaf98307fe7a Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 30 Aug 2008 15:36:44 -0400 Subject: [PATCH] Initial commit --- CMakeLists.txt | 37 + license.txt | 10 + readme.txt | 24 + relnotes.txt | 53 ++ runSelfTests.sh | 6 + samples/counter.mno | 10 + samples/counter2.mno | 18 + samples/echo.mno | 17 + samples/hello.mno | 4 + samples/hello_isolated.mno | 11 + samples/large_proto.mno | 37 + samples/object_method.mno | 11 + samples/pass_array.mno | 16 + samples/pass_array2.mno | 22 + samples/pass_array3.mno | 30 + samples/pass_string.mno | 13 + samples/threadring.mno | 33 + samples/threadring2.mno | 33 + src/aquarium/aquarium.cpp | 1014 +++++++++++++++++++++++++ src/aquarium/aquarium.hpp | 248 +++++++ src/aquarium/simplearray.c | 94 +++ src/aquarium/simplearray.h | 23 + src/minnow/analyser.cpp | 712 ++++++++++++++++++ src/minnow/analyser.hpp | 55 ++ src/minnow/codegen_cppoutput.cpp | 1519 ++++++++++++++++++++++++++++++++++++++ src/minnow/codegen_cppoutput.hpp | 489 ++++++++++++ src/minnow/lexer.cpp | 209 ++++++ src/minnow/lexer.hpp | 73 ++ src/minnow/main.cpp | 167 +++++ src/minnow/parser.cpp | 788 ++++++++++++++++++++ src/minnow/parser.hpp | 365 +++++++++ 31 files changed, 6141 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 license.txt create mode 100644 readme.txt create mode 100644 relnotes.txt create mode 100755 runSelfTests.sh create mode 100644 samples/counter.mno create mode 100644 samples/counter2.mno create mode 100644 samples/echo.mno create mode 100644 samples/hello.mno create mode 100644 samples/hello_isolated.mno create mode 100644 samples/large_proto.mno create mode 100644 samples/object_method.mno create mode 100644 samples/pass_array.mno create mode 100644 samples/pass_array2.mno create mode 100644 samples/pass_array3.mno create mode 100644 samples/pass_string.mno create mode 100644 samples/threadring.mno create mode 100644 samples/threadring2.mno create mode 100644 src/aquarium/aquarium.cpp create mode 100644 src/aquarium/aquarium.hpp create mode 100644 src/aquarium/simplearray.c create mode 100644 src/aquarium/simplearray.h create mode 100644 src/minnow/analyser.cpp create mode 100644 src/minnow/analyser.hpp create mode 100644 src/minnow/codegen_cppoutput.cpp create mode 100644 src/minnow/codegen_cppoutput.hpp create mode 100644 src/minnow/lexer.cpp create mode 100644 src/minnow/lexer.hpp create mode 100644 src/minnow/main.cpp create mode 100644 src/minnow/parser.cpp create mode 100644 src/minnow/parser.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7ccdc3d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 2.6) + +SET(CMAKE_BUILD_TYPE gdb) +SET(CMAKE_CXX_FLAGS_GDB " -Wall -ggdb -O3") +SET(CMAKE_C_FLAGS_GDB " -Wall -ggdb -O3") + +PROJECT (minnow) + +INCLUDE (FindDoxygen) + +SET(Boost_USE_STATIC_LIBS ON) +SET(Boost_USE_MULTITHREAD ON) + +#For MinGW+Boost install +IF (WIN32) + SET(BOOST_ROOT c:/MinGW) + SET(BOOST_INCLUDEDIR c:/MinGW/include) + SET(BOOST_LIBRARYDIR c:/MinGW/lib) +ENDIF (WIN32) + +FIND_PACKAGE(Boost 1.35.0 REQUIRED COMPONENTS thread program_options ) + +IF (DOXYGEN_FOUND) + MESSAGE(STATUS "Running doxygen...") + EXECUTE_PROCESS(COMMAND ${DOXYGEN_EXECUTABLE} OUTPUT_QUIET) +ENDIF (DOXYGEN_FOUND) + +INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS} src/aquarium src/minnow) +SET (AQUARIUM_SRCS src/aquarium/aquarium.cpp) +SET (MINNOW_SRCS src/minnow/main.cpp src/minnow/parser.cpp src/minnow/codegen_cppoutput.cpp src/minnow/lexer.cpp) + +ADD_LIBRARY (aquarium SHARED ${AQUARIUM_SRCS}) +TARGET_LINK_LIBRARIES (aquarium debug ${Boost_LIBRARIES} optimized ${Boost_LIBRARIES}) + +ADD_EXECUTABLE (minnow ${MINNOW_SRCS}) +TARGET_LINK_LIBRARIES (minnow debug ${Boost_LIBRARIES} optimized ${Boost_LIBRARIES}) + diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..10bb8ea --- /dev/null +++ b/license.txt @@ -0,0 +1,10 @@ +Copyright (c) 2008, Jonathan Turner +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Jonathan Turner nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..58f5a8d --- /dev/null +++ b/readme.txt @@ -0,0 +1,24 @@ +Minnow Programming Language +Pre-release 0.xx + +http://minnow-lang.org +http://code.google.com/p/minnow-language + +See license.txt for license details + +--- +REQUIREMENTS: + + * Boost 1.35 + +NOTES: + +Please note this is a pre-release, which currently means "expect few things to work". Minnow is under rapid development, so expect its state to change quickly from day to day until it comes to build a release for public consumption. + +The Minnow programming language is split into two parts: + + * 'minnow': a Minnow-to-C++ translator that outputs code which links to aquarium + * 'aquarium': a library that handles the nuts and bolts of messaging, creating, rebalancing, isolating, and destroying actors + +The translator can attempt to use a C++ compiler on your system, if it's available. To change the commandline it uses see src/minnow/main.cpp + diff --git a/relnotes.txt b/relnotes.txt new file mode 100644 index 0000000..abea81f --- /dev/null +++ b/relnotes.txt @@ -0,0 +1,53 @@ +Minnow 1.0 alpha 1 - Release Notes +(see http://www.minnow-lang.org for more information) + +[Features] + + * Efficient, lightweight actors + * Efficient, lightweight message passing + * Multicore aware runtime system with active workload rebalance + * Objects, with methods and attributes + * Simple arrays + * Basic foreign function interface via the "extern" keyword + * If/Else blocks + * While blocks + +[Known issues] + + * Embedded function calls like "y = f(g(x))" may not work correctly under some circumstances, where the timeslice is given up in between the calls to g(x) and f(x). As a workaround, do not embed function calls in this alpha. + * Scope dereference via the '.' operator only works on variables, not more complex items like array dereferences and function return values. As a workaround, store the reference in a temporary, and access it that way. + * Multidimensional/embedded arrays are not supported + * Function/action/method overloading is not yet supported + +Note to Windows Users: You may need to use the compile line on line 135 of src/minnow/main.cpp, changing it to meet what your system needs. + +Please Note: The Minnow programming language, as presented in alpha 1, is subject to change. Though I don't think it will change much (beyond some syntax additions) between now and the final 1.0 release, it's worth keeping an eye on the website for notices. + +It's also worth noting that this is an alpha, so it will probably have strange issues that still need to get worked out. If you find one, let us know. + +[Requirements] + + * gcc/g++ (or something equivalent) + * boost 1.35 (or greater) + +[Usage] + +Compile the "minnow" executable from the root directory using the commands: + + * cmake -DCMAKE_BUILD_TYPE=gdb . + * make + +This will build the systems with -O3, while still keeping gdb information. Once compiled, you can compile one of the samples from the root directory by running: + +In Linux and OS X + + * ./minnow samples/hello.mno -o samples/hello.bin + +In Windows: + + * minnow.exe samples/hello.mno -o samples/hello.exe + +The "-o" option will convert the minnow source to C++ and then use one of the lines in main.cpp to try to compile it to an executable. If the compile doesn't work for you, try changing the line to better match your setup. If that still doesn't seem to fix it, you can work on the C++ output directly by creating it instead of an executable using the "-c" option instead of the "-o" option: + + * ./minnow samples/hello.mno -c samples/hello.cpp + diff --git a/runSelfTests.sh b/runSelfTests.sh new file mode 100755 index 0000000..492384b --- /dev/null +++ b/runSelfTests.sh @@ -0,0 +1,6 @@ +#!/bin/bash +for filename in $( ls samples/*.mno); do + output=${filename/.mno/.out} + echo "Compiling... $filename" + ./minnow $filename -o $output +done diff --git a/samples/counter.mno b/samples/counter.mno new file mode 100644 index 0000000..4929855 --- /dev/null +++ b/samples/counter.mno @@ -0,0 +1,10 @@ +action main(string[] args) + var int i = convertToInt(args[0]) + var int total = 0 + while (i > 0) + total = total + 1 + i = i - 1 + end + puti(total) + exit(0) +end diff --git a/samples/counter2.mno b/samples/counter2.mno new file mode 100644 index 0000000..489ae2b --- /dev/null +++ b/samples/counter2.mno @@ -0,0 +1,18 @@ +def int counter(int i) + var int total = 0 + while (i > 0) + total = total + 1 + i = i - 1 + end + return(total) +end + +action main(string[] args) + var int i = convertToInt(args[0]) + var int total + + total = counter(i) + + puti(total) + exit(0) +end diff --git a/samples/echo.mno b/samples/echo.mno new file mode 100644 index 0000000..543243f --- /dev/null +++ b/samples/echo.mno @@ -0,0 +1,17 @@ +actor echo + action ping() + putstring("ponging this") + this::pong() + end + + action pong() + putstring("done") + exit(0) + end +end + +action main() + spawn echo e + putstring("pinging e") + e::ping() +end diff --git a/samples/hello.mno b/samples/hello.mno new file mode 100644 index 0000000..40c8b13 --- /dev/null +++ b/samples/hello.mno @@ -0,0 +1,4 @@ +action main() + putstring("Hello") + exit(0) +end diff --git a/samples/hello_isolated.mno b/samples/hello_isolated.mno new file mode 100644 index 0000000..343e1e9 --- /dev/null +++ b/samples/hello_isolated.mno @@ -0,0 +1,11 @@ +isolated actor isolated_actor + action greet() + putstring("Hello from an isolated actor") + exit(0) + end +end + +action main() + spawn isolated_actor iso + iso::greet() +end diff --git a/samples/large_proto.mno b/samples/large_proto.mno new file mode 100644 index 0000000..cdae034 --- /dev/null +++ b/samples/large_proto.mno @@ -0,0 +1,37 @@ +actor Sender + action setup(Biggy b) + var int i = 0 + while (i < 10000000) + i = i + 1 + end + + b::big(99, 100, 101, 102, 103, 104) + end +end + +actor Biggy + action setup() + var int i = 0 + while (i < 10000000) + i = i + 1 + end + end + + action big(int a, int b, int c, int d, int e, int f) + puti(a) + puti(b) + puti(c) + puti(d) + puti(e) + puti(f) + + exit(0) + end +end + +action main() + spawn Biggy b + spawn Sender s + b::setup() + s::setup(b) +end diff --git a/samples/object_method.mno b/samples/object_method.mno new file mode 100644 index 0000000..6461f2e --- /dev/null +++ b/samples/object_method.mno @@ -0,0 +1,11 @@ +class MethodHolder + def void greet() + putstring("Hello, from an object") + end +end + +action main() + new MethodHolder mh + mh.greet() + exit(0) +end diff --git a/samples/pass_array.mno b/samples/pass_array.mno new file mode 100644 index 0000000..8dc3018 --- /dev/null +++ b/samples/pass_array.mno @@ -0,0 +1,16 @@ +def int[] passarray(int[] input) + input[0] = input[0] + 3 + input[1] = input[1] + 3 + return(input) +end + +action main() + var int[] arr[2] + arr[0] = 3 + arr[1] = 4 + arr = passarray(arr) + puti(arr[0]) + puti(arr[1]) + + exit(0) +end diff --git a/samples/pass_array2.mno b/samples/pass_array2.mno new file mode 100644 index 0000000..6237806 --- /dev/null +++ b/samples/pass_array2.mno @@ -0,0 +1,22 @@ +actor Handler + action handle(int[] input) + input[0] = input[0] + 3 + input[1] = input[1] + 3 + + puti(input[0]) + puti(input[1]) + + exit(0) + end +end + +action main() + spawn Handler h + + var int[] arr[2] + arr[0] = 3 + arr[1] = 4 + + h::handle(arr) + +end diff --git a/samples/pass_array3.mno b/samples/pass_array3.mno new file mode 100644 index 0000000..8478a3c --- /dev/null +++ b/samples/pass_array3.mno @@ -0,0 +1,30 @@ +class Data + var int x +end + +actor Handler + action handle(Data[] input) + var Data d1 = input[0] + var Data d2 = input[1] + puti(d1.x) + puti(d2.x) + + exit(0) + end +end + +action main() + spawn Handler h + + var Data[] arr[2] + new Data d1 + new Data d2 + d1.x = 3 + d2.x = 4 + + arr[0] = d1 + arr[1] = d2 + + h::handle(arr) + +end diff --git a/samples/pass_string.mno b/samples/pass_string.mno new file mode 100644 index 0000000..e90298a --- /dev/null +++ b/samples/pass_string.mno @@ -0,0 +1,13 @@ +actor receiver + action get(string s) + putstring(s) + exit(0) + end +end + +action main() + spawn receiver r + var string s + s = "Hello, world" + r::get(s) +end diff --git a/samples/threadring.mno b/samples/threadring.mno new file mode 100644 index 0000000..347f009 --- /dev/null +++ b/samples/threadring.mno @@ -0,0 +1,33 @@ +actor Passer + var int m_id + var Passer m_next + + action pass(int token) + if (token == 0) + puti(m_id) + exit(0) + else + token = token - 1 + m_next::pass(token) + end + end + + action setIdAndNext(int id, Passer next) + m_id = id + m_next = next + end +end + +action main(string[] args) + spawn Passer[] passers[503] + var int i = 0 + while (i < 502) + passers[i]::setIdAndNext(i+1, passers[i+1]) + i = i + 1 + end + passers[502]::setIdAndNext(503, passers[0]) + + passers[0]::pass(convertToInt(args[0])) + done +end + diff --git a/samples/threadring2.mno b/samples/threadring2.mno new file mode 100644 index 0000000..2ff7044 --- /dev/null +++ b/samples/threadring2.mno @@ -0,0 +1,33 @@ +actor Passer + var int m_id + var Passer m_next + + action pass(int token) + if (token == 0) + puti(m_id) + exit(0) + else + token = token - 1 + m_next::pass(token) + end + end + + action setIdAndNext(int id, Passer next) + m_id = id + m_next = next + end +end + +action main(string[] args) + var int numPassers = 50300 + spawn Passer[] passers[numPassers] + var int i = 0 + while (i < numPassers-1) + passers[i]::setIdAndNext(i+1, passers[i+1]) + i = i + 1 + end + passers[numPassers-1]::setIdAndNext(numPassers, passers[0]) + + passers[0]::pass(convertToInt(args[0])) +end + diff --git a/src/aquarium/aquarium.cpp b/src/aquarium/aquarium.cpp new file mode 100644 index 0000000..505b56c --- /dev/null +++ b/src/aquarium/aquarium.cpp @@ -0,0 +1,1014 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "aquarium.hpp" + +Actor *Thread::ActorIfLocal(actorId_t actorId, Actor *local) { + Actor *foundActor = NULL; + + for (int i = 0; i < RECEIVER_CACHE_SIZE; ++i) { + if (local->receiverCache[i].containedId == actorId) { + if (local->receiverCache[i].actorPoolRevId == this->actorPoolRevId) { + return local->receiverCache[i].actor; + } + } + } + + __gnu_cxx::hash_map::iterator finder = actorIds.find(actorId); + if (finder != actorIds.end()) { + foundActor = finder->second; + int index = local->nextCacheIndex + 1; + if (index >= RECEIVER_CACHE_SIZE) { + index = 0; + } + local->receiverCache[index].actorPoolRevId = this->actorPoolRevId; + local->receiverCache[index].actor = foundActor; + local->receiverCache[index].containedId = actorId; + local->nextCacheIndex = index; + } + return foundActor; +} +/** + Thread scheduler uses this to send messages to itself +*/ + +void Thread::SendMessage(const Message &message) +{ + __gnu_cxx::hash_map::iterator finder = actorIds.find(message.recipient); + if (finder != actorIds.end()) { + Actor *foundActor = finder->second; + + //Receiving actors can be in one of two states to get a solution immediately. This allows quick turn around time + //in some cases even before the maintenance task has run. + if (message.messageType == MessageType::ACTION_MESSAGE) { + + if (foundActor->actorState == ActorState::WAITING_FOR_ACTION) { + foundActor->task = message.task; + for (int i=0; i < message.numArgs; ++i) { + foundActor->heapStack.push_back(message.arg[i]); + } + + foundActor->actorState = ActorState::ACTIVE; + this->hotActor = foundActor; + + if (foundActor->runQueueRevId != this->runQueueRevId) { + runningActors.push_back(foundActor); + foundActor->runQueueRevId = this->runQueueRevId; + } + } + else { + foundActor->actionMessages.push_back(message); + } + } + /* + else if ((foundActor->actorState == ActorState::WAITING_FOR_DATA) && (message.messageType == MessageType::DATA_MESSAGE)) { + __gnu_cxx::hash_map::iterator findHandler = foundActor->dataHandlers.find(message.dataTaskTypeId); + + if (findHandler != foundActor->dataHandlers.end()) { + //Set up our CPS to resume into a data receive + TypeUnion tu1; + tu1.UInt32 = findHandler->second; + for (int i=0; i < message.numArgs; ++i) { + foundActor->heapStack.push_back(message.arg[i]); + } + foundActor->heapStack.push_back(tu1); + foundActor->actorState = ActorState::ACTIVE; + foundActor->isResuming = true; + + this->hotActor = foundActor; + + //if (foundActor->runQueueRevId != this->runQueueRevId) { + runningActors.push_back(foundActor); + //foundActor->runQueueRevId = this->runQueueRevId; + //} + } + else { + localMail.push_back(message); + } + + } + */ + else { + localMail.push_back(message); + //foundActor->actionMessages.push_back(message); + } + } + else { + //we don't have this person, so send the message out to a mailman + //printf("<%i", message.recipient ); fflush(stdout); + outgoingChannel->sendMessage(message); + } + //printf("[%i]", message.recipient ); fflush(stdout); +} + +/** + Thread schedule uses this to check queued messages to see if any are ready to be consumed. +*/ + +void Thread::ReceiveMessages() { + + int pos = 0; + int end = localMail.size(); + + while (pos < end) { + Message &message = localMail[pos]; + + if (message.messageType == MessageType::ACTION_MESSAGE) { + __gnu_cxx::hash_map::iterator finder = actorIds.find(message.recipient); + //Check to see if the recipient is local + if (finder != actorIds.end()) { + Actor *foundActor = finder->second; + + if (foundActor->actorState == ActorState::WAITING_FOR_ACTION) { + foundActor->task = message.task; + //BOOST_ASSERT(message.numArgs < 5); + if (message.numArgs > 4) { + std::vector *vtu = (std::vector *)(message.arg[0].VoidPtr); + for (std::vector::iterator vtu_iter = vtu->begin(), vtu_end = vtu->end(); vtu_iter != vtu_end; ++vtu_iter) { + foundActor->heapStack.push_back(*vtu_iter); + } + delete vtu; + } + else { + for (int i=0; i < message.numArgs; ++i) { + foundActor->heapStack.push_back(message.arg[i]); + } + } + foundActor->actorState = ActorState::ACTIVE; + if (foundActor->runQueueRevId != this->runQueueRevId) { + runningActors.push_back(foundActor); + foundActor->runQueueRevId = this->runQueueRevId; + } + } + else { + //If the actor isn't waiting for action, re-add them to the queue (the mail queue works similarly to + //the running actor process queue. Only the current batch is live at any moment, each cycle clears + //the old messages in one go (which is more efficient than picking them out one by one) + //localMail.push_back(message); + foundActor->actionMessages.push_back(message); + } + + } + else { + //we don't have this person, so send the message out to a mailman + //printf(">%i", message.recipient ); fflush(stdout); + outgoingChannel->sendMessage(message); + } + } + /* + else if (message.messageType == MessageType::DATA_MESSAGE) { + __gnu_cxx::hash_map::iterator finder = actorIds.find(message.recipient); + if (finder != actorIds.end()) { + Actor *foundActor = finder->second; + __gnu_cxx::hash_map::iterator findHandler = foundActor->dataHandlers.find(message.dataTaskTypeId); + + if (findHandler != foundActor->dataHandlers.end()) { + + if (foundActor->actorState == ActorState::WAITING_FOR_DATA) { + //Set up our CPS to resume into a data receive + TypeUnion tu1; + tu1.UInt32 = findHandler->second; + if (message.numArgs > 4) { + std::vector *vtu = (std::vector *)message.arg[0].VoidPtr; + for (std::vector::iterator vtu_iter = vtu->begin(), vtu_end = vtu->end(); vtu_iter != vtu_end; ++vtu_iter) { + foundActor->heapStack.push_back(*vtu_iter); + } + delete vtu; + } + else { + for (int i=0; i < message.numArgs; ++i) { + foundActor->heapStack.push_back(message.arg[i]); + } + } + foundActor->heapStack.push_back(tu1); + foundActor->actorState = ActorState::ACTIVE; + foundActor->isResuming = true; + + //if this activates the recipient, put it on the active queue + //if (foundActor->runQueueRevId != this->runQueueRevId) { + runningActors.push_back(foundActor); + //foundActor->runQueueRevId = this->runQueueRevId; + //} + } + else { + localMail.push_back(message); + } + } + else { + localMail.push_back(message); + } + } + else { + //we don't have this person, so send the message out to a mailman + outgoingChannel->sendMessage(message); + } + } + */ + else { + //The only type of mail that should get into localMail are mail messages themselves. Messages request + //a service are handled by the maintenance actor + std::cout << "ERROR: Unknown message type" << std::endl; + localMail.push_back(message); + } + ++pos; + } + + //Clean out all messages we've looked through this pass (ones that were rescheduled live further in) + if (end != 0) { + localMail.erase(localMail.begin(), localMail.begin() + end); + } +} + +/** + Schedules an already existing actor onto the current thread; +*/ +void Thread::ScheduleExistingActor(Actor *actor) { + actor->parentThread = this; + + //This is our phonebook for the actors on this thread + actorIds[actor->actorId] = actor; + + if (actor->actorState == ActorState::ACTIVE) + { + runningActors.push_back(actor); + } + + //clear out this actor's receiver cache, as it's no longer valid + for (int i = 0; i < RECEIVER_CACHE_SIZE; ++i) { + actor->receiverCache[i].containedId = -1; + } + actor->nextCacheIndex = 0; + + actor->runQueueRevId = 0; + //actor->timesActive = 0; + + Message message; + message.messageType = MessageType::ADD_ACTOR_ID; + message.arg[0].UInt32 = threadId; + message.arg[1].UInt32 = actor->actorId; + message.numArgs = 2; + outgoingChannel->sendMessage(message); +} + +/** + Schedules a new actor on the current thread +*/ +void Thread::ScheduleNewActor(Actor *actor) { + actor->actorId = nextActorId; + actor->actorState = ActorState::WAITING_FOR_ACTION; + actor->actorStateBeforeMove = ActorState::WAITING_FOR_ACTION; + + ScheduleExistingActor(actor); + + ++nextActorId; + if ((nextActorId & 0xffffff) == 0xffffff) { + //we have rolled over, just quit with an error for now + std::cout << "Exceeded allowed number of actors on scheduler " << this->threadId << std::endl; + std::cout << "This is currently a fatal error, and we must exit." << std::endl; + exit(1); + } +} + +void Thread::ScheduleNewIsolatedActor(Actor *actor) { + //std::cout << "New Isolated: " << nextActorId << std::endl; + actor->actorId = nextActorId; + actor->actorState = ActorState::WAITING_FOR_ACTION; + actor->actorStateBeforeMove = ActorState::WAITING_FOR_ACTION; + + ++nextActorId; + if ((nextActorId & 0xffffff) == 0xffffff) { + //we have rolled over, just quit with an error for now + std::cout << "Exceeded allowed number of actors on scheduler " << this->threadId << std::endl; + std::cout << "This is currently a fatal error, and we must exit." << std::endl; + exit(1); + } + + Message message; + message.messageType = MessageType::CREATE_ISOLATED_ACTOR; + message.arg[0].VoidPtr = (void *)actor; + message.numArgs = 1; + this->outgoingChannel->sendMessage(message); +} + +/** + Removes an actor from the scheduler and passes a message up the chain to remove him from the phonebooks. + This version does not free the memory the actor was using, for that use 'DeleteActor' instead +*/ +void Thread::RemoveActor(Actor *actor) { + RemoveActor(actor, true); +} + +void Thread::RemoveActors(std::vector *actors) { + std::vector *toBeDeleted = new std::vector(); + + for (std::vector::iterator iter=actors->begin(), end=actors->end(); iter!=end; ++iter) { + RemoveActor(*iter, false); + toBeDeleted->push_back((*iter)->actorId); + } + + Message message; + message.messageType = MessageType::DELETE_ACTOR_IDS; + message.arg[0].UInt32 = threadId; + message.arg[1].VoidPtr = toBeDeleted; + message.numArgs = 2; + outgoingChannel->sendMessage(message); +} + +void Thread::RemoveActor(Actor *actor, bool sendDeleteMsg) { + __gnu_cxx::hash_map::iterator iter = actorIds.find(actor->actorId); + std::vector::iterator iterActor; + + ++this->actorPoolRevId; + if (this->actorPoolRevId == 0) { + //if we have gone so long that we roll over, then quickly invalidate our local actors' receiver caches so they don't get false positives + for (__gnu_cxx::hash_map::iterator resetIter = actorIds.begin(), resetEnd = actorIds.end(); resetIter != resetEnd; ++resetIter) { + for (int i = 0; i < RECEIVER_CACHE_SIZE; ++i) { + resetIter->second->receiverCache[i].containedId = -1; + } + } + } + + if (iter != actorIds.end()) { + actorIds.erase(iter); + + if (sendDeleteMsg) { + Message message; + message.messageType = MessageType::DELETE_ACTOR_ID; + message.arg[0].UInt32 = threadId; + message.arg[1].UInt32 = actor->actorId; + message.numArgs = 2; + outgoingChannel->sendMessage(message); + } + unsigned int j = 0; + while (j < runningActors.size()) { + if (runningActors[j]->actorId == actor->actorId) + runningActors.erase(runningActors.begin() + j); + else + ++j; + } + } + else { + std::cout << "Trying to remove " << actor->actorId << " from " << this->threadId << " but can't find it." << std::endl; + } +} + +/** + Remove the actor and completely delete it (for actors that were killed) +*/ +void Thread::DeleteActor(Actor *actor) { + + RemoveActor(actor); + delete actor; +} + +/** + Schedules an already existing actor onto the current thread; +*/ +void Thread::SendStatus() { + Message message; + message.messageType = MessageType::LOAD_STATUS; + message.arg[0].UInt32 = this->threadId; + message.arg[1].Int32 = this->numberActiveActors; + + message.numArgs = 2; + + outgoingChannel->sendMessage(message); +} + +/** + Moves the actor which has been running the longest to another scheduler. + Note: Very likely better solutions exist, ones that could reschedule clumps of actors that have been working together, + but for now this will do. +*/ + +//JDT: REFACTOR - breaking rebalance until we put in locality calculator +void Thread::MoveHeaviestActor(threadId_t destination, uint32_t count) { + //std::vector::iterator iter, chosen; + //bool foundOne; + std::vector *group; + if (count > 1) { + group = new std::vector(); + } + + //std::cout << "Moving " << count << std::endl; + //for (int k = 0; k < count; ++k) { + //for (iter = runningActors.begin(); iter != runningActors.end(); ++iter) { + unsigned int i = 0; + while (i < runningActors.size()) { + if ( (runningActors[i]->actorState == ActorState::ACTIVE) && (runningActors[i]->actorId != 0xffffffff)) { + + //std::cout << "Found one, moving it" << std::endl; + Actor *chosenActor = runningActors[i]; + chosenActor->actorStateBeforeMove = chosenActor->actorState; + + chosenActor->actorState = ActorState::MOVED; + chosenActor->parentThread = NULL; + + + if (count == 1) { + //std::cout << "$$ " << count << " $$" << std::endl; + RemoveActor(chosenActor); + + Message message; + message.messageType = MessageType::PLEASE_RECV_ACTOR; + message.arg[0].UInt32 = destination; + message.arg[1].VoidPtr = (void *)chosenActor; + + message.numArgs = 2; + + outgoingChannel->sendMessage(message); + break; + + } + else { + group->push_back(chosenActor); + if (group->size() == count) { + break; + } + } + + } + else { + ++i; + } + } + + if (count > 1) { + RemoveActors(group); + + //std::cout << "@@ " << count << " " << group->size() << " @@" << std::endl; + Message message; + message.messageType = MessageType::PLEASE_RECV_ACTORS; + message.arg[0].UInt32 = destination; + message.arg[1].VoidPtr = (void *)group; + + message.numArgs = 2; + + outgoingChannel->sendMessage(message); + } + +} + +/** + The rebalancing algorithm the kernel uses to make sure the actors are equitably distributed across scheduler threads +*/ +void Thread::TaskRebalance() { + __gnu_cxx::hash_map::const_iterator iter; + threadId_t lightest, heaviest; + int lightest_weight, heaviest_weight; + + lightest = 0; + heaviest = 0; + lightest_weight = 99999999; + heaviest_weight = 0; + + //The rebalance works by looking through the list of schedulers and their number of active actors, and if there is a + //disparatity greater than a threshhold (currently 2), it asks the more active scheduler to pass one of its actors + //to the lesser active scheduler. + + iter = scheduleWeights->begin(); + while (iter != scheduleWeights->end()) { + if (iter->second < lightest_weight) { + lightest = iter->first; + lightest_weight = iter->second; + } + if (iter->second > heaviest_weight) { + heaviest = iter->first; + heaviest_weight = iter->second; + } + ++iter; + } + if ((lightest != heaviest) && (lightest != 0) && (heaviest != 0)) { + if ((heaviest_weight - lightest_weight) > 1 + (heaviest_weight / 10)) { + //uint32_t amount = (heaviest_weight - lightest_weight) / 2; //(50 + heaviest_weight - lightest_weight) / 50; + uint32_t amount = (50 + heaviest_weight - lightest_weight) / 50; + //std::cout << "Moving: " << amount << " from " << heaviest << " to " << lightest << std::endl; + + //if (amount > 500) + // amount = 500; + + Message message; + message.messageType = MessageType::PLEASE_MOVE_ACTOR; + message.arg[0].UInt32 = lightest; + message.arg[1].UInt32 = amount; + message.numArgs = 2; + (*mailChannelOutgoing)[heaviest]->sendMessage(message); + iter = scheduleWeights->begin(); + /* + while (iter != scheduleWeights->end()) { + //std::cout << iter->first << ":" << iter->second << " "; + ++iter; + } + */ + //std::cout << std::endl; + } + } +} + +/** + The mail check loop that mailmen actors use. +*/ +void Thread::MailCheck() { + int pos = 0; + int end = mailListIncoming->size(); + + //FIXME: There has to be a nicer way to keep track of lost messages + std::vector lostMessages; + + while (pos < end) { + MailChannel *mChannel = (*mailListIncoming)[pos]; + + //For each channel, check mail. + if (mChannel->empty() == false) { + + std::vector *incomingRemoteMail = mChannel->recvMessages(); + std::vector::iterator inMailIter = incomingRemoteMail->begin(); + + while (inMailIter != incomingRemoteMail->end()) { + Message message = *inMailIter; + + if ((message.messageType == MessageType::ACTION_MESSAGE)||(message.messageType == MessageType::DATA_MESSAGE)) { + //find recipient + __gnu_cxx::hash_map::iterator finder = mailAddresses->find(message.recipient); + //we found who it goes to + if (finder != mailAddresses->end()) { + + //if the actor isn't in transition/limbo, we deliver + if (finder->second != 0) { + __gnu_cxx::hash_map *channelMap = mailChannelOutgoing; + MailChannel *outgoing = (*channelMap)[finder->second]; + outgoing->sendMessage(message); + } + else { + std::cout << "Actor is in LIMBO" << std::endl; + } + } + + else { + //can't find anyone, so send the message to myself + lostMessages.push_back(message); + } + + } + else if (message.messageType == MessageType::ADD_ACTOR_ID) { + threadId_t threadId = message.arg[0].UInt32; + actorId_t actorId = message.arg[1].UInt32; + __gnu_cxx::hash_map *mailMap = mailAddresses; + (*mailMap)[actorId] = threadId; + } + else if (message.messageType == MessageType::CREATE_ISOLATED_ACTOR) { + //FIXME: This assumes receiver is a KERNEL but doesn't pass it along if it's a MAILMAN + + //actorId_t actorId = message.arg[0].UInt32; + if (threadType == ThreadType::KERNEL) { + bool found = false; + + Message messageIso; + messageIso.messageType = MessageType::PLEASE_RECV_ACTOR; + //message.arg[0] gets filled in below + messageIso.arg[1] = message.arg[0]; //Pass the isolated actor over to the receive actor message + + std::vector::iterator poolIter = threadPoolThreads->begin(); + while ( (found == false) && (poolIter != threadPoolThreads->end()) ) { + //std::cout << "Inside search loop..." << std::endl; + if (poolIter->available == true) { + poolIter->available = false; + found = true; + messageIso.arg[0].UInt32 = poolIter->threadId; + //std::cout << "Found: " << poolIter->threadId << std::endl; + } + ++poolIter; + } + + if (found == false) { + ThreadPoolThread tpt; + //std::cout << "Not found, creating one... " << actor->mailChannelOutgoing->size() + 1 << std::endl; + + tpt.threadId = mailChannelOutgoing->size() + 1;// + actor->threadPoolThreads->size(); + + Thread *newThread = new Thread(tpt.threadId, (tpt.threadId-1) * 0x1000000); + + //By not sending status, we'll take ourselves out of the rebalancer, which we need to do if we're in the thread pool for isolated actors. + newThread->sendStatus = false; + //std::cout << "Thread ID..." << newThread->threadId << " and " << tpt.threadId << std::endl; + + tpt.thread = new boost::thread(boost::bind(&Thread::SchedulerLoop, newThread)); + tpt.available = false; //we're about to schedule something, so it's not available for new actors yet + + messageIso.arg[0].UInt32 = newThread->threadId; + mailListIncoming->push_back(newThread->outgoingChannel); + + (*mailChannelOutgoing)[newThread->threadId] = newThread->incomingChannel; + + threadPoolThreads->push_back(tpt); + } + + (*mailChannelOutgoing)[messageIso.arg[0].UInt32]->sendMessage(messageIso); + + } + } + else if (message.messageType == MessageType::DELETE_ACTOR_ID) { + //FIXME: When we move to a mailmen+kernel model instead of just a kernel model, we need to pass up the deleted actor + //threadId_t threadId = message.arg[0].UInt32; + + actorId_t actorId = message.arg[1].UInt32; + + __gnu_cxx::hash_map::iterator finder = mailAddresses->find(actorId); + if (finder != mailAddresses->end()) { + threadId_t threadId = finder->second; + __gnu_cxx::hash_map *mailMap = mailAddresses; + mailMap->erase(actorId); + for (unsigned int k = 0; k < threadPoolThreads->size(); ++k) { + if ((*threadPoolThreads)[k].threadId == threadId) { + (*threadPoolThreads)[k].available = true; + } + } + } + } + else if (message.messageType == MessageType::DELETE_ACTOR_IDS) { + //FIXME: When we move to a mailmen+kernel model instead of just a kernel model, we need to pass up the deleted actor + //threadId_t threadId = message.arg[0].UInt32; + std::vector *actorIds = (std::vector *)(message.arg[1].VoidPtr); + //std::cout << "Kernel: deleting " << actorIds->size() << " actors" << std::endl; + for (std::vector::iterator actorIter = actorIds->begin(), actorEnd = actorIds->end(); actorIter != actorEnd; ++actorIter) { + __gnu_cxx::hash_map::iterator finder = mailAddresses->find(*actorIter); + if (finder != mailAddresses->end()) { + threadId_t threadId = finder->second; + __gnu_cxx::hash_map *mailMap = mailAddresses; + mailMap->erase(*actorIter); + for (unsigned int k = 0; k < threadPoolThreads->size(); ++k) { + if ((*threadPoolThreads)[k].threadId == threadId) { + (*threadPoolThreads)[k].available = true; + } + } + } + } + delete actorIds; + } + else if (message.messageType == MessageType::LOAD_STATUS) { + threadId_t threadId = message.arg[0].UInt32; + int32_t activeActors = message.arg[1].Int32; + + std::cout << "Status: " << threadId << ": " << activeActors << std::endl; + (*(scheduleWeights))[threadId] = activeActors; + TaskRebalance(); + } + else { + threadId_t threadId = message.arg[0].UInt32; + (*(mailChannelOutgoing))[threadId]->sendMessage(message); + } + ++inMailIter; + } + + incomingRemoteMail->clear(); + + //Return to sender the messages that I don't have an address for + std::vector::iterator it = lostMessages.begin(); + while (it != lostMessages.end()) { + mChannel->sendMessage(*it); + ++it; + } + lostMessages.clear(); + } + ++pos; + } + +} + +/** + The main scheduler loop for the thread. Runs through each actor and runs the ready ones. + Also checks for queued messages, activating them as necessary. +*/ + +void Thread::SchedulerLoop() { + int pos; + const bool sendStatusUpdate = this->sendStatus; + std::vector *vtu; + + std::vector deletedActors; + int slicePos; + + this->previousActiveActors = -1; + this->numberActiveActors = 0; + + pos = 0; + Actor *thisActor = NULL; + int sliceMultiplier = 1; + + while (this->isRunning) { + if (this->runningActors[pos]->actorState == ActorState::ACTIVE) { + + thisActor = this->runningActors[pos]; + + switch (thisActor->actorId) { + case (0xffffffff) : + //MAINTENANCE LOOP + if (this->threadType == ThreadType::KERNEL) { + MailCheck(); + ++this->numberActiveActors; + } + if (this->numberActiveActors == 0) { + boost::xtime xt; + boost::xtime_get(&xt, boost::TIME_UTC); + xt.nsec += 1000000; + boost::thread::sleep(xt); + } + + + ++this->runQueueRevId; + if (this->runQueueRevId == 0) { + //if 0, we've rolled over, so reset everyone + for (__gnu_cxx::hash_map::iterator resetIter = this->actorIds.begin(), resetEnd = this->actorIds.end(); resetIter != resetEnd; ++resetIter) + { + (resetIter->second)->runQueueRevId = 0; + } + this->runQueueRevId = 1; + } + + + //Clean out old slots, using ourselves (the maintenance actor) as the marker. Actors that follow us were + //rescheduled (pushed) in this cycle so they are our active queue for the next cycle. + this->runningActors.erase(this->runningActors.begin(), this->runningActors.begin() + (1 + pos)); + + //Pull out any actors that were deleted during the last cycle + if (deletedActors.size() > 0) { + for (std::vector::iterator deletedIter = deletedActors.begin(), deletedEnd = deletedActors.end(); deletedIter != deletedEnd; ++deletedIter) + { + DeleteActor(*deletedIter); + } + deletedActors.clear(); + } + + //Sort through incoming system messages (which may also have mail) + if (this->incomingChannel->empty() == false) { + //std::cout << "Thread checking mail" << std::endl; + std::vector *incomingRemoteMail = this->incomingChannel->recvMessages(); + std::vector::iterator inMailIter = incomingRemoteMail->begin(); + + while (inMailIter != incomingRemoteMail->end()) { + Message message = *inMailIter; + + if ((message.messageType == MessageType::ACTION_MESSAGE)||(message.messageType == MessageType::DATA_MESSAGE)) { + this->localMail.push_back(message); + } + else if (message.messageType == MessageType::PLEASE_MOVE_ACTOR) { + threadId_t threadId = message.arg[0].UInt32; + uint32_t amount = message.arg[1].UInt32; + MoveHeaviestActor(threadId, amount); + } + else if (message.messageType == MessageType::PLEASE_RECV_ACTOR) { + Actor *newActor = (Actor *)(message.arg[1].VoidPtr); + newActor->actorState = newActor->actorStateBeforeMove; + ScheduleExistingActor(newActor); + } + else if (message.messageType == MessageType::PLEASE_RECV_ACTORS) { + std::vector *newActors = (std::vector *)(message.arg[1].VoidPtr); + //std::cout << "New message: " << newActors->size() << std::endl; + for(std::vector::iterator newIter = newActors->begin(), newEnd = newActors->end(); newIter != newEnd; ++newIter) { + //std::cout << "***MOVED***" << std::endl; + (*newIter)->actorState = (*newIter)->actorStateBeforeMove; + ScheduleExistingActor(*newIter); + } + delete newActors; + } + ++inMailIter; + } + incomingRemoteMail->clear(); + } + pos = 0; + ReceiveMessages(); + + //Send a status update to the kernel if we've changed number of active actors this cycle. + if (sendStatusUpdate) { + if (this->numberActiveActors != this->previousActiveActors) { + SendStatus(); + this->previousActiveActors = this->numberActiveActors; + } + + } + this->numberActiveActors = 0; + + break; + default: + this->timeSliceEndTime = TIMESLICE_QUANTUM; + if (thisActor->actionMessages.size() > TIMESLICE_QUANTUM) { + sliceMultiplier = thisActor->actionMessages.size() / TIMESLICE_QUANTUM; + this->timeSliceEndTime *= sliceMultiplier; + } + + this->hotActor = NULL; + + //Running the actor if it's active, as long as it is active and has slice remaining + + slicePos = -1; + while ((thisActor->actorState == ActorState::ACTIVE) && (this->timeSliceEndTime > 0)) { + //Run the actor's current task + thisActor->task(thisActor); + + switch(thisActor->actorState) { + case (ActorState::ACTIVE) : + thisActor->isResuming = true; + ++this->numberActiveActors; + //std::cout << "NAA: " << this->numberActiveActors << std::endl; + break; + case (ActorState::WAITING_FOR_ACTION) : + if (thisActor->actionMessages.size() > slicePos+1) { + ++slicePos; + Message message = thisActor->actionMessages[slicePos]; + thisActor->task = message.task; + + if (message.numArgs > 4) { + vtu = (std::vector *)(message.arg[0].VoidPtr); + for (std::vector::iterator vtu_iter = vtu->begin(), vtu_end = vtu->end(); vtu_iter != vtu_end; ++vtu_iter) { + thisActor->heapStack.push_back(*vtu_iter); + } + delete vtu; + } + else { + for (int i=0; i < message.numArgs; ++i) { + thisActor->heapStack.push_back(message.arg[i]); + } + } + + thisActor->actorState = ActorState::ACTIVE; + + if (this->timeSliceEndTime == 0) { + if (thisActor->runQueueRevId != this->runQueueRevId) { + thisActor->runQueueRevId = this->runQueueRevId; + this->runningActors.push_back(thisActor); + } + } + } + break; + case (ActorState::DELETED) : + deletedActors.push_back(thisActor); + break; + } + if ((thisActor->actorState != ActorState::ACTIVE) && (this->hotActor != NULL)) { + if (slicePos >= 0) { + thisActor->actionMessages.erase(thisActor->actionMessages.begin(), thisActor->actionMessages.begin() + slicePos + 1); + --slicePos; + } + thisActor = this->hotActor; + slicePos = -1; + } + } + + + if (slicePos >= 0) { + thisActor->actionMessages.erase(thisActor->actionMessages.begin(), thisActor->actionMessages.begin() + slicePos + 1); + --slicePos; + } + + + //In the case the actor is no longer active, it may have just sent a message. This will let another + //actor immediately go active (called here a 'hot actor'), allowing them to use the rest of the slice. + //This idea comes from "Improving IPC by Kernel Design" (l4ka.org/publications/1993/improving-ipc.pdf) + /* + if (thisActor->actorState != ActorState::ACTIVE) { + + if (this->hotActor != NULL) { + Actor *hotActor = this->hotActor; + while ((this->timeSliceEndTime > 0) && (this->hotActor != NULL)) { + hotActor = this->hotActor; + + this->hotActor = NULL; + hotActor->task(hotActor); + + //If it's still active afterward, it's an active actor, so count it + if (hotActor->actorState == ActorState::ACTIVE) { + hotActor->isResuming = true; + ++this->numberActiveActors; + //std::cout << "NAA: " << this->numberActiveActors << std::endl; + } + //If instead it's waiting for an action message, see if we have one to give it + else if (hotActor->actorState == ActorState::WAITING_FOR_ACTION) { + if (hotActor->actionMessages.size() > slicePos+1) { + ++slicePos; + Message message = hotActor->actionMessages[slicePos]; + hotActor->task = message.task; + //BOOST_ASSERT(message.numArgs <= 4); + + for (int i=0; i < message.numArgs; ++i) { + hotActor->heapStack.push_back(message.arg[i]); + } + hotActor->actorState = ActorState::ACTIVE; + + if (this->timeSliceEndTime == 0) { + if (hotActor->runQueueRevId != this->runQueueRevId) { + hotActor->runQueueRevId = this->runQueueRevId; + this->runningActors.push_back(hotActor); + } + } + } + } + else if (hotActor->actorState == ActorState::DELETED) { + deletedActors.push_back(hotActor); + } + } + + } + + //if (thisActor->actorState == ActorState::DELETED) { + // deletedActors.push_back(thisActor); + //} + + } + */ + + ++pos; + break; + + } + //If we're active, keep us in the running list + if ((thisActor->actorState == ActorState::ACTIVE) && (thisActor->runQueueRevId != this->runQueueRevId)) { + thisActor->runQueueRevId = this->runQueueRevId; + this->runningActors.push_back(thisActor); + } + } + else { + ++pos; //something that was active now isn't, so let's just skip over it. + } + } +} + +int VM_Main(int argc, char *argv[], void(*task)(Actor *), bool passCmdLine) { + //boost::xtime xt; + int NUM_SCHED_THREADS = boost::thread::hardware_concurrency(); + + Thread *thread[NUM_SCHED_THREADS]; + + //FIXME!!!!!! This assumes only a certain number of actors will every be created by any one scheduler, and this might be a bad assumption. + //Also, actor ids are not currently being recycled, but likely should be. + for (int i = 0; i < NUM_SCHED_THREADS; ++i) { + thread[i] = new Thread(i+1, i * 0x1000000); + } + + //Make the kernel thread the first scheduler thread + //It will do all normal scheduler duties and include kernel duties into them + thread[0]->threadType = ThreadType::KERNEL; + thread[0]->mailListIncoming = new std::vector(); + thread[0]->mailAddresses = new __gnu_cxx::hash_map(); + thread[0]->mailChannelOutgoing = new __gnu_cxx::hash_map(); + thread[0]->scheduleWeights = new __gnu_cxx::hash_map(); + thread[0]->threadPoolThreads = new std::vector(); + + //connect up channels + for (int i = 0; i < NUM_SCHED_THREADS; ++i) { + thread[0]->mailListIncoming->push_back(thread[i]->outgoingChannel); + (*(thread[0])->mailChannelOutgoing)[thread[i]->threadId] = thread[i]->incomingChannel; + } + + std::vector *commandLineArgs = new std::vector(); + //Grab commandline args + for (int i = 1; i < argc; ++i) { + commandLineArgs->push_back(argv[i]); + } + + //Schedule the main actor (like the main function in the program) + Actor *mainActor = new Actor(); + thread[0]->ScheduleNewActor(mainActor); + + if (passCmdLine) { + //And send it the first message + Message message; + message.recipient = 0; + message.numArgs = 1; + message.messageType = MessageType::ACTION_MESSAGE; + message.task = task; + message.arg[0].VoidPtr = commandLineArgs; + thread[0]->SendMessage(message); + } + else { + Message message; + message.recipient = 0; + message.numArgs = 0; + message.messageType = MessageType::ACTION_MESSAGE; + message.task = task; + thread[0]->SendMessage(message); + } + + boost::thread *bThread[NUM_SCHED_THREADS]; + for (int i = 1; i < NUM_SCHED_THREADS; ++i) { + bThread[i] = new boost::thread(boost::bind(&Thread::SchedulerLoop, thread[i])); + } + + thread[0]->SchedulerLoop(); + + //join the threads here. + for (int i = 1; i < NUM_SCHED_THREADS; ++i) { + bThread[i]->join(); + } + return 0; +} + diff --git a/src/aquarium/aquarium.hpp b/src/aquarium/aquarium.hpp new file mode 100644 index 0000000..055e18b --- /dev/null +++ b/src/aquarium/aquarium.hpp @@ -0,0 +1,248 @@ +#ifndef AQUARIUM_HPP +#define AQUARIUM_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "simplearray.h" + +//FIXME: I'm using the union in the code with UInt32, but in 64-bit machines that probably isn't best +#define actorId_t uint32_t +#define threadId_t uint32_t + +const int RECEIVER_CACHE_SIZE=3; +const unsigned int TIMESLICE_QUANTUM=2000; + +class ThreadType { public: enum Type { THREAD, MAILMAN, KERNEL}; }; +class ActorState { public: enum State { ACTIVE, WAITING_FOR_ACTION, WAITING_FOR_DATA, DELETED, MOVED }; }; +class MessageType { public: enum Type { ACTION_MESSAGE, DATA_MESSAGE, ADD_ACTOR_ID, MOVE_ACTOR_ID, DELETE_ACTOR_ID, DELETE_ACTOR_IDS, CREATE_ISOLATED_ACTOR, LOAD_STATUS, PLEASE_MOVE_ACTOR, PLEASE_RECV_ACTOR, PLEASE_RECV_ACTORS }; }; + +typedef struct Thread; +typedef struct Actor; + +/** + Hold a meta container for all basic types. + As a side note: All functions are re-entrant, using the actor->heapStack to store state, so they only need a pointer to that actor for all their activities. +*/ +union TypeUnion { + int8_t Int8; + uint8_t UInt8; + int16_t Int16; + uint16_t UInt16; + int32_t Int32; + uint32_t UInt32; + int64_t Int64; + uint64_t UInt64; + float Float; + double Double; + bool Bool; + void(*Function)(Actor *); + void *VoidPtr; +}; + +/** + Message container. For messages with longer number of arguments, create a vector and use VoidPtr in TypeUnion. +*/ +struct Message { + MessageType::Type messageType; + actorId_t recipient; + uint32_t dataTaskTypeId; + void(*task)(Actor *); + int numArgs; + TypeUnion arg[4]; +}; + +/** + Channel between threads, and between threads and the kernel. +*/ +class MailChannel { + std::vector *msgChannelIncoming, *msgChannel1, *msgChannel2; + volatile int currentChannel; + boost::mutex mailLock; + volatile bool isEmpty; + + /* + void DebugMessage(const Message &message) { + std::cout << " Message: " << message.messageType << " rec:" << message.recipient << " taskId:"; + std::cout << message.dataTaskTypeId << " nArgs:" << message.numArgs << " arg0:" << message.arg[0].UInt32 << " " << message.arg[1].UInt32 << " " << message.arg[2].UInt32 << std::endl; + } + */ + + public: + void sendMessage(const Message &message) { + boost::mutex::scoped_lock lock(mailLock); + msgChannelIncoming->push_back(message); + isEmpty = false; + } + + std::vector *recvMessages() { + boost::mutex::scoped_lock lock(mailLock); + + if (currentChannel == 1) { + msgChannelIncoming = msgChannel2; + currentChannel = 2; + isEmpty = msgChannelIncoming->empty(); + return msgChannel1; + } + else { + msgChannelIncoming = msgChannel1; + currentChannel = 1; + isEmpty = msgChannelIncoming->empty(); + return msgChannel2; + } + } + + bool empty() { + //boost::mutex::scoped_lock lock(mailLock); + return isEmpty; + } + + MailChannel() { + msgChannel1 = new std::vector(); + msgChannel2 = new std::vector(); + + msgChannelIncoming = msgChannel1; + currentChannel = 1; + isEmpty = true; + } +}; + +struct ThreadPoolThread { + bool available; + threadId_t threadId; + boost::thread *thread; +}; + +struct ActorWrapper { + struct Actor* actor; + actorId_t containedId; + uint32_t actorPoolRevId; +}; + +/** + The microthread task, which we call an actor. +*/ +struct Actor { + public: + ActorState::State actorState; + ActorState::State actorStateBeforeMove; + actorId_t actorId; + void(*task)(Actor *); + Thread *parentThread; + + struct ActorWrapper receiverCache[RECEIVER_CACHE_SIZE]; + int nextCacheIndex; + + uint32_t runQueueRevId; + + //incoming DATA_MESSAGE messages have a queue and handlers for each enumerated type. In the action, a switch statement allows for continuations + //based on the id of the continuation (basically which slot to jump back into when the function is restarted). + std::vector dataMessages; + __gnu_cxx::hash_map dataHandlers; + + //For CPS + bool isResuming; + + std::vector actionMessages; + std::vector heapStack; + //SimpleArray actionMessages; + //SimpleArray heapStack; + + Actor() : runQueueRevId(0), isResuming(false) {} +}; + + +/** + The actual OS-level thread. This will act as a scheduler for actors(microthreads). +*/ +class Thread { + private: + __gnu_cxx::hash_map actorIds; + std::vector localMail; + bool isRunning; + actorId_t nextActorId; + bool sendStatus; + + int32_t numberActiveActors; + int32_t previousActiveActors; + + public: + ThreadType::Type threadType; + std::vector runningActors; + threadId_t threadId; + MailChannel *incomingChannel; + MailChannel *outgoingChannel; + + //For mailmen + std::vector *mailListIncoming; + __gnu_cxx::hash_map *mailAddresses; + __gnu_cxx::hash_map *mailChannelOutgoing; + + //For the kernel + __gnu_cxx::hash_map *scheduleWeights; //I'm assuming int is big enough + std::vector *threadPoolThreads; + + //For local receiver caching, whenever our actor pool members change, we rev this so that local receiver caches can be recreated + uint32_t actorPoolRevId; + + //For more efficient run queue handling, this prevents actors from rescheduling themselves in loop scenerios + uint32_t runQueueRevId; + + //For scheduling tasks, we need to know how much time is remaining + int timeSliceEndTime; + Actor *hotActor; + + Thread(threadId_t id, actorId_t nextActor) { + threadType = ThreadType::THREAD; + isRunning = true; + sendStatus = true; + nextActorId = nextActor; + + threadId = id; + + actorPoolRevId = 0; + runQueueRevId = 1; //0 denotes "never scheduled", and is the reset value + + incomingChannel = new MailChannel(); + outgoingChannel = new MailChannel(); + + Actor *maintenance = new Actor(); + maintenance->actorId = 0xFFFFFFFF; + maintenance->actorState = ActorState::ACTIVE; + runningActors.push_back(maintenance); + } + + void SendMessage(const Message &message); + //JDT REFACTOR + void ReceiveMessages(); + void ScheduleExistingActor(Actor *actor); + void ScheduleNewActor(Actor *actor); + void ScheduleNewIsolatedActor(Actor *actor); + void RemoveRunningActor(Actor *actor); + void RemoveActor(Actor *actor); + void RemoveActors(std::vector *actors); + void RemoveActor(Actor *actor, bool sendDeleteMsg); + + void DeleteActor(Actor *actor); + void SendStatus(); + void MoveHeaviestActor(threadId_t threadId, uint32_t amount); + Actor* ActorIfLocal(actorId_t actorId, Actor *local); + void TaskRebalance(); + void MailCheck(); + void SchedulerLoop(); +}; + +//Main startup procedure +int VM_Main(int argc, char *argv[], void(*task)(Actor *), bool passCmdLine); + +#endif //AQUARIUM_HPP diff --git a/src/aquarium/simplearray.c b/src/aquarium/simplearray.c new file mode 100644 index 0000000..1b2924d --- /dev/null +++ b/src/aquarium/simplearray.c @@ -0,0 +1,94 @@ +#include +#include + +#include "simplearray.h" + +void initialize_simplearray(SimpleArray *container, unsigned int size) { + container->contents = (void*)malloc(DEFAULT_SIMPLEARRAY_SIZE * size); + container->bufferSize = DEFAULT_SIMPLEARRAY_SIZE; + container->currentSize = 0; + container->elemSize = size; +} + +void push_onto_simplearray(SimpleArray *container, void *value) { + if (container->currentSize >= container->bufferSize) { + container->bufferSize *= 2; + void* temp = (void*)realloc(container->contents, container->bufferSize * container->elemSize); + container->contents = temp; + } + + switch (container->elemSize) { + case (4) : ((int*)(container->contents))[container->currentSize] = *(int *)value; + break; + case (2) : ((short*)(container->contents))[container->currentSize] = *(short *)value; + break; + case (1) : ((char*)(container->contents))[container->currentSize] = *(char *)value; + break; + default: memcpy (container->contents + container->currentSize * container->elemSize, value, container->elemSize); + break; + } + ++(container->currentSize); +} + +void pop_off_simplearray(SimpleArray *container) { + if (container->currentSize > 0) + --(container->currentSize); +} + +void insert_into_simplearray(SimpleArray *container, void* value, unsigned int pos) { + if (pos > container->currentSize) { + push_onto_simplearray(container, value); + } + else { + if (container->currentSize >= container->bufferSize) { + container->bufferSize *= 2; + void* temp = (void*)realloc(container->contents, container->bufferSize * container->elemSize); + container->contents = temp; + } + + memmove(container->contents + (pos + 1) * container->elemSize, container->contents + pos * container->elemSize, + container->elemSize * (container->currentSize - pos)); + + switch (container->elemSize) { + case (4) : ((int*)(container->contents))[pos] = *(int *)value; + break; + case (2) : ((short*)(container->contents))[pos] = *(short *)value; + break; + case (1) : ((char*)(container->contents))[pos] = *(char *)value; + break; + default: memcpy (container->contents + pos * container->elemSize, value, container->elemSize); + break; + } + ++(container->currentSize); + } +} +void delete_from_simplearray(SimpleArray *container, unsigned int pos) { + if (pos > container->currentSize) { + pop_off_simplearray(container); + } + else if (pos == container->currentSize) { + if (pos > 0) { + --(container->currentSize); + } + } + else { + memmove(container->contents + pos * container->elemSize, container->contents + (pos+1) * container->elemSize, + container->elemSize * (container->currentSize - pos)); + --(container->currentSize); + } +} +void delete_from_simplearray_range(SimpleArray *container, unsigned int pos, unsigned int amount) { + if (pos > container->currentSize) { + pop_off_simplearray(container); + } + else if (pos == container->currentSize) { + if (pos > 0) { + --(container->currentSize); + } + } + else { + memmove(container->contents + pos * container->elemSize, container->contents + (pos+amount) * container->elemSize, + container->elemSize * (container->currentSize - pos)); + --(container->currentSize); + } +} diff --git a/src/aquarium/simplearray.h b/src/aquarium/simplearray.h new file mode 100644 index 0000000..c9d399c --- /dev/null +++ b/src/aquarium/simplearray.h @@ -0,0 +1,23 @@ +#ifndef SIMPLEARRAY_H_ +#define SIMPLEARRAY_H_ + +typedef struct { + void* contents; + unsigned int elemSize; + unsigned int currentSize; + unsigned int bufferSize; +} SimpleArray; + +#define STORAGE_VAR(x) ((void*)&(x)) +#define INDEX_AT(c,x,y) (*((y*)(c.contents + (x) * c.elemSize))) +#define DEFAULT_SIMPLEARRAY_SIZE 4 + +void initialize_simplearray(SimpleArray *container, unsigned int size); +void push_onto_simplearray(SimpleArray *container, void *value); +void pop_off_simplearray(SimpleArray *container); +void insert_into_simplearray(SimpleArray *container, void* value, unsigned int pos); +void delete_from_simplearray(SimpleArray *container, unsigned int pos); +void delete_from_simplearray_range(SimpleArray *container, unsigned int pos, unsigned int amount); + + +#endif /* SIMPLEARRAY_H_ */ diff --git a/src/minnow/analyser.cpp b/src/minnow/analyser.cpp new file mode 100644 index 0000000..e803892 --- /dev/null +++ b/src/minnow/analyser.cpp @@ -0,0 +1,712 @@ +#include + +#include "analyser.hpp" +#include "parser.hpp" + +ASTNode *Analyser::scopeAndTypesPass(ASTNode* input) { + VariableInfo *vi; + + NumberExprAST *neast; + BooleanExprAST *boeast; + QuoteExprAST *qeast; + VariableExprAST *veast; + ArrayIndexedExprAST *aieast; + BinaryExprAST *beast; + CallExprAST *ceast; + PrototypeAST *proto; + VarDeclExprAST *vdeast; + ActorAST *actorast; + ClassAST *classast; + + std::ostringstream msg; + + unsigned int varScopeDepth, funScopeDepth; + + if (input == NULL) { + //NOP + //throw CompilerException("Null value found"); + return NULL; + } + //Number, Variable, ArrayIndexed, Binary, Quote, Call, DefFun, End, VarDecl, ArrayDecl, If, While + switch (input->type()) { + case (NodeType::Number) : + neast = dynamic_cast(input); + if (neast == NULL) { + throw CompilerException("FIXME: Number compiler exception\n"); + } + //FIXME: Check for dot for floats + input->programmaticType.declType = "int"; + input->programmaticType.containerType = ContainerType::Scalar; + break; + case (NodeType::Boolean) : + boeast = dynamic_cast(input); + if (boeast == NULL) { + throw CompilerException("FIXME: Boolean compiler exception\n"); + } + input->programmaticType.declType = "bool"; + input->programmaticType.containerType = ContainerType::Scalar; + break; + + case (NodeType::Variable) : + veast = dynamic_cast(input); + if (veast == NULL) { + throw CompilerException("FIXME: Variable compiler exception\n"); + } + if ((veast->name == "done") || (veast->name == "return")) { + break; + } + vi = findVarInScope(veast->name); + if (vi != NULL) { + input->programmaticType = vi->type; + } + else { + std::ostringstream msg; + msg << "Can not find variable '" << veast->name << "'"; + throw CompilerException(msg.str(), input->filepos); + } + break; + case (NodeType::ArrayIndexed) : + aieast = dynamic_cast(input); + if (input->children.size() == 0) { + throw CompilerException("Incomplete array index", input->filepos); + } + scopeAndTypesPass(input->children[0]); + if (input->children[0]->programmaticType.declType != "int") { + throw CompilerException("Non-integer array index", input->children[0]->filepos); + } + if (aieast == NULL) { + throw CompilerException("FIXME: Array indexed compiler exception\n"); + } + vi = findVarInScope(aieast->name); + if (vi != NULL) { + if (vi->type.containerType != ContainerType::Array) { + throw CompilerException("Indexing of non-array variable", input->filepos); + } + else { + if (vi->type.containedTypes.size() < 1) { + throw CompilerException("Internal error: array representation insufficient"); + } + else { + input->programmaticType = vi->type.containedTypes[0]; + } + } + + } + else { + std::ostringstream msg; + msg << "Can not find variable '" << aieast->name << "'"; + throw CompilerException(msg.str(), input->filepos); + } + break; + case (NodeType::Binary) : + beast = dynamic_cast(input); + if (beast == NULL) { + throw CompilerException("FIXME: Binary compiler exception\n"); + } + if (input->children.size() != 2) { + throw CompilerException("Internal Error: incorrect number of binary children", + input->filepos); + } + scopeAndTypesPass(input->children[0]); + + if (beast->op == "::") { + std::map::iterator location = + this->actors.find(input->children[0]->programmaticType.declType); + if (location == this->actors.end()) { + msg << "Could not find referenced actor: '" << + input->children[0]->programmaticType.declType << "'"; + throw CompilerException(msg.str(), input->children[0]->filepos); + } + else { + CallExprAST *as_call = dynamic_cast(input->children[1]); + if (as_call != NULL) { + //visit children + for (unsigned int i = 0; i < as_call->children.size(); ++i) { + scopeAndTypesPass(as_call->children[i]); + } + } + + bool isFound = false; + for (std::vector::iterator citer = location->second->children.begin(), + cend = location->second->children.end(); citer != cend; ++citer) { + + if ((*citer)->type() == NodeType::Action) { + proto = dynamic_cast((*citer)->children[0]); + //FIXME: This does -not- allow overloading + if (as_call->name == proto->name) { + isFound = true; + } + } + } + if (!isFound) { + throw CompilerException("Could not find action", input->children[1]->filepos); + } + } + input->programmaticType.declType = "void"; + input->programmaticType.containerType = ContainerType::Scalar; + } + else if (beast->op == "."){ + if (input->children[0]->programmaticType.containerType != ContainerType::Scalar) { + throw CompilerException("Attempting to reference a non-scalar type", input->filepos); + } + std::map::iterator location = + this->classes.find(input->children[0]->programmaticType.declType); + if (location == this->classes.end()) { + throw CompilerException("Could not find referenced class", input->filepos); + } + else { + VariableExprAST *as_var = dynamic_cast(input->children[1]); + CallExprAST *as_call = dynamic_cast(input->children[1]); + if ((as_var == NULL) && (as_call == NULL)) { + std::ostringstream msg; + msg << "Can't use '.' in this context"; + throw CompilerException(msg.str(), input->filepos); + } + if (as_call != NULL) { + //visit children + for (unsigned int i = 0; i < as_call->children.size(); ++i) { + scopeAndTypesPass(as_call->children[i]); + } + } + bool isFound = false; + for (std::vector::iterator citer = location->second->children.begin(), + cend = location->second->children.end(); citer != cend; ++citer) { + + if (as_var != NULL) { + if ((*citer)->type() == NodeType::VarDecl) { + VarDeclExprAST *varDecl = dynamic_cast(*citer); + if (varDecl->vi->name == as_var->name) { + isFound = true; + input->programmaticType = varDecl->vi->type; + input->children[1]->programmaticType = varDecl->vi->type; + } + } + } + else if (as_call != NULL) { + if ((*citer)->type() == NodeType::Function) { + if ((*citer)->children.size() == 0) { + std::ostringstream msg; + msg << "Internal error: function missing prototype"; + throw CompilerException(msg.str(), (*citer)->filepos); + } + PrototypeAST *proto = dynamic_cast((*citer)->children[0]); + + //FIXME: this does _not_ allow overloading + if (proto->name == as_call->name) { + isFound = true; + input->programmaticType = proto->returnType; + input->children[1]->programmaticType = proto->returnType; + } + } + + } + } + if (!isFound) { + throw CompilerException("Could not find referenced item", input->children[1]->filepos); + } + } + } + else { + scopeAndTypesPass(input->children[1]); + + if (input->children[0]->programmaticType != + input->children[1]->programmaticType) { + msg << "Unmatched types: " << input->children[0]->programmaticType.declType + << "(" << input->children[0]->programmaticType.containerType << ")" + << "[" << input->children[0]->programmaticType.containedTypes.size() << "]" + << " vs " << input->children[1]->programmaticType.declType + << "(" << input->children[1]->programmaticType.containerType << ")" + << "[" << input->children[1]->programmaticType.containedTypes.size() << "]"; + throw CompilerException(msg.str(), input->filepos); + } + else { + input->programmaticType = input->children[0]->programmaticType; + } + } + break; + + case (NodeType::Quote) : + qeast = dynamic_cast(input); + if (qeast == NULL) { + throw CompilerException("FIXME: Quote compiler exception\n"); + } + input->programmaticType.declType = "string"; + input->programmaticType.containerType = ContainerType::Scalar; + break; + case (NodeType::Call) : + ceast = dynamic_cast(input); + if (ceast == NULL) { + throw CompilerException("FIXME: Call compiler exception\n"); + } + //FIXME: Need to make sure "return" is in function scope + if (ceast->name != "return") { + input->programmaticType = lookupReturnTypeInfo(ceast); + } + for (unsigned int i = 0; i < input->children.size(); ++i) { + scopeAndTypesPass(input->children[i]); + } + break; + case (NodeType::Prototype): + proto = dynamic_cast(input); + if (proto == NULL) { + throw CompilerException("FIXME: prototype compiler exception"); + } + //std::cout << "Pushed: " << proto->name << std::endl; + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + scopeAndTypesPass(*cnode); + } + break; + case (NodeType::VarDecl): + vdeast = dynamic_cast(input); + if (vdeast == NULL) { + throw CompilerException("FIXME: vardecl compiler exception"); + } + + varScopeStack.push_back(vdeast->vi); + input->programmaticType = vdeast->vi->type; + break; + case (NodeType::Function): + //prototype is captured earlier + varScopeDepth = varScopeStack.size(); + for (unsigned int i = 0; i < input->children.size(); ++i) { + scopeAndTypesPass(input->children[i]); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + break; + case (NodeType::Action): + varScopeDepth = varScopeStack.size(); + for (unsigned int i = 0; i < input->children.size(); ++i) { + scopeAndTypesPass(input->children[i]); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + break; + case (NodeType::While): + varScopeDepth = varScopeStack.size(); + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + scopeAndTypesPass(*cnode); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + break; + case (NodeType::If): + varScopeDepth = varScopeStack.size(); + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + scopeAndTypesPass(*cnode); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + break; + case (NodeType::Block): + varScopeDepth = varScopeStack.size(); + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + scopeAndTypesPass(*cnode); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + break; + case (NodeType::Actor): + funScopeDepth = funScopeStack.size(); + varScopeDepth = varScopeStack.size(); + + actorast = dynamic_cast(input); + if (actorast == NULL) { + throw CompilerException("Internal error: actor reference is null", + input->filepos); + } + + //push variable that will represent "this" + vi = new VariableInfo(); + vi->name = "this"; + vi->scopeType = ScopeType::Struct; + vi->type.declType = actorast->name; + vi->type.containerType = ContainerType::Scalar; + varScopeStack.push_back(vi); + + //find prototypes and variables that are in the closed scope, and + //do those first. + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + + if (*cnode == NULL) { + throw CompilerException("Internal error: child is null", input->filepos); + } + switch ((*cnode)->type()) { + case (NodeType::Function) : + proto = dynamic_cast((*cnode)->children[0]); + funScopeStack.push_back(proto); + + //analyseScopeAndTypes((*cnode)->children[0]); + break; + case (NodeType::Action) : + proto = dynamic_cast((*cnode)->children[0]); + funScopeStack.push_back(proto); + + //analyseScopeAndTypes((*cnode)->children[0]); + break; + case (NodeType::VarDecl) : + scopeAndTypesPass(*cnode); + break; + default : + //do nothing + break; + } + } + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + switch ((*cnode)->type()) { + case (NodeType::VarDecl) : + break; + default : + scopeAndTypesPass(*cnode); + break; + } + + //analyseScopeAndTypes(*cnode); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + while (funScopeDepth != funScopeStack.size()) { + funScopeStack.pop_back(); + } + break; + case (NodeType::Class): + funScopeDepth = funScopeStack.size(); + varScopeDepth = varScopeStack.size(); + + classast = dynamic_cast(input); + if (classast == NULL) { + throw CompilerException("Internal error: class reference is null", + input->filepos); + } + + //FIXME: this will leak + //push variable that will represent "this" + vi = new VariableInfo(); + vi->name = "this"; + vi->scopeType = ScopeType::Struct; + vi->type.declType = classast->name; + vi->type.containerType = ContainerType::Scalar; + varScopeStack.push_back(vi); + + //find prototypes and variables that are in the closed scope, and + //do those first. + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + + if (*cnode == NULL) { + throw CompilerException("Internal error: child is null", input->filepos); + } + switch ((*cnode)->type()) { + case (NodeType::Function) : + proto = dynamic_cast((*cnode)->children[0]); + funScopeStack.push_back(proto); + + //analyseScopeAndTypes((*cnode)->children[0]); + break; + case (NodeType::VarDecl) : + scopeAndTypesPass(*cnode); + default : + //do nothing + break; + } + } + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + switch ((*cnode)->type()) { + case (NodeType::VarDecl) : + break; + default : + scopeAndTypesPass(*cnode); + break; + } + + //analyseScopeAndTypes(*cnode); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + while (funScopeDepth != funScopeStack.size()) { + funScopeStack.pop_back(); + } + break; + case (NodeType::App): + funScopeDepth = funScopeStack.size(); + varScopeDepth = varScopeStack.size(); + //find prototypes and variables that are in the closed scope, and + //do those first. + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + + if (*cnode == NULL) { + throw CompilerException("Internal error: child is null", input->filepos); + } + switch ((*cnode)->type()) { + case (NodeType::Function) : + proto = dynamic_cast((*cnode)->children[0]); + funScopeStack.push_back(proto); + + //analyseScopeAndTypes((*cnode)->children[0]); + break; + case (NodeType::Action) : + proto = dynamic_cast((*cnode)->children[0]); + funScopeStack.push_back(proto); + + //analyseScopeAndTypes((*cnode)->children[0]); + break; + case (NodeType::Actor) : + actorast = dynamic_cast(*cnode); + if (actorast == NULL) { + throw CompilerException("Internal error: actor is null", input->filepos); + } + actors[actorast->name] = actorast; + break; + case (NodeType::Class) : + classast = dynamic_cast(*cnode); + if (classast == NULL) { + throw CompilerException("Internal error: class is null", input->filepos); + } + classes[classast->name] = classast; + break; + case (NodeType::VarDecl) : + scopeAndTypesPass(*cnode); + break; + default : + //do nothing + break; + } + } + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + + switch ((*cnode)->type()) { + case (NodeType::VarDecl) : + break; + default : + scopeAndTypesPass(*cnode); + break; + } + + //analyseScopeAndTypes(*cnode); + } + while (varScopeDepth != varScopeStack.size()) { + varScopeStack.pop_back(); + } + while (funScopeDepth != funScopeStack.size()) { + funScopeStack.pop_back(); + } + break; + default: + msg << "Unhandled element during analysis: " << input->type(); + throw CompilerException(msg.str(), input->filepos); + break; + } + + return input; +} + +void Analyser::debugOutputPass(ASTNode* input, unsigned int depth) { + NumberExprAST *neast; + BooleanExprAST *boeast; + QuoteExprAST *qeast; + VariableExprAST *veast; + ArrayIndexedExprAST *aieast; + BinaryExprAST *beast; + CallExprAST *ceast; + PrototypeAST *proto; + VarDeclExprAST *vdeast; + ActorAST *actorast; + ClassAST *classast; + + std::ostringstream msg; + + if (input == NULL) { + //NOP + return; + } + switch (input->type()) { + case (NodeType::Number) : + neast = dynamic_cast(input); + if (neast == NULL) { + throw CompilerException("FIXME: Number compiler exception\n"); + } + indentDepth(depth); + std::cout << "Num: " << neast->val << std::endl; + break; + case (NodeType::Boolean) : + boeast = dynamic_cast(input); + if (boeast == NULL) { + throw CompilerException("FIXME: Boolean compiler exception\n"); + } + indentDepth(depth); + std::cout << "Bool: " << boeast->val << std::endl; + break; + case (NodeType::Variable) : + veast = dynamic_cast(input); + if (veast == NULL) { + throw CompilerException("FIXME: Variable compiler exception\n"); + } + indentDepth(depth); + std::cout << "Var: " << veast->name << std::endl; + break; + case (NodeType::ArrayIndexed) : + aieast = dynamic_cast(input); + if (input->children.size() == 0) { + throw CompilerException("Incomplete array index", input->filepos); + } + indentDepth(depth); + std::cout << "Indexed: " << aieast->name << std::endl; + debugOutputPass(input->children[0], depth+1); + break; + case (NodeType::Binary) : + beast = dynamic_cast(input); + if (beast == NULL) { + throw CompilerException("FIXME: Binary compiler exception\n"); + } + indentDepth(depth); + std::cout << "Binary: " << beast->op << std::endl; + debugOutputPass(input->children[0], depth+1); + debugOutputPass(input->children[1], depth+1); + break; + + case (NodeType::Quote) : + qeast = dynamic_cast(input); + if (qeast == NULL) { + throw CompilerException("FIXME: Quote compiler exception\n"); + } + indentDepth(depth); + std::cout << "String: " << qeast->val << std::endl; + break; + case (NodeType::Call) : + ceast = dynamic_cast(input); + if (ceast == NULL) { + throw CompilerException("FIXME: Call compiler exception\n"); + } + indentDepth(depth); + std::cout << "Call: " << ceast->name << std::endl; + for (unsigned int i = 0; i < input->children.size(); ++i) { + debugOutputPass(input->children[i], depth+1); + } + break; + case (NodeType::Prototype): + proto = dynamic_cast(input); + if (proto == NULL) { + throw CompilerException("FIXME: prototype compiler exception"); + } + indentDepth(depth); + std::cout << "Proto: " << proto->name << std::endl; + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + debugOutputPass(*cnode, depth+1); + } + break; + case (NodeType::VarDecl): + vdeast = dynamic_cast(input); + if (vdeast == NULL) { + throw CompilerException("FIXME: vardecl compiler exception"); + } + indentDepth(depth); + std::cout << "Decl: " << vdeast->vi->name << std::endl; + break; + case (NodeType::Function): + indentDepth(depth); + std::cout << "Function: " << std::endl; + for (unsigned int i = 0; i < input->children.size(); ++i) { + debugOutputPass(input->children[i], depth+1); + } + break; + case (NodeType::Action): + indentDepth(depth); + std::cout << "Action: " << std::endl; + for (unsigned int i = 0; i < input->children.size(); ++i) { + debugOutputPass(input->children[i], depth+1); + } + break; + case (NodeType::While): + indentDepth(depth); + std::cout << "While: " << std::endl; + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + debugOutputPass(*cnode, depth+1); + } + break; + case (NodeType::If): + indentDepth(depth); + std::cout << "If: " << std::endl; + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + debugOutputPass(*cnode, depth+1); + } + break; + case (NodeType::Block): + indentDepth(depth); + std::cout << "Block: " << std::endl; + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + debugOutputPass(*cnode, depth+1); + } + break; + case (NodeType::Actor): + actorast = dynamic_cast(input); + if (actorast == NULL) { + throw CompilerException("Internal error: actor reference is null", + input->filepos); + } + indentDepth(depth); + std::cout << "Actor: " << actorast->name << std::endl; + + //find prototypes and variables that are in the closed scope, and + //do those first. + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + + debugOutputPass(*cnode, depth+1); + } + + break; + case (NodeType::Class): + classast = dynamic_cast(input); + if (classast == NULL) { + throw CompilerException("Internal error: class reference is null", + input->filepos); + } + + indentDepth(depth); + std::cout << "Class: " << classast->name << std::endl; + + //find prototypes and variables that are in the closed scope, and + //do those first. + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + + debugOutputPass(*cnode, depth+1); + } + break; + case (NodeType::App): + indentDepth(depth); + std::cout << "App: " << std::endl; + //find prototypes and variables that are in the closed scope, and + //do those first. + for (std::vector::iterator cnode = input->children.begin(), + cend = input->children.end(); cnode != cend; ++cnode) { + + debugOutputPass(*cnode, depth+1); + } + break; + default: + msg << "Unhandled element during analysis: " << input->type(); + throw CompilerException(msg.str(), input->filepos); + break; + } +} diff --git a/src/minnow/analyser.hpp b/src/minnow/analyser.hpp new file mode 100644 index 0000000..cb55059 --- /dev/null +++ b/src/minnow/analyser.hpp @@ -0,0 +1,55 @@ +#ifndef ANALYSER_HPP +#define ANALYSER_HPP + +#include +#include "parser.hpp" + +class Analyser { +public: + std::map actors; + std::map classes; + std::vector varScopeStack; + std::vector funScopeStack; + + VariableInfo* findVarInScope(const std::string &name) { + for (std::vector::reverse_iterator iter = varScopeStack.rbegin(), + end = varScopeStack.rend(); iter != end; ++iter) { + + + if ((*iter) == NULL) { + throw CompilerException("Internal Error: variableinfo is null"); + } + if ((*iter)->name == name) { + return *iter; + } + } + + return NULL; + } + + TypeInfo lookupReturnTypeInfo(const CallExprAST *ast) { + for (std::vector::reverse_iterator iter = funScopeStack.rbegin(), + end = funScopeStack.rend(); iter != end; ++iter) { + + //FIXME: This is insufficient for overloaded functions + if ((*iter)->name == ast->name) { + //TypeInfo ti((*iter)->type, TypeType::Scalar); + return (*iter)->returnType; + } + } + + std::ostringstream msg; + msg << "Can not find function '" << ast->name << "'"; + std::string outmsg = msg.str(); + throw CompilerException(outmsg, ast->filepos); + } + + void indentDepth(unsigned int depth) { + for (unsigned int i = 0; i < depth; ++i) + std::cout << " "; + } + ASTNode *scopeAndTypesPass(ASTNode* input); + void debugOutputPass(ASTNode* input, unsigned int depth); +}; + +#endif /* ANALYSER_HPP */ diff --git a/src/minnow/codegen_cppoutput.cpp b/src/minnow/codegen_cppoutput.cpp new file mode 100644 index 0000000..6fe4446 --- /dev/null +++ b/src/minnow/codegen_cppoutput.cpp @@ -0,0 +1,1519 @@ +#include "codegen_cppoutput.hpp" + +#include +#include +#include + +boost::shared_ptr CodegenCPPOutput::handleCall(CallExprAST *ast, const std::string &container, const std::string &container_name) { + boost::shared_ptr gc(new GeneratedCode), gc_temp; + std::vector > gc_args; + bool isExtern = checkIfExtern(ast->name); + + for (int i = 0, j = ast->children.size(); i != j; ++i) { + gc_args.push_back(visit (ast->children[i])); + } + + for (int i = 0, j = ast->children.size(); i != j; ++i) { + //if (i != 0) + // gc.get()->inits << ", "; + if (gc_args[i].get()->decls.str() != "") { + gc.get()->decls << gc_args[i].get()->decls.str(); + } + if (gc_args[i].get()->inits.str() != "") { + gc.get()->inits << gc_args[i].get()->inits.str(); + } + } + + if (ast->name == "return") { + //if this is a return call, we need to clear our scope stack up to where we came from + + if (gc_args.size() > 1) { + throw CompilerException("Too many arguments in return", ast->filepos); + } + + int unwindAmount = currentScopeCount.back(); + for (int i = 0; i < unwindAmount; ++i) { + VariableInfo *vi = scopeStack[scopeStack.size()-1-i]; + if ((vi->type.requiresCopyDelete())&&(!checkIfActor(vi->type.declType))) { + //since it's a return, we don't want to delete what we're returning, so be careful. This isn't the best way to do this, so I'm open to suggestions. + if ((gc_args.size() == 1) && (vi->name != gc_args[0].get()->output.str())) { + gc.get()->inits << "if (" << vi->name << " != NULL) {" << std::endl; + gc.get()->inits << " delete(" << vi->name << ");" << std::endl; + gc.get()->inits << "}" << std::endl; + } + } + } + + gc.get()->output << "return ("; + for (int i = 0, j = ast->children.size(); i != j; ++i) { + if (i != 0) + gc.get()->output << ", "; + + if (gc_args[i].get()->output.str() != "") { + gc.get()->output << gc_args[i].get()->output.str(); + } + } + gc.get()->output << ")"; + + //"return" is a special case, so get out of here + return gc; + } + + std::string tmpName = nextTemp(); + std::string retType = lookupReturnType(ast, container); + if (retType != "void") { + gc.get()->decls << retType << " " << tmpName << ";" << std::endl; + } + + gc.get()->inits << "case(" << currentContId << "):" << std::endl; + ++currentContId; + + gc.get()->inits << outputResumeBlock(); + + if (!isExtern) { + gc.get()->inits << "actor__->parentThread->timeSliceEndTime = timeLeft__;" << std::endl; + } + + if (retType != "void") { + gc.get()->inits << tmpName << " = "; + if (container_name != "") { + gc.get()->inits << container_name << "->"; + } + gc.get()->inits << ast->name << "("; + } + else { + if (container_name != "") { + gc.get()->inits << container_name << "->"; + } + gc.get()->inits << ast->name << "("; + } + + for (int i = 0, j = ast->children.size(); i != j; ++i) { + if (i != 0) + gc.get()->inits << ", "; + + if (gc_args[i].get()->output.str() != "") { + gc.get()->inits << gc_args[i].get()->output.str(); + } + } + + if (!isExtern) { + if (ast->children.size() > 0) { + gc.get()->inits << ", "; + } + gc.get()->inits << "actor__"; + } + + gc.get()->inits << ");" << std::endl; + if (isExtern) { + //since the external call will no decrement the timeslice, we need to do it manually + gc.get()->inits << "--timeLeft__;" << std::endl; + } + if (!isExtern) { + gc.get()->inits << "timeLeft__ = actor__->parentThread->timeSliceEndTime;" << std::endl; + } + gc.get()->inits << outputPauseBlock(false); + + if (retType != "void") { + gc.get()->output << tmpName; + } + + return gc; +} + +//catch all that will dispatch out to others +boost::shared_ptr CodegenCPPOutput::visit(ASTNode *ast) { + boost::shared_ptr gc; + + NumberExprAST *neast; + BooleanExprAST *boeast; + QuoteExprAST *qeast; + VariableExprAST *veast; + VarDeclExprAST *vdeast; + ArrayIndexedExprAST *aieast; + //ArrayDeclExprAST *adeast; + EndExprAST *eeast; + IfExprAST *ieast; + WhileExprAST *weast; + BinaryExprAST *beast; + CallExprAST *ceast; + ActionAST *actast; + FunctionAST *funast; + ActorAST *actorast; + ClassAST *classast; + AppAST *appast; + + if (ast == NULL) { + //NOP + gc = boost::shared_ptr(new GeneratedCode); + return gc; + } + //Number, Variable, ArrayIndexed, Binary, Quote, Call, DefFun, End, VarDecl, If, While + switch (ast->type()) { + case (NodeType::Number) : + neast = dynamic_cast(ast); + if (neast == NULL) { + printf("FIXME: Number compiler exception\n"); + } + gc = visit(neast); + break; + case (NodeType::Boolean) : + boeast = dynamic_cast(ast); + if (boeast == NULL) { + printf("FIXME: Number compiler exception\n"); + } + gc = visit(boeast); + break; + case (NodeType::Variable) : + veast = dynamic_cast(ast); + if (veast == NULL) { + printf("FIXME: Variable compiler exception\n"); + } + gc = visit(veast); + break; + case (NodeType::ArrayIndexed) : + aieast = dynamic_cast(ast); + if (aieast == NULL) { + printf("FIXME: Array indexed compiler exception\n"); + } + gc = visit(aieast); + break; + case (NodeType::Binary) : + beast = dynamic_cast(ast); + if (beast == NULL) { + printf("FIXME: Variable compiler exception\n"); + } + gc = visit(beast); + break; + case (NodeType::Quote) : + qeast = dynamic_cast(ast); + if (qeast == NULL) { + printf("FIXME: Variable compiler exception\n"); + } + gc = visit(qeast); + break; + case (NodeType::Call) : + ceast = dynamic_cast(ast); + if (ceast == NULL) { + printf("FIXME: Call compiler exception\n"); + } + gc = visit(ceast); + break; + case (NodeType::End) : + eeast = dynamic_cast(ast); + if (eeast == NULL) { + printf("FIXME: End compiler exception\n"); + } + gc = visit(eeast); + break; + case (NodeType::VarDecl) : + vdeast = dynamic_cast(ast); + if (vdeast == NULL) { + printf("FIXME: VarDecl compiler exception\n"); + } + gc = visit(vdeast); + break; + case (NodeType::If) : + ieast = dynamic_cast(ast); + if (ieast == NULL) { + printf("FIXME: If compiler exception\n"); + } + + gc = visit(ieast); + break; + case (NodeType::While) : + weast = dynamic_cast(ast); + if (weast == NULL) { + printf("FIXME: While compiler exception\n"); + } + gc = visit(weast); + break; + case (NodeType::Action) : + actast = dynamic_cast(ast); + if (actast == NULL) { + printf("FIXME: action compiler exception\n"); + } + gc = visit(actast); + break; + case (NodeType::Function) : + funast = dynamic_cast(ast); + if (funast == NULL) { + printf("FIXME: function compiler exception\n"); + } + gc = visit(funast); + break; + case (NodeType::Actor) : + actorast = dynamic_cast(ast); + if (actorast == NULL) { + printf("FIXME: actor compiler exception\n"); + } + gc = visit(actorast); + break; + case (NodeType::Class) : + classast = dynamic_cast(ast); + if (classast == NULL) { + printf("FIXME: class compiler exception\n"); + } + gc = visit(classast); + break; + case (NodeType::App) : + appast = dynamic_cast(ast); + if (appast == NULL) { + printf("FIXME: app compiler exception\n"); + } + gc = visit(appast); + break; + default : + throw CompilerException("Unknown element", ast->filepos); + } + + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(NumberExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + + gc.get()->output << ast->val; + + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(QuoteExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + + gc.get()->output << "\"" << ast->val << "\""; + + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(BooleanExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + + if (ast->val == true) { + gc.get()->output << "true"; + } + else { + gc.get()->output << "false"; + } + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(VariableExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + + //TODO: Putting these here for now, but we should handle keywords in a more special place in the future + if ((ast->name == "return") || (ast->name == "done")) { + if (inAction) { + //if this is a return call, we need to clear our scope stack up to where we came from + int unwindAmount = currentScopeCount.back(); + for (int i = 0; i < unwindAmount; ++i) { + VariableInfo *vi = scopeStack[scopeStack.size()-1-i]; + if ((vi->type.requiresCopyDelete())&&(!checkIfActor(vi->type.declType))) { + gc.get()->output << "if (" << vi->name << " != NULL) {" << std::endl; + gc.get()->output << " delete(" << vi->name << ");" << std::endl; + gc.get()->output << "}" << std::endl; + } + } + + if (ast->name == "return") { + gc.get()->output << "--timeLeft__;" << std::endl; + gc.get()->output << "actor__->parentThread->timeSliceEndTime = timeLeft__;" << std::endl; + gc.get()->output << "actor__->actorState = ActorState::WAITING_FOR_ACTION;" << std::endl; + gc.get()->output << "return; " << std::endl; + } + else if (ast->name == "done") { + gc.get()->output << "--timeLeft__;" << std::endl; + gc.get()->output << "actor__->parentThread->timeSliceEndTime = timeLeft__;" << std::endl; + gc.get()->output << "actor__->actorState = ActorState::DELETED;" << std::endl; + gc.get()->output << "return; " << std::endl; + } + return gc; + } + } + + VariableInfo *vi = findVarInScope(ast->name); + if (vi == NULL) { + std::ostringstream oss; + oss << "Unknown variable '" << ast->name << "'"; + throw CompilerException(oss.str(), ast->filepos); + } + + if (vi->scopeType == ScopeType::Actor) { + if (vi->name == "this") { + gc.get()->output << "actor__->actorId"; + } + else { + gc.get()->output << "actor__->" << ast->name; + } + } + else { + gc.get()->output << ast->name; + } + + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(VarDeclExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + + VariableInfo *vi = ast->vi; //new VariableInfo(ast->name, ast->declType, ContainerType::Scalar, ScopeType::CodeBlock); + + if (vi->type.containerType == ContainerType::Scalar) { + std::map::iterator finder = actors.find(vi->type.declType); + + if (finder != actors.end()) { + if (ast->isSpawn) { + std::string actorTemp = nextTemp(); + gc.get()->decls << "actorId_t " << vi->name << ";" << std::endl; + gc.get()->inits << vi->type.declType << " *" << actorTemp << ";" << std::endl; + gc.get()->output << "{ " << actorTemp << " = new " << vi->type.declType << "();" << std::endl; + if (finder->second->isIsolated) { + gc.get()->output << " actor__->parentThread->ScheduleNewIsolatedActor(" << actorTemp << ");" << std::endl; + } + else { + gc.get()->output << " actor__->parentThread->ScheduleNewActor(" << actorTemp << ");" << std::endl; + } + gc.get()->output << " " << vi->name << " = " << actorTemp << "->actorId; }" << std::endl; + } + else { + gc.get()->decls << "actorId_t " << vi->name << ";" << std::endl; + } + } + else { + gc.get()->decls << lookupAssocType(vi->type) << " " << vi->name << ";" << std::endl; + if (ast->isAlloc) { + //TRIM + std::string allocType = lookupAssocType(vi->type); + allocType = allocType.erase(allocType.size()-1); + + gc.get()->output << vi->name << " = new " << allocType << "();" << std::endl; + } + gc.get()->output << vi->name; + } + } + else if (vi->type.containerType == ContainerType::Array) { + std::map::iterator finder = actors.find(vi->type.declType); + + if (finder != actors.end()) { + if (ast->isSpawn) { + std::string tmpName = nextTemp(); + gc.get()->decls << "int " << tmpName << ";" << std::endl; + + std::string actorTemp = nextTemp(); + std::string loopTemp = nextTemp(); + gc.get()->decls << vi->type.declType << " *" << actorTemp << ";" << std::endl; + + gc.get()->decls << "std::vector *" << vi->name << ";" << std::endl; + + gc.get()->output << tmpName << " = "; + + if (vi->size != NULL) { + boost::shared_ptr gc_temp = visit (vi->size); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + } + else { + gc.get()->output << "0"; + } + gc.get()->output << ";" << std::endl; + gc.get()->output << vi->name << " = new std::vector(" << tmpName << ");" << std::endl; + + gc.get()->output << "for (int " << loopTemp << "=0; " << loopTemp << " < " << tmpName << "; ++" << loopTemp << ") {" << std::endl; + gc.get()->output << " " << actorTemp << " = new " << vi->type.declType << "();" << std::endl; + if (finder->second->isIsolated) { + gc.get()->output << " actor__->parentThread->ScheduleNewIsolatedActor(" << actorTemp << ");" << std::endl; + } + else { + gc.get()->output << " actor__->parentThread->ScheduleNewActor(" << actorTemp << ");" << std::endl; + } + gc.get()->output << " (*" << vi->name << ")[" << loopTemp << "] = " << actorTemp << "->actorId;" << std::endl; + gc.get()->output << "}" << std::endl; + } + else { + gc.get()->decls << "std::vector *" << vi->name << ";" << std::endl; + gc.get()->output << vi->name << " = new std::vector("; + if (vi->size != NULL) { + boost::shared_ptr gc_temp = visit (vi->size); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + } + else { + gc.get()->output << "0"; + } + gc.get()->output << ");" << std::endl; + } + } + else { + gc.get()->decls << lookupAssocType(vi->type) << " " << vi->name << ";" << std::endl; + std::string allocType = lookupAssocType(vi->type); + allocType = allocType.erase(allocType.size()-1); //TRIM off the trailing '*' + gc.get()->output << vi->name << " = new " << allocType << "("; + if (vi->size != NULL) { + boost::shared_ptr gc_temp = visit (vi->size); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + } + else { + gc.get()->output << "0"; + } + gc.get()->output << ")"; + + //FIXME? Would you ever follow an array decl with an assignment? If so, we should probably check for that + } + } + ++(currentScopeCount.back()); + scopeStack.push_back(vi); + + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(ArrayIndexedExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + + VariableInfo *vi = findVarInScope(ast->name); + if (vi == NULL) { + std::ostringstream oss; + oss << "Unknown array variable '" << ast->name << "'"; + throw CompilerException(oss.str(), ast->filepos); + } + else if (vi->type.containerType != ContainerType::Array) { + std::ostringstream oss; + oss << "Variable '" << ast->name << "' is not of an array type"; + throw CompilerException(oss.str(), ast->filepos); + } + if (vi->scopeType == ScopeType::Actor) { + gc.get()->output << "(*(actor__->" << ast->name << "))"; + } + else { + gc.get()->output << "(*" << ast->name << ")"; + } + gc.get()->output << "["; + boost::shared_ptr gc_temp = visit (ast->children[0]); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + gc.get()->output << "]"; + + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(EndExprAST *ast) { + //do nothing + boost::shared_ptr gc(new GeneratedCode); + + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(IfExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + boost::shared_ptr gc_cond; + + std::string condTemp = nextTemp(); + + gc.get()->decls << "bool " << condTemp << ";" << std::endl; + + gc_cond = visit (ast->children[0]); + if (gc_cond.get()->decls.str() != "") { + gc.get()->decls << gc_cond.get()->decls.str(); + } + if (gc_cond.get()->inits.str() != "") { + gc.get()->output << gc_cond.get()->inits.str(); + } + + if (gc_cond.get()->output.str() != "") { + gc.get()->output << condTemp << " = " << gc_cond.get()->output.str() << ";" << std::endl; + } + + gc.get()->output << "if (" << condTemp << ") {" << std::endl; + + for (std::vector::iterator iter = ast->children[1]->children.begin(), + end = ast->children[1]->children.end(); iter != end; ++iter) { + + boost::shared_ptr gc_temp = visit (*iter); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + gc.get()->output << ";" << std::endl; + } + gc.get()->output << "}"; + if (ast->children[2]->children.size() > 0) { + gc.get()->output << "else {" << std::endl; + for (std::vector::iterator iter = ast->children[2]->children.begin(), + end = ast->children[2]->children.end(); iter != end; ++iter) { + + boost::shared_ptr gc_temp = visit (*iter); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + gc.get()->output << ";" << std::endl; + } + gc.get()->output << "}" << std::endl; + } + + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(WhileExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + boost::shared_ptr gc_cond, gc_temp; + + std::string condTemp = nextTemp(); + + gc.get()->decls << "bool " << condTemp << ";" << std::endl; + gc.get()->output << "case(" << currentContId << "):" << std::endl; + ++currentContId; + gc.get()->output << (outputResumeBlock()); + + gc_cond = visit (ast->children[0]); + if (gc_cond.get()->decls.str() != "") { + gc.get()->decls << gc_cond.get()->decls.str(); + } + if (gc_cond.get()->inits.str() != "") { + gc.get()->output << gc_cond.get()->inits.str(); + } + if (gc_cond.get()->output.str() != "") { + gc.get()->output << condTemp << " = " << gc_cond.get()->output.str() << ";" << std::endl; + } + gc.get()->output << "while (" << condTemp << ") {" << std::endl; + gc.get()->output << (outputPauseBlock(true)); + for (std::vector::iterator iter = ast->children[1]->children.begin(), + end = ast->children[1]->children.end(); iter != end; ++iter) { + gc_temp = visit (*iter); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << ";" << std::endl; + } + gc_cond = visit (ast->children[0]); + if (gc_cond.get()->decls.str() != "") { + gc.get()->decls << gc_cond.get()->decls.str(); + } + if (gc_cond.get()->inits.str() != "") { + gc.get()->output << gc_cond.get()->inits.str(); + } + if (gc_cond.get()->output.str() != "") { + gc.get()->output << condTemp << " = " << gc_cond.get()->output.str() << ";" << std::endl; + } + + gc.get()->output << "}"; + + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(BinaryExprAST *ast) { + boost::shared_ptr gc(new GeneratedCode); + boost::shared_ptr gc_temp; + + if (ast->op == "::") { + CallExprAST *ceast = dynamic_cast(ast->children[1]); + VariableExprAST *veast = dynamic_cast(ast->children[0]); + ArrayIndexedExprAST *aieast = dynamic_cast(ast->children[0]); + + VariableInfo *vi; + + if (ceast == NULL) { + std::ostringstream msg; + msg << "Can't build message, right hand side is type: " << ast->children[1]->type(); + throw CompilerException(msg.str(), ast->filepos); + } + + if ((veast == NULL) && (aieast == NULL)) { + std::ostringstream msg; + msg << "Can't build message, left hand side is type: " << ast->children[0]->type(); + throw CompilerException(msg.str(), ast->filepos); + } + else if (veast != NULL) { + vi = findVarInScope(veast->name); + if (vi == NULL) { + std::ostringstream oss; + oss << "Unknown variable for message '" << veast->name << "'"; + throw CompilerException(oss.str(), veast->filepos); + } + } + else { //if (aieast != NULL) { + vi = findVarInScope(aieast->name); + if (vi == NULL) { + std::ostringstream oss; + oss << "Unknown variable for message '" << aieast->name << "'"; + throw CompilerException(oss.str(), aieast->filepos); + } + } + + //int argSize = ceast->args.size(); + std::string msgName = nextTemp(); + std::string actorIfLocal = nextTemp(); + std::string msgArray; + + gc.get()->decls << "Actor *" << actorIfLocal << ";" << std::endl; + + gc.get()->output << actorIfLocal << " = actor__->parentThread->ActorIfLocal("; + gc_temp = visit (ast->children[0]); + + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + gc.get()->output << ", actor__);" << std::endl; + + gc.get()->output << "if ((" << actorIfLocal << " == NULL) || (" << actorIfLocal << "->actorState != ActorState::WAITING_FOR_ACTION)) {" << std::endl; + gc.get()->decls << "Message " << msgName << ";" << std::endl; + gc.get()->output << " " << msgName << ".recipient = "; + + gc_temp = visit (ast->children[0]); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << ";" << std::endl; + gc.get()->output << " " << msgName << ".numArgs = " << ceast->children.size() << ";" << std::endl; + gc.get()->output << " " << msgName << ".messageType = MessageType::ACTION_MESSAGE;" << std::endl; + gc.get()->output << " " << msgName << ".task = &" << vi->type.declType << "__" << ceast->name << "_action;" << std::endl; + + //std::cout << "arg size: " << ceast->args.size() << std::endl; + if (ceast->children.size() > 4 ) { + msgArray = nextTemp(); + gc.get()->decls << "std::vector *" << msgArray << " = new std::vector();" << std::endl; + } + for (unsigned int i = 0; i < ceast->children.size(); ++i) { + //boost::shared_ptr ti = resolveType(ceast->children[i]); + + boost::shared_ptr gc_temp = visit (ceast->children[i]); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + //gc.get()->output << gc_temp.get()->output.str(); + gc.get()->output << " " << lookupPushForTypeAndBlock + (ceast->children[i]->programmaticType, gc_temp.get()->output.str()); + + if (ceast->children.size() > 4 ) { + gc.get()->output << " " << msgArray << "->push_back(tmpTU__);" << std::endl; + } + else { + gc.get()->output << " " << msgName << ".arg[" << i << "] = tmpTU__;" << std::endl; + } + } + } + if (ceast->children.size() > 4 ) { + gc.get()->output << " " << msgName << ".arg[0].VoidPtr = " << msgArray << ";" << std::endl; + } + + gc.get()->output << "actor__->parentThread->SendMessage(" << msgName << ");" << std::endl; + gc.get()->output << "}" << std::endl; + gc.get()->output << "else {" << std::endl; + + for (unsigned int i = 0; i < ceast->children.size(); ++i) { + //boost::shared_ptr ti = resolveType(ceast->children[i]); + //gc.get()->output << "tmpTU__.UInt32 = "; + + boost::shared_ptr gc_temp = visit (ceast->children[i]); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + //gc.get()->output << gc_temp.get()->output.str(); + gc.get()->output << " " << lookupPushForTypeAndBlock + (ceast->children[i]->programmaticType, gc_temp.get()->output.str()); + //gc.get()->output << " " << msgName << ".arg[" << i << "] = tmpTU__;" << std::endl; + } + + gc.get()->output << ";" << std::endl; + gc.get()->output << actorIfLocal << "->heapStack.push_back(tmpTU__);" << std::endl; + } + + gc.get()->output << actorIfLocal << "->task = &" << vi->type.declType << "__" << ceast->name << "_action;" << std::endl; + gc.get()->output << actorIfLocal << "->actorState = ActorState::ACTIVE;" << std::endl; + + gc.get()->output << "actor__->parentThread->hotActor = " << actorIfLocal << ";" << std::endl; + gc.get()->output << "if (" << actorIfLocal << "->runQueueRevId != actor__->parentThread->runQueueRevId) {" << std::endl; + gc.get()->output << " actor__->parentThread->runningActors.push_back(" << actorIfLocal << ");" << std::endl; + gc.get()->output << " " << actorIfLocal << "->runQueueRevId = actor__->parentThread->runQueueRevId;" << std::endl; + gc.get()->output << " }" << std::endl; + gc.get()->output << "}" << std::endl; + } + else if (ast->op == ".") { + VariableExprAST *veast = dynamic_cast(ast->children[1]); + CallExprAST *ceast = dynamic_cast(ast->children[1]); + if ((veast == NULL) && (ceast == NULL)) { + std::cout << "Can't use '.' in this context, is type: " << ast->children[1]->type() << std::endl; + exit(1); + } + else { + VariableExprAST *lhs_ast = dynamic_cast(ast->children[0]); + //ArrayIndexedExprAST *lhs_aieast = dynamic_cast(ast->LHS); + if (lhs_ast == NULL) { + throw CompilerException("Left hand side is not a variable (this is a limitation of the current system)", ast->filepos); + } + //boost::shared_ptr lhs_type = resolveType(ast->children[0]); + //std::cout << "Accessing type: " << lhs_type.get()->declType << std::endl; + + if (veast != NULL) { + ClassAST* s = this->classes[ast->children[0]->programmaticType.declType]; + //check to see if the struct has an attribute member named this + + bool foundVar = false; + for (std::vector::iterator iter = s->children.begin(), + end = s->children.end(); iter != end; ++iter) { + + VarDeclExprAST *vdeast = dynamic_cast(*iter); + if (vdeast != NULL) { + if (vdeast->vi->name == veast->name) { + gc.get()->output << lhs_ast->name << "->" << veast->name; + foundVar = true; + break; + } + } + } + if (foundVar == false) { + std::ostringstream msg; + msg << "Can't find '" << veast->name << "' inside of '" << lhs_ast->name << "'"; + throw CompilerException(msg.str(), ast->filepos); + } + } + if (ceast != NULL) { + gc_temp = handleCall(ceast, ast->children[0]->programmaticType.declType, lhs_ast->name); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + } + } + } + else { + gc_temp = visit (ast->children[0]); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->inits << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + gc.get()->output << ast->op; + gc_temp = visit (ast->children[1]); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->inits << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + } + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(CallExprAST *ast) { + return handleCall(ast, "", ""); +} + + +boost::shared_ptr CodegenCPPOutput::visit(ClassAST *ast, DeclStage::Stage stage) { + boost::shared_ptr gc(new GeneratedCode), gc_temp(new GeneratedCode); + + FunctionAST *funast; + VarDeclExprAST *varDecl; + + //DeclStage::Stage stage; + if (stage == DeclStage::FORWARD) { + this->classes[ast->name] = ast; + gc.get()->output << "class " << ast->name << ";" << std::endl; + } + if (stage == DeclStage::IMPL) { + std::map::iterator finder; + + currentScopeCount.push_back(0); + scopeContainerId = currentScopeCount.size(); //remember where we started + gc.get()->output << "class " << ast->name << " {" << std::endl << "public: " << std::endl; + currentFunGroup = funStack.size(); + + //then push a variable that will be how we message ourselves + std::string name("this"); + VariableInfo *vi = new VariableInfo(name, ast->name, ContainerType::Scalar, ScopeType::Struct); + ++(currentScopeCount.back()); + scopeStack.push_back(vi); + + for (std::vector::iterator iter = ast->children.begin(), end = ast->children.end(); iter != end; ++iter) { + switch ((*iter)->type()) { + case (NodeType::Function) : + funStack.push_back(dynamic_cast((*iter)->children[0])); + break; + case (NodeType::VarDecl) : + varDecl = dynamic_cast(*iter); + + varDecl->vi->scopeType = ScopeType::Struct; + + ++(currentScopeCount.back()); + scopeStack.push_back(varDecl->vi); + break; + default: + throw CompilerException("Unknown feature insde of class", (*iter)->filepos); + break; + } + } + + for (std::vector::iterator iter = ast->children.begin(), end = ast->children.end(); iter != end; ++iter) { + switch ((*iter)->type()) { + case (NodeType::Function) : + funast = dynamic_cast(*iter); + gc_temp = visit(funast, stage); + break; + case (NodeType::VarDecl) : + varDecl = dynamic_cast(*iter); + + finder = actors.find(varDecl->vi->type.declType); + + if (finder != actors.end()) { + if (varDecl->vi->type.containerType == ContainerType::Scalar) { + gc.get()->output << "actorId_t " << varDecl->vi->name << ";" << std::endl; + } + else if (varDecl->vi->type.containerType == ContainerType::Array) { + gc.get()->output << "actorId_t " << varDecl->vi->name << "["; + + boost::shared_ptr gc_temp = visit (varDecl->vi->size); + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << "];" << std::endl; + } + + } + else { + if (varDecl->vi->type.containerType == ContainerType::Scalar) { + gc.get()->output << lookupAssocType(varDecl->vi->type) << " " << varDecl->vi->name << ";" << std::endl; + } + else if (varDecl->vi->type.containerType == ContainerType::Array) { + gc.get()->output << lookupAssocType(varDecl->vi->type) << " " << varDecl->vi->name << "["; + boost::shared_ptr gc_temp = visit (varDecl->vi->size); + + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << "];" << std::endl; + } + + } + break; + default: + throw CompilerException("Unknown feature insde of class", (*iter)->filepos); + break; + } + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + } + + gc.get()->output << ast->name << "() { }" << std::endl; + + //TODO: I'm not sure if I need a traditional copy constructor, or this pointer style + //gc.get()->output << ast->name << "(const " << ast->name << "& p) {" << std::endl; + gc.get()->output << ast->name << "(" << ast->name << "* p__) {" << std::endl; + + for (std::vector::iterator iter = ast->children.begin(), end = ast->children.end(); iter != end; ++iter) { + VarDeclExprAST *vdeast = dynamic_cast(*iter); + if (vdeast != NULL) { + if (vdeast->vi->type.requiresCopyDelete()) { + gc.get()->output << "if (" << vdeast->vi->name + << " != NULL) { delete " << vdeast->vi->name << "; };" << std::endl; + gc.get()->output << vdeast->vi->name << " = new " + << lookupAssocType(vdeast->vi->type) << "(p__->" << vdeast->vi->name << ");" << std::endl; + } + else { + gc.get()->output << vdeast->vi->name << " = " << "p__->" << vdeast->vi->name << ";" << std::endl; + } + } + } + gc.get()->output << "}" << std::endl; + gc.get()->output << "};" << std::endl; + + int unwindAmount = currentScopeCount.back(); + for (int i = 0; i < unwindAmount; ++i) { + scopeStack.pop_back(); + } + currentScopeCount.pop_back(); + + //Get us back to seeing only the functions we could see before we entered the actor + while (funStack.size() != currentFunGroup) { + funStack.pop_back(); + } + } + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(ActorAST *ast, DeclStage::Stage stage) { + boost::shared_ptr gc(new GeneratedCode), gc_temp(new GeneratedCode); + + ActionAST *actast; + FunctionAST *funast; + VarDeclExprAST *varDecl; + + //DeclStage::Stage stage; + if (stage == DeclStage::DECL) { + currentScopeCount.push_back(0); + scopeContainerId = currentScopeCount.size(); //remember where we started + gc.get()->output << "class " << ast->name << " : public Actor {" << std::endl << "public: " << std::endl; + currentFunGroup = funStack.size(); + + //then push a variable that will be how we message ourselves + std::string name("this"); + VariableInfo *vi = new VariableInfo(name, ast->name, ContainerType::Scalar, ScopeType::Actor); + ++(currentScopeCount.back()); + scopeStack.push_back(vi); + + actors[ast->name] = ast; + } + for (std::vector::iterator iter = ast->children.begin(), end = ast->children.end(); iter != end; ++iter) { + switch ((*iter)->type()) { + case (NodeType::Function) : + if (stage == DeclStage::DECL) { + funStack.push_back(dynamic_cast((*iter)->children[0])); + } + funast = dynamic_cast(*iter); + gc_temp = visit(funast, stage); + break; + case (NodeType::VarDecl) : + varDecl = dynamic_cast(*iter); + if (stage == DeclStage::DECL) { + varDecl->vi->scopeType = ScopeType::Actor; + + ++(currentScopeCount.back()); + scopeStack.push_back(varDecl->vi); + std::map::iterator finder = actors.find(varDecl->vi->type.declType); + + if (finder != actors.end()) { + if (varDecl->vi->type.containerType == ContainerType::Scalar) { + gc.get()->output << "actorId_t " << varDecl->vi->name << ";" << std::endl; + } + else if (varDecl->vi->type.containerType == ContainerType::Array) { + gc.get()->output << "actorId_t " << varDecl->vi->name << "["; + + boost::shared_ptr gc_temp = visit (varDecl->vi->size); + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << "];" << std::endl; + } + + } + else { + if (varDecl->vi->type.containerType == ContainerType::Scalar) { + gc.get()->output << lookupAssocType(varDecl->vi->type) << " " << varDecl->vi->name << ";" << std::endl; + } + else if (varDecl->vi->type.containerType == ContainerType::Array) { + gc.get()->output << lookupAssocType(varDecl->vi->type) << " " << varDecl->vi->name << "["; + boost::shared_ptr gc_temp = visit (varDecl->vi->size); + + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << "];" << std::endl; + } + + } + + } + break; + case (NodeType::Action) : + //handled later + break; + default: + throw CompilerException("Unhandled element in actor definition", (*iter)->filepos); + break; + } + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + } + if (stage == DeclStage::DECL) { + gc.get()->output << "};" << std::endl; + } + + for (std::vector::iterator iter = ast->children.begin(), end = ast->children.end(); iter != end; ++iter) { + switch ((*iter)->type()) { + case (NodeType::Action) : + actast = dynamic_cast(*iter); + gc_temp = visit(actast, ast->name, stage); + + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + break; + case (NodeType::Function) : + //handled earlier + break; + case (NodeType::VarDecl) : + //handled earlier + break; + default: + throw CompilerException("Unhandled element in actor definition", (*iter)->filepos); + break; + } + } + + if (stage == DeclStage::IMPL) { + int unwindAmount = currentScopeCount.back(); + for (int i = 0; i < unwindAmount; ++i) { + scopeStack.pop_back(); + } + currentScopeCount.pop_back(); + + //Get us back to seeing only the functions we could see before we entered the actor + while (funStack.size() != currentFunGroup) { + funStack.pop_back(); + } + } + + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(PrototypeAST *ast, DeclStage::Stage stage) { + boost::shared_ptr gc(new GeneratedCode); + + bool isExtern = checkIfExtern(ast->name); + + gc.get()->output << lookupAssocType(ast->returnType) << " " << ast->name << "("; + for (int i = 0, j = ast->children.size(); i != j; ++i) { + VarDeclExprAST *varDecl = dynamic_cast(ast->children[i]); + if (stage == DeclStage::IMPL) { + ++(currentScopeCount.back()); + scopeStack.push_back(varDecl->vi); + } + if (i != 0) { + gc.get()->output << ", "; + } + + gc.get()->output << lookupAssocType(varDecl->vi->type) << " " << varDecl->vi->name; + } + if (!isExtern) { + if (ast->children.size() > 0) { + gc.get()->output << ", "; + } + gc.get()->output << "Actor *actor__"; + } + gc.get()->output << ")"; + + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(FunctionAST *ast, DeclStage::Stage stage) { + boost::shared_ptr gc(new GeneratedCode); + + PrototypeAST *proto = dynamic_cast(ast->children[0]); + + if (stage == DeclStage::FORWARD) { + if (ast->children[1]->children.size() == 0) { + gc.get()->output << "extern "; + //this->externFns.push_back(ast->proto->name); + } + boost::shared_ptr gc_temp = visit (proto, stage); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << ";" << std::endl; + } + else if (stage == DeclStage::IMPL) { + if (ast->children[1]->children.size() == 0) { + boost::shared_ptr gc_temp(new GeneratedCode); + return gc_temp; + } + else { + scopeContainerId = currentScopeCount.size(); //remember where we started + currentScopeCount.push_back(0); + + boost::shared_ptr gc_temp = visit (proto, stage); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->decls << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->decls << gc_temp.get()->output.str(); + } + + setupDontCare(proto->returnType); + + currentContId = 0; + + gc.get()->decls << "{" << std::endl; + + gc.get()->output << "TypeUnion tmpTU__;" << std::endl; + gc.get()->output << "unsigned int timeLeft__ = actor__->parentThread->timeSliceEndTime;" << std::endl; + + gc.get()->output << "actor__->parentThread->timeSliceEndTime = timeLeft__;" << std::endl; + gc.get()->output << "int contId__ = 0;" << std::endl; + gc.get()->output << "if (actor__->isResuming) {" << std::endl; + gc.get()->output << " contId__ = actor__->heapStack.back().Int32; actor__->heapStack.pop_back();" << std::endl; + gc.get()->output << " if (actor__->heapStack.size() == 0) {" << std::endl; + gc.get()->output << " actor__->isResuming = false;" << std::endl; + gc.get()->output << " }" << std::endl; + gc.get()->output << "}" << std::endl; + gc.get()->output << "actor__->parentThread->timeSliceEndTime = timeLeft__;" << std::endl; + + gc.get()->output << "switch(contId__) {" <output << "case(" << currentContId << "):" << std::endl; + + ++currentContId; + + for (std::vector::iterator iter = ast->children[1]->children.begin(), + end = ast->children[1]->children.end(); iter != end; ++iter) { + boost::shared_ptr gc_temp = visit (*iter); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + gc.get()->output << ";" << std::endl; + + } + + gc.get()->output << " }" << std::endl; + + int unwindAmount = currentScopeCount.back(); + for (int i = 0; i < unwindAmount; ++i) { + VariableInfo *vi = scopeStack.back(); + if (vi->type.containerType == ContainerType::Array) { + gc.get()->output << "if (" << vi->name << " != NULL) {" << std::endl; + if (isCopyDelete(vi->type)) { + gc.get()->output << " for (int i__=0; i__ < " << vi->name << "->size(); ++i__) { if (" << vi->name << "[i__] != NULL) { delete " << vi->name << "[i__];} }" << std::endl; + } + gc.get()->output << " " << vi->name << "->clear();" << std::endl; + gc.get()->output << " delete(" << vi->name << ");" << std::endl; + gc.get()->output << "}" << std::endl; + } + else if ((vi->type.requiresCopyDelete())&&(!checkIfActor(vi->type.declType))) { + gc.get()->output << "if (" << vi->name << " != NULL) {" << std::endl; + gc.get()->output << " delete(" << vi->name << ");" << std::endl; + gc.get()->output << "}" << std::endl; + } + scopeStack.pop_back(); + } + currentScopeCount.pop_back(); + + gc.get()->output << "}" << std::endl; + + } + } + return gc; +} + +boost::shared_ptr CodegenCPPOutput::visit(ActionAST *ast, std::string actorName, DeclStage::Stage stage) { + boost::shared_ptr gc(new GeneratedCode); + inAction = true; + + PrototypeAST *proto = dynamic_cast(ast->children[0]); + + if (stage == DeclStage::DECL) { + gc.get()->output << "void " << actorName << "__" << proto->name << "_action(Actor *a__);" << std::endl; + } + else if (stage == DeclStage::IMPL) { + gc.get()->decls << "void " << actorName << "__" << proto->name << "_action(Actor *a__) "; + + scopeContainerId = currentScopeCount.size(); //remember where we started + currentScopeCount.push_back(0); + + currentContId = 0; + //std::string retVal = "void"; + //setupDontCare(retVal); + this->dontCareReturnVal = ""; + + gc.get()->decls << "{" << std::endl; + + gc.get()->decls << actorName << " *actor__ = static_cast<" << actorName << "*>(a__);" << std::endl; + gc.get()->decls << "TypeUnion tmpTU__;" << std::endl; + gc.get()->decls << "unsigned int timeLeft__ = actor__->parentThread->timeSliceEndTime;" << std::endl; + + gc.get()->output << "int contId__ = 0;" << std::endl; + gc.get()->output << "if (actor__->isResuming) {" << std::endl; + gc.get()->output << " contId__ = actor__->heapStack.back().Int32; actor__->heapStack.pop_back();" << std::endl; + gc.get()->output << " if (actor__->heapStack.size() == 0) {" << std::endl; + gc.get()->output << " actor__->isResuming = false;" << std::endl; + gc.get()->output << " }" << std::endl; + gc.get()->output << "}" << std::endl; + gc.get()->output << "else {" << std::endl; + + for (std::vector::reverse_iterator iter = ast->children[0]->children.rbegin(), end = ast->children[0]->children.rend(); iter != end; ++iter) { + VarDeclExprAST *varDecl = dynamic_cast(*iter); + gc.get()->decls << lookupAssocType(varDecl->vi->type) << " " << varDecl->vi->name << ";" << std::endl; + gc.get()->output << lookupPopForVar(varDecl->vi) << ";" << std::endl; + + ++(currentScopeCount.back()); + scopeStack.push_back(varDecl->vi); + } + gc.get()->output << "}" << std::endl; + + gc.get()->output << "switch(contId__) {" <output << "case(" << currentContId << "):" << std::endl; + ++currentContId; + + for (std::vector::iterator iter = ast->children[1]->children.begin(), + end = ast->children[1]->children.end(); iter != end; ++iter) { + boost::shared_ptr gc_temp = visit (*iter); + if (gc_temp.get()->decls.str() != "") { + gc.get()->decls << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + gc.get()->output << ";" << std::endl; + } + + gc.get()->output << "}" << std::endl; + gc.get()->output << "--timeLeft__;" << std::endl; + gc.get()->output << "actor__->parentThread->timeSliceEndTime = timeLeft__;" << std::endl; + gc.get()->output << "actor__->actorState = ActorState::WAITING_FOR_ACTION;" << std::endl; + + int unwindAmount = currentScopeCount.back(); + for (int i = 0; i < unwindAmount; ++i) { + VariableInfo *vi = scopeStack.back(); + if ((vi->type.requiresCopyDelete())&&(!checkIfActor(vi->type.declType))) { + gc.get()->output << "if (" << vi->name << " != NULL) {" << std::endl; + gc.get()->output << " delete(" << vi->name << ");" << std::endl; + gc.get()->output << "}" << std::endl; + } + scopeStack.pop_back(); + } + currentScopeCount.pop_back(); + + gc.get()->output << "}" << std::endl; + + } + inAction = false; + return gc; +} +boost::shared_ptr CodegenCPPOutput::visit(AppAST *ast) { + boost::shared_ptr gc(new GeneratedCode), gc_temp(new GeneratedCode); + + ActionAST *actast; + FunctionAST *funast; + ActorAST *actorast; + ClassAST *classast; + + ActionAST *mainAction = NULL; + + gc.get()->decls << "//automatically generated by Minnow->C++ codegen (0.1)" << std::endl; + gc.get()->decls << "#include \"aquarium.hpp\"" << std::endl; + + gc.get()->decls << "inline int convertToInt(std::string s){std::istringstream i(s);int x;i >> x;return x;}" << std::endl; + gc.get()->decls << "inline void puti(int i){std::cout << i << std::endl; }" << std::endl; + gc.get()->decls << "inline void putstring(std::string s){std::cout << s << std::endl; }" << std::endl; + + DeclStage::Stage stage; + for (int i = 0; i < 3; ++i) { + switch(i) { + case (0) : stage = DeclStage::FORWARD; break; + case (1) : stage = DeclStage::DECL; break; + case (2) : stage = DeclStage::IMPL; break; + } + for (std::vector::iterator iter = ast->children.begin(), end = ast->children.end(); iter != end; ++iter) { + //std::cout << "CHILD: " << (*iter)->nodeType << " at " << (*iter)->filepos.lineNumber << " " << (*iter)->filepos.colStart << std::endl; + switch ((*iter)->type()) { + case (NodeType::Action) : + actast = dynamic_cast(*iter); + if (stage == DeclStage::FORWARD) { + if (dynamic_cast(actast->children[0])->name != "main") { + throw CompilerException("Only the 'main' action is allowed at the top level"); + } + else { + mainAction = actast; + } + } + gc_temp = visit(actast, "Actor", stage); + break; + case (NodeType::Function) : + if (stage == DeclStage::FORWARD) { + funStack.push_back(dynamic_cast((*iter)->children[0])); + } + funast = dynamic_cast(*iter); + gc_temp = visit(funast, stage); + break; + case (NodeType::Actor) : + actorast = dynamic_cast(*iter); + gc_temp = visit(actorast, stage); + break; + case (NodeType::Class) : + classast = dynamic_cast(*iter); + gc_temp = visit(classast, stage); + break; + default: + throw CompilerException("Unhandled element in application", (*iter)->filepos); + break; + } + if (gc_temp.get()->decls.str() != "") { + gc.get()->output << gc_temp.get()->decls.str(); + } + if (gc_temp.get()->inits.str() != "") { + gc.get()->output << gc_temp.get()->inits.str(); + } + if (gc_temp.get()->output.str() != "") { + gc.get()->output << gc_temp.get()->output.str(); + } + + } + } + + gc.get()->output << "int main(int argc, char *argv[]){" << std::endl; + + if (mainAction == NULL) { + throw CompilerException("Can not find 'main' action"); + } + else { + PrototypeAST* mainProto = dynamic_cast(mainAction->children[0]); + + if (mainProto->children.size() > 0) { + gc.get()->output << "VM_Main(argc, argv, Actor__main_action, true);" << std::endl; + } + else { + gc.get()->output << "VM_Main(argc, argv, Actor__main_action, false);" << std::endl; + } + } + + + gc.get()->output << "return(0);" << std::endl; + gc.get()->output << "};" << std::endl; + return gc; +} + +std::string CodegenCPPOutput::translate(ASTNode *ast) { + boost::shared_ptr gc(new GeneratedCode); + + gc = visit(ast); + + std::ostringstream output; + if (gc.get()->decls.str() != "") { + output << gc.get()->decls.str(); + } + if (gc.get()->inits.str() != "") { + output << gc.get()->inits.str(); + } + if (gc.get()->output.str() != "") { + output << gc.get()->output.str(); + } + + return output.str(); +} diff --git a/src/minnow/codegen_cppoutput.hpp b/src/minnow/codegen_cppoutput.hpp new file mode 100644 index 0000000..bae09fd --- /dev/null +++ b/src/minnow/codegen_cppoutput.hpp @@ -0,0 +1,489 @@ +#ifndef CODEGEN_CPPOUTPUT_HPP +#define CODEGEN_CPPOUTPUT_HPP + +#include +#include +#include +#include + +#include + +#include "parser.hpp" + +class DeclStage { public: enum Stage {FORWARD, DECL, IMPL}; }; + +struct GeneratedCode { + std::ostringstream decls; + std::ostringstream inits; + std::ostringstream output; +}; + +class CodegenCPPOutput { + std::map actors; + std::map classes; + //std::vector builtins; + std::vector scopeStack; + std::vector funStack; + //std::vector externFns; + //std::vector externFns; + std::string dontCareReturnVal; + long startOfLinePos; + int scopeContainerId; + std::vector currentScopeCount; //counts the number of elems local to the current scope + unsigned int currentFunGroup; + int currentContId; + int tempNumber; + + bool inAction; + + std::string nextTemp() { + char buffer[30]; + sprintf(buffer, "tmp%i__", tempNumber); + ++tempNumber; + return std::string(buffer); + } + + VariableInfo* findVarInScope(const std::string &name) { + for (std::vector::reverse_iterator iter = scopeStack.rbegin(), end = scopeStack.rend(); iter != end; ++iter) { + if ((*iter)->name == name) { + return *iter; + } + } + + return NULL; + } + + //FIXME: This won't work with function overloading + + bool checkIfExtern(const std::string &fn) { + for (std::vector::reverse_iterator iter = funStack.rbegin(), end = funStack.rend(); iter != end; ++iter) { + if ((*iter)->name == fn) { + return (*iter)->isExtern; + } + } + + return false; + +// if (find(externFns.begin(), externFns.end(), fn) != externFns.end()) { +// return true; +// } +// else { +// return false; +// } + } + + bool checkIfActor(const std::string &s) { + std::map::iterator finder = actors.find(s); + + return (finder != actors.end()); + } + + //void setupDontCare(const std::string &typeName) { + void setupDontCare(const TypeInfo &ti) { + //FIXME: Add in the position this happened + + if (ti.containerType == ContainerType::Array) { + dontCareReturnVal = "NULL"; + } + else if (ti.declType == "void") { + dontCareReturnVal = ""; + } + else if (ti.declType == "bool") { + dontCareReturnVal = "false"; + } + else if (ti.declType == "int") { + dontCareReturnVal = "0"; + } + else if (ti.declType == "double") { + dontCareReturnVal = "0.0"; + } + else if (ti.declType == "string") { + dontCareReturnVal = "\"\""; + } + else { + std::ostringstream msg; + msg << "Unknown typename '" << ti.declType << "'"; + + throw CompilerException(msg.str()); + } + } + + bool isCopyDelete(const TypeInfo &ti) { + bool answer = true; + if (ti.declType == "void") { + answer = false; + } + else if (ti.declType == "bool") { + answer = false; + } + else if (ti.declType == "int") { + answer = false; + } + else if (ti.declType == "double") { + answer = false; + } + else if (ti.declType == "string") { + answer = false; + } + + return answer; + } + + std::string lookupAssocType(const TypeInfo &ti) { + //std::map::iterator finder = actors.find(ti.declType); + + //if (finder != actors.end()) { + if (checkIfActor(ti.declType)) { + if (ti.containerType == ContainerType::Array) { + return "std::vector*"; + } + else { + return "actorId_t"; + } + } + else { + std::ostringstream arrayType; + if (ti.containerType == ContainerType::Array) { + if ((ti.declType == "int") || (ti.declType == "float") || (ti.declType == "bool") || (ti.declType == "double") || (ti.declType == "void")) { + arrayType << "std::vector<" << ti.declType << ">*"; + } + else if (ti.declType == "string") { + arrayType << "std::vector*"; + } + else { + arrayType << "std::vector<" << ti.declType << "*>*"; + } + return arrayType.str(); + } + else { + if ((ti.declType == "int") || (ti.declType == "float") || (ti.declType == "bool") || (ti.declType == "double") || (ti.declType == "void")) { + arrayType << ti.declType; + } + else if (ti.declType == "string") { + arrayType << "std::string"; + } + else { + arrayType << ti.declType << "*"; + } + return arrayType.str(); + } + } + } + + //std::string lookupPushForTypeAndBlock(const boost::shared_ptr ti, const std::string &block) { + std::string lookupPushForTypeAndBlock(TypeInfo &ti, const std::string &block) { + std::ostringstream o; + //std::map::iterator finder = actors.find(ti.get()->declType); + //if (finder != actors.end()) { + if (checkIfActor(ti.declType)) { + o << " tmpTU__.UInt32 = " << block << ";" << std::endl; + } + else if (ti.containerType == ContainerType::Array) { + //o << " tmpTU__.VoidPtr = " << block << ";" << std::endl; + std::string allocType = lookupAssocType(ti); + allocType = allocType.erase(allocType.size()-1); + + + o << " tmpTU__.VoidPtr = new " << allocType << "();" << std::endl; + if (ti.containedTypes[0].requiresCopyDelete()) { + o << " for (int i__=0; i__ < " << block << "->size(); ++i__) { ((" + << lookupAssocType(ti) << ")(tmpTU__.VoidPtr))->push_back(new " + << ti.declType << "((*" << block << ")[i__])); }" << std::endl; + } + else { + o << " for (int i__=0; i__ < " << block << "->size(); ++i__) { ((" + << lookupAssocType(ti) << ")(tmpTU__.VoidPtr))->push_back((*" + << block << ")[i__]); }" << std::endl; + } + } + else if (ti.declType == "int") { + o << " tmpTU__.UInt32 = " << block << ";" << std::endl; + } + else if (ti.declType == "string") { + o << " tmpTU__.VoidPtr = strcpy(new char[" << block << ".size() + 1], " << block << ".c_str());" << std::endl; + } + else if (ti.declType == "double") { + o << " tmpTU__.Double = " << block << ";" << std::endl; + } + else if (ti.declType == "float") { + o << " tmpTU__.Float = " << block << ";" << std::endl; + } + else if (ti.declType == "bool") { + o << " tmpTU__.Bool = " << block << ";" << std::endl; + } + else { + //TRIM + std::string allocType = lookupAssocType(ti); + allocType = allocType.erase(allocType.size()-1); + o << " tmpTU__.VoidPtr = new " << allocType << "(" << block << ");" << std::endl; + } + return o.str(); + } + + std::string lookupPushForVar(const VariableInfo *vi) { + std::ostringstream o; + if (vi->type.containerType == ContainerType::Array) { + o << " tmpTU__.VoidPtr = " << vi->name << ";" << std::endl; + } + else if (vi->type.declType == "int") { + o << " tmpTU__.UInt32 = " << vi->name << ";" << std::endl; + } + else if (vi->type.declType == "string") { + o << " tmpTU__.VoidPtr = strcpy(new char[" << vi->name << ".size() + 1], " << vi->name << ".c_str());" << std::endl; + } + else if (vi->type.declType == "double") { + o << " tmpTU__.Double = " << vi->name << ";" << std::endl; + } + else if (vi->type.declType == "float") { + o << " tmpTU__.Float = " << vi->name << ";" << std::endl; + } + else if (vi->type.declType == "bool") { + o << " tmpTU__.Bool = " << vi->name << ";" << std::endl; + } + else { + //std::map::iterator finder = actors.find(vi->type.declType); + + //if (finder != actors.end()) { + if (checkIfActor(vi->type.declType)) { + o << " tmpTU__.UInt32 = " << vi->name << ";" << std::endl; + } + else { + o << " tmpTU__.VoidPtr = " << vi->name << ";" << std::endl; + } + + } + o << " actor__->heapStack.push_back(tmpTU__);" << std::endl; + return o.str(); + } + + std::string lookupPopForVar(const VariableInfo *vi) { + std::ostringstream o; + if (vi->type.containerType == ContainerType::Array) { + o << " " << vi->name << " = (" << lookupAssocType(vi->type) << ")(actor__->heapStack.back().VoidPtr); actor__->heapStack.pop_back();" << std::endl; + } + else if (vi->type.declType == "int") { + o << " " << vi->name << " = actor__->heapStack.back().UInt32; actor__->heapStack.pop_back();" << std::endl; + } + else if (vi->type.declType == "string") { + o << " " << vi->name << ".assign((char *)(actor__->heapStack.back().VoidPtr)); actor__->heapStack.pop_back(); delete((char *)(tmpTU__.VoidPtr));" << std::endl; + } + else if (vi->type.declType == "double") { + o << " " << vi->name << " = actor__->heapStack.back().Double; actor__->heapStack.pop_back();" << std::endl; + } + else if (vi->type.declType == "float") { + o << " " << vi->name << " = actor__->heapStack.back().Float; actor__->heapStack.pop_back();" << std::endl; + } + else if (vi->type.declType == "bool") { + o << " " << vi->name << " = actor__->heapStack.back().Bool; actor__->heapStack.pop_back();" << std::endl; + } + else { + //std::map::iterator finder = actors.find(vi->type.declType); + + //if (finder != actors.end()) { + if (checkIfActor(vi->type.declType)) { + o << " " << vi->name << " = (" << lookupAssocType(vi->type) << ")(actor__->heapStack.back().UInt32); actor__->heapStack.pop_back();" << std::endl; + } + else { + o << " " << vi->name << " = (" << lookupAssocType(vi->type) << ")(actor__->heapStack.back().VoidPtr); actor__->heapStack.pop_back();" << std::endl; + } + + } + return o.str(); + } + + /* + std::string lookupInternalType(const VariableInfo *vi) { + std::map::iterator finder = actors.find(vi->type.declType); + + if (finder != actors.end()) { + if (vi->type.typeType == TypeType::Array) { + return "std::vector*"; + } + else { + return "actorId_t"; + } + } + else { + if (vi->type.typeType == TypeType::Array) { + std::ostringstream arrayType; + arrayType << "std::vector<" << lookupAssocType(vi->type.declType) << ">*"; + return arrayType.str(); + } + else { + return lookupAssocType(vi->type.declType); + } + } + } + */ + + TypeInfo lookupReturnTypeInfo(const CallExprAST *ast) { + //std::string returnVal("int"); + + for (std::vector::reverse_iterator iter = funStack.rbegin(), + end = funStack.rend(); iter != end; ++iter) { + //FIXME: This is insufficient for overloaded functions + if ((*iter)->name == ast->name) { + //TypeInfo ti((*iter)->type, TypeType::Scalar); + return (*iter)->returnType; + } + } + + /* + if (find(externFns.begin(), externFns.end(), ast->name) == externFns.end()) { + std::ostringstream msg; + msg << "Can not find function '" << ast->name << "'"; + std::string outmsg = msg.str(); + throw CompilerException(outmsg, ast->pos); + } + else { + TypeInfo ti; + return ti; + } + */ + //return ""; + //return returnVal; + + std::ostringstream msg; + msg << "Can not find function '" << ast->name << "'"; + std::string outmsg = msg.str(); + throw CompilerException(outmsg, ast->filepos); + } + + std::string lookupReturnType(const CallExprAST *ast, const std::string &container) { + //std::string returnVal("int"); + + if (container == "") { + for (std::vector::reverse_iterator iter = funStack.rbegin(), end = funStack.rend(); iter != end; ++iter) { + //FIXME: This is insufficient for overloaded functions + if ((*iter)->name == ast->name) { + //TypeInfo ti((*iter)->type, TypeType::Scalar); + return lookupAssocType((*iter)->returnType); + } + } + } + //TODO: Re-enable this + + else { + for (std::vector::iterator iter = classes[container]->children.begin(), + end = classes[container]->children.end(); iter != end; ++iter) { + + FunctionAST *fast = dynamic_cast(*iter); + if (fast != NULL) { + //FIXME: This is insufficient for overloaded functions + PrototypeAST *proto = dynamic_cast(fast->children[0]); + if (proto->name == ast->name) { + //TypeInfo ti((*iter)->type, TypeType::Scalar); + return lookupAssocType(proto->returnType); + } + } + } + } + + std::ostringstream msg; + msg << "Can not find function '" << ast->name << "'"; + std::string outmsg = msg.str(); + throw CompilerException(outmsg, ast->filepos); + + /* + if (find(externFns.begin(), externFns.end(), ast->name) == externFns.end()) { + std::ostringstream msg; + msg << "Can not find function '" << ast->name << "'"; + std::string outmsg = msg.str(); + throw CompilerException(outmsg, ast->pos); + return ""; + } + else { + return ""; + } + */ + //return returnVal; + } + + std::string outputResumeBlock() { + std::ostringstream output; + + int resumeCount = 0; + int scopeStackSize = scopeStack.size() - 1; + for (unsigned int i = scopeContainerId; i < currentScopeCount.size(); ++i) { + resumeCount += currentScopeCount[i]; + } + + output << "if (actor__->isResuming) {" << std::endl; + for (int i = 0; i < resumeCount; ++i) { + VariableInfo *vi = scopeStack[scopeStackSize-i]; + output << lookupPopForVar(vi); + } + output << " if (actor__->heapStack.size() == 0) {" << std::endl; + output << " actor__->isResuming = false;" << std::endl; + output << " }" << std::endl; + output << "}" << std::endl; + + return output.str(); + } + + std::string outputPauseBlock(bool decrement) { + std::ostringstream output; + + int resumeCount = 0; + int scopeStackSize = scopeStack.size() - 1; + for (unsigned int i = scopeContainerId; i < currentScopeCount.size(); ++i) { + resumeCount += currentScopeCount[i]; + } + + if (decrement) { + output << "if (timeLeft__ > 0) {" << std::endl; + output << " --timeLeft__;" << std::endl; + output << "}" << std::endl; + output << "else {" << std::endl; + } + else { + output << "if (timeLeft__ == 0) {" << std::endl; + } + + output << " actor__->parentThread->timeSliceEndTime = timeLeft__;" << std::endl; + + for (int i = scopeStackSize-resumeCount + 1; i <= scopeStackSize; ++i) { + VariableInfo *vi = scopeStack[i]; + output << lookupPushForVar(vi); + } + output << " tmpTU__.UInt32 = " << (currentContId-1) << ";" << std::endl; + output << " actor__->heapStack.push_back(tmpTU__);" << std::endl; + output << " return " << dontCareReturnVal << ";" << std::endl; + output << "}" << std::endl; + + return output.str(); + } + + boost::shared_ptr resolveType(ASTNode *ast); + boost::shared_ptr handleCall(CallExprAST *ast, const std::string &container, const std::string &container_name); + + boost::shared_ptr visit(ASTNode *ast); //catch all that will dispatch out to others + boost::shared_ptr visit(NumberExprAST *ast); + boost::shared_ptr visit(BooleanExprAST *ast); + boost::shared_ptr visit(QuoteExprAST *ast); + boost::shared_ptr visit(VariableExprAST *ast); + boost::shared_ptr visit(VarDeclExprAST *ast); + boost::shared_ptr visit(ArrayIndexedExprAST *ast); + //boost::shared_ptr visit(ArrayDeclExprAST *ast); + boost::shared_ptr visit(EndExprAST *ast); + boost::shared_ptr visit(IfExprAST *ast); + boost::shared_ptr visit(WhileExprAST *ast); + boost::shared_ptr visit(BinaryExprAST *ast); + boost::shared_ptr visit(CallExprAST *ast); + boost::shared_ptr visit(ClassAST *ast, DeclStage::Stage stage); + boost::shared_ptr visit(ActorAST *ast, DeclStage::Stage stage); + boost::shared_ptr visit(PrototypeAST *ast, DeclStage::Stage stage); + boost::shared_ptr visit(ActionAST *ast, std::string actorName, DeclStage::Stage stage); + boost::shared_ptr visit(FunctionAST *ast, DeclStage::Stage stage); + boost::shared_ptr visit(AppAST *ast); +public: + std::string translate(ASTNode *ast); + + CodegenCPPOutput() { + //builtins.push_back("int"); + tempNumber = 0; + inAction = false; + }; +}; + +#endif diff --git a/src/minnow/lexer.cpp b/src/minnow/lexer.cpp new file mode 100644 index 0000000..6ea950e --- /dev/null +++ b/src/minnow/lexer.cpp @@ -0,0 +1,209 @@ +#include + +#include + +#include "lexer.hpp" + +#define INC_LOC ++strIter; ++colNumber; + +inline bool ispunctother(char c) { + if (ispunct(c) && (c != '"') && (c != '\'') && (c != '{') && (c != '}') && (c != '[') && (c != ']') && (c != '(') && + (c != ')')) + { + return true; + } + else { + return false; + } +} + +std::vector tokenize(std::string sourceText, std::string filename) { + std::string::iterator strIter = sourceText.begin(), strEnd = sourceText.end(); + std::vector tokens; + int lineNumber = 1; + int colNumber = 1; + Token *tok; + bool extendLine = false; + + while (strIter != strEnd) { + if (isspace(*strIter)) { + if (*strIter == '\r') { + int colStart = colNumber; + + if (*strIter == '\n') { + ++strIter; + } + + if (!extendLine) { + tok = new Token(TokenType::EOL, "", filename, lineNumber, colStart, colNumber); + tokens.push_back(tok); + } + extendLine = false; + + ++lineNumber; + colNumber = 1; + ++strIter; + } + else if (*strIter == '\n') { + int colStart = colNumber; + + if (!extendLine) { + tok = new Token(TokenType::EOL, "", filename, lineNumber, colStart, colNumber); + tokens.push_back(tok); + } + extendLine = false; + + ++lineNumber; + colNumber = 1; + ++strIter; + } + else { + INC_LOC; + } + } + else if (isalpha(*strIter)) { + //we're either in a reserved word or an identifier + std::string id; + int colStart = colNumber; + while ((isalnum(*strIter) || (*strIter == '_')) && (strIter != strEnd)) { + id += *strIter; + INC_LOC; + } + //FIXME: why is 'def' more special than other keywords not handled like this? + if (id == "def") { + tok = new Token(TokenType::Def, id, filename, lineNumber, colStart, colNumber); + } + else if ((id == "true") || (id == "false")) { + tok = new Token(TokenType::Bool, id, filename, lineNumber, colStart, colNumber); + } + else { + tok = new Token(TokenType::Id, id, filename, lineNumber, colStart, colNumber); + } + tokens.push_back(tok); + } + else if (isdigit(*strIter)) { + std::string num; + int colStart = colNumber; + bool hasDot = false; + while ((isdigit(*strIter) || (*strIter == '.')) && (strIter != strEnd)) { + if (*strIter == '.') { + if (hasDot) + throw CompilerException("Too many decimal places in number", lineNumber, colStart, colNumber, filename); + else + hasDot = true; + } + num += *strIter; + INC_LOC; + } + tok = new Token(TokenType::Num, num, filename, lineNumber, colStart, colNumber); + //we're in a number + tokens.push_back(tok); + } + else if (*strIter == '"') { + //quoted string + std::string strVal; + int colStart = colNumber; + INC_LOC; + while ((strIter != strEnd) && (*strIter != '"')) { + if (*strIter == '\\') { + ++strIter; + if (strIter != strEnd) { + switch (*strIter) { + default: + strVal += *strIter; + } + } + } + else { + strVal += *strIter; + } + INC_LOC; + } + + if (*strIter == '"') ++strIter; + + tok = new Token(TokenType::String, strVal, filename, lineNumber, colStart, colNumber); + //we're in a number + tokens.push_back(tok); + } + else if (*strIter == '\'') { + //quoted string + std::string chrVal; + int colStart = colNumber; + INC_LOC; + while ((strIter != strEnd) && (*strIter != '\'')) { + if (*strIter == '\\') { + ++strIter; + if (strIter != strEnd) { + switch (*strIter) { + default: + chrVal += *strIter; + } + } + } + else { + chrVal += *strIter; + } + INC_LOC; + } + + if (*strIter == '\'') ++strIter; + + tok = new Token(TokenType::Char, chrVal, filename, lineNumber, colStart, colNumber); + //we're in a number + tokens.push_back(tok); + } + else if ((*strIter == '(') || (*strIter == ')') || (*strIter == '[') || (*strIter == ']') || (*strIter == '{') || + (*strIter == '}')) { + + //Handle the "one off" type of symbols, those that don't clump and have to be handled separately + std::string oneoff; + oneoff += *strIter; + int colStart = colNumber; + tok = new Token(TokenType::Symbol, oneoff, filename, lineNumber, colStart, colNumber); + tokens.push_back(tok); + INC_LOC; + } + else if (ispunctother(*strIter)) { + std::string punct; + int colStart = colNumber; + while ((ispunctother(*strIter)) && (strIter != strEnd)) { + punct += *strIter; + INC_LOC; + } + if (punct == "...") { + //this means the line continues to the next line, so let's eat that carriage return and keep going + extendLine = true; + } + else if (punct == "/*") { + INC_LOC; + while (!((*strIter == '*')&&(*(strIter+1) == '/')) && (strIter != strEnd)) { + INC_LOC; + } + if (strIter == strEnd) { + throw CompilerException("Unclosed comment", lineNumber, colStart, colStart, filename); + } + else { + //if we're okay, skip over the end of the comment + INC_LOC; + INC_LOC; + } + } + else if (punct == "//") { + INC_LOC; + while ((*strIter != '\r')&&(*strIter != '\n') && (strIter != strEnd)) { + INC_LOC; + } + } + else { + tok = new Token(TokenType::Symbol, punct, filename, lineNumber, colStart, colNumber); + tokens.push_back(tok); + } + } + else { + throw CompilerException("Unknown character", lineNumber, colNumber, colNumber, filename); + } + } + + return tokens; +} diff --git a/src/minnow/lexer.hpp b/src/minnow/lexer.hpp new file mode 100644 index 0000000..84cad99 --- /dev/null +++ b/src/minnow/lexer.hpp @@ -0,0 +1,73 @@ +#ifndef LEXER_HPP +#define LEXER_HPP + +#include +#include +#include +#include + +class TokenType { +public: + enum Type { Id, Num, Char, String, Def, Bool, Symbol, EOL}; +}; + +struct FilePos { + std::string filename; + unsigned int lineNumber, colStart, colEnd; + + FilePos& operator=( const FilePos& newPos ) { + filename = newPos.filename; + lineNumber = newPos.lineNumber; + colStart = newPos.colStart; + colEnd = newPos.colEnd; + + return *this; + } +}; + +struct Token { + TokenType::Type tokenType; + std::string data; + FilePos pos; + + Token () { } + Token(TokenType::Type tType, std::string tData) : tokenType(tType), data(tData) { } + Token(TokenType::Type tType, std::string tData, std::string tFilename, int tLineNumber, int tColStart, int tColEnd) : + tokenType(tType), data(tData) { + pos.filename = tFilename; + pos.lineNumber = tLineNumber; + pos.colStart = tColStart; + pos.colEnd = tColEnd; + } +}; + +class CompilerException : public std::runtime_error { +public: + CompilerException(const std::string &msg, const int lineNumber, const int colStart, const int colEnd, const std::string &filename) + : std::runtime_error(build_message(msg, lineNumber, colStart, colEnd, filename)) { + } + + CompilerException(const std::string &msg, const Token *token) + : std::runtime_error(build_message(msg, token->pos.lineNumber, token->pos.colStart, token->pos.colEnd, token->pos.filename)) { + } + + CompilerException(const std::string &msg, const FilePos &pos) + : std::runtime_error(build_message(msg, pos.lineNumber, pos.colStart, pos.colEnd, pos.filename)) { + } + + CompilerException(const std::string &msg) + : std::runtime_error(msg) { + } + + std::string build_message(const std::string &msg, const int lineNumber, const int colStart, const int colEnd, const std::string &filename) const { + std::ostringstream msg_builder; + + msg_builder << msg << " at line " << lineNumber << " column " << colStart << " in '" << filename << "'"; + + return msg_builder.str(); + } +}; + +std::vector tokenize(std::string sourceText, std::string filename); + +#endif //LEXER_HPP diff --git a/src/minnow/main.cpp b/src/minnow/main.cpp new file mode 100644 index 0000000..32e017b --- /dev/null +++ b/src/minnow/main.cpp @@ -0,0 +1,167 @@ +#include +#include +#include + +#include + +#include + +#include "parser.hpp" +#include "analyser.hpp" +#include "codegen_cppoutput.hpp" + +namespace po = boost::program_options; + +int main(int argc, char *argv[]) { + std::string outfile_name = ""; + std::string outexe_name = ""; + bool output_debug = false; + std::vector allToks; + CodegenCPPOutput cppoutput; + Analyser analyser; + + po::options_description general_opts("General options"); + general_opts.add_options() + ("help,h", "help message") + ("debugtree,t", "output debug parse tree") + ("tocppfile,c", po::value(), "output to C++ file") + ("toexe,o", po::value(), "(attempt to) compile to an exe") + ; + po::options_description hidden_opts("Hidden options"); + hidden_opts.add_options() + ("input-file", po::value< std::vector >(), "input file") + ; + po::positional_options_description p; + p.add("input-file", -1); + + po::options_description visible_opts(""); + visible_opts.add(general_opts); + + po::options_description all_opts("All options"); + all_opts.add(general_opts).add(hidden_opts); + + + po::variables_map vm; + //po::store(po::parse_command_line(argc, argv, desc), vm); + po::store(po::command_line_parser(argc, argv).options(all_opts).positional(p).run(), vm); + po::notify(vm); + + std::ostringstream headerBlock; + headerBlock << "extern int convertToInt(string s)" << std::endl; + headerBlock << "extern void puti(int i)" << std::endl; + headerBlock << "extern void putstring(string s)" << std::endl; + headerBlock << "extern void exit(int i)" << std::endl; + std::vector toksPrelude = tokenize(headerBlock.str(), "standard include"); + allToks.insert(allToks.end(), toksPrelude.begin(), toksPrelude.end()); + + if (vm.count("help")) { + std::cout << "Usage: minnow " << std::endl; + std::cout << visible_opts << std::endl; + return 0; + } + + if (vm.count("debugtree")) { + output_debug = true; + } + + if (vm.count("tocppfile")) { + //std::cout << "Outputing to: " << vm["tocppfile"].as() << std::endl; + outfile_name = vm["tocppfile"].as(); + } + + if (vm.count("toexe")) { + //std::cout << "Outputing to: " << vm["tocppfile"].as() << std::endl; + outexe_name = vm["toexe"].as(); + } + + if (vm.count("input-file")) { + //std::cout << "Input files are: " << std::endl; + std::vector files = vm["input-file"].as< std::vector >(); + for (std::vector::iterator iter = files.begin(), end = files.end(); iter != end; ++iter) { + std::ifstream sourceFile(iter->c_str(), std::ios::binary); + if (sourceFile.good()) { + // get length of file: + sourceFile.seekg (0, std::ios::end); + int length = sourceFile.tellg(); + sourceFile.seekg (0, std::ios::beg); + + char *fBuffer = new char[length+1]; + sourceFile.read(fBuffer, length); + fBuffer[length] = 0; + std::string sourceCode(fBuffer); + + //std::string sourceFilename(argv[i]); + + //printf("Size: %i %i\n", length, strlen(fBuffer)); + //puts(fBuffer); + + std::vector toks = tokenize(sourceCode, *iter); + if (output_debug) { + std::vector::iterator beginToks = toks.begin(), endToks = toks.end(); + ASTNode *astDebug = parse(beginToks, endToks); + std::cout << "Source file: " << *iter << std::endl; + analyser.debugOutputPass(astDebug, 0); + } + + allToks.insert(allToks.end(), toks.begin(), toks.end()); + + } + else { + std::cout << "Can not find file: " << *iter << std::endl; + exit(1); + } + } + } + else { + std::cout << "Usage: minnow " << std::endl; + std::cout << visible_opts << std::endl; + return 0; + } + + std::vector::iterator beginToks = allToks.begin(), endToks = allToks.end(); + + try { + + ASTNode *ast = parse(beginToks, endToks); + ast = analyser.scopeAndTypesPass(ast); + std::string output = cppoutput.translate(ast); + + if (outexe_name != "") { + std::ofstream outfile; + outfile.open("tmpXXXXX.cpp"); + outfile << output; + outfile.close(); + std::ostringstream exe_cmdline; + + //Linux (tested on Ubuntu w/ boost 1.35 packages) + //exe_cmdline << "g++ -O3 -o " << outexe_name << " tmpXXXXX.cpp -Isrc/aquarium -L. -laquarium -lboost_thread"; + + //OS X + MacPorts + exe_cmdline << "g++ -O3 -o " << outexe_name << " tmpXXXXX.cpp -Isrc/aquarium -L. -laquarium -I/opt/local/include/boost-1_35 -L/opt/local/lib -lboost_thread-mt -lboost_program_options-mt"; + + //MinGW+Boost setup + //exe_cmdline << "g++ -O3 -o " << outexe_name << " tmpXXXXX.cpp -Isrc/aquarium -L. -laquarium -I/mingw/include -L/mingw/lib -lboost_thread -lboost_program_options"; + //exe_cmdline << "g++ -ggdb -O3 -o " << outexe_name << " tmpXXXXX.cpp -I../../aquarium -L. -laquarium -I/mingw/include -L/mingw/lib -lboost_thread -lboost_program_options"; + + if (system(exe_cmdline.str().c_str()) == 0) { + //remove("tmpXXXXX.cpp"); + } + else { + std::cout << "Used cmdline: " << exe_cmdline.str() << std::endl; + } + } + else if (outfile_name != "") { + std::ofstream outfile; + outfile.open(outfile_name.c_str()); + outfile << output; + outfile.close(); + } + else { + //std::cout << output; + } + } + catch (CompilerException &ce) { + std::cout << "Error: " << ce.what() << std::endl; + } + +} diff --git a/src/minnow/parser.cpp b/src/minnow/parser.cpp new file mode 100644 index 0000000..742abff --- /dev/null +++ b/src/minnow/parser.cpp @@ -0,0 +1,788 @@ +#include + +#include "lexer.hpp" +#include "parser.hpp" + +const int EQUATE_PREC = 2; +const int MSG_PREC = 4; +const int COMMA_GROUP_PREC = 5; +const int SCOPE_PREC = 6; +const int COMPARE_PREC = 10; +const int ADD_SUB_PREC = 20; +const int MULT_DIV_PREC = 30; +const int POWER_PREC = 40; + +TypeInfo parse_typesig(std::vector::iterator &iter, + std::vector::iterator &end) { + + TypeInfo ti; + if (iter == end) { + throw CompilerException("Incomplete type signature", *(--iter)); + } + ti.declType = (*iter)->data; + + ++iter; + + if ((*iter)->data == "[") { + ++iter; + if (iter == end) { + throw CompilerException("Incomplete type signature", *(--iter)); + } + if ((*iter)->data != "]") { + throw CompilerException("Unsupported type signature", *(--iter)); + } + ++iter; + ti.containerType = ContainerType::Array; + + TypeInfo containedType; + containedType.declType = ti.declType; + containedType.containerType = ContainerType::Scalar; + ti.containedTypes.push_back(containedType); + } + + if (iter == end) { + throw CompilerException("Incomplete type signature", *(--iter)); + } + + return ti; +} + +VarDeclExprAST* parse_variable_decl(std::vector::iterator &iter, + std::vector::iterator &end) { + + bool isSpawn = false; + bool isAlloc = false; + + VariableInfo *vi = new VariableInfo(); + VarDeclExprAST *returnVal = NULL; + + if (iter == end) { + throw CompilerException("Expected variable declaration", *(--iter)); + } + + if ((*iter)->data == "var") { + //nothing + ++iter; + } + //FIXME: this is the _old_ style + else if ((*iter)->data == "spawn") { + isSpawn = true; + ++iter; + } + else if ((*iter)->data == "new") { + isAlloc = true; + ++iter; + } + + vi->type = parse_typesig(iter, end); + if ((*iter)->tokenType != TokenType::Id) { + std::ostringstream msg; + msg << "Invalid variable declaration for '" << (*iter)->data << "'"; + throw CompilerException(msg.str(), *iter); + //throw CompilerException("Invalid variable declaration", *(iter)); + } + vi->name = (*iter)->data; + + returnVal = new VarDeclExprAST(vi, isSpawn, isAlloc); + returnVal->filepos = (*iter)->pos; + ++iter; + + if (iter != end) { + if ((*iter)->data == "[") { + ++iter; + if (iter == end) { + throw CompilerException("Incomplete default size", *(--iter)); + } + vi->size = parse_expression(iter, end); + /* + vi->type.containerType = ContainerType::Array; + + TypeInfo containedInfo; + containedInfo.containerType = ContainerType::Scalar; + containedInfo.declType = vi->type.declType; + vi->type.containedTypes.push_back(containedInfo); + */ + if ((*iter)->data != "]") { + throw CompilerException("Incomplete default size", *(--iter)); + } + ++iter; + } + } + + return returnVal; +} + +int get_precedence(const std::string &op) { + int binPrec = -1; + if (( op == "<") || ( op == ">") || (op == ">=") || (op == "<=") || + (op == "==") || (op == "!=") ) { + + binPrec = COMPARE_PREC; + } + else if (( op == "+") || ( op == "-")) { + binPrec = ADD_SUB_PREC; + } + else if (( op == "*") || ( op == "/")) { + binPrec = MULT_DIV_PREC; + } + else if ( op == "^") { + binPrec = POWER_PREC; + } + else if ( op == "=") { + binPrec = EQUATE_PREC; + } + else if ( (op == "!") || (op == "::") ) { + binPrec = MSG_PREC; + } + else if ( op == ".") { + binPrec = SCOPE_PREC; + } + else if ( op == ",") { + binPrec = COMMA_GROUP_PREC; + } + + return binPrec; +} + +ASTNode *parse_parens(std::vector::iterator &iter, + std::vector::iterator &end) { + ASTNode *returnVal; + ++iter; + if (iter == end) { + throw CompilerException("Unclosed parentheses", *(--iter)); + } + else { + returnVal = parse_expression(iter, end); + returnVal->filepos = (*iter)->pos; + if ((*iter)->data != ")") { + throw CompilerException("Unclosed parentheses", *(--iter)); + } + else { + ++iter; + } + } + + return returnVal; +} + +ASTNode *parse_array_index(std::vector::iterator &iter, + std::vector::iterator &end) { + ASTNode *returnVal; + ++iter; + if (iter == end) { + throw CompilerException("Unclosed square brackets", *(--iter)); + } + else { + returnVal = parse_expression(iter, end); + returnVal->filepos = (*iter)->pos; + if ((*iter)->data != "]") { + throw CompilerException("Unclosed square brackets", *(--iter)); + } + else { + ++iter; + } + } + + return returnVal; +} + +ASTNode *parse_fun_call(std::vector::iterator &iter, + std::vector::iterator &end) { + ASTNode *returnVal = NULL; + std::string name; + std::vector args; + std::vector unwinder; + + name = (*iter)->data; + ++iter; + if ((iter == end) || ((*iter)->data != "(")) { + --iter; + throw CompilerException("Internal integrity error relating to function calls", *iter); + } + ++iter; + if (iter == end) { + throw CompilerException("Expecting ')' for function call", *iter); + } + while (((*iter)->data != ")") && (iter != end)) { + args.push_back(parse_expression(iter, end)); + } + + if (iter != end) { + //skip the trailing ')' + ++iter; + } + + //what we have is probably a tree of args, but we want a flat list, so fix it + if (args.size() == 1) { + BinaryExprAST *beast = dynamic_cast(args[0]); + ASTNode *LHS; + if ((beast != NULL) && (beast->op == ",")) { + while ((beast != NULL) && (beast->op == ",")) { + unwinder.push_back(beast->children[1]); + LHS = beast->children[0]; + beast = dynamic_cast(LHS); + } + unwinder.push_back(LHS); + } + else { + unwinder.push_back(args[0]); + } + } + args.clear(); + for (std::vector::reverse_iterator riter=unwinder.rbegin(), + rend=unwinder.rend(); riter != rend; ++riter) { + args.push_back(*riter); + } + + returnVal = new CallExprAST(name); + returnVal->children.swap(args); + + returnVal->filepos = (*iter)->pos; + + return returnVal; +} + +ASTNode *parse_if_block(std::vector::iterator &iter, std::vector::iterator &end) { + IfExprAST *returnVal = new IfExprAST(); + returnVal->filepos = (*iter)->pos; + + ++iter; + if (iter == end) { + throw CompilerException("Expected if block", *(--iter)); + } + + returnVal->children.push_back(parse_expression(iter, end)); + + BlockAST *thenBlock = new BlockAST(); + while ((iter != end) && ((*iter)->data != "end") && ((*iter)->data != "else")) { + thenBlock->children.push_back(parse_expression(iter, end)); + } + returnVal->children.push_back(thenBlock); + + if (iter == end) { + throw CompilerException("Expected 'end' to end if block", *(--iter)); + } + + BlockAST *elseBlock = new BlockAST(); + if ((*iter)->data == "else") { + ++iter; + while ((iter != end) && ((*iter)->data != "end")) { + elseBlock->children.push_back(parse_expression(iter, end)); + } + } + returnVal->children.push_back(elseBlock); + + if (iter == end) { + throw CompilerException("Expected 'end' to end if block", *(--iter)); + } + else { + ++iter; + } + + return returnVal; +} + +ASTNode *parse_while_block(std::vector::iterator &iter, + std::vector::iterator &end) { + + WhileExprAST *returnVal = new WhileExprAST(); + returnVal->filepos = (*iter)->pos; + + ++iter; + if (iter == end) { + throw CompilerException("Expected while block", *(--iter)); + } + + returnVal->children.push_back(parse_expression(iter, end)); + + BlockAST *loopBlock = new BlockAST(); + while ((iter != end) && ((*iter)->data != "end")) { + ASTNode *ast = parse_expression(iter, end); + loopBlock->children.push_back(ast); + } + returnVal->children.push_back(loopBlock); + + if (iter == end) { + throw CompilerException("Expected 'end' to end while block", *(--iter)); + } + else { + ++iter; + } + + return returnVal; +} + + +ASTNode *parse_primary(std::vector::iterator &iter, + std::vector::iterator &end) { + ASTNode *returnVal = NULL; + ASTNode *index = NULL; + double val; + bool boolval; + + std::string name, type; + + switch ((*iter)->tokenType) { + case (TokenType::Num): + val = atof((*iter)->data.c_str()); + returnVal = new NumberExprAST(val); + returnVal->filepos = (*iter)->pos; + ++iter; + break; + case (TokenType::String): + returnVal = new QuoteExprAST((*iter)->data); + returnVal->filepos = (*iter)->pos; + ++iter; + break; + case (TokenType::Id): + if ((*iter)->data == "end") { + returnVal = new EndExprAST(); + returnVal->filepos = (*iter)->pos; + } + else if ((*iter)->data == "var") { + returnVal = parse_variable_decl(iter, end); + } + else if ((*iter)->data == "spawn") { + returnVal = parse_variable_decl(iter, end); + } + else if ((*iter)->data == "new") { + returnVal = parse_variable_decl(iter, end); + } + else if ((*iter)->data == "if") { + returnVal = parse_if_block(iter, end); + } + else if ((*iter)->data == "while") { + returnVal = parse_while_block(iter, end); + } + /* + else if ((*iter)->data == "receive") { + returnVal = parseDataRecvBlock(iter, end); + } + */ + else { + name = (*iter)->data; + ++iter; + + if (iter == end) { + returnVal = new VariableExprAST(name); + returnVal->filepos = (*iter)->pos; + } + else { + if ((*iter)->data == "(") { + --iter; + returnVal = parse_fun_call(iter, end); + } + else if ((*iter)->data == "[") { + index = parse_array_index(iter, end); + returnVal = new ArrayIndexedExprAST(name, index); + returnVal->filepos = (*iter)->pos; + } + else { + returnVal = new VariableExprAST(name); + returnVal->filepos = (*iter)->pos; + } + } + } + break; + case (TokenType::EOL): + ++iter; + break; + case (TokenType::Symbol): + if ((*iter)->data == "(") { + returnVal = parse_parens(iter, end); + } + /* + else if ((*iter)->data == "@") { + returnVal = parsePointcut(iter, end); + } + */ + else { + //std::cout << "Element: " << (*iter)->data << std::endl; + throw CompilerException("Unknown symbol", *iter); + } + break; + case (TokenType::Bool): + if ((*iter)->data == "true") { + boolval = true; + } + else if ((*iter)->data == "false") { + boolval = false; + } + else { + throw CompilerException("Unknown boolean value", *iter); + } + returnVal = new BooleanExprAST(boolval); + returnVal->filepos = (*iter)->pos; + ++iter; + + break; + default: + std::ostringstream msg; + msg << "Unknown token type:" << (*iter)->tokenType; + throw CompilerException(msg.str(), *iter); + } + + return returnVal; +} + +ASTNode *parse_binary_op(std::vector::iterator &iter, + std::vector::iterator &end, int currPrec, ASTNode *expr) { + + //This uses Chris Lattner's cool precedence parsing trick + //For more information see his kaleidoscope tutorial on the llvm website + int binPrec; + //let's see if what we're sitting on is a math symbol + while (iter != end) { + std::string op = (*iter)->data; + binPrec = get_precedence(op); + if (binPrec < currPrec) { + //the next symbol in line is lower precedence than we need + return expr; + } + ++iter; + if (iter != end) { + //check the precedence of the next hop + ASTNode *rhs = parse_primary(iter, end); + if (rhs == NULL) { + throw CompilerException("Incomplete math equation", *iter); + } + + //++iter; + + if (iter != end) { + std::string op = (*iter)->data; + int nextPrec = get_precedence(op); + + //std::cout << "Outside dive: " << (*iter)->data << " " << binPrec << " " << nextPrec << std::endl; + if (binPrec < nextPrec) { + //--iter; + //std::cout << "Diving in: " << (*iter)->data << std::endl; + rhs = parse_binary_op(iter, end, binPrec+1, rhs); + } + } + expr = new BinaryExprAST(op, expr, rhs); + expr->filepos = (*iter)->pos; + + } + else { + throw CompilerException("Incomplete math equation", *(--iter)); + } + } + return expr; +} + +ASTNode *parse_expression(std::vector::iterator &iter, std::vector::iterator &end) { + ASTNode *lhs = parse_primary(iter, end); + if (lhs != NULL) { + return parse_binary_op(iter, end, 0, lhs); + } + else { + return NULL; + } + + while (((*iter)->tokenType == TokenType::EOL) && (iter != end)) { + //skip EOL chars + ++iter; + } +} + +PrototypeAST* parse_prototype(std::vector::iterator &iter, + std::vector::iterator &end) { + PrototypeAST* returnVal = new PrototypeAST(); + returnVal->filepos = (*iter)->pos; + + //parse the return type + returnVal->returnType = parse_typesig(iter, end); + + if ((*iter)->data == "(") { + returnVal->name = returnVal->returnType.declType; + returnVal->returnType.declType = "void"; + } + else { + returnVal->name = (*iter)->data; + ++iter; + if (iter == end) { + throw CompilerException("Expected prototype definition", *(--iter)); + } + } + + if ((*iter)->data == "(") { + ++iter; + } + else { + throw CompilerException("Incomplete function definition", *iter); + } + + if (iter == end) { + throw CompilerException("Incomplete function definition", *iter); + } + + while (((*iter)->data != ")") && (iter != end)) { + returnVal->children.push_back(parse_variable_decl(iter, end)); + if ((iter != end) && ((*iter)->data == ",")) { + ++iter; + } + } + + if (iter != end) { + //skip the trailing ')' + ++iter; + } + return returnVal; +} + +FunctionAST* parse_function(std::vector::iterator &iter, + std::vector::iterator &end) { + + FunctionAST* returnVal = NULL; + + if ((iter != end) && ((*iter)->data == "extern")) { + ++iter; + if (iter != end) { + returnVal = new FunctionAST(); + returnVal->filepos = (*iter)->pos; + PrototypeAST *proto = parse_prototype(iter, end); + if (proto == NULL) { + throw CompilerException("Expecting external function definition", *iter); + } + proto->isExtern = true; + returnVal->children.push_back(proto); + returnVal->children.push_back(new BlockAST()); //add the code empty code block for consistency + } + } + else if ((iter != end) && ((*iter)->data == "def")) { + //we may be at a definition + ++iter; + if (iter != end) { + returnVal = new FunctionAST(); + returnVal->filepos = (*iter)->pos; + + PrototypeAST *proto = parse_prototype(iter, end); + if (proto == NULL) { + throw CompilerException("Expecting function definition", *iter); + } + proto->isExtern = false; + returnVal->children.push_back(proto); + + BlockAST *bodyBlock = new BlockAST(); + ASTNode *ast = parse_expression(iter, end); + while (iter != end) { + if (ast != NULL) { + if (ast->type() == NodeType::End) { + ++iter; + break; + } + else { + bodyBlock->children.push_back(ast); + } + } + ast = parse_expression(iter, end); + } + returnVal->children.push_back(bodyBlock); + } + } + + return returnVal; +} + +ActionAST* parse_action(std::vector::iterator &iter, + std::vector::iterator &end) { + + ActionAST* returnVal = NULL; + + if ((iter != end) && ((*iter)->data == "action")) { + //we may be at a definition + ++iter; + if (iter != end) { + returnVal = new ActionAST(); + returnVal->filepos = (*iter)->pos; + + PrototypeAST *proto = parse_prototype(iter, end); + if (proto == NULL) { + throw CompilerException("Expecting function definition", *iter); + } + proto->isExtern = false; + returnVal->children.push_back(proto); + + BlockAST *bodyBlock = new BlockAST(); + ASTNode *ast = parse_expression(iter, end); + while (iter != end) { + if (ast != NULL) { + if (ast->type() == NodeType::End) { + ++iter; + break; + } + else { + bodyBlock->children.push_back(ast); + } + } + ast = parse_expression(iter, end); + } + returnVal->children.push_back(bodyBlock); + } + } + + return returnVal; +} + +ClassAST* parse_class(std::vector::iterator &iter, + std::vector::iterator &end) { + + ClassAST *returnVal = NULL; + + if ((iter != end) && ((*iter)->data == "class")) { + ++iter; + if (iter != end) { + returnVal = new ClassAST(); + returnVal->name = (*iter)->data; + returnVal->filepos = (*iter)->pos; + + ++iter; + while ((iter != end) && ((*iter)->data != "end")) { + if ((*iter)->data == "def") { + FunctionAST *fun = parse_function(iter, end); + if (fun != NULL) { + returnVal->children.push_back(fun); + } + } + else if ((*iter)->tokenType == TokenType::Id) { + VarDeclExprAST *varDecl = parse_variable_decl(iter, end); + if (varDecl != NULL) { + //FIXME: Check for duplicate definitions + returnVal->children.push_back(varDecl); + } + else { + throw CompilerException("Unknown element in class", *(--iter)); + } + } + else { + //FIXME: Not sure if this is correct + if (iter != end) { + ++iter; + } + } + } + + if (iter == end) { + throw CompilerException("Expected 'end' at end of class", *(--iter)); + } + else { + ++iter; + } + } + } + + return returnVal; +} + +ActorAST* parse_actor(std::vector::iterator &iter, + std::vector::iterator &end) { + + ActorAST *returnVal = NULL; + bool isIsolated = false; + + if ((iter != end) && ((*iter)->data == "isolated")) { + isIsolated = true; + ++iter; + } + + if ((iter != end) && ((*iter)->data == "actor")) { + ++iter; + if (iter != end) { + returnVal = new ActorAST(isIsolated); + returnVal->filepos = (*iter)->pos; + returnVal->name = (*iter)->data; + + ++iter; + while ((iter != end) && ((*iter)->data != "end")) { + if ((*iter)->data == "def") { + FunctionAST *fun = parse_function(iter, end); + if (fun != NULL) { + returnVal->children.push_back(fun); + } + } + else if ((*iter)->data == "action") { + ActionAST *act = parse_action(iter, end); + if (act != NULL) { + returnVal->children.push_back(act); + } + } + else if ((*iter)->tokenType == TokenType::Id){ + //std::cout << "Var decl enter" << std::endl; + VarDeclExprAST *varDecl = parse_variable_decl(iter, end); + //std::cout << "Var decl leave" << std::endl; + if (varDecl != NULL) { + //FIXME: Check for duplicate definitions + returnVal->children.push_back(varDecl); + } + else { + throw CompilerException("Unknown element in class", *(--iter)); + } + } + else { + //FIXME: Not sure if this is correct + if (iter != end) { + ++iter; + } + } + } + + if (iter == end) { + throw CompilerException("Expected 'end' at end of class", *(--iter)); + } + else { + ++iter; //skip over "end" + } + } + } + + return returnVal; +} + +AppAST* parse(std::vector::iterator &iter, + std::vector::iterator &end) { + + AppAST *returnVal; + ASTNode *child; + + returnVal = new AppAST(); + returnVal->filepos = (*iter)->pos; + + while (iter != end) { + child = parse_function(iter, end); + if (child != NULL) { + //std::cout << "Pushing function" << std::endl; + returnVal->children.push_back(child); + continue; + } + child = parse_class(iter, end); + if (child != NULL) { + //std::cout << "Pushing class" << std::endl; + returnVal->children.push_back(child); + continue; + } + child = parse_actor(iter, end); + if (child != NULL) { + //std::cout << "Pushing actor" << std::endl; + returnVal->children.push_back(child); + continue; + } + child = parse_action(iter, end); + if (child != NULL) { + //std::cout << "Pushing action" << std::endl; + returnVal->children.push_back(child); + continue; + } + + if ((*iter)->tokenType == TokenType::EOL) { + ++iter; + } + else { + std::ostringstream msg; + msg << "Unknown element '" << (*iter)->data << "' of type " << (*iter)->tokenType; + throw CompilerException(msg.str(), *iter); + } + } + + return returnVal; +} diff --git a/src/minnow/parser.hpp b/src/minnow/parser.hpp new file mode 100644 index 0000000..ca456d3 --- /dev/null +++ b/src/minnow/parser.hpp @@ -0,0 +1,365 @@ +#ifndef PARSER_HPP +#define PARSER_HPP + +#include "lexer.hpp" + +class ScopeType { +public: + enum Type { CodeBlock, Struct, Actor, Prototype }; +}; + +class ContainerType { +public: + enum Type { Scalar, Array }; +}; + +class TypeInfo { +public: + std::string declType; + ContainerType::Type containerType; + std::vector containedTypes; + + bool requiresCopyDelete() { + bool needsCopyDelete = false; //default, now check for exceptions + if (containerType == ContainerType::Array) { + needsCopyDelete = true; + } + //FIXME: This is _ugly_ + else if ((declType != "void") && (declType != "bool") && + (declType != "int") && (declType != "double") && + (declType != "string")) { + needsCopyDelete = true; + } + + return needsCopyDelete; + } + + TypeInfo(const TypeInfo &tInfo) { + declType = tInfo.declType; + containerType = tInfo.containerType; + for (int i = 0, end = tInfo.containedTypes.size(); i < end; ++i) { + containedTypes.push_back(tInfo.containedTypes[i]); + } + } + TypeInfo(const std::string &decl, ContainerType::Type &type, + const std::vector &contained) : + declType(decl), containerType(type) { + + for (int i = 0, end = contained.size(); i < end; ++i) { + containedTypes.push_back(contained[i]); + } + } + + TypeInfo(const std::string &decl, ContainerType::Type type) : + declType(decl), containerType(type) { + + } + + bool operator!=(TypeInfo const &rhs) { + if (this->declType != rhs.declType) { + return true; + } + if (this->containerType != rhs.containerType) { + return true; + } + if (this->containedTypes.size() != rhs.containedTypes.size()) { + return true; + } + for (int i = 0, end = this->containedTypes.size(); i != end; ++i) { + if (this->containedTypes[i] != rhs.containedTypes[i]) { + return true; + } + } + return false; + } + + bool operator==(TypeInfo const &rhs) { + return !(*this != rhs); + } + + + TypeInfo() { + declType = "void"; + containerType = ContainerType::Scalar; + } +}; + +class NodeType { +public: + enum Type { Number, Boolean, Variable, ArrayIndexed, Binary, Quote, Call, + End, VarDecl, ArrayDecl, If, While, Pointcut, Advice, Aspect, Lambda, + Recv, Msg, Block, Prototype, Function, Action, Class, Actor, Namespace, + FileStart, FileEnd, App }; +}; + +class ASTNode { + NodeType::Type nodeType; +public: + FilePos filepos; + TypeInfo programmaticType; + std::vector children; + + virtual NodeType::Type type() = 0; +}; + +class VariableInfo { +public: + std::string name; + TypeInfo type; + ASTNode *size; + ScopeType::Type scopeType; + + VariableInfo(const std::string &vname, const std::string &decl, + const ContainerType::Type ty, const ScopeType::Type scope) : + name(vname), type(decl, ty), scopeType(scope) + { } + VariableInfo(const std::string &vname, const std::string &decl, + const ContainerType::Type ty, ASTNode *sizeast, ScopeType::Type scope) : + name(vname), type(decl, ty), size(sizeast), scopeType(scope) + { } + + VariableInfo(const VariableInfo &vinfo) { + name = vinfo.name; + type = vinfo.type; + size = vinfo.size; + scopeType = vinfo.scopeType; + } + + VariableInfo() { + scopeType = ScopeType::CodeBlock; + } +}; + +class BlockAST : public ASTNode{ +public: + //container for other nodes (keeps groups clean) + + virtual NodeType::Type type() { return NodeType::Block; } +}; + +class NumberExprAST : public ASTNode { +public: + double val; + + NumberExprAST(double nVal) : val(nVal) {} + + virtual NodeType::Type type() { return NodeType::Number; } +}; + +class BooleanExprAST : public ASTNode { +public: + bool val; + + BooleanExprAST(bool bVal) : val(bVal) {} + + virtual NodeType::Type type() { return NodeType::Boolean; } +}; + +class QuoteExprAST : public ASTNode { +public: + std::string val; + + QuoteExprAST(std::string &nVal) : val(nVal) {} + + virtual NodeType::Type type() { return NodeType::Quote; } +}; + +class VariableExprAST : public ASTNode { +public: + std::string name; + explicit VariableExprAST(const std::string vName) : name(vName) {} + + virtual NodeType::Type type() { return NodeType::Variable; } +}; + +class VarDeclExprAST : public ASTNode { +public: + VariableInfo *vi; + bool isSpawn; + bool isAlloc; + + explicit VarDeclExprAST(VariableInfo *varInfo, bool spawn, bool alloc) : + vi(varInfo), isSpawn(spawn), isAlloc(alloc) + {} + + virtual NodeType::Type type() { return NodeType::VarDecl; } +}; + +class ArrayIndexedExprAST : public ASTNode { +public: + std::string name; + explicit ArrayIndexedExprAST(const std::string vName, ASTNode* vIndex) : + name(vName) {children.push_back(vIndex); } + + virtual NodeType::Type type() { return NodeType::ArrayIndexed; } +}; + +//FIXME: Check for possible leaks +class EndExprAST : public ASTNode { +public: + explicit EndExprAST() {} + + virtual NodeType::Type type() { return NodeType::End; } +}; + +class IfExprAST : public ASTNode { +public: + explicit IfExprAST() {} + //three nodes: first is the condition of the if, the second is block for the + //then, and the third the block for the else. + + virtual NodeType::Type type() { return NodeType::If; } +}; + +class WhileExprAST : public ASTNode { +public: + explicit WhileExprAST() {} + //two nodes: the cond and the loop block + + virtual NodeType::Type type() { return NodeType::While; } +}; + +class BinaryExprAST : public ASTNode { +public: + std::string op; //, binaryType; + + explicit BinaryExprAST(const std::string bOp, ASTNode* lhs, ASTNode* rhs) + : op(bOp) { children.push_back(lhs); children.push_back(rhs); } + + virtual NodeType::Type type() { return NodeType::Binary; } +}; + +class CallExprAST : public ASTNode { +public: + std::string name; + //children are the args to the call + + CallExprAST(const std::string &fName) + : name(fName) {} + + virtual NodeType::Type type() { return NodeType::Call; } +}; + +class LambdaExprAST : public ASTNode{ +public: + //children is one child, the block of nodes + + virtual NodeType::Type type() { return NodeType::Lambda; } +}; + +class PointcutExprAST : public ASTNode { +public: + std::string pattern; + bool throwExOnFalse; + + //one child, the lambda that will be used as the closure + + PointcutExprAST () : throwExOnFalse(false) { + children.push_back(new LambdaExprAST()); + } + virtual NodeType::Type type() { return NodeType::Pointcut; } +}; + +class PrototypeAST : public ASTNode { +public: + std::string name; + bool isExtern; + TypeInfo returnType; + //children are the variable decls that will be args + + virtual NodeType::Type type() { return NodeType::Prototype; } +}; + +class DataMsgExprAST : public ASTNode { +public: + //two children: the pattern to match (a prototype) and + //the block of asts for this closure. + //FIXME: use a lambda? + + explicit DataMsgExprAST() {} + + virtual NodeType::Type type() { return NodeType::Msg; } +}; + +class DataRecvExprAST : public ASTNode { +public: + //children are the msg matches for the receive + + explicit DataRecvExprAST() {} + + virtual NodeType::Type type() { return NodeType::Recv; } +}; + +class FunctionAST : public ASTNode { +public: + //prototype is first child, block is second + virtual NodeType::Type type() { return NodeType::Function; } +}; + +class ActionAST : public ASTNode { +public: + //prototype is first child, block is second + virtual NodeType::Type type() { return NodeType::Action; } +}; + +class ClassAST : public ASTNode { + public: + std::string name; + + virtual NodeType::Type type() { return NodeType::Class; } +}; + +class ActorAST : public ASTNode { + public: + std::string name; + bool isIsolated; + ActorAST(bool isolated) : isIsolated(isolated) {} + + virtual NodeType::Type type() { return NodeType::Actor; } +}; + +class AppAST : public ASTNode { + virtual NodeType::Type type() { return NodeType::App; } +}; + +TypeInfo parse_typesig(std::vector::iterator &iter, + std::vector::iterator &end); + +VarDeclExprAST* parse_variable_decl(std::vector::iterator &iter, + std::vector::iterator &end); + +ASTNode *parse_parens(std::vector::iterator &iter, + std::vector::iterator &end); + +ASTNode *parse_array_index(std::vector::iterator &iter, + std::vector::iterator &end); + +ASTNode *parse_primary(std::vector::iterator &iter, + std::vector::iterator &end); + +ASTNode *parse_binary_op(std::vector::iterator &iter, + std::vector::iterator &end, int currPrec, ASTNode *expr); + +ASTNode *parse_expression(std::vector::iterator &iter, + std::vector::iterator &end); + +PrototypeAST* parse_prototype(std::vector::iterator &iter, + std::vector::iterator &end); + +FunctionAST* parse_function(std::vector::iterator &iter, + std::vector::iterator &end); + +ActionAST* parse_action(std::vector::iterator &iter, + std::vector::iterator &end); + +ClassAST* parse_class(std::vector::iterator &iter, + std::vector::iterator &end); + +ActorAST* parse_actor(std::vector::iterator &iter, + std::vector::iterator &end); + +AppAST* parse(std::vector::iterator &iter, + std::vector::iterator &end); + + +#endif /* PARSER_HPP */ -- 2.11.4.GIT