├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── doxygen ├── CMakeLists.txt └── Doxyfile.in ├── examples ├── CMakeLists.txt ├── function-1.cpp ├── parse-1.cpp ├── parse-2.cpp └── workbench │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── docs │ └── shell.md │ ├── evaluator.cpp │ ├── evaluator.h │ ├── fakestdio.cpp │ ├── fakestdio.h │ ├── images │ ├── CREDITS.md │ ├── Celldrifter-Muku-Style-Doc-Network.ico │ ├── Celldrifter-Muku-Style-Sys-Desktop.ico │ ├── banner-logo.png │ ├── copy.png │ ├── cut.png │ ├── new.png │ ├── open.png │ ├── paste.png │ ├── rebol-logo.png │ ├── red-logo.png │ └── save.png │ ├── include │ └── README.md │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── renconsole.cpp │ ├── renconsole.h │ ├── rendata │ └── copyright.ren │ ├── rengarden_fr.ts │ ├── renpackage.cpp │ ├── renpackage.h │ ├── renshell.cpp │ ├── renshell.h │ ├── replpad.cpp │ ├── replpad.h │ ├── scripts │ ├── combine.reb │ └── helpers │ │ ├── README.md │ │ ├── autocomplete.reb │ │ ├── edit-buffer.reb │ │ └── shell.reb │ ├── valueexplorer.cpp │ ├── valueexplorer.h │ ├── watchlist.cpp │ ├── watchlist.h │ ├── workbench.qrc │ └── workbench.rc ├── external └── catch │ └── CMakeLists.txt ├── git-hooks ├── README.md └── pre-commit ├── include ├── optional │ └── optional.hpp └── rencpp │ ├── arrays.hpp │ ├── atoms.hpp │ ├── common.hpp │ ├── context.hpp │ ├── engine.hpp │ ├── error.hpp │ ├── function.hpp │ ├── helpers.hpp │ ├── hooks.h │ ├── image.hpp │ ├── rebol.hpp │ ├── ren.hpp │ ├── runtime.hpp │ ├── series.hpp │ ├── strings.hpp │ ├── value.hpp │ └── words.hpp ├── src ├── arrays.cpp ├── atoms.cpp ├── common.hpp ├── context.cpp ├── engine.cpp ├── errors.cpp ├── function.cpp ├── helpers.cpp ├── hooks.cpp ├── image.cpp ├── runtime.cpp ├── series.cpp ├── stdio.cpp ├── strings.cpp ├── value.cpp └── words.cpp └── tests ├── CMakeLists.txt ├── apply-test.cpp ├── assign-test.cpp ├── block-test.cpp ├── cast-test.cpp ├── context-test.cpp ├── form-test.cpp ├── function-test.cpp ├── isolated-test.cpp ├── iterator-test.cpp ├── literals-test.cpp ├── main.cpp ├── rebol-test.cpp └── red-test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | /CMakeLists.txt.user* 3 | *.autosave 4 | /rebol 5 | /red 6 | .DS_Store 7 | *.un~ 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/.gitmodules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | on_success: never 3 | 4 | language: cpp 5 | 6 | compiler: 7 | - clang 8 | - gcc 9 | 10 | env: 11 | matrix: 12 | # let the 32 bit build double as a "no runtime" test 13 | - REBOL=metaeducation/ren-c OS_ID=0.4.4 CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 14 | - RUNTIME=rebol REBOL=metaeducation/ren-c OS_ID=0.4.40 15 | # - RUNTIME=red CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 16 | 17 | matrix: 18 | exclude: 19 | # Don't try 32-bit Clang builds 20 | - compiler: clang 21 | env: REBOL=metaeducation/ren-c OS_ID=0.4.4 CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 22 | - compiler: clang 23 | env: RUNTIME=red CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 24 | 25 | before_install: 26 | # Add PPA for GCC 4.8 27 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 28 | # Add repository for Clang 3.5 29 | - sudo bash -c 'echo "deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main" > /etc/apt/sources.list.d/llvm.list' 30 | - sudo apt-get update 31 | 32 | install: 33 | # NB. Travis CI concats multi-line "scripts" to a single line. Beware! 34 | # 35 | # --- RenCpp Prerequisites --- 36 | # 37 | # Install GCC 4.8 for building RenCpp, if tests are run with g++ 38 | - if [ "${CXX}" = "g++" ]; then 39 | sudo apt-get install -y gcc-4.8-multilib g++-4.8-multilib && 40 | export CC=gcc-4.8 CXX=g++; 41 | fi 42 | - if [ "${CXX}" = "g++" ]; then export CC=gcc-4.8 CXX=g++-4.8; fi 43 | # Install Clang 3.5 for building RenCpp, if tests are run with clang 44 | - if [ "${CXX}" = "clang++" ]; then 45 | sudo apt-get install -y --allow-unauthenticated clang-3.5 && 46 | export CC=clang-3.5 CXX=clang++-3.5; 47 | fi 48 | # 49 | # --- Rebol Prerequisites --- 50 | # 51 | # Set up 32-bit multiarch for building Rebol 52 | - if [ "$(uname -m)" = "x86_64" ]; then 53 | sudo apt-get install -y gcc-multilib g++-multilib libc6:i386; 54 | fi 55 | # Set up our runtimes 56 | - mkdir runtimes/ 57 | # Fetch a Rebol binary needed for building Rebol, needed for both the 58 | # Rebol runtime and also currently for "no runtime" (to implement the 59 | # data structure, basically stripped down Rebol...) 60 | - if [[ (-z ${RUNTIME+x}) || ("${RUNTIME}" = "rebol") ]]; then 61 | wget http://www.rebol.com/r3/downloads/r3-a111-4-2.tar.gz && 62 | tar xvzf r3-a111-4-2.tar.gz; 63 | fi 64 | # Fetch and build Rebol 65 | - if [[ (-z ${RUNTIME+x}) || ("${RUNTIME}" = "rebol") ]]; then 66 | git clone https://github.com/${REBOL} runtimes/rebol && 67 | cp r3 runtimes/rebol/make/r3-make && 68 | make -C runtimes/rebol/make/ -f makefile.boot OS_ID=${OS_ID}; 69 | fi 70 | # Make sure valgrind is installed 71 | - sudo apt-get install valgrind 72 | # On the 32-bit runs, valgrind insists on this to work: 73 | - sudo apt-get install libc6-dbg:i386 74 | 75 | 76 | before_script: 77 | # Have CMake generate the makefiles 78 | - mkdir build/ 79 | - pushd build/ 80 | - if [ "${RUNTIME}" = "rebol" ]; then cmake -DRUNTIME=${RUNTIME} -DRUNTIME_PATH=$(pwd)/../runtimes/${RUNTIME} ..; fi 81 | - if [ "${RUNTIME}" = "red" ]; then cmake -DRUNTIME=${RUNTIME} ..; fi 82 | # We now support not setting a runtime, as it's a possible "default" it 83 | # is simply unset and not "none". The +x trick is how to check for unset 84 | # http://stackoverflow.com/a/13864829/211160 85 | - if [ -z ${RUNTIME+x} ]; then cmake -URUNTIME -DRUNTIME_PATH=$(pwd)/../runtimes/rebol ..; fi 86 | - popd 87 | 88 | script: 89 | # Build RenCpp 90 | - make -C build/ 91 | 92 | # Run RenCpp all-in-one test runner. Only run tests tagged with the 93 | # runtime in effect (which may be nothing). For list of Catch 94 | # command-line parameters, see: 95 | # 96 | # https://github.com/philsquared/Catch/blob/master/docs/command-line.md 97 | # 98 | # Also, run it under valgrind. With --error-exitcode option, valgrind 99 | # will return that number instead of what the child process returned 100 | # if there are errors found. 101 | 102 | - valgrind --leak-check=full build/tests/test-rencpp "[${RUNTIME}]" "[isolated]" 103 | 104 | - valgrind --error-exitcode=222 --leak-check=full build/tests/test-rencpp "[${RUNTIME}]" "~[isolated]" 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RenCpp 2 | 3 | This is an experimental C++11 Language binding for the [Rebol and Red][1] 4 | programming languages. They are unique in use of [definitional scoping][2] 5 | and a boilerplate-free syntax to act as *"the most freeform programming 6 | languages ever invented"*. 7 | 8 | The homepage for this project will be: 9 | 10 | http://rencpp.hostilefork.com/ 11 | 12 | Though build instructions are maintained on the GitHub wiki: 13 | 14 | https://github.com/metaeducation/ren-cpp/wiki 15 | 16 | The code is not "released" in any kind of formal fashion. It is only on GitHub 17 | for review by collaborators in the initial design. However, when released it 18 | will be targeting the Boost Software License 1.0 (BSL), so consider the work 19 | in progress to be under that license as well. 20 | 21 | The repository also currently includes code for the [Ren Garden][2] 22 | cross-platform "IDC" (or "Interactive Development Console"), based on RenCpp. 23 | Both are kept in the same repository only for convenience, and Ren Garden 24 | has a separate issue tracker on GitHub and will be moved. Ren Garden's C++/Qt 25 | codebase is under the GPLv3 license, with its "helper" routines (currently 26 | written in Rebol) under the BSD license. 27 | 28 | --- 29 | [1]: http://blog.hostilefork.com/why-rebol-red-parse-cool/ 30 | [2]: http://stackoverflow.com/a/33469555/211160 31 | [2]: https://www.youtube.com/watch?v=0exDvv5WEv4 32 | -------------------------------------------------------------------------------- /doxygen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # https://majewsky.wordpress.com/2010/08/14/tip-of-the-day-cmake-and-doxygen/ 2 | # add a target to generate API documentation with Doxygen 3 | find_package(Doxygen) 4 | if(DOXYGEN_FOUND) 5 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) 6 | add_custom_target(doc 7 | ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 8 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 9 | COMMENT "Generating API documentation with Doxygen" VERBATIM 10 | ) 11 | endif(DOXYGEN_FOUND) 12 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is an input file for the CMake makefile generator 2 | 3 | # See notes in root directory, where this is added via add_subdirectory 4 | 5 | if(DEFINED RUNTIME) 6 | 7 | add_executable(parse-1 parse-1.cpp) 8 | target_link_libraries(parse-1 RenCpp) 9 | 10 | add_executable(parse-2 parse-2.cpp) 11 | target_link_libraries(parse-2 RenCpp) 12 | 13 | add_executable(function-1 function-1.cpp) 14 | target_link_libraries(function-1 RenCpp) 15 | 16 | endif() 17 | 18 | 19 | 20 | # 21 | # These demos are in subdirectories and have more complex dependencies 22 | # besides just baseline C++11. They must be requested specifically, such 23 | # as passing in: -DGARDEN=yes 24 | # 25 | 26 | 27 | if(GARDEN) 28 | 29 | # Ren Garden is temporarily being built in the Rencpp project, but has 30 | # its own issue tracker and will be migrated to: 31 | # 32 | # https://github.com/metaeducation/ren-garden 33 | # 34 | 35 | add_subdirectory(workbench) 36 | 37 | endif() 38 | -------------------------------------------------------------------------------- /examples/function-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rencpp/ren.hpp" 5 | 6 | using namespace ren; 7 | 8 | int main(int, char **) { 9 | 10 | auto printBlockString = Function::construct( 11 | " {Demonstration of the C++ Extension mechanism}" 12 | " blk [block!] {The block to print}", 13 | 14 | [](Block const & blk) -> Logic { 15 | print("blk is", blk); 16 | return true; 17 | } 18 | ); 19 | 20 | // Add extension to the environment. For why we need the "quote", see 21 | // the answers here: 22 | // 23 | // http://stackoverflow.com/questions/27641809/ 24 | 25 | runtime("some-ext: quote", printBlockString); 26 | 27 | // Call the extension under its new name 28 | 29 | runtime("some-ext [1 2 3]"); 30 | } 31 | -------------------------------------------------------------------------------- /examples/parse-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "rencpp/ren.hpp" 6 | 7 | using namespace ren; 8 | 9 | // 10 | // This shows a "more C++ and less Ren" variant of how you might use the 11 | // binding. Instead of being mostly Ren text with objects spliced in, it 12 | // is mostly objects with text runs spliced in. 13 | // 14 | 15 | int main(int, char **) { 16 | std::string data {"Hello [Ren C++ Binding] World!"}; 17 | 18 | Word variable {"foo"}; 19 | 20 | Block rule { 21 | "thru {[}", 22 | "copy", variable, "to {]}", 23 | "to end" 24 | }; 25 | 26 | auto result = static_cast(*runtime("parse", data, rule)); 27 | 28 | if (result) 29 | std::cout << "Success and target was " << variable() << "\n"; 30 | else 31 | std::cout << "PARSE failed."; 32 | } 33 | -------------------------------------------------------------------------------- /examples/parse-2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "rencpp/ren.hpp" 6 | 7 | using namespace ren; 8 | 9 | // 10 | // This shows a "less C++ and more Ren" variant of how you might use the 11 | // binding. All we've pulled out is the variableName; the printing is 12 | // done from inside of Ren. 13 | // 14 | 15 | int main(int, char **) { 16 | auto variable = Word {"foo"}; 17 | 18 | optional result = runtime( 19 | "data: {Hello [Ren C++ Binding] World!}" 20 | 21 | "rule:", Block { 22 | "thru {[}" 23 | "copy", variable, "to {]}" 24 | "to end" 25 | }, 26 | 27 | "either result: parse data rule", Block { 28 | "print", Block {"{Success and target was}", variable}, 29 | }, "[" 30 | "print {PARSE failed.}" 31 | "]", 32 | "result" 33 | ); 34 | } 35 | 36 | // 37 | // (Note: Although runs of characters in a block construction may not break 38 | // up a pairing of delimiters, if C++ sees a string end on one line and get 39 | // picked up on the next it will merge the character data.) 40 | // 41 | -------------------------------------------------------------------------------- /examples/workbench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file inherits the definitions for the Ren binding from root 3 | # 4 | 5 | # 6 | # For the way to include Qt in CMake that is in fashion *this* week (on the 7 | # site you visit to read Qt documentation for the next few days until they 8 | # change that again too) see here: 9 | # 10 | # http://doc.qt.io/qt-5/cmake-manual.html 11 | # 12 | 13 | cmake_minimum_required(VERSION 2.8.11) 14 | 15 | 16 | # Find includes in corresponding build directories 17 | 18 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 19 | 20 | 21 | # Add includes for libraries such as optional in the "include" directory 22 | 23 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) 24 | 25 | 26 | # Instruct CMake to run moc automatically when needed. 27 | 28 | set(CMAKE_AUTOMOC ON) 29 | 30 | 31 | # Ren Garden needs the Qt Widgets (tab controls, list controls) which will 32 | # automatically bring in the GUI and Qt Core. 33 | 34 | find_package(Qt5Widgets REQUIRED) 35 | 36 | 37 | # Qt's network is only being used experimentally in Ren Garden. Linkage to 38 | # it was added as a way to try and think about bringing in module updates for 39 | # Rebol code...but that was before Ren/C was created and brought in support 40 | # for HTTPS from the Atronix and Saphirion builds. 41 | # 42 | # It can be removed if there is a compelling reason to do so, but it may be 43 | # useful in implementing (for instance) an experimental alternative port 44 | # system to replace the existing code. 45 | 46 | find_package(Qt5Network REQUIRED) 47 | 48 | 49 | # These rigid warnings from Ren/C++ are too strict for building Qt5 projects 50 | 51 | try_remove_cxx_flag("-Wsign-conversion") 52 | try_remove_cxx_flag("-Wctor-dtor-privacy") 53 | try_remove_cxx_flag("-Wswitch-default") 54 | try_remove_cxx_flag("-Wmissing-declarations") 55 | 56 | 57 | # These workarounds were added as an experiment for getting Ren Garden to be 58 | # able to have the EXE and DLLs packed together with MoleBox: 59 | # 60 | # http://molebox.com/ 61 | # 62 | # Among application virtualizers for Windows, it's relatively lightweight. 63 | # And with the workarounds, it *seems* to be viable on WinXP through Win8. 64 | # In any case, even if we don't use MoleBox specifically, the workarounds 65 | # may be necessary in other virtual solutions. 66 | 67 | if(GARDEN_BOXED) 68 | if(NOT WIN32) 69 | message(FATAL "Can only build Ren Garden for Boxing on Windows") 70 | endif() 71 | add_cxx_flags("-DREN_GARDEN_BOXED") 72 | endif() 73 | 74 | 75 | 76 | # Resource files need to be compiled, and then the compiled resource is set 77 | # into the ${RESOURCE_ADDED} variable 78 | 79 | qt5_add_resources(RESOURCE_ADDED workbench.qrc) 80 | 81 | 82 | # CMake is able to automatically do configuration for the linker in order 83 | # to get the right GUI type if you mention the platform (as of CMake 2.8.11, 84 | # which is a dependency for this build file). 85 | # 86 | # However we can't put an if statement in the middle of `add_executable()`. 87 | # So instead of putting `add_executable()` inside of a conditional we make 88 | # a `GUI_TYPE` variable and include that. 89 | 90 | if(WIN32) 91 | set(GUI_TYPE WIN32) 92 | message(STATUS "win32 guitype") 93 | endif(WIN32) 94 | 95 | if(APPLE) 96 | set(GUI_TYPE MACOSX_BUNDLE) 97 | endif(APPLE) 98 | 99 | if(WIN32) 100 | set(WINDOWS_RC "workbench.rc") 101 | endif() 102 | 103 | 104 | # Source files included in the executable 105 | 106 | add_executable( 107 | workbench 108 | 109 | ${GUI_TYPE} 110 | 111 | # Resource in compiled form (and .RES if windows for icon) 112 | 113 | ${RESOURCE_ADDED} 114 | ${WINDOWS_RC} 115 | 116 | # Source files 117 | 118 | main.cpp 119 | mainwindow.cpp 120 | replpad.cpp 121 | fakestdio.cpp 122 | evaluator.cpp 123 | renconsole.cpp 124 | watchlist.cpp 125 | renshell.cpp 126 | renpackage.cpp 127 | valueexplorer.cpp 128 | 129 | # Include files - not technically needed for the build but good to have 130 | # a mention of because then the generated project file will show them in 131 | # the file browser 132 | 133 | mainwindow.h 134 | evaluator.h 135 | renconsole.h 136 | replpad.h 137 | fakestdio.h 138 | watchlist.h 139 | renshell.h 140 | renpackage.h 141 | valueexplorer.h 142 | ) 143 | 144 | 145 | # Linker settings 146 | 147 | target_link_libraries( 148 | workbench 149 | RenCpp 150 | Qt5::Widgets 151 | Qt5::Network 152 | ) 153 | 154 | -------------------------------------------------------------------------------- /examples/workbench/README.md: -------------------------------------------------------------------------------- 1 | > **NOTE:** The Ren Garden project emerged from a "usage sample" to show how 2 | > to write a functioning console using RenCpp, which could evaluate expressions 3 | > and cancel their evaluation. As testing evolved it quickly grew into 4 | > something more promising. 5 | > 6 | > For early experimenters it is still being kept in the RenCpp project to ease 7 | > building and versioning concerns. But please do not report Ren Garden feature 8 | > ideas or bugs to the RenCpp repository, and instead use: 9 | > 10 | > https://github.com/metaeducation/ren-garden/issues 11 | > 12 | > The code will be moved into that repository at the appropriate time. 13 | 14 | # Ren Garden 15 | 16 | Ren Garden is a pre-release demo of a novel console for working with Rebol and 17 | Red code evaluation, which uses widgets from the Qt5 toolkit. 18 | 19 | Under the hood, the console is implemented as a QTextEdit; and it has 20 | many features including: 21 | 22 | * Evaluations run on a thread separate from the GUI, allowing for 23 | responsiveness and cancellation 24 | 25 | * Switching between single and multi-line editing, with a respectable suite 26 | of multi-line editing features 27 | 28 | * Browsing of previous input while a command is running, with terminal 29 | heuristics to keep you in control of the positioning or advancing in lockstep 30 | with command output 31 | 32 | * Ability to undo an evaluation's output and return the console to the state 33 | of input prior to the evaluation, including the cursor positioning 34 | 35 | * Dynamic addition of watch variables and expressions through a WATCH dialect 36 | 37 | * Management of a persistent command shell process which can be controlled via 38 | a SHELL dialect 39 | 40 | * ...and much more... 41 | 42 | 43 | ### Novelty 44 | 45 | While many seemingly-similar initiatives like [iPython][1] do exist, what 46 | differentiates Ren Garden is largely a matter of implementation methodology. 47 | It derives a peculiar set of advantages from the uncompromising C++11 binding 48 | "RenCpp"--itself standing on the shoulders of the unique design of Rebol 49 | and Red. 50 | 51 | So central to the purpose of Ren Garden is to be a challenging test case for 52 | the RenCpp binding. It's an exploration of what value using Rebol/Red from 53 | inside C++ can offer (and vice-versa). 54 | 55 | [1]: http://ipython.org/ipython-doc/dev/interactive/qtconsole.html 56 | 57 | 58 | ### Building 59 | 60 | Building Ren Garden is currently covered in the RenCpp build instructions. 61 | 62 | For assistance in building the project, please stop by 63 | [Rebol and Red chat](http://rebolsource.net/go/chat-faq) or leave a note on 64 | the issue database. 65 | 66 | 67 | ### License 68 | 69 | RenCpp is under the liberal Boost software license. However, Ren Garden is 70 | being developed as a GPLv3 project. 71 | -------------------------------------------------------------------------------- /examples/workbench/docs/shell.md: -------------------------------------------------------------------------------- 1 | One of Ren Garden's features is to allow interaction with persistent shell 2 | processes. The implementation of the process spawning logistics and 3 | data piping is done in a spirit similar to Tcl's EXPECT: 4 | 5 | http://en.wikipedia.org/wiki/Expect 6 | 7 | It is the beginning of implementing such an idea for Ren Garden. What 8 | largely differentiates it is choice of tools (Rebol/Red, C++11, and Qt's 9 | [QProcess](http://doc.qt.io/qt-5/qprocess.html)). 10 | 11 | How the dialect input is translated from a Ren block into a string for the 12 | shell is delegated to the routines in a Rebol helper script: 13 | 14 | https://github.com/hostilefork/rencpp/blob/develop/examples/workbench/scripts/helpers/shell.reb 15 | 16 | As the dialect is in its earliest days of implementation, there are many 17 | open questions (including the question of whether to go ahead and make it 18 | built upon a more generalized EXPECT dialect). Yet this is the beginnings 19 | of a working document describing what has been done so far: 20 | 21 | 22 | ### LITERAL STRINGS ### 23 | 24 | If you give the shell a string literal, it will be passed uninterpreted: 25 | 26 | shell [{echo $FOO}] 27 | 28 | The dialect continues this pattern for any string literals which are found, 29 | which will be separated by a single space from their neighboring content. 30 | 31 | Additionally, any legal words are currently left untouched by the dialect, 32 | They are simply converted to their spellings: 33 | 34 | shell [echo {$FOO}] 35 | 36 | Should you be lucky enough that the entirety of your shell command can be 37 | expressed as words, you won't need any string escaping: 38 | 39 | shell [ls -alF] 40 | 41 | > **Note:** For almost every choice in the dialect design, it will overlap 42 | > with some pre-existing behavior. In curly braces case, it is used 43 | > for expansion and grouping: 44 | > 45 | > http://stackoverflow.com/questions/8748831/bash-why-do-we-need-curly-braces-in-variables 46 | > 47 | > All cases of wanting to use such features of a shell can work by passing 48 | > literal strings, and it's easy to nest curly brace literals. The goal 49 | > is to have the dialecting features allow augmentations, especially shifting 50 | > more of the "metaprogramming" to the more pleasing devices of Rebol and Red. 51 | 52 | 53 | ### ENVIRONMENT VARIABLES ### 54 | 55 | Environment variables can be accessed with literal strings in the usual 56 | way (as in the `{echo $FOO}` above). Yet as $FOO would be an "invalid 57 | money value" as a REN literal, GET-WORD! is used instead. 58 | 59 | shell [{echo} :FOO] 60 | 61 | That would transmit to the shell `echo $FOO`. SET-WORD! also has special 62 | processing...and since `*.cpp` is a legal word we can write: 63 | 64 | shell [FOO: *.cpp] 65 | 66 | Under the hood, that would send to a shell (if it were based on UNIX /bin/sh) 67 | a string that would look like: 68 | 69 | export FOO="*.cpp" 70 | 71 | > **Note:** For pre-existing behavior in colon's case, bash uses it as a 72 | > sort of "quoting operator": 73 | > 74 | > http://superuser.com/questions/423980/colon-command-for-bash 75 | > 76 | > `{:}` would be an adequate substitute, modulo implicit spacing concerns. 77 | 78 | 79 | ### EMBEDDED EVALUATION ### 80 | 81 | Shell blocks are currently implicitly preprocessed by COMPOSE prior to 82 | running. So all of the parenthesized code will run prior to any of the 83 | shell code: 84 | 85 | shell [ls (reverse {Fla-})] 86 | 87 | That would be seen by the shell as `ls -alF`. 88 | 89 | > **Note:** The shells use parentheses for their own evaluation, but 90 | > that can be done with {(} and {)} if you really needed it. 91 | 92 | 93 | ### SEPARATING COMMANDS ### 94 | 95 | Because Ren code is not newline-sensitive outside of strings, a first idea 96 | was to puncutate multiple commands during one SHELL invocation using inner 97 | blocks: 98 | 99 | shell [ 100 | [{echo} :FOO] 101 | [ls :FOO] 102 | ] 103 | 104 | It's up for debate on this idea (as with many) if there might be a better 105 | application for the construct, as you could just as easily write: 106 | 107 | shell [ 108 | {echo} :FOO 109 | {;} 110 | ls :FOO 111 | ] 112 | 113 | Probably done better as: 114 | 115 | return-codes: copy [] 116 | 117 | each cmd [ 118 | [{echo} :FOO] 119 | [ls :FOO] 120 | ] [ 121 | append return-codes (shell cmd) 122 | ] 123 | 124 | More interesting feature ideas might come up, though. Maybe there would be 125 | some way to ask the shell processor to run commands in parallel, and return 126 | a block of exit codes once they all finish: 127 | 128 | shell [ 129 | parallel 130 | [ ... ] 131 | [ ... ] 132 | [ ... ] 133 | ] 134 | 135 | Other less ambitious ideas could be when you want a group of things to be 136 | combined without spacing: 137 | 138 | shell [ 139 | {;} 140 | echo [foo :BAZ bar] 141 | ] 142 | 143 | If `:BAZ` were to produce `{$BAZ}`, this would give the ability to make 144 | things like `echo foo{$BAZ}bar`. 145 | 146 | 147 | > **Note:** `[` as well as `[[` are actual commands in UNIX, believe it 148 | > or not: 149 | > 150 | > $ which [ 151 | > /bin/[ 152 | > 153 | > The idea again being that if you needed that, {[} and {[[} would be 154 | > adequate substitutes. 155 | 156 | 157 | ### EVERYTHING ELSE ### 158 | 159 | So far, everything else turns into its TO-STRING form (as implemented in 160 | Ren Garden's TO-STRING variant). So if you want to use a file path you 161 | can write `%/dir/file.txt`. This would integrate more nicely with SHELL 162 | if NewPath existed: 163 | 164 | http://blog.hostilefork.com/new-path-debate-rebol-red/ 165 | 166 | 167 | ### THE FUTURE...? ### 168 | 169 | Lots of work to do. Good news for the Rebol/Red crowd is that a lot of 170 | it doesn't require C++ expertise. Discussions at: 171 | 172 | http://rebolsource.net/go/chat-faq 173 | 174 | -------------------------------------------------------------------------------- /examples/workbench/evaluator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // evaluator.cpp 3 | // This file is part of Ren Garden 4 | // Copyright (C) 2015-2018 MetÆducation 5 | // 6 | // Ren Garden is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // Ren Garden is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with Ren Garden. If not, see . 18 | // 19 | // See http://ren-garden.metaeducation.com for more information on this project 20 | // 21 | 22 | #include 23 | 24 | #include "rencpp/ren.hpp" 25 | using namespace ren; 26 | 27 | #include "evaluator.h" 28 | 29 | 30 | void EvaluatorWorker::initialize() { 31 | // Ren/C++ does lazy initialization right now, so the first usage will 32 | // configure it without an explicit call. (Interesting as an option, 33 | // though it shouldn't be the only way...and you should be able to 34 | // compile with that disabled) 35 | 36 | ren::String lazy = "lazy initialize"; 37 | 38 | emit initializeDone(); 39 | } 40 | 41 | 42 | void EvaluatorWorker::doWork( 43 | ren::AnyValue const & dialectValue, 44 | ren::AnyValue const & contextValue, 45 | QString const & input, 46 | bool meta 47 | ) { 48 | // See notes on MainWindow about qRegisterMetaType about why dialect 49 | // and context are passed as ren::AnyValue instead of ren::Function and 50 | // ren::AnyContext (also why it needs the ren:: prefix for slots) 51 | 52 | Function dialect = static_cast(dialectValue); 53 | AnyContext context = static_cast(contextValue); 54 | 55 | optional result; 56 | bool success = false; 57 | 58 | try { 59 | // We *always* generate a block to pass to the dialect. This 60 | // is Ren Garden, not "arbitrary shell"... so if you want to 61 | // pass an arbitrary string you must type it in as {49+3hfa} in 62 | // a properly loadable string. 63 | 64 | auto loaded = context.create(input.toUtf8().constData()); 65 | 66 | if (meta) { 67 | if (!runtime("find words-of quote", dialect, "/meta")) 68 | throw Error ("current dialect has no /meta refinement"); 69 | 70 | result = context(Path {dialect, "meta"}, loaded); 71 | } 72 | else 73 | result = context(dialect, loaded); 74 | 75 | success = true; 76 | } 77 | catch (evaluation_throw const & t) { 78 | if (hasType(t.name())) { 79 | Function func = static_cast(*t.name()); 80 | Function qt = static_cast(*runtime(":quit")); 81 | Function ex = static_cast(*runtime(":exit")); 82 | 83 | if (func.isEqualTo(qt) || func.isEqualTo(ex)) { 84 | // A programmatic request to quit the system (e.g. QUIT). 85 | // Might be interesting to have some UI to configure it 86 | // not actually exiting the whole GUI app, if you don't 87 | // want it to: 88 | // 89 | // https://github.com/metaeducation/ren-garden/issues/17 90 | 91 | if (t.value() == nullopt || hasType(t.value())) { 92 | qApp->exit(0); 93 | } 94 | else if (hasType(t.value())) { 95 | qApp->exit(static_cast(*t.value())); 96 | } 97 | else { 98 | // Do whatever Rebol does... 99 | qApp->exit(1); 100 | } 101 | 102 | // We have submitted our quit message but will have to 103 | // get back to the message pump... go ahead and return 104 | // none... 105 | result = nullopt; 106 | success = true; 107 | } 108 | } 109 | 110 | if (!success) { 111 | std::string message = std::string("No CATCH for: ") + t.what(); 112 | result = Error (message.c_str()); 113 | } 114 | } 115 | catch (load_error const & e) { 116 | // Syntax errors which would trip up RenCpp even if no runtime was 117 | // loaded, so things like `runtime("print {Foo");` 118 | 119 | result = e.error(); 120 | } 121 | catch (evaluation_error const & e) { 122 | // Evaluation errors, like `first 100` 123 | 124 | result = e.error(); 125 | } 126 | catch (evaluation_halt const &) { 127 | // Cancellation as with hitting the escape key during an infinite 128 | // loop. (Such requests must originate from the GUI thread.) 129 | // Let returning none for the error mean cancellation. 130 | 131 | result = nullopt; 132 | } 133 | catch (std::exception const & e) { 134 | const char * what = e.what(); 135 | 136 | emit caughtNonRebolException(what); 137 | 138 | // !!! MainWindow will show a dialog box. Should we make up an error? 139 | result = nullopt; 140 | } 141 | catch (...) { 142 | // !!! MainWindow will show a dialog box. Should we make up an error? 143 | result = nullopt; 144 | 145 | emit caughtNonRebolException(nullptr); 146 | } 147 | 148 | emit resultReady(success, result); 149 | } 150 | -------------------------------------------------------------------------------- /examples/workbench/evaluator.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_EVALUATOR_H 2 | #define RENGARDEN_EVALUATOR_H 3 | 4 | // 5 | // evaluator.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | // QMessageBox is not really used in this header but for some reason there are 26 | // bizarre errors coming from the moc build if a GUI file is not included: 27 | // http://stackoverflow.com/questions/32773868/ 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include "rencpp/ren.hpp" 34 | 35 | // 36 | // WORKER OBJECT FOR HANDLING REN EVALUATIONS 37 | // 38 | 39 | // 40 | // We push this item to the worker thread and let it do the actual evaluation 41 | // while we keep monitoring the GUI for an interrupt 42 | // 43 | // http://doc.qt.io/qt-5/qthread.html#details 44 | // 45 | // Ultimately it should be the case that the GUI never calls an "open coded" 46 | // arbitrary evaluation of user code in the runtime. Short things 47 | // might be okay if you are *certain* the evaluator is not currently running. 48 | // 49 | 50 | class EvaluatorWorker : public QObject 51 | { 52 | Q_OBJECT 53 | 54 | public slots: 55 | // See notes on MainWindow about qRegisterMetaType about why dialect 56 | // and context are passed as ren::AnyValue instead of ren::Function and 57 | // ren::AnyContext (also why it needs the ren:: prefix for slots) 58 | void doWork( 59 | ren::AnyValue const & dialectValue, 60 | ren::AnyValue const & contextValue, 61 | QString const & input, 62 | bool meta 63 | ); 64 | 65 | void initialize(); 66 | 67 | signals: 68 | void resultReady( 69 | bool success, 70 | ren::optional const & result // `ren::` needed for signal! 71 | ); 72 | 73 | void initializeDone(); 74 | 75 | void caughtNonRebolException(char const * what); 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /examples/workbench/fakestdio.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // fakestdio.cpp 3 | // This file is part of Ren Garden 4 | // Copyright (C) 2015-2018 MetÆducation 5 | // 6 | // Ren Garden is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // Ren Garden is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with Ren Garden. If not, see . 18 | // 19 | // See http://ren-garden.metaeducation.com for more information on this project 20 | // 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #include "fakestdio.h" 33 | #include "replpad.h" 34 | 35 | 36 | // This wound up seeming a lot more complicated than it needed to be. See 37 | // notes on the articles sourcing the information and a request for 38 | // review here: 39 | // 40 | // https://github.com/metaeducation/ren-garden/issues/2 41 | 42 | 43 | // 44 | // FAKE STANDARD OUTPUT 45 | // 46 | 47 | 48 | 49 | FakeStdoutBuffer::FakeStdoutBuffer(ReplPad & repl, std::size_t buff_sz) : 50 | FakeStdoutResources (buff_sz), 51 | repl (repl) 52 | { 53 | // -1 makes authoring of overflow() easier, so it can put the character 54 | // into the buffer when it happens. 55 | setp(buffer_.data(), buffer_.data() + buff_sz - 1); 56 | } 57 | 58 | bool FakeStdoutBuffer::processAndFlush() { 59 | std::ptrdiff_t n = pptr() - pbase(); 60 | pbump(-n); 61 | 62 | if (n == 0) 63 | return true; 64 | 65 | *(pbase() + n) = '\0'; 66 | 67 | // BUG: If this is UTF8 encoded we might end up on half a character... 68 | // https://github.com/metaeducation/ren-garden/issues/1 69 | 70 | repl.appendText(QString(pbase())); 71 | 72 | return true; 73 | } 74 | 75 | std::streambuf::int_type FakeStdoutBuffer::overflow(int_type ch) { 76 | if (ch != traits_type::eof()) 77 | { 78 | assert(std::less_equal()(pptr(), epptr())); 79 | *pptr() = ch; 80 | pbump(1); 81 | 82 | if (processAndFlush()) 83 | return ch; 84 | } 85 | 86 | return traits_type::eof(); 87 | } 88 | 89 | int FakeStdoutBuffer::sync() { 90 | return processAndFlush() ? 0 : -1; 91 | } 92 | 93 | 94 | 95 | 96 | 97 | 98 | // 99 | // FAKE STANDARD INPUT 100 | // 101 | 102 | FakeStdinBuffer::FakeStdinBuffer ( 103 | ReplPad & repl, 104 | std::size_t buff_sz, 105 | std::size_t put_back 106 | ) : 107 | repl (repl), 108 | put_back_ (std::max(put_back, size_t(1))), 109 | buffer_ (std::max(buff_sz, put_back_) + put_back_) 110 | { 111 | char *end = &buffer_.front() + buffer_.size(); 112 | setg(end, end, end); 113 | } 114 | 115 | 116 | std::streambuf::int_type FakeStdinBuffer::underflow() { 117 | if (gptr() < egptr()) // buffer not exhausted 118 | return traits_type::to_int_type(*gptr()); 119 | 120 | char *base = &buffer_.front(); 121 | char *start = base; 122 | 123 | if (eback() == base) // true when this isn't the first fill 124 | { 125 | // Make arrangements for putback characters 126 | 127 | // Note MSVC doesn't like using std::copy here withuot its own non 128 | // standard iterator. 129 | // 130 | /* std::copy(egptr() - put_back_, egptr(), base); */ 131 | memcpy(base, egptr() - put_back_, put_back_); 132 | 133 | start += put_back_; 134 | } 135 | 136 | 137 | // start is now the start of the buffer, proper. 138 | 139 | int readCapacity = buffer_.size() - (start - base); 140 | 141 | // We need to lock the caller up until the repl signals us 142 | // that it has read data. We'll go by line for now. 143 | 144 | QMutexLocker lock {&repl.inputMutex}; 145 | emit requestInput(); 146 | repl.inputAvailable.wait(lock.mutex()); 147 | 148 | std::size_t n = std::min(readCapacity, repl.input.size()); 149 | 150 | // MSVC does not like using std::copy here without it's own non-standard 151 | // safe iterator format. 152 | // 153 | memcpy(start, repl.input.data(), n); 154 | 155 | // !!! This was just sitting here, but it has no side effect...and a 156 | // raised compiler warning caught that. What was it here for? 157 | // 158 | /* repl.input.right(repl.input.size() - n); */ 159 | 160 | if (n == 0) 161 | return traits_type::eof(); 162 | 163 | // Set buffer pointers 164 | setg(base, start, start + n); 165 | 166 | return traits_type::to_int_type(*gptr()); 167 | } 168 | -------------------------------------------------------------------------------- /examples/workbench/fakestdio.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_FAKESTDIO_H 2 | #define RENGARDEN_FAKESTDIO_H 3 | 4 | // 5 | // fakestdio.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | // This wound up seeming a lot more complicated than it needed to be. See 38 | // notes on the articles sourcing the information and a request for 39 | // review here: 40 | // 41 | // https://github.com/metaeducation/ren-garden/issues/2 42 | 43 | 44 | class ReplPad; 45 | 46 | 47 | // 48 | // NULL OUTPUT 49 | // 50 | 51 | // http://stackoverflow.com/a/8244052/211160 52 | 53 | class NulStreambuf : public std::streambuf 54 | { 55 | char dummyBuffer[64]; 56 | protected: 57 | virtual int overflow(int c) 58 | { 59 | setp(dummyBuffer, dummyBuffer + sizeof(dummyBuffer)); 60 | return (c == traits_type::eof()) ? '\0' : c; 61 | } 62 | }; 63 | 64 | class NulOStream : private NulStreambuf, public std::ostream 65 | { 66 | public: 67 | NulOStream () : std::ostream (this) {} 68 | NulStreambuf const * rdbuf() const { return this; } 69 | }; 70 | 71 | 72 | 73 | // 74 | // FAKE STANDARD OUTPUT 75 | // 76 | 77 | class FakeStdoutResources { 78 | public: 79 | FakeStdoutResources(size_t s) : 80 | buffer_ (s + 1) // for null terminator... 81 | { 82 | } 83 | 84 | ~FakeStdoutResources() 85 | { 86 | } 87 | 88 | protected: 89 | std::vector buffer_; 90 | }; 91 | 92 | 93 | 94 | class FakeStdoutBuffer : protected FakeStdoutResources, public std::streambuf 95 | { 96 | private: 97 | ReplPad & repl; 98 | 99 | public: 100 | explicit FakeStdoutBuffer(ReplPad & repl, std::size_t buff_sz = 256); 101 | 102 | protected: 103 | bool processAndFlush(); 104 | 105 | int_type overflow(int_type ch) override; 106 | 107 | int sync() override; 108 | }; 109 | 110 | 111 | class FakeStdout : protected FakeStdoutBuffer, public std::ostream { 112 | public: 113 | FakeStdout (ReplPad & repl) : 114 | FakeStdoutBuffer (repl), 115 | std::ostream (static_cast(this)) 116 | { 117 | } 118 | 119 | ~FakeStdout () override 120 | { 121 | FakeStdoutBuffer::sync(); 122 | } 123 | }; 124 | 125 | 126 | 127 | // 128 | // FAKE STANDARD INPUT 129 | // 130 | 131 | class FakeStdinBuffer : public QObject, public std::streambuf 132 | { 133 | Q_OBJECT 134 | 135 | ReplPad & repl; 136 | const std::size_t put_back_; 137 | std::vector buffer_; 138 | 139 | public: 140 | explicit FakeStdinBuffer ( 141 | ReplPad & repl, 142 | std::size_t buff_sz = 256, 143 | std::size_t put_back = 8 144 | ); 145 | 146 | signals: 147 | void requestInput(); 148 | 149 | private: 150 | int_type underflow() override; 151 | }; 152 | 153 | 154 | class FakeStdin : public FakeStdinBuffer, public std::istream { 155 | 156 | public: 157 | FakeStdin (ReplPad & repl) : 158 | FakeStdinBuffer (repl), 159 | std::istream (static_cast(this)) 160 | { 161 | } 162 | 163 | ~FakeStdin () override 164 | { 165 | FakeStdinBuffer::sync(); 166 | } 167 | }; 168 | 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /examples/workbench/images/CREDITS.md: -------------------------------------------------------------------------------- 1 | This will ultimately be a list of credits for technologies and resources borrowed. 2 | 3 | For the moment I'm making a note that the interim app icon comes from Muku Style Icons by celldrifter: 4 | 5 | http://www.iconarchive.com/show/muku-style-icons-by-celldrifter.html 6 | 7 | CC Attribution Sharealike 4.0 -------------------------------------------------------------------------------- /examples/workbench/images/Celldrifter-Muku-Style-Doc-Network.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/Celldrifter-Muku-Style-Doc-Network.ico -------------------------------------------------------------------------------- /examples/workbench/images/Celldrifter-Muku-Style-Sys-Desktop.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/Celldrifter-Muku-Style-Sys-Desktop.ico -------------------------------------------------------------------------------- /examples/workbench/images/banner-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/banner-logo.png -------------------------------------------------------------------------------- /examples/workbench/images/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/copy.png -------------------------------------------------------------------------------- /examples/workbench/images/cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/cut.png -------------------------------------------------------------------------------- /examples/workbench/images/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/new.png -------------------------------------------------------------------------------- /examples/workbench/images/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/open.png -------------------------------------------------------------------------------- /examples/workbench/images/paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/paste.png -------------------------------------------------------------------------------- /examples/workbench/images/rebol-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/rebol-logo.png -------------------------------------------------------------------------------- /examples/workbench/images/red-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/red-logo.png -------------------------------------------------------------------------------- /examples/workbench/images/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metaeducation/ren-cpp/0d56facc7501f6cb21fe29796ca887549c207d19/examples/workbench/images/save.png -------------------------------------------------------------------------------- /examples/workbench/include/README.md: -------------------------------------------------------------------------------- 1 | This include directory is here because it once housed Ren Garden's inclusion of 2 | Andrzej Krzemienski's `std::experimental::optional`. That has migrated out to 3 | be a dependency of Ren/C++ proper, but the build script still includes this 4 | empty directory...should Ren Garden pick up any third-party includes (or move 5 | its own includes here) 6 | -------------------------------------------------------------------------------- /examples/workbench/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_MAINWINDOW_H 2 | #define RENGARDEN_MAINWINDOW_H 3 | 4 | // 5 | // mainwindow.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | 26 | #include 27 | #include 28 | 29 | #include "evaluator.h" 30 | 31 | class RenConsole; 32 | class WatchList; 33 | class ValueExplorer; 34 | 35 | class QAction; 36 | class QMenu; 37 | 38 | extern bool forcingQuit; 39 | 40 | class MainWindow : public QMainWindow 41 | { 42 | Q_OBJECT 43 | 44 | public: 45 | MainWindow(); 46 | ~MainWindow(); 47 | 48 | protected: 49 | void closeEvent(QCloseEvent *event); 50 | 51 | private slots: 52 | void cut(); 53 | void copy(); 54 | void paste(); 55 | 56 | void about(); 57 | void updateMenus(); 58 | void switchLayoutDirection(); 59 | 60 | private: 61 | // Cool trick... hold down escape to fade window and "poof". 62 | 63 | // We start the opacity value a little bit higher than 1.0, so the first 64 | // short while after requesting a fade out no effect is seen (and we don't 65 | // need a separate timing for that). 66 | 67 | static constexpr qreal const initialOpacity = 1.1; 68 | static constexpr qreal const quittingOpacity = 0.5; 69 | static constexpr qreal const deltaOpacity = 0.05; 70 | static int const msecInterval = 150; 71 | 72 | qreal opacity; 73 | bool fading; 74 | QTimer * fadeTimer; 75 | 76 | EvaluatorWorker *worker; 77 | 78 | void onFadeOutToQuit(bool active); 79 | 80 | private: 81 | void createActions(); 82 | void createMenus(); 83 | void createStatusBar(); 84 | void readSettings(); 85 | void writeSettings(); 86 | 87 | // Should be private but just doing hack-and-slash testing right now 88 | // of Rencpp, not overly concerned about the elegance of this program 89 | // until that is assuredly elegant...! 90 | public: 91 | RenConsole * console; 92 | QDockWidget * dockWatch; 93 | QDockWidget * dockValueExplorer; 94 | 95 | private: 96 | QAction * separatorAct; 97 | 98 | QMenu * fileMenu; 99 | QAction * exitAct; 100 | 101 | QMenu * editMenu; 102 | QAction * cutAct; 103 | QAction * copyAct; 104 | QAction * pasteAct; 105 | 106 | QMenu * languageMenu; 107 | QAction * proposalsAct; 108 | 109 | QMenu * windowMenu; 110 | QAction * newTabAct; 111 | QAction * nextTabAct; 112 | QAction * previousTabAct; 113 | QAction * closeTabAct; 114 | QAction * watchListAct; 115 | QAction * valueExplorerAct; 116 | 117 | QMenu * helpMenu; 118 | QAction * aboutAct; 119 | 120 | private: 121 | QThread workerThread; 122 | 123 | private slots: 124 | void onShowDockRequested(WatchList * watchList); 125 | void onHideDockRequested(WatchList * watchList); 126 | 127 | void finishInitializing(); 128 | 129 | void cppExceptionNotice(char const * what); 130 | 131 | signals: 132 | void initializeEvaluator(); 133 | }; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /examples/workbench/renconsole.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_RENCONSOLE_H 2 | #define RENGARDEN_RENCONSOLE_H 3 | 4 | // 5 | // renconsole.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include "rencpp/ren.hpp" 32 | 33 | #include "evaluator.h" 34 | #include "replpad.h" 35 | #include "renshell.h" 36 | #include "renpackage.h" 37 | #include "watchlist.h" 38 | 39 | class MainWindow; 40 | 41 | class RenConsole : 42 | public QTabWidget, public IReplPadHooks, public IReplPadSyntaxer 43 | { 44 | Q_OBJECT 45 | 46 | public: 47 | RenConsole (EvaluatorWorker * worker, QWidget * parent = nullptr); 48 | ~RenConsole () override; 49 | 50 | private: 51 | // Syntax highlighting hooks 52 | std::pair rangeForWholeToken( 53 | QString buffer, int offset 54 | ) const override; 55 | 56 | std::pair autoComplete( 57 | QString const & text, int index, bool backwards 58 | ) override; 59 | 60 | private: 61 | ren::optional helpersContext; 62 | ren::AnyContext userContext; 63 | ren::AnyContext libContext; 64 | std::unique_ptr shell; 65 | 66 | public: 67 | ReplPad const & repl() const { 68 | return *qobject_cast(currentWidget()); 69 | } 70 | ReplPad const & replFromIndex(int index) const { 71 | return *qobject_cast(widget(index)); 72 | } 73 | 74 | ReplPad & repl() { 75 | return *qobject_cast(currentWidget()); 76 | } 77 | ReplPad & replFromIndex(int index) { 78 | return *qobject_cast(widget(index)); 79 | } 80 | 81 | private: 82 | struct TabInfo { 83 | ren::Function dialect; // what dialect processor tab is running 84 | ren::optional label; // label of the tab 85 | ren::AnyContext context; 86 | WatchList * watchList; 87 | }; 88 | 89 | std::unordered_map tabinfos; 90 | 91 | TabInfo & getTabInfo(ReplPad const & pad) { 92 | auto it = tabinfos.find(&pad); 93 | return it->second; 94 | } 95 | 96 | 97 | signals: 98 | void switchWatchList(WatchList * watchList); 99 | 100 | void showDockRequested(WatchList * watchList); 101 | 102 | void hideDockRequested(WatchList * watchList); 103 | 104 | void exploreValue( 105 | ren::AnyValue const & helpFunction, 106 | ren::optional const & value 107 | ); 108 | 109 | protected: 110 | bool bannerPrinted; 111 | void printBanner(); 112 | QString getPromptString(ReplPad & pad) override; 113 | 114 | protected: 115 | bool isReadyToModify(ReplPad & pad, bool escaping) override; 116 | 117 | private: 118 | ReplPad * evaluatingRepl; 119 | NulOStream nulOstream; 120 | 121 | private: 122 | ren::AnyValue consoleFunction; 123 | 124 | void escape(ReplPad & pad) override; 125 | 126 | private: 127 | // Experimental facility for writing the shell's output to a string 128 | 129 | ren::optional target; 130 | 131 | // You can set up a value that represents what the pending console 132 | // buffer will be. It is evaluated after the command is done to 133 | // produce a string, and will be forgotten once used. If there is 134 | // an error in the command, no buffer will be loaded. 135 | 136 | QString pendingBuffer; 137 | int pendingAnchor; 138 | int pendingPosition; 139 | 140 | public slots: 141 | void handleResults( 142 | bool success, 143 | ren::optional const & result 144 | ); 145 | signals: 146 | // keep terminology from Qt sample 147 | void operate( 148 | ren::AnyValue const & dialectValue, 149 | ren::AnyValue const & contextValue, 150 | QString const & input, 151 | bool meta 152 | ); 153 | void finishedEvaluation(); 154 | protected: 155 | void evaluate(ReplPad & pad, QString const & input, bool meta) override; 156 | 157 | // Primitive "package management", which is just an experiment to get 158 | // some basic caching and updating started (a test case for figuring out 159 | // where to put local files and settings, etc.) Consider it a "from 160 | // scratch attempt to make MODULE!" and then compare the implemenations. 161 | private: 162 | QSharedPointer proposalsPackage; 163 | ren::optional proposalsContext; 164 | QSharedPointer helpersPackage; 165 | QSharedPointer rendataPackage; 166 | 167 | signals: 168 | void reportStatus(QString const & str); 169 | 170 | public: 171 | void createNewTab(); 172 | void tryCloseTab(int index); 173 | void updateTabLabels(); 174 | 175 | private: 176 | bool useProposals; 177 | public: 178 | void setUseProposals(bool useProposals); 179 | bool getUseProposals() { return useProposals; } 180 | }; 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /examples/workbench/rendata/copyright.ren: -------------------------------------------------------------------------------- 1 | Ren [ 2 | Title: {Beginning of strings data} 3 | 4 | Description: { 5 | Putting the copyright string in with an embedded UTF8 character was 6 | a first test of running Rebol scripts from a resource file. That 7 | doesn't necessarily make a lot of sense in the long run, as there 8 | already is a format for string tables to be used by Qt...and even 9 | tools to localize them. 10 | 11 | However, the question of files for holding arbitrary data used 12 | by C++ is a premise of the Ren exchange format in the first place. 13 | This file is a placeholder for a study of the application of 14 | *non-executable* data...(e.g. that which is processed via LOAD 15 | and not with DO). 16 | } 17 | ] 18 | 19 | ; Unicode test (and promotion of @HostileFork's school of philosophy...) 20 | 21 | copyright: "Ren Garden is © 2015-2018 MetÆducation, GPL3" 22 | -------------------------------------------------------------------------------- /examples/workbench/rengarden_fr.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainWindow 6 | 7 | watch 8 | 9 | 10 | 11 | Ren [人] Garden 12 | 13 | 14 | 15 | About Ren [人] Garden 16 | À propos de Ren [人] Garden 17 | 18 | 19 | The <b>Ren [人] Garden</b> workbench integrates Rebol or Red evaluators into a Qt-based environment, by utilizing the Rencpp binding. 20 | 21 | Copyright © 2015 MetÆducation, GPL License 22 | 23 | Rebol, Red, and Qt are governed by the terms of their licenses. 24 | L'établi <b>Ren [人] Garden</b> intègre les interpréteurs Rebol et Red dans un environnement Qt, en utilisant le binding Rencpp. 25 | 26 | Copyright © 2015 MetÆducation, GPL License 27 | 28 | Rebol, Red, et Qt sont régis par les modalités de leurs licences respectives. 29 | 30 | 31 | E&xit 32 | &Quitter 33 | 34 | 35 | Exit the application 36 | Quitter l'application 37 | 38 | 39 | Cu&t 40 | Cou&per 41 | 42 | 43 | Cut the current selection's contents to the clipboard 44 | Coupe la sélection courante dans le presse-papiers 45 | 46 | 47 | &Copy 48 | &Copier 49 | 50 | 51 | Copy the current selection's contents to the clipboard 52 | Copie la sélection courante dans le presse-papiers 53 | 54 | 55 | &Paste 56 | &Coller 57 | 58 | 59 | Paste the clipboard's contents into the current selection 60 | Colle le contenu du presse-papier dans la sélection courante 61 | 62 | 63 | &About 64 | &À propos 65 | 66 | 67 | Show the application's About box 68 | Affiche la rubrique À propos de l'application 69 | 70 | 71 | About &Qt 72 | À propos de &Qt 73 | 74 | 75 | Show the Qt library's About box 76 | Affiche la rubrique À propos de la bibliothèque Qt 77 | 78 | 79 | &File 80 | &Fichier 81 | 82 | 83 | Switch layout direction 84 | Inverser la disposition des icônes 85 | 86 | 87 | &Edit 88 | &Édition 89 | 90 | 91 | &Help 92 | &? 93 | 94 | 95 | Ready 96 | Prêt 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /examples/workbench/renpackage.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // renpackage.cpp 3 | // This file is part of Ren Garden 4 | // Copyright (C) 2015-2018 MetÆducation 5 | // 6 | // Ren Garden is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // Ren Garden is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with Ren Garden. If not, see . 18 | // 19 | // See http://ren-garden.metaeducation.com for more information on this project 20 | // 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "rencpp/ren.hpp" 30 | 31 | #include "renpackage.h" 32 | 33 | 34 | // Work in progress...currently just a stub 35 | 36 | using namespace ren; 37 | 38 | RenPackage::RenPackage ( 39 | QString rcPrefix, 40 | QString /*urlPrefix*/, 41 | Block const & scripts, 42 | ren::optional context 43 | ) { 44 | if (context == nullopt) { 45 | data = Block {}; 46 | } 47 | 48 | for (auto filename : scripts) { 49 | QFile file {rcPrefix + static_cast(filename)}; 50 | file.open(QIODevice::ReadOnly); 51 | QByteArray bytes = file.readAll(); 52 | if (context) { 53 | // 54 | // !!! This file was just an experiment, but it seems to have 55 | // used the feature for loading and binding information into 56 | // a context. But what would this do? There's no binding. 57 | // 58 | (*context)(bytes.data()); 59 | } 60 | else { 61 | // because pure Ren hasn't been thought about yet, and 62 | // runtime defaults to USER context, we have to force 63 | // unbound loading behavior. (Remember again this is a thought 64 | // experiment contributing to hammer on the design of module 65 | // and on the API. Note we are cheating by using runtime 66 | // functions to perform a data-oriented task. 67 | 68 | runtime("append", *data, filename); 69 | runtime( 70 | "append/only", *data, 71 | "load/type", String {bytes.data()}, "'unbound" 72 | ); 73 | } 74 | } 75 | } 76 | 77 | QNetworkAccessManager & RenPackage::getNetwork() { 78 | static QNetworkAccessManager network; // no parent, dies at shutdown 79 | return network; 80 | } 81 | 82 | void RenPackage::downloadLocally() { 83 | // DataLocation is deprecated as of 5.4 and wants you to use 84 | // "AppDataLocation" instead 85 | 86 | QStringList paths = 87 | QStandardPaths::standardLocations(QStandardPaths::DataLocation); 88 | 89 | QNetworkAccessManager & network = getNetwork(); 90 | 91 | QNetworkReply * reply = network.get(QNetworkRequest( 92 | QUrl("https://raw.githubusercontent.com/hostilefork/rebmu/master/rebmu.reb") 93 | )); 94 | 95 | connect( 96 | &network, &QNetworkAccessManager::finished, 97 | this, &RenPackage::replyFinished, 98 | Qt::DirectConnection 99 | ); 100 | } 101 | 102 | void RenPackage::replyFinished(QNetworkReply * reply) { 103 | assert(reply->error() == QNetworkReply::NoError); 104 | 105 | QString contentType = 106 | reply->header(QNetworkRequest::ContentTypeHeader).toString(); 107 | 108 | assert(contentType.contains("charset=utf-8")); 109 | 110 | QString data = QString::fromUtf8(reply->readAll()); 111 | 112 | data.truncate(256); 113 | QMessageBox::information(nullptr, "Data retrieved...", data); 114 | 115 | // We are responsible for submitting a delete request; cannot delete 116 | // directly in this handler 117 | reply->deleteLater(); 118 | } 119 | 120 | RenPackage::~RenPackage () { 121 | } 122 | -------------------------------------------------------------------------------- /examples/workbench/renpackage.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_RENPACKAGE_H 2 | #define RENGARDEN_RENPACKAGE_H 3 | 4 | // 5 | // renpackage.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | #include 26 | 27 | // 28 | // Another exploratory attempt...which could be improved somehow or perhaps 29 | // replaced entirely. It may be a synonym for what MODULE! was supposed 30 | // to be able to do, but does not quite do today. 31 | // 32 | // A RenPackage is simply a way of keeping some files up to date in the 33 | // Ren Garden system. A package's files may be cached in the binary as 34 | // resources, but it may be desirable to update them even without updating 35 | // Ren Garden. The goal is to find a suitable directory on the user's 36 | // system to put the files. 37 | // 38 | // It doesn't do any dynamic updates as of yet, only testing the network 39 | // API. Just gets the files out of the QRC resource embedded in the 40 | // Ren Garden executable for now. 41 | // 42 | 43 | #include "optional/optional.hpp" 44 | 45 | #include 46 | 47 | #include "rencpp/ren.hpp" 48 | 49 | class RenPackage : public QObject { 50 | 51 | Q_OBJECT 52 | 53 | public: 54 | RenPackage ( 55 | QString rcPrefix, 56 | QString urlPrefix, 57 | ren::Block const & spec, 58 | ren::optional context 59 | ); 60 | 61 | virtual ~RenPackage (); 62 | 63 | private: 64 | QNetworkAccessManager & getNetwork(); 65 | 66 | public: 67 | void downloadLocally(); 68 | 69 | private slots: 70 | void replyFinished(QNetworkReply * reply); 71 | 72 | private: 73 | // If data-only, the loaded data for each file, which can be indexed 74 | ren::optional data; 75 | public: 76 | ren::AnyValue getData(ren::Filename const & filename) const { 77 | return (*data)[filename]; 78 | } 79 | }; 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /examples/workbench/renshell.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_RENSHELL_H 2 | #define RENGARDEN_RENSHELL_H 3 | 4 | // 5 | // renshell.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "rencpp/ren.hpp" 31 | 32 | // 33 | // Implements the shelldialect by using QProcess to do I/O with bash 34 | // or sh or command...while utilizing RenCpp to do processing on that 35 | // input or maybe output. 36 | // 37 | // VERY EARLY TECHNICAL HACK DRAFT JUST TO SEE HOW TO GET IT WORKING 38 | // 39 | 40 | class RenShell : public QObject 41 | { 42 | Q_OBJECT 43 | 44 | public: 45 | RenShell (ren::AnyContext const & helpers, QObject * parent = nullptr); 46 | ~RenShell () override; 47 | 48 | private: 49 | QMutex shellDoneMutex; 50 | QWaitCondition shellDone; 51 | bool shellDoneSuccess; 52 | int shellDoneResult; 53 | 54 | private: 55 | ren::AnyContext helpers; 56 | ren::AnyValue shellFunction; 57 | bool testMode; 58 | public: 59 | ren::Function getShellDialectFunction() { 60 | return static_cast(shellFunction); 61 | } 62 | 63 | private: 64 | QThread workerThread; 65 | 66 | public slots: 67 | void handleResults(int result); 68 | signals: 69 | // keep terminology from Qt sample 70 | void operate(QString const & input, std::ostream * os); 71 | void finishedEvaluation(); 72 | public: 73 | void evaluate(QString const & input, std::ostream & os); 74 | }; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /examples/workbench/scripts/helpers/README.md: -------------------------------------------------------------------------------- 1 | This directory contains helper scripts in native Ren format for Ren Garden. 2 | They implement parts of the functionality which are easier to write (or 3 | maintain) than if they were inlined into the C++ source. 4 | 5 | The more of the console's behavior that can be implemented in these scripts, 6 | the more easily they can be maintained by non-C++ programmers. Pursuant to 7 | the goal of being used and understood outside of the context of being 8 | called from a C++ binding, Rebol conventions are followed wherever possible 9 | (such as 1-based indexing). 10 | 11 | Note these files are embedded into the executable by being put into the 12 | .qrc file, and accessed via the resource mechanism. That means a rebuild 13 | will be necessary to pick up any changes. A more dynamic mechanism that 14 | would allow reloading or updating these scripts without needing a new 15 | executable is in the early stages of design. 16 | 17 | 18 | ### LICENSE ### 19 | 20 | Ren Garden is a GPL project. Yet these helper scripts are BSD-licensed, and 21 | other non-C++-or-RenCpp-based consoles are free to borrow them. The 22 | dialects and their design are welcome to be used in other Rebol/Red-based 23 | projects. 24 | 25 | (At time of writing not particularly significant to say so. But who knows-- 26 | this portion of the project might grow to be the majority of Ren Garden's 27 | general logic.) 28 | 29 | 30 | ### A NOTE ABOUT RATIONALE ### 31 | 32 | Entering Rebol and Red source code directly is possible with RenCpp, and 33 | is made as painless as the medium permits: 34 | 35 | Block rule { 36 | "thru {[}", 37 | "copy", some-cpp-variable, "to {]}", 38 | "to end" 39 | }; 40 | 41 | This is the only way to splice Ren values which have been wrapped as C++ 42 | objects directly into series constructions. However, let's say you just 43 | want to inline some code that LOADs a value with no embedded C++ 44 | variables, e.g. a function: 45 | 46 | Function putInQuotes = { 47 | "function [foo bar] [ " 48 | "quoted: combine [{\"} foo bar {\"}] " 49 | "print quoted " 50 | "return quoted " 51 | "]" 52 | }; 53 | 54 | There are several inconveniences: 55 | 56 | * **Different Escaping Rules** - String literals in C++ are victim to the 57 | usual legacy concerns and visual blight the C-family-language style is 58 | known for. It's fortunate that Ren's alternate string form with curly 59 | braces can be embedded without necessitating two escapes for *every* string. 60 | Yet the backslashes do wind up making their appearance at times, and 61 | become all the more confusing when trying to read mixed-medium code. 62 | 63 | * **Multi-Line Strings** - There are two ways to do multi-line strings in C++ 64 | and [neither is optimal](http://stackoverflow.com/a/1135862/211160). If 65 | If you terminate with a quote and then start with a quote on the next line, 66 | they will be merged...but this requires remembering to leave a space on the 67 | end. (In the above example, not putting the space after "print quoted" would 68 | lead to "print quotedreturn quoted"). Using backslashes is ugly and will 69 | also lead to "fattening" the constant at runtime with wasted space for any 70 | indentation. 71 | 72 | * **Permanent Cost** - String constants embedded in the program in this way 73 | are part of the constant pool and address space for the entire program run. 74 | That is additional storage to keeping around the loaded representation. 75 | The source code of helpers loaded from these files is fetched from disk 76 | into memory, loaded, and then freed...keeping only the loaded representation. 77 | 78 | * **Must Touch C++ to Edit** - Many contributors who could improve the 79 | scripts would not be able (or willing) to do so if it involved editing C++ 80 | source files, compiling to make sure they didn't have syntax errors, etc. 81 | 82 | So it's best to factor out plain source instead of inlining it, whenever 83 | the specific dynamism of RenCpp is not required. 84 | 85 | -------------------------------------------------------------------------------- /examples/workbench/scripts/helpers/edit-buffer.reb: -------------------------------------------------------------------------------- 1 | Rebol [ 2 | Title: {Ren Garden Edit Buffer Helpers} 3 | 4 | Description: { 5 | The user may ask to preload the buffer, the dialect (at present) 6 | allows them to make selections as well. So it might look like: 7 | 8 | console/meta ["Hello" space || "Selection" | space "World"] 9 | 10 | The | indicates the position point of a selection, and the || 11 | indicates the anchor. What Ren Garden needs back is a triple of 12 | the total string and the integer positions of the start and the end. 13 | So the above would need to produce: 14 | 15 | ["Hello Selection World" 15 6] 16 | 17 | (Bear in mind that if a string is N characters long, it has N + 1 18 | positions... and we're using Rebol indexing so they can start at 19 | 1 and go up to N in the return result. If the position and 20 | anchor are equal, then the selection is "collapsed" to a point.) 21 | 22 | This was an early demo of a piece of functionality that was easier 23 | to write as a Rebol script than as the corresponding C++ code. 24 | So it was broken out into a "Helper". The ultimate balance of 25 | power may be shifted such that more of the functionality of Ren 26 | Garden is driven by the scripts, so the shape of these interfaces 27 | may change to be more of a "don't call me, I'll call you" with 28 | the selection requests coming from the scripting side. 29 | } 30 | 31 | License: 'BSD 32 | ] 33 | 34 | 35 | console-buffer-helper: function [value [block! string!]] [ 36 | 37 | ; A plain old string with no markers on it is just treated 38 | ; like a collapsed selection at the very tail of the string 39 | ; (position = anchor) 40 | 41 | if string? value [ 42 | return reduce [(value) (1 + length value) (1 + length value)] 43 | ] 44 | 45 | 46 | ; Otherwise it's a block, so see if there are any markers 47 | ; inside of it. 48 | 49 | ; !!! What if the markers aren't at the top level? combine does permit 50 | ; nested block structure, so this should perhaps be done with some kind 51 | ; of FIND/DEEP in a perfect world (that would make all this tricky!) 52 | 53 | position: find value '| 54 | anchor: find value '|| 55 | 56 | 57 | ; Give an error back if they've used too many anchors. (Might it be 58 | ; okay if they use | twice, though, to indicate they don't care 59 | ; which end of the selection the cursor winds up on? It's easy to 60 | ; forget and do that...) 61 | 62 | if any [ 63 | find next position '| 64 | find next anchor '|| 65 | ][ 66 | fail {Buffer dialect only permits one | and || mark in a block} 67 | ] 68 | 69 | 70 | ; Three cases to cover: no markers, just |, and both | and || 71 | 72 | case [ 73 | 74 | ; simple case: a block with no selection markers is stringified 75 | ; with cursor at the end of the generated string 76 | ; 77 | not any [position anchor] [ 78 | buffer: unspaced value 79 | position-index: anchor-index: 1 + length-of buffer 80 | ] 81 | 82 | 83 | ; If there's only one mark, then we do stringification in two phases: 84 | ; the part before it, and the part after it. We use the length 85 | ; of the first half of the combination to find the cursor position 86 | 87 | all [position | not anchor] [ 88 | buffer: unspaced copy/part value position 89 | position-index: anchor-index: 1 + length-of buffer 90 | 91 | append buffer unspaced (next position) 92 | ] 93 | 94 | 95 | ; Two marks...a similar method to before, just with three divisions 96 | ; for the stringification instead of two 97 | 98 | true [ 99 | ; The only difference between the marks being | then || vs 100 | ; || then | is which side the cursor winds up on. A forward 101 | ; selection will have the anchor before the position. If 102 | ; it's not a forward selection, swap the positions and then 103 | ; swap the results later. 104 | 105 | backward: (index-of position) < (index-of anchor) 106 | if backward [ 107 | ; @HostileFork had the idea for a SWAP-VALUES and notes 108 | ; that @Maxim came up with the same name for the same 109 | ; function implementation, so perhaps it should exist: 110 | ; 111 | ; http://www.rebol.org/aga-display-posts.r?post=r3wp824x957 112 | 113 | temp: position 114 | position: anchor 115 | anchor: temp 116 | ] 117 | 118 | buffer: unspaced copy/part value anchor 119 | anchor-index: 1 + length-of buffer 120 | 121 | append buffer unspaced copy/part (next anchor) position 122 | position-index: 1 + length-of buffer 123 | 124 | append buffer unspaced [next position] 125 | 126 | ; If necessary, reverse the index results to account for our 127 | ; earlier reversal of marker positions 128 | 129 | if backward [ 130 | temp: anchor-index 131 | anchor-index: position-index 132 | position-index: temp 133 | ] 134 | ] 135 | ] 136 | 137 | 138 | ; Return the triple as the final result from the dialect 139 | 140 | return reduce [buffer position-index anchor-index] 141 | ] 142 | -------------------------------------------------------------------------------- /examples/workbench/scripts/helpers/shell.reb: -------------------------------------------------------------------------------- 1 | Rebol [ 2 | Title: {Ren Garden Shell Dialect Helpers} 3 | 4 | Description: { 5 | One of Ren Garden's features is to allow interaction with persistent 6 | shell processes. It is implemented as a "command-line dialect", 7 | so you can write things like: 8 | 9 | shell [FOO: *.cpp] 10 | 11 | ...and under the hood, what will actually be sent to the shell 12 | process (e.g. with UNIX /bin/sh) will look like: 13 | 14 | export FOO="*.cpp" 15 | 16 | The implementation of the process spawning logistics and data 17 | piping is done with [QProcess](http://doc.qt.io/qt-5/qprocess.html) 18 | and implemented on the C++ side. However, how the dialect input is 19 | translated from a Ren block into a string for the shell is delegated 20 | to the routines in this script. 21 | 22 | Please update the file in the docs directory if you change the 23 | behavior of the script. 24 | } 25 | 26 | Homepage: https://github.com/hostilefork/rencpp/blob/develop/examples/workbench/docs/shell.md 27 | 28 | License: 'BSD 29 | ] 30 | 31 | 32 | block-to-shell-strings: function [arg [block!] windows [logic!]] [ 33 | 34 | ; The simplest idea for what to do here would be to just run 35 | ; a COMPOSE/DEEP and then convert the result to a string. But 36 | ; then you get situations where if escaped Rebol code resulted 37 | ; in a FILE!, it would still have the % sign in front of it. 38 | 39 | ; So for now, just a one-level deep parentheses substitution 40 | ; where we can treat the result of the substitution differently 41 | ; than if it hadn't been substituted 42 | 43 | ; set words are used for setting environment variables, and 44 | ; get words are used for retrieving them 45 | 46 | result: make block! 1 47 | str: make string! (5 * length of arg) 48 | 49 | for-next arg [ 50 | case [ 51 | group? arg/1 [ 52 | evaluated: do arg/1 53 | append str form evaluated 54 | ] 55 | 56 | set-word? arg/1 [ 57 | append str unspaced [ 58 | windows ?? {set} !! {export} 59 | space 60 | spelling-of arg/1 {=} 61 | not windows ?? {"} 62 | form either group? arg/2 [do arg/2] [arg/2] 63 | not windows ?? {"} 64 | ] 65 | arg: next arg 66 | ] 67 | 68 | get-word? arg/1 [ 69 | append str unspaced [ 70 | windows ?? {%} !! {$} 71 | spelling-of arg/1 72 | windows ?? {%} 73 | ] 74 | ] 75 | 76 | block? arg/1 [ 77 | ; A formality issue... should a shell dialect have to either 78 | ; be all block elements or no block elements? We allow the 79 | ; flip for the moment but end the previous command; the 80 | ; implementation leaves a trailing space in that case ATM 81 | 82 | unless empty? str [ 83 | append result str 84 | ] 85 | 86 | append result block-to-shell-strings arg/1 windows 87 | 88 | str: make string! (5 * length of arg/1) 89 | ] 90 | ] else [ 91 | append str form arg/1 92 | ] 93 | 94 | unless any [block? arg/1 | last? arg] [ 95 | append str space 96 | ] 97 | ] 98 | 99 | unless empty? str [ 100 | append result str 101 | ] 102 | 103 | result 104 | ] 105 | 106 | 107 | ; The shell helper must return a block of strings that will be passed to 108 | ; the shell as sequential commands. 109 | 110 | shell-dialect-to-strings: function [ 111 | 'arg [ word! block! string!] 112 | windows [logic!] 113 | ][ 114 | result: make block! 1 115 | 116 | switch type-of arg [ 117 | _ [ 118 | fail "Altering command processor state...soon" 119 | ] 120 | 121 | :word! [ 122 | append result (form arg) 123 | ] 124 | 125 | :string! [ 126 | append result (arg) 127 | ] 128 | 129 | :block! [ 130 | result: block-to-shell-strings arg windows 131 | ] 132 | ] 133 | 134 | result 135 | ] 136 | -------------------------------------------------------------------------------- /examples/workbench/valueexplorer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // valueexplorer.cpp 3 | // This file is part of Ren Garden 4 | // Copyright (C) 2015-2018 MetÆducation 5 | // 6 | // Ren Garden is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // Ren Garden is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with Ren Garden. If not, see . 18 | // 19 | // See http://ren-garden.metaeducation.com for more information on this project 20 | // 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "valueexplorer.h" 27 | 28 | using namespace ren; 29 | 30 | ValueExplorer::ValueExplorer (QWidget * parent) : 31 | QPlainTextEdit (parent) 32 | { 33 | setReadOnly(true); 34 | zoomOut(); // make slightly smaller than normal? :-/ 35 | } 36 | 37 | 38 | void ValueExplorer::setValue( 39 | AnyValue const & helpFunction, 40 | optional const & value 41 | ) { 42 | if (!this->isVisible()) 43 | return; 44 | 45 | if (value == nullopt) { 46 | document()->setPlainText(""); 47 | } 48 | else { 49 | try { 50 | std::stringstream ss; 51 | 52 | auto & oldStream = Engine::runFinder().setOutputStream(ss); 53 | static_cast(helpFunction)(value); 54 | Engine::runFinder().setOutputStream(oldStream); 55 | 56 | document()->setPlainText(ss.str().c_str()); 57 | } 58 | catch (std::exception const & e) { 59 | // Some error... tell user so (but don't crash) 60 | auto msg = e.what(); 61 | QMessageBox::information( 62 | NULL, 63 | "HELP function internal error during Value ValueExplorer", 64 | msg 65 | ); 66 | } 67 | catch (...) { 68 | assert(false); // some non-std::exception?? :-/ 69 | throw; 70 | } 71 | } 72 | } 73 | 74 | 75 | ValueExplorer::~ValueExplorer () 76 | { 77 | } 78 | -------------------------------------------------------------------------------- /examples/workbench/valueexplorer.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_VALUEEXPLORER_H 2 | #define RENGARDEN_VALUEEXPLORER_H 3 | 4 | // 5 | // valueexplorer.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | #include "optional/optional.hpp" 26 | 27 | #include 28 | 29 | #include "rencpp/ren.hpp" 30 | 31 | class ValueExplorer : public QPlainTextEdit { 32 | Q_OBJECT 33 | 34 | private: 35 | ren::optional value; 36 | 37 | public: 38 | ValueExplorer (QWidget * parent); 39 | ~ValueExplorer () override; 40 | 41 | public slots: 42 | void setValue( 43 | ren::AnyValue const & helpFunction, 44 | ren::optional const & value 45 | ); 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /examples/workbench/watchlist.h: -------------------------------------------------------------------------------- 1 | #ifndef RENGARDEN_WATCHLIST_H 2 | #define RENGARDEN_WATCHLIST_H 3 | 4 | // 5 | // watchlist.h 6 | // This file is part of Ren Garden 7 | // Copyright (C) 2015-2018 MetÆducation 8 | // 9 | // Ren Garden is free software: you can redistribute it and/or modify 10 | // it under the terms of the GNU General Public License as published by 11 | // the Free Software Foundation, either version 3 of the License, or 12 | // (at your option) any later version. 13 | // 14 | // Ren Garden is distributed in the hope that it will be useful, 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | // GNU General Public License for more details. 18 | // 19 | // You should have received a copy of the GNU General Public License 20 | // along with Ren Garden. If not, see . 21 | // 22 | // See http://ren-garden.metaeducation.com for more information on this project 23 | // 24 | 25 | #include 26 | #include 27 | 28 | #include "optional/optional.hpp" 29 | 30 | #include 31 | 32 | #include "rencpp/ren.hpp" 33 | 34 | class MainWindow; 35 | 36 | class WatchList : public QTableWidget 37 | { 38 | Q_OBJECT 39 | 40 | public: 41 | class Watcher { 42 | friend class WatchList; 43 | 44 | ren::AnyValue watch; 45 | bool recalculates; 46 | ren::optional value; 47 | ren::optional error; 48 | ren::optional label; 49 | bool frozen; 50 | 51 | public: 52 | // Construct will also evaluate to capture at the time of the watch 53 | // being added (particularly important if it's a cell) 54 | Watcher ( 55 | ren::AnyValue const & watch, 56 | bool recalculates, 57 | ren::optional const & label 58 | ); 59 | 60 | // Evaluates and returns error if there was one, or none 61 | void evaluate(bool firstTime = false); 62 | 63 | QString getWatchString() const; 64 | 65 | QString getValueString() const; 66 | }; 67 | 68 | std::vector> watchers; 69 | 70 | public: 71 | WatchList (QWidget * parent = nullptr); 72 | 73 | protected slots: 74 | void customMenuRequested(QPoint pos); 75 | 76 | signals: 77 | void watchCalled( 78 | ren::AnyValue vars, 79 | bool recalculates, 80 | ren::AnyValue label 81 | ); 82 | 83 | void showDockRequested(WatchList * watchList); 84 | 85 | void hideDockRequested(WatchList * watchList); 86 | 87 | void pushWatcherRequested(Watcher * watcherUnique); 88 | 89 | void removeWatcherRequested(int index); 90 | 91 | void freezeItemRequested(int index, bool frozen); 92 | 93 | void recalulatesItemRequested(int index, bool recalculates); 94 | 95 | void reportStatus(QString message); 96 | 97 | public slots: 98 | void updateWatcher(int index); 99 | 100 | void updateAllWatchers(); 101 | 102 | private slots: 103 | void pushWatcher(Watcher * watcherUnique); 104 | 105 | void removeWatcher(int index); 106 | 107 | void duplicateWatcher(int index); 108 | 109 | void setFreezeState(int index, bool frozen); 110 | 111 | void setRecalculatesState(int index, bool recalculates); 112 | 113 | void onItemChanged(QTableWidgetItem * item); 114 | 115 | protected: 116 | void mousePressEvent(QMouseEvent * event) override; 117 | 118 | QSize sizeHint() const override; // see comment on implementation 119 | 120 | public: 121 | // aaaand... magic! :-) 122 | ren::optional watchDialect( 123 | ren::AnyValue const & arg, 124 | ren::optional const & label 125 | ); 126 | }; 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /examples/workbench/workbench.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/copy.png 4 | images/cut.png 5 | images/new.png 6 | images/open.png 7 | images/paste.png 8 | images/save.png 9 | images/rebol-logo.png 10 | images/red-logo.png 11 | scripts/helpers/autocomplete.reb 12 | scripts/helpers/edit-buffer.reb 13 | scripts/helpers/shell.reb 14 | scripts/combine.reb 15 | images/banner-logo.png 16 | rendata/copyright.ren 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/workbench/workbench.rc: -------------------------------------------------------------------------------- 1 | id ICON "images/Celldrifter-Muku-Style-Sys-Desktop.ico" -------------------------------------------------------------------------------- /external/catch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(catch_builder CXX) 3 | 4 | include(ExternalProject) 5 | 6 | find_package(Git REQUIRED) 7 | 8 | ExternalProject_Add(catch 9 | PREFIX ${CMAKE_BINARY_DIR}/external/catch/upstream 10 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 11 | TIMEOUT 10 12 | LOG_DOWNLOAD ON 13 | UPDATE_COMMAND ${GIT_EXECUTABLE} pull 14 | CONFIGURE_COMMAND "" 15 | BUILD_COMMAND "" 16 | INSTALL_COMMAND "" 17 | ) 18 | 19 | # Expose required variable (CATCH_INCLUDE_DIR) to parent scope 20 | ExternalProject_Get_Property(catch SOURCE_DIR) 21 | set(CATCH_INCLUDE_DIR 22 | ${SOURCE_DIR}/single_include CACHE INTERNAL 23 | "Path to include folder for Catch" 24 | ) 25 | -------------------------------------------------------------------------------- /git-hooks/README.md: -------------------------------------------------------------------------------- 1 | The hooks that git executes (such as a "pre-commit" hook), are placed 2 | in `.git/hooks`. These are *not* part of the tracked files under 3 | version control, so you will not get them installed from a `git clone` 4 | 5 | So it is necessary for each developer to copy these over after they 6 | have cloned. The hooks may rely on functions that are not available 7 | on some build systems, so that should be considered. 8 | 9 | Improvements are welcome. For now, the goal is just to keep tabs from 10 | sneaking into files where they shouldn't be, but much more can be 11 | done. Please at minimum do this if you are going to be contributing: 12 | 13 | cp git-hooks/pre-commit .git/hooks/pre-commit 14 | -------------------------------------------------------------------------------- /git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # An example hook script to verify what is about to be committed. 4 | # Called by git-commit with no arguments. The hook should 5 | # exit with non-zero status after issuing an appropriate message if 6 | # it wants to stop the commit. 7 | # 8 | # To enable this hook, make this file executable. 9 | 10 | # This is slightly modified from Andrew Morton's Perfect Patch. 11 | # Lines you introduce should not have trailing whitespace. 12 | # Also check for an indentation that has SP before a TAB. 13 | 14 | # derived from: 15 | # https://gist.github.com/benprew/6384274 16 | # http://git.xiph.org/speex.git/hooks/pre-commit 17 | 18 | if git rev-parse --verify HEAD 2>/dev/null 19 | then 20 | git diff-index -p -M --cached HEAD 21 | else 22 | # NEEDSWORK: we should produce a diff with an empty tree here 23 | # if we want to do the same verification for the initial import. 24 | : 25 | fi | 26 | perl -e ' 27 | my $found_bad = 0; 28 | my $filename; 29 | my $reported_filename = ""; 30 | my $lineno; 31 | sub bad_line { 32 | my ($why, $line) = @_; 33 | if (!$found_bad) { 34 | print STDERR "*\n"; 35 | print STDERR "* You have some suspicious patch lines:\n"; 36 | print STDERR "*\n"; 37 | $found_bad = 1; 38 | } 39 | if ($reported_filename ne $filename) { 40 | print STDERR "* In $filename\n"; 41 | $reported_filename = $filename; 42 | } 43 | print STDERR "* $why (line $lineno)\n"; 44 | print STDERR "$filename:$lineno:$line\n"; 45 | } 46 | while (<>) { 47 | if (m|^diff --git a/(.*) b/\1$|) { 48 | $filename = $1; 49 | next; 50 | } 51 | if (/^@@ -\S+ \+(\d+)/) { 52 | $lineno = $1 - 1; 53 | next; 54 | } 55 | if (/^ /) { 56 | $lineno++; 57 | next; 58 | } 59 | if (s/^\+//) { 60 | $lineno++; 61 | chomp; 62 | if (/\s$/) { 63 | bad_line("trailing whitespace", $_); 64 | } 65 | if (/^\s* /) { 66 | bad_line("indent SP followed by a TAB", $_); 67 | } 68 | if (/^(?:[<>=]){7}/) { 69 | bad_line("unresolved merge conflict", $_); 70 | } 71 | } 72 | } 73 | exit($found_bad); 74 | ' 75 | 76 | -------------------------------------------------------------------------------- /include/rencpp/context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_CONTEXT_HPP 2 | #define RENCPP_CONTEXT_HPP 3 | 4 | // 5 | // context.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include 23 | 24 | #include "value.hpp" 25 | #include "runtime.hpp" 26 | 27 | namespace ren { 28 | 29 | 30 | // 31 | // CONTEXT FOR BINDING 32 | // 33 | 34 | // 35 | // Historically the Rebol language used the terms CONTEXT and OBJECT somewhat 36 | // interchangeably, although the data type was called OBJECT! Ren-C has 37 | // redefined the terminology so that ANY-CONTEXT! is the superclass of 38 | // ERROR!, OBJECT!, PORT! etc. (in the spirit of not having the superclass 39 | // share a name with any specific member of said class). 40 | // 41 | // !!! Under Rebol's hood, an object was implemented as a pair of series. 42 | // Accessing the object by position was not allowed, though some natives 43 | // offered features that demonstrated positional awareness (e.g. SET). 44 | // This is likely to be deprecated in favor of more fluidity in the 45 | // implementation of object. 46 | // 47 | 48 | class Engine; 49 | 50 | class AnyContext : public AnyValue { 51 | protected: 52 | friend class AnyValue; 53 | AnyContext (Dont) noexcept : AnyValue (Dont::Initialize) {} 54 | static bool isValid(REBVAL const * cell); 55 | 56 | // Friending doesn't seem to be enough for gcc 4.6, see SO writeup: 57 | // http://stackoverflow.com/questions/32983193/ 58 | public: 59 | friend class Object; 60 | static void initObject(REBVAL *cell); 61 | friend class Error; 62 | static void initError(REBVAL *cell); 63 | // 64 | // !!! Ports, Modules, Frames... (just Object and error for starters) 65 | // 66 | 67 | public: 68 | AnyContext copy(bool deep = true) { 69 | return static_cast(AnyValue::copy(deep)); 70 | } 71 | 72 | public: 73 | using Finder = std::function; 74 | 75 | private: 76 | friend class AnyArray; 77 | friend class AnyString; 78 | friend class AnyWord; 79 | friend class Runtime; 80 | 81 | static Finder finder; 82 | RenEngineHandle getEngine() const { return origin; } 83 | 84 | public: 85 | 86 | static AnyContext lookup(char const * name, Engine * engine = nullptr); 87 | 88 | static Finder setFinder( 89 | Finder const & newFinder 90 | ) { 91 | auto result = finder; 92 | finder = newFinder; 93 | return result; 94 | } 95 | 96 | // The reason that context finding is dependent on the engine has to do 97 | // with the default execution for Engine e; then e(...) 98 | // If there was only a context finder that didn't depend on the engine, 99 | // such calls could return a context from the wrong engine. 100 | // 101 | // Passing as a pointer in order to be able to optimize out the cases 102 | // where you don't care, but the parameter is there if you want to use it 103 | 104 | static AnyContext current(Engine * engine = nullptr); 105 | 106 | 107 | // Patterned after AnyArray; you can construct a context from the same 108 | // data that can be used to make a block. 109 | protected: 110 | AnyContext ( 111 | internal::Loadable const loadables[], 112 | size_t numLoadables, 113 | internal::CellFunction F, 114 | AnyContext const * contextPtr, 115 | Engine * engine 116 | ); 117 | 118 | AnyContext ( 119 | AnyValue const values[], 120 | size_t numValues, 121 | internal::CellFunction F, 122 | AnyContext const * contextPtr, 123 | Engine * engine 124 | ); 125 | 126 | 127 | // If you use the apply operation in a context, then it means "do this 128 | // code in this context" 129 | // 130 | public: 131 | template 132 | inline optional operator()(Ts &&... args) const { 133 | return apply( 134 | {std::forward(args)...}, 135 | internal::ContextWrapper {*this} 136 | ); 137 | } 138 | 139 | template 140 | inline R create(Ts &&... args) const { 141 | return R { 142 | {std::forward(args)...}, 143 | internal::ContextWrapper {*this} 144 | }; 145 | } 146 | }; 147 | 148 | 149 | namespace internal { 150 | 151 | // 152 | // ANYCONTEXT_ SUBTYPE HELPER 153 | // 154 | 155 | template 156 | class AnyContext_ : public AnyContext { 157 | protected: 158 | friend class AnyValue; 159 | AnyContext_ (Dont) : AnyContext (Dont::Initialize) {} 160 | 161 | public: 162 | AnyContext_ ( 163 | AnyValue const values[], 164 | size_t numValues, 165 | internal::ContextWrapper const & wrapper 166 | ) : 167 | AnyContext (values, numValues, F, &wrapper.context, nullptr) 168 | { 169 | } 170 | 171 | AnyContext_ ( 172 | AnyValue const values[], 173 | size_t numValues, 174 | Engine * engine 175 | ) : 176 | AnyContext (values, numValues, F, nullptr, engine) 177 | { 178 | } 179 | 180 | AnyContext_ ( 181 | std::initializer_list const & loadables, 182 | internal::ContextWrapper const & wrapper 183 | ) : 184 | AnyContext ( 185 | loadables.begin(), 186 | loadables.size(), 187 | F, 188 | &wrapper.context, 189 | nullptr 190 | ) 191 | { 192 | } 193 | 194 | AnyContext_ (AnyContext const & context) : 195 | AnyContext (static_cast(nullptr), 0, F, &context, nullptr) 196 | { 197 | } 198 | 199 | AnyContext_ ( 200 | std::initializer_list const & loadables, 201 | Engine * engine = nullptr 202 | ) : 203 | AnyContext (loadables.begin(), loadables.size(), F, nullptr, engine) 204 | { 205 | } 206 | 207 | AnyContext_ (Engine * engine = nullptr) : 208 | AnyContext (static_cast(nullptr), 0, F, nullptr, engine) 209 | { 210 | } 211 | }; 212 | 213 | } // end namespace internal 214 | 215 | 216 | 217 | // 218 | // CONCRETE CONTEXT TYPES 219 | // 220 | 221 | // 222 | // For why these are classes and not typedefs: 223 | // 224 | // https://github.com/hostilefork/rencpp/issues/49 225 | // 226 | 227 | 228 | class Object 229 | : public internal::AnyContext_ 230 | { 231 | using AnyContext::initObject; 232 | 233 | protected: 234 | static bool isValid(REBVAL const * cell); 235 | 236 | public: 237 | friend class AnyValue; 238 | using internal::AnyContext_::AnyContext_; 239 | }; 240 | 241 | 242 | } // end namespace ren 243 | 244 | #endif 245 | -------------------------------------------------------------------------------- /include/rencpp/engine.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_ENGINE_HPP 2 | #define RENCPP_ENGINE_HPP 3 | 4 | // 5 | // exceptions.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "value.hpp" 27 | #include "runtime.hpp" 28 | 29 | namespace ren { 30 | 31 | 32 | // 33 | // ENGINE OBJECT FOR SANDBOXING INTERPRETER STATE 34 | // 35 | 36 | // 37 | // Each Engine represents a kind of "sandbox", so setting a variable "x" 38 | // in one does not mean it will be readable in another one. This means that 39 | // operations like getting words and values need to know which one you mean. 40 | // 41 | // You can always be explicit about which environment to use. But if you are 42 | // not...then a global handler is called to give back a reference to the one 43 | // that is "currently in effect". The handler you register takes no parameters 44 | // and must rely on some state-known-to-it to decide (such as which thread 45 | // you are on). 46 | // 47 | // For "making simple things simple", there is a default handler. If you make 48 | // any calls to manipulate Values or call into the runtime before registering 49 | // a different one, that handler will automatically allocate an environment 50 | // for you that will have a lifetime through the end of the program. 51 | // 52 | 53 | 54 | class Engine { 55 | public: 56 | using Finder = std::function; 57 | 58 | private: 59 | friend class AnyValue; 60 | friend class AnyArray; 61 | friend class AnyString; 62 | friend class AnyWord; 63 | friend class AnyContext; 64 | 65 | RenEngineHandle handle; 66 | 67 | static Finder finder; 68 | 69 | // These should maybe be internalized behind the binding and not data 70 | // members of the C++ class 71 | private: 72 | std::ostream * osPtr; 73 | std::istream * isPtr; 74 | 75 | public: 76 | // Disable copy construction and assignment. 77 | 78 | Engine (Engine const & other) = delete; 79 | Engine & operator= (Engine const & other) = delete; 80 | 81 | 82 | public: 83 | Engine () : 84 | osPtr (&std::cout), 85 | isPtr (&std::cin) 86 | { 87 | if (::RenAllocEngine(&handle) != REN_SUCCESS) { 88 | throw std::runtime_error ("Couldn't initialize red runtime"); 89 | } 90 | } 91 | 92 | RenEngineHandle getHandle() const { 93 | return handle; 94 | } 95 | 96 | static Finder setFinder( 97 | Finder const & newFinder 98 | ) { 99 | auto result = finder; 100 | finder = newFinder; 101 | return result; 102 | } 103 | 104 | static Engine & runFinder() { 105 | if (finder == nullptr) { 106 | finder = [] () -> Engine & { 107 | static Engine global; 108 | return global; 109 | }; 110 | } 111 | 112 | return finder(); 113 | } 114 | 115 | 116 | // 117 | // As a C++ library it seemed fitting to let you hook your own istream 118 | // and ostream up for the "host kit". Writing a custom iostream is a 119 | // bit confusing, however, and it may not be the best interface. 120 | // 121 | // One could easily imagine a different Engine having a different place 122 | // it is streaming its output to. 123 | // 124 | public: 125 | std::ostream & setOutputStream(std::ostream & os); 126 | 127 | std::istream & setInputStream(std::istream & is); 128 | 129 | std::ostream & getOutputStream(); 130 | 131 | std::istream & getInputStream(); 132 | 133 | // 134 | // See notes on how close() is used for catching exceptions, while the 135 | // destructor should not throw: 136 | // 137 | // http://stackoverflow.com/a/130123/211160 138 | // 139 | 140 | void close() { 141 | auto releaseMe = handle; 142 | handle = REN_ENGINE_HANDLE_INVALID; 143 | if (::RenFreeEngine(releaseMe) != 0) { 144 | throw std::runtime_error ("Failed to shut down red environment"); 145 | } 146 | } 147 | 148 | 149 | virtual ~Engine() { 150 | if (!REN_IS_ENGINE_HANDLE_INVALID(handle)) 151 | ::RenFreeEngine(handle); 152 | } 153 | 154 | public: 155 | static optional evaluate( 156 | std::initializer_list> loadables, 157 | Engine & engine 158 | ); 159 | 160 | template 161 | AnyValue operator()(Ts... args) { 162 | return evaluate({args...}, *this); 163 | } 164 | }; 165 | 166 | } // end namespace ren 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /include/rencpp/error.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_ERROR_HPP 2 | #define RENCPP_ERROR_HPP 3 | 4 | // 5 | // error.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include 23 | 24 | #include "value.hpp" 25 | #include "context.hpp" 26 | 27 | 28 | namespace ren { 29 | 30 | 31 | // 32 | // ERROR VALUE 33 | // 34 | 35 | // 36 | // Ren has its own competing "exception"-like type called ERROR!. And 37 | // if you throw a C++ exception, there is no way for the Ren runtime to 38 | // catch it. And in fact, "throw" and "catch" are distinct from Rebol's 39 | // notion of "trying" and "raising" an error: 40 | // 41 | // http://stackoverflow.com/questions/24412153/ 42 | // 43 | // One way to get the runtime to "raise" an error is to apply it: 44 | // 45 | // ren::Error myerror {"Invalid hedgehog found"}; 46 | // myerror.apply(); 47 | // throw "Unreachable Code"; // make compiler happy? 48 | // 49 | // That should within the guts of apply end up throwing a ren::evaluation_error 50 | // with the error object inside of it. Those are derived from std::exception 51 | // for proper C++ error handling. 52 | // 53 | // However, if you know yourself to be writing code that is inside of 54 | // a ren::Function, a shorthand is provided in the form of: 55 | // 56 | // throw ren::Error ("Invalid hedgehog found"); 57 | // 58 | // If you use the () form of construction then it will interpret the string 59 | // literal as a string. But if you use the {} initializer list form, it will 60 | // assume you want to LOAD the code: 61 | // 62 | // throw ren::Error {"{Invalid} animal-type {found}"}; 63 | // 64 | // Because C++ throws cannot be caught by Ren runtime's CATCH, the meaning 65 | // chosen for throwing an error object is effectively to "raise" an error, as 66 | // if you had written: 67 | // 68 | // ren::runtime("fail {Invalid Hedgehog found}"); 69 | // 70 | // Yet you should not throw other value types; they will be handled as 71 | // exceptions if you do. And when using this convenience, remember that 72 | // throwing an exception intended to be caught directly by C++ that isn't 73 | // derived from std::exception is a poor practice: 74 | // 75 | // http://stackoverflow.com/questions/1669514/ 76 | // 77 | // So if you are writing code that may-or-may-not be inside of a ren::Function, 78 | // consider throwing a ren::evaluation_error instead of the error directly. 79 | // That is "universal", and can be processed both by ren::Function as well as 80 | // in the typical C++ execution stack. 81 | // 82 | 83 | class Error 84 | : public internal::AnyContext_ 85 | { 86 | using AnyContext::initError; 87 | 88 | protected: 89 | static bool isValid(REBVAL const * cell); 90 | 91 | public: 92 | friend class AnyValue; 93 | using internal::AnyContext_::AnyContext_; 94 | 95 | public: 96 | Error (const char * msg, Engine * engine = nullptr); 97 | }; 98 | 99 | 100 | // When you try to LOAD badly formatted data, you will get this, e.g. 101 | // if you say something like: 102 | // 103 | // Block {"1 2 {Foo"}; // missing closing brace... 104 | // 105 | // Unlike evaluation_error, these can happen even if there's no runtime. 106 | 107 | class load_error : public std::exception { 108 | private: 109 | Error errorValue; 110 | std::string whatString; 111 | 112 | public: 113 | load_error (Error const & error) : 114 | errorValue (error), 115 | whatString (to_string(errorValue)) 116 | { 117 | } 118 | 119 | char const * what() const noexcept override { 120 | return whatString.c_str(); 121 | } 122 | 123 | Error error() const noexcept { 124 | return errorValue; 125 | } 126 | }; 127 | 128 | 129 | class evaluation_error : public std::exception { 130 | private: 131 | Error errorValue; 132 | std::string whatString; 133 | 134 | public: 135 | evaluation_error (Error const & error) : 136 | errorValue (error), 137 | whatString (to_string(errorValue)) 138 | { 139 | } 140 | 141 | char const * what() const noexcept override { 142 | return whatString.c_str(); 143 | } 144 | 145 | Error error() const noexcept { 146 | return errorValue; 147 | } 148 | }; 149 | 150 | 151 | // 152 | // HALTED EXCEPTION 153 | // 154 | 155 | // 156 | // Halting of evaluations (such as in the console with ^C) has no 157 | // user-facing error object in the ren runtime, because it is "meta" and 158 | // means "stop evaluating". There is no way to "catch" it. 159 | // 160 | // However, when a multithreaded C++ host has an evaluator on one thread 161 | // and requests a cancellation from another, then this exception will be 162 | // thrown to the evaluation thread when (and if) the cancellation request 163 | // is processed. If running as an interpreted loop, it should (modulo 164 | // bugs in the interpreter) always be possible to interrupt this way 165 | // in a timely manner. 166 | // 167 | // What should the interface for cancellations of evaluations be? How might 168 | // timeouts or quotas of operations be managed? 169 | // 170 | // https://github.com/hostilefork/rencpp/issues/19 171 | 172 | class evaluation_halt : public std::exception { 173 | public: 174 | evaluation_halt () 175 | { 176 | } 177 | 178 | char const * what() const noexcept override { 179 | return "ren::evaluation_halt"; 180 | } 181 | }; 182 | 183 | 184 | } 185 | 186 | #endif 187 | -------------------------------------------------------------------------------- /include/rencpp/helpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_HELPERS_HPP 2 | #define RENCPP_HELPERS_HPP 3 | 4 | // 5 | // helpers.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include 23 | #include 24 | 25 | #include "engine.hpp" 26 | 27 | namespace ren { 28 | 29 | // 30 | // PRINTING HELPER CLASS EXPERIMENT 31 | // 32 | 33 | // 34 | // One misses Rebol/Red PRINT when doing debugging, so this little printer 35 | // class brings you that functionality via variadic functions. It's easy 36 | // to use, just say: 37 | // 38 | // ren::print("This", "will", "have", "spaces"); 39 | // 40 | // To not get the spaces, use the .only function call. 41 | // 42 | // ren::print.only("This", "won't", "be", "spaced"); 43 | // 44 | // This is JUST AN EXPERIMENT to see how people might use it if it were 45 | // available. By writing it this way and not calling into the evaluator 46 | // it will not match up with what print does necessarily, even if it were 47 | // a complete reimplementation of the default print behavior (it is not). 48 | // 49 | 50 | // 51 | // Moving the IO hook to be a "per-Engine property" created the first real 52 | // question about coming up with something like that, so the Engine::runFinder 53 | // calls in here are pretty rough, but we'll see where that goes. 54 | // 55 | 56 | 57 | class Printer { 58 | public: 59 | Printer () 60 | { 61 | } 62 | 63 | template 64 | void writeArgs(bool , T && t) { 65 | Engine::runFinder().getOutputStream() << std::forward(t); 66 | } 67 | 68 | template 69 | void writeArgs(bool spaced, T && t, Ts &&... args) { 70 | writeArgs(spaced, std::forward(t)); 71 | if (spaced) 72 | Engine::runFinder().getOutputStream() << " "; 73 | writeArgs(spaced, std::forward(args)...); 74 | } 75 | 76 | template 77 | void corePrint(bool spaced, bool linefeed, Ts &&... args) { 78 | writeArgs(spaced, std::forward(args)...); 79 | if (linefeed) 80 | Engine::runFinder().getOutputStream() << std::endl; 81 | } 82 | 83 | template 84 | void operator()(Ts &&... args) { 85 | corePrint(true, true, std::forward(args)...); 86 | } 87 | 88 | template 89 | void only(Ts &&... args) { 90 | corePrint(false, false, std::forward(args)...); 91 | } 92 | 93 | ~Printer () { 94 | } 95 | }; 96 | 97 | extern Printer print; 98 | 99 | } // end namespace ren 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /include/rencpp/hooks.h: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_HOOKS_H 2 | #define RENCPP_HOOKS_H 3 | 4 | /* 5 | * hooks.h 6 | * This file is part of RenCpp 7 | * Copyright (C) 2015-2018 HostileFork.com 8 | * 9 | * Licensed under the Boost License, Version 1.0 (the "License") 10 | * 11 | * http://www.boost.org/LICENSE_1_0.txt 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | * implied. See the License for the specific language governing 17 | * permissions and limitations under the License. 18 | * 19 | * See http://rencpp.hostilefork.com for more information on this project 20 | */ 21 | 22 | 23 | /* 24 | * For the sake of the thought experiment, this file a generalized one that 25 | * can be built with either C or C++. One might consider how much work could 26 | * potentially be reused between the two bindings. 27 | */ 28 | 29 | 30 | /* 31 | * int32_t is in #include , generally you should only use the 32 | * specific size definitions when doing external interfaces like this. 33 | * 34 | * http://stackoverflow.com/q/6144682/211160 35 | */ 36 | #include 37 | 38 | 39 | /* 40 | * Return codes used by the binding. They start at 10 at present due to 41 | * the hack to use the equal value of R_OUT and REN_SUCCESS to avoid 42 | * trying to force Red to have R_XXX return conventions from RenShimPointer 43 | */ 44 | #define REN_SUCCESS 5 /* same value as R_OUT */ 45 | #define REN_APPLY_THREW 1 /* same value as R_OUT_THREW */ 46 | 47 | #define REN_CONSTRUCT_ERROR 10 48 | #define REN_APPLY_ERROR 11 49 | #define REN_ERROR_NO_SUCH_CONTEXT 13 50 | #define REN_BUFFER_TOO_SMALL 14 51 | #define REN_SHIM_INITIALIZED 15 52 | #define REN_EVALUATION_HALTED 16 53 | #define REN_BAD_ENGINE_HANDLE 17 54 | 55 | 56 | /* 57 | * The RenResult does double duty as the result code from functions and the 58 | * Rebol NATIVE! function return type, so it has to match the latter. Rebol 59 | * uses fixed size types to get -style compatibility. This may 60 | * have to be adapted for Red, which also may have an entirely different 61 | * prototype needed for shims speaking the stack protocol. 62 | */ 63 | typedef uint32_t RenResult; // REBCNT-compatible 64 | 65 | 66 | /* 67 | * The original concept of RenCpp was to work with raw REBVAL cells, which 68 | * are structs that are 4 platform pointers in size. Practical issues of GC 69 | * extension for this model, included with the advent of "singular" REBSER 70 | * nodes that could hold a single value, led to switching so that the 71 | * responsibility for the cell's memory is on the engine side. This idea 72 | * was replicated in "libRebol". 73 | * 74 | * In order to avoid pulling in all of the Rebol includes to every client 75 | * of Ren/C++, we make an "opaque type": 76 | * 77 | * https://en.wikipedia.org/wiki/Opaque_data_type 78 | * 79 | */ 80 | 81 | struct Reb_Specific_Value; // actually lives in a REBSER node "pairing" 82 | typedef struct Reb_Specific_Value REBVAL; 83 | 84 | struct Reb_Frame; 85 | 86 | typedef struct { 87 | int data; 88 | } RebolEngineHandle; 89 | 90 | const RebolEngineHandle REBOL_ENGINE_HANDLE_INVALID = {-1}; 91 | #define REBOL_IS_ENGINE_HANDLE_INVALID(handle) \ 92 | ((handle).data == REBOL_ENGINE_HANDLE_INVALID.data) 93 | 94 | 95 | /** 96 | ** MAP REBOL TYPES TO REN EQUIVALENTS 97 | **/ 98 | 99 | typedef RebolEngineHandle RenEngineHandle; 100 | #define REN_ENGINE_HANDLE_INVALID REBOL_ENGINE_HANDLE_INVALID 101 | #define REN_IS_ENGINE_HANDLE_INVALID REBOL_IS_ENGINE_HANDLE_INVALID 102 | 103 | 104 | /* 105 | * If the evaluator is cancelled by a signal from outside, and the exception 106 | * makes it to the shim, it will be processed by this function in the shim 107 | */ 108 | 109 | void RL_Move(REBVAL *out, REBVAL const *v); 110 | 111 | REBVAL *RL_Arg(void *frame, int index); 112 | 113 | 114 | /* 115 | * Cannot use ERROR! as this deals with init and shutdown of the code that 116 | * carries Red Values. The free takes a pointer and asks Red to put a value 117 | * in it that it will recognize as invalid. 118 | */ 119 | 120 | RenResult RenAllocEngine(RenEngineHandle * out); 121 | 122 | RenResult RenFreeEngine(RenEngineHandle engine); 123 | 124 | 125 | 126 | /* 127 | * While Engines conceptually isolate one set of words from another in a 128 | * sort of sandboxed way, a Context is merely a *binding* context within an 129 | * Engine. When symbols are loaded, they provide that implicit argument 130 | * to bind. System contexts or otherwise may be looked up by name. 131 | */ 132 | 133 | RenResult RenFindContext( 134 | REBVAL *out, 135 | RenEngineHandle engine, 136 | char const * name 137 | ); 138 | 139 | 140 | /* 141 | * It's hard to know exactly where to draw the line in terms of offering core 142 | * functionality as an API hook vs. using the generalized Apply. But FORM 143 | * is a very basic one that is needed everywhere...including iostream 144 | * operators, string casting, and for debug output. 145 | * 146 | * This is the kind of API that takes a buffer size in, and tells you how 147 | * many bytes the UTF-8 string needs. If the number of bytes is greater than 148 | * buffer, you get the first bufSize bytes and an error code warning you 149 | * that you didn't get the whole string. You can then call it again with a 150 | * new buffer of the appropriate size. 151 | */ 152 | 153 | RenResult RenFormAsUtf8( 154 | RenEngineHandle engine, 155 | REBVAL const * cell, 156 | unsigned char * buffer, 157 | size_t bufSize, 158 | size_t * numBytesOut 159 | ); 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /include/rencpp/image.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_IMAGE_HPP 2 | #define RENCPP_IMAGE_HPP 3 | 4 | // 5 | // image.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include "value.hpp" 23 | 24 | 25 | namespace ren { 26 | 27 | 28 | // 29 | // IMAGE 30 | // 31 | 32 | // 33 | // Rebol has a native IMAGE! type, which a few codecs have been written for 34 | // to save and load. We don't do much with it in RenCpp at this point 35 | // unless you are building with the Qt classlib, in which case we need to 36 | // go back and forth with a QImage. 37 | // 38 | // It's not clear if this should be in the standard RenCpp or if it belongs 39 | // in some kind of extensions module. In Rebol at least, the IMAGE! was 40 | // available even in non-GUI builds. It seems that the "codecs" and 41 | // "extensions" do need to add new types that behave a bit as if they were 42 | // built in, while perhaps having a more limited range of evaluator 43 | // behavior. This is under review. 44 | // 45 | // !!! Is an image an "atom"? It is a sort of container, in the same sense 46 | // that a string is. We'll keep it a AnyValue for now. 47 | 48 | class Image : public AnyValue { 49 | protected: 50 | friend class AnyValue; 51 | Image (Dont) noexcept : AnyValue (Dont::Initialize) {} 52 | static bool isValid(REBVAL const * cell); 53 | 54 | public: 55 | #if REN_CLASSLIB_QT == 1 56 | explicit Image (QImage const & image, Engine * engine = nullptr); 57 | operator QImage () const; 58 | #endif 59 | }; 60 | 61 | } // end namespace ren 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /include/rencpp/rebol.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_REBOL_HPP 2 | #define RENCPP_REBOL_HPP 3 | 4 | // 5 | // rebol.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include 23 | #include "runtime.hpp" 24 | 25 | #ifndef NDEBUG 26 | #include 27 | #endif 28 | 29 | namespace ren { 30 | 31 | namespace internal { 32 | class RebolHooks; 33 | } 34 | 35 | // Not only is Runtime implemented on a per-binding basis 36 | // (hence not requiring virtual methods) but you can add more 37 | // specialized methods that are peculiar to just this runtime 38 | 39 | class RebolRuntime : public Runtime { 40 | private: 41 | AnyContext * defaultContext; 42 | bool initialized; 43 | 44 | public: 45 | friend class internal::Loadable; 46 | 47 | private: 48 | friend class internal::RebolHooks; 49 | 50 | public: // !!! temporary--values need it 51 | bool lazyInitializeIfNecessary(); 52 | 53 | 54 | public: 55 | RebolRuntime (bool someExtraInitFlag); 56 | 57 | void doMagicOnlyRebolCanDo(); 58 | 59 | void cancel() override; 60 | 61 | ~RebolRuntime() override; 62 | }; 63 | 64 | extern RebolRuntime runtime; 65 | 66 | namespace internal { 67 | // Placeholder for better solution: mutex for management of refcounts 68 | extern std::mutex refcountMutex; 69 | } 70 | 71 | } // end namespace ren 72 | 73 | 74 | namespace rebol = ren; 75 | 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /include/rencpp/ren.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_REN_HPP 2 | #define RENCPP_REN_HPP 3 | 4 | // 5 | // ren.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | // 23 | // This is currently the main include file for using Ren in projects. At 24 | // one time, there was an attempt to make "ren.hpp" only speak about data 25 | // and "runtime.hpp" provide the runtime services, with the additional 26 | // ability to include either "red.hpp" or "rebol.hpp" to get specific 27 | // features. However, which runtime you control is currently done by 28 | // the build... not the headers, and you just include ren.hpp. 29 | // 30 | 31 | #include "value.hpp" 32 | #include "atoms.hpp" 33 | #include "words.hpp" 34 | #include "series.hpp" 35 | #include "strings.hpp" 36 | #include "arrays.hpp" 37 | #include "error.hpp" 38 | #include "function.hpp" 39 | #include "runtime.hpp" 40 | #include "engine.hpp" 41 | #include "context.hpp" 42 | 43 | // !!! Even non-GUI builds want to be able to process images. Yet this 44 | // probably should be in the category of things done with a plug-in, 45 | // (once plug-ins can act like fairly natural types) 46 | // 47 | #include "image.hpp" 48 | 49 | 50 | // 51 | // INCLUDE REBOL OR RED RUNTIME INSTANCE 52 | // 53 | // They will define an object derived from ren::Runtime, named ren::runtime 54 | // 55 | 56 | 57 | #include "rencpp/rebol.hpp" // cheating for now, no C++ version... 58 | 59 | 60 | // 61 | // HELPER TOOLS 62 | // 63 | 64 | // 65 | // Things like the variadic print. These perhaps should not be automatically 66 | // included, but fun to do so for now. Consider them a grab bag of ideas... 67 | // like premade parse helper classes should go in there. 68 | // 69 | 70 | #include "helpers.hpp" 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/rencpp/runtime.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_RUNTIME_HPP 2 | #define RENCPP_RUNTIME_HPP 3 | 4 | // 5 | // runtime.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include 23 | 24 | #include "common.hpp" 25 | #include "value.hpp" 26 | #include "arrays.hpp" 27 | 28 | 29 | namespace ren { 30 | 31 | 32 | // 33 | // BASE RUNTIME CLASS 34 | // 35 | 36 | // 37 | // The runtime class is a singleton, and at the time of writing a bit of 38 | // a hodge-podge as to whether it should be all static methods or 39 | // what. One reason it is a singleton came from performance and also 40 | // having it "taken care of automatically" in the examples. The downside 41 | // of instantiating it automatically is that people can't derive from it; 42 | // but the advantages of doing so are not clear when Engine() and Context() 43 | // exist. evaluate is static, and used by Engine and Context which are 44 | // independent of either the Rebol or Red runtime instance (ren::runtime) 45 | // 46 | // Note operator overloads must be members, so being able to talk to 47 | // a ren::runtime.foo as well as say ren::runtime(...) requires such 48 | // methods to *not* be static, which curiously could make evaluate faster 49 | // than the paren notation 50 | // 51 | 52 | class Runtime { 53 | protected: 54 | friend class AnyArray; 55 | friend class AnyValue; 56 | friend class AnyString; 57 | friend class AnyWord; 58 | friend class Engine; 59 | 60 | static optional evaluate( 61 | internal::Loadable const loadables[], 62 | size_t numLoadables, 63 | AnyContext const * contextPtr, 64 | Engine * engine 65 | ); 66 | 67 | public: 68 | static optional evaluate( 69 | std::initializer_list loadables, 70 | Engine * engine = nullptr 71 | ) { 72 | return evaluate(loadables.begin(), loadables.size(), nullptr, engine); 73 | } 74 | 75 | static optional evaluate( 76 | std::initializer_list> loadables, 77 | internal::ContextWrapper const & wrapper 78 | ) { 79 | return evaluate( 80 | loadables.begin(), 81 | loadables.size(), 82 | &wrapper.context, 83 | nullptr 84 | ); 85 | } 86 | 87 | // Has ambiguity error from trying to turn the nullptr into a Loadable; 88 | // investigate what it is about the static method that has this problem 89 | 90 | /*template 91 | static inline AnyValue evaluate(Ts const &... args) { 92 | return evaluate({args...}, static_cast(nullptr)); 93 | }*/ 94 | 95 | template 96 | inline optional operator()(Ts const &... args) const { 97 | return evaluate({args...}, static_cast(nullptr)); 98 | } 99 | 100 | 101 | // 102 | // How to do a cancellation interface properly in threading environments 103 | // which may be making many requests? This simple interface assumes one 104 | // evaluator thread to whom a cancel is being made from another thread 105 | // (that is not able to do evaluations at the same time)... because that 106 | // is what Rebol implemented. A better interface is needed. 107 | // 108 | // https://github.com/hostilefork/rencpp/issues/19 109 | // 110 | public: 111 | virtual void cancel() = 0; 112 | 113 | virtual ~Runtime () { 114 | } 115 | }; 116 | 117 | 118 | } // end namespace ren 119 | 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /include/rencpp/series.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_SERIES_HPP 2 | #define RENCPP_SERIES_HPP 3 | 4 | // 5 | // series.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include "value.hpp" 23 | 24 | namespace ren { 25 | 26 | namespace internal { 27 | 28 | // This class is necessary because we can't define a Series::iterator class 29 | // to wrap a Series inside of a Series--it would be an incomplete definition 30 | 31 | class AnySeries_ : public AnyValue { 32 | protected: 33 | friend class AnyValue; 34 | AnySeries_ (Dont) noexcept : AnyValue (Dont::Initialize) {} 35 | static bool isValid(REBVAL const * cell); 36 | 37 | public: 38 | // We don't return values here because that would leak the internal 39 | // types. It's technically possible to write a variant of things like 40 | // head() and tail() for every type but reinventing Rebol/Red is not 41 | // really the point. Mutating types as they are vs. returning a new 42 | // base type could be a good option for working in the C++ world, if 43 | // iterators are available for things like enumerating. 44 | 45 | void operator++(); 46 | void operator--(); 47 | 48 | void operator++(int); 49 | void operator--(int); 50 | 51 | AnyValue operator*() const; 52 | AnyValue operator->() const; // see notes on AnyValue::operator-> 53 | 54 | void head(); 55 | void tail(); 56 | }; 57 | 58 | } // end namespace internal 59 | 60 | 61 | class AnySeries : public ren::internal::AnySeries_ { 62 | protected: 63 | friend class AnyValue; 64 | AnySeries (Dont) noexcept : AnySeries_ (Dont::Initialize) {} 65 | static bool isValid(REBVAL const * cell); 66 | 67 | // 68 | // If you wonder why C++ would need a separate iterator type for a Series 69 | // instead of doing as Rebol does and just using a Series, see this: 70 | // 71 | // https://github.com/hostilefork/rencpp/issues/25 72 | // 73 | // The series thus functions as the state, but is a separate type that 74 | // has to be wrapped up. 75 | public: 76 | class iterator { 77 | friend class AnySeries; 78 | internal::AnySeries_ state; 79 | iterator (internal::AnySeries_ const & state) : 80 | state (state) 81 | { 82 | } 83 | 84 | public: 85 | iterator & operator++() { 86 | ++state; 87 | return *this; 88 | } 89 | 90 | iterator & operator--() { 91 | --state; 92 | return *this; 93 | } 94 | 95 | iterator operator++(int) { 96 | auto temp = *this; 97 | operator++(); 98 | return temp; 99 | } 100 | 101 | iterator operator--(int) { 102 | auto temp = *this; 103 | operator--(); 104 | return temp; 105 | } 106 | 107 | bool operator==(iterator const & other) const 108 | { return state.isSameAs(other.state); } 109 | bool operator!=(iterator const & other) const 110 | { return !state.isSameAs(other.state); } 111 | 112 | AnyValue operator * () const { return *state; } 113 | AnyValue operator-> () const { return state.operator->(); } 114 | }; 115 | 116 | iterator begin() const { 117 | return iterator (*this); 118 | } 119 | 120 | iterator end() const { 121 | auto temp = *this; 122 | temp.tail(); // see remarks on tail modifying vs. returning a value 123 | return iterator (temp); 124 | } 125 | 126 | size_t length() const; 127 | 128 | bool isEmpty() const { return length() == 0; } 129 | 130 | 131 | // All series can be accessed by index, but there is no general rule 132 | // about any other way to index into them. But if you have a base 133 | // class series and don't know what it is, you need to be able to use 134 | // the broader indexing method on it. So this takes any AnyValue, with 135 | // risk of giving you a runtime error for a bad combination. 136 | 137 | // Note: Rebol/Red use 1-based indexing with a "zero-hole" by default 138 | 139 | AnyValue operator[](AnyValue const & index) const; 140 | }; 141 | 142 | } // end namespace ren 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /include/rencpp/strings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_STRINGS_HPP 2 | #define RENCPP_STRINGS_HPP 3 | 4 | // 5 | // strings.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include "value.hpp" 23 | #include "atoms.hpp" 24 | #include "series.hpp" 25 | 26 | namespace ren { 27 | 28 | // 29 | // ANYSTRING 30 | // 31 | 32 | class AnyString : public AnySeries 33 | { 34 | protected: 35 | friend class AnyValue; 36 | AnyString (Dont) noexcept : AnySeries (Dont::Initialize) {} 37 | static bool isValid(REBVAL const * cell); 38 | 39 | // Friending doesn't seem to be enough for gcc 4.6, see SO writeup: 40 | // http://stackoverflow.com/questions/32983193/ 41 | public: 42 | friend class String; 43 | static void initString(REBVAL *cell); 44 | friend class Tag; 45 | static void initTag(REBVAL *cell); 46 | friend class Filename; 47 | static void initFilename(REBVAL *cell); 48 | 49 | protected: 50 | AnyString( 51 | char const * cstr, 52 | internal::CellFunction cellfun, 53 | Engine * engine = nullptr 54 | ); 55 | 56 | AnyString ( 57 | std::string const & str, 58 | internal::CellFunction cellfun, 59 | Engine * engine = nullptr 60 | ); 61 | 62 | #if REN_CLASSLIB_QT == 1 63 | AnyString ( 64 | QString const & str, 65 | internal::CellFunction cellfun, 66 | Engine * engine = nullptr 67 | ); 68 | #endif 69 | 70 | public: 71 | // This lets you pass ren::AnyString to anything that expected a 72 | // std::string and do so implicitly, which may or may not be a great idea: 73 | // 74 | // https://github.com/hostilefork/rencpp/issues/6 75 | 76 | operator std::string () const { return to_string(*this); } 77 | 78 | #if REN_CLASSLIB_QT == 1 79 | operator QString () const { return to_QString(*this); } 80 | #endif 81 | 82 | 83 | public: 84 | class iterator { 85 | friend class AnyString; 86 | AnySeries state; 87 | iterator (AnySeries const & state) : 88 | state (state) 89 | { 90 | } 91 | 92 | public: 93 | iterator & operator++() { 94 | ++state; 95 | return *this; 96 | } 97 | 98 | iterator & operator--() { 99 | --state; 100 | return *this; 101 | } 102 | 103 | iterator operator++(int) { 104 | auto temp = *this; 105 | operator++(); 106 | return temp; 107 | } 108 | 109 | iterator operator--(int) { 110 | auto temp = *this; 111 | operator--(); 112 | return temp; 113 | } 114 | 115 | bool operator==(iterator const & other) const 116 | { return state.isSameAs(other.state); } 117 | bool operator!=(iterator const & other) const 118 | { return !state.isSameAs(other.state); } 119 | 120 | Character operator * () const { 121 | return static_cast(*state); 122 | } 123 | Character operator-> () const { 124 | return static_cast(state.operator->()); } 125 | }; 126 | 127 | iterator begin() const { 128 | return iterator (*this); 129 | } 130 | 131 | iterator end() const { 132 | auto temp = *this; 133 | temp.tail(); 134 | return iterator (temp); 135 | } 136 | 137 | public: 138 | template 139 | T spellingOf() const { 140 | throw std::runtime_error("Unspecialized version of spellingOf called"); 141 | } 142 | 143 | std::string spellingOf_STD() const; 144 | 145 | #if REN_CLASSLIB_QT == 1 146 | QString spellingOf_QT() const; 147 | #endif 148 | 149 | bool hasSpelling(char const * spelling) const { 150 | return spellingOf_STD() == spelling; 151 | } 152 | 153 | bool isEqualTo(char const * cstr) const { 154 | return static_cast(*this) == cstr; 155 | } 156 | }; 157 | 158 | 159 | // http://stackoverflow.com/a/3052604/211160 160 | 161 | template<> 162 | inline std::string AnyString::spellingOf() const { 163 | return spellingOf_STD(); 164 | } 165 | 166 | #if REN_CLASSLIB_QT == 1 167 | template<> 168 | inline QString AnyString::spellingOf() const { 169 | return spellingOf_QT(); 170 | } 171 | #endif 172 | 173 | 174 | 175 | // 176 | // ANYSTRING_ SUBTYPE HELPER 177 | // 178 | 179 | namespace internal { 180 | 181 | template 182 | class AnyString_ : public AnyString { 183 | protected: 184 | friend class AnyValue; 185 | AnyString_ (Dont) noexcept : AnyString (Dont::Initialize) {} 186 | 187 | public: 188 | explicit AnyString_ (Engine * engine = nullptr) : 189 | AnyString ("", F, engine) 190 | { 191 | } 192 | 193 | explicit AnyString_ (char const * cstr, Engine * engine = nullptr) : 194 | AnyString (cstr, F, engine) 195 | { 196 | } 197 | 198 | explicit AnyString_ (std::string const & str, Engine * engine = nullptr) : 199 | AnyString (str.c_str(), F, engine) 200 | { 201 | } 202 | 203 | #if REN_CLASSLIB_QT == 1 204 | explicit AnyString_ (QString const & str, Engine * engine = nullptr) : 205 | AnyString (str, F, engine) 206 | { 207 | } 208 | #endif 209 | 210 | }; 211 | 212 | } // end namespace internal 213 | 214 | 215 | 216 | // 217 | // CONCRETE STRING TYPES 218 | // 219 | 220 | // 221 | // For why these are classes and not typedefs: 222 | // 223 | // https://github.com/hostilefork/rencpp/issues/49 224 | // 225 | 226 | class String 227 | : public internal::AnyString_ 228 | { 229 | protected: 230 | static bool isValid(REBVAL const * cell); 231 | 232 | protected: 233 | String (Dont) noexcept : AnyString_ (Dont::Initialize) {} 234 | friend class AnyValue; 235 | 236 | // Only String allows you to use implicit construction from string 237 | // classes, because trying otherwise for the other string classes 238 | // proved to be too accident-prone: 239 | // 240 | // https://github.com/hostilefork/rencpp/issues/6 241 | // 242 | // We can't inherit the constructors when we are switching them from 243 | // implicit to explicit when there are default parameters. (???) 244 | // They become ambiguous for some reason. @Morwenn has pointed out that 245 | // constructor inheritance is tricky and often breaks down: 246 | // 247 | // http://stackoverflow.com/questions/24912280/ 248 | // 249 | // So we retype them here, minus the "explicit". :-/ 250 | 251 | public: 252 | String (char const * cstr, Engine * engine = nullptr) : 253 | AnyString_ (cstr, engine) 254 | { 255 | } 256 | 257 | String (std::string const & str, Engine * engine = nullptr) : 258 | AnyString_ (str.c_str(), engine) 259 | { 260 | } 261 | 262 | #if REN_CLASSLIB_QT == 1 263 | String (QString const & str, Engine * engine = nullptr) : 264 | AnyString_ (str, engine) 265 | { 266 | } 267 | #endif 268 | }; 269 | 270 | 271 | class Tag 272 | : public internal::AnyString_ 273 | { 274 | protected: 275 | static bool isValid(REBVAL const * cell); 276 | 277 | public: 278 | friend class AnyValue; 279 | using AnyString_::AnyString_; 280 | }; 281 | 282 | 283 | class Filename : 284 | public internal::AnyString_ 285 | { 286 | protected: 287 | static bool isValid(REBVAL const * cell); 288 | 289 | public: 290 | friend class AnyValue; 291 | using AnyString_::AnyString_; 292 | }; 293 | 294 | } // end namespace ren 295 | 296 | #endif 297 | -------------------------------------------------------------------------------- /include/rencpp/words.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_WORDS_HPP 2 | #define RENCPP_WORDS_HPP 3 | 4 | // 5 | // words.hpp 6 | // This file is part of RenCpp 7 | // Copyright (C) 2015-2018 HostileFork.com 8 | // 9 | // Licensed under the Boost License, Version 1.0 (the "License") 10 | // 11 | // http://www.boost.org/LICENSE_1_0.txt 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 16 | // implied. See the License for the specific language governing 17 | // permissions and limitations under the License. 18 | // 19 | // See http://rencpp.hostilefork.com for more information on this project 20 | // 21 | 22 | #include "value.hpp" 23 | 24 | namespace ren { 25 | 26 | // 27 | // ANYWORD 28 | // 29 | 30 | class AnyWord : public AnyValue { 31 | protected: 32 | friend class AnyValue; 33 | AnyWord (Dont) : AnyValue (Dont::Initialize) {} 34 | static bool isValid(REBVAL const * cell); 35 | 36 | // Friending doesn't seem to be enough for gcc 4.6, see SO writeup: 37 | // http://stackoverflow.com/questions/32983193/ 38 | public: 39 | friend class Word; 40 | static void initWord(REBVAL *cell); 41 | friend class GetWord; 42 | static void initGetWord(REBVAL *cell); 43 | friend class SetWord; 44 | static void initSetWord(REBVAL *cell); 45 | friend class LitWord; 46 | static void initLitWord(REBVAL *cell); 47 | friend class Refinement; 48 | static void initRefinement(REBVAL *cell); 49 | 50 | protected: 51 | explicit AnyWord ( 52 | char const * cstr, 53 | internal::CellFunction cellfun, 54 | AnyContext const * context = nullptr, 55 | Engine * engine = nullptr 56 | ); 57 | 58 | #if REN_CLASSLIB_QT == 1 59 | explicit AnyWord ( 60 | QString const & str, 61 | internal::CellFunction cellfun, 62 | AnyContext const * context = nullptr, 63 | Engine * engine = nullptr 64 | ); 65 | #endif 66 | 67 | // Copy from any other AnyWord, preserve binding but change type 68 | explicit AnyWord (AnyWord const & other, internal::CellFunction cellfun); 69 | 70 | 71 | protected: 72 | explicit AnyWord ( 73 | std::string const & str, 74 | internal::CellFunction cellfun, 75 | AnyContext const & context 76 | ) : 77 | AnyWord (str.c_str(), cellfun, &context, nullptr) 78 | { 79 | } 80 | 81 | explicit AnyWord ( 82 | std::string const & str, 83 | internal::CellFunction cellfun, 84 | Engine * engine = nullptr 85 | ) : 86 | AnyWord (str.c_str(), cellfun, nullptr, engine) 87 | { 88 | } 89 | 90 | 91 | #if REN_CLASSLIB_QT == 1 92 | explicit AnyWord ( 93 | QString const & str, 94 | internal::CellFunction cellfun, 95 | AnyContext * context = nullptr 96 | ); 97 | #endif 98 | 99 | public: 100 | template 101 | T spellingOf() const { 102 | throw std::runtime_error("Unspecialized version of spellingOf called"); 103 | } 104 | 105 | std::string spellingOf_STD() const; 106 | 107 | #if REN_CLASSLIB_QT == 1 108 | QString spellingOf_QT() const; 109 | #endif 110 | 111 | bool hasSpelling(char const * spelling) const { 112 | return spellingOf_STD() == spelling; 113 | } 114 | }; 115 | 116 | // http://stackoverflow.com/a/3052604/211160 117 | 118 | template<> 119 | inline std::string AnyWord::spellingOf() const { 120 | return spellingOf_STD(); 121 | } 122 | 123 | #if REN_CLASSLIB_QT == 1 124 | template<> 125 | inline QString AnyWord::spellingOf() const { 126 | return spellingOf_QT(); 127 | } 128 | #endif 129 | 130 | 131 | 132 | // 133 | // ANYWORD_ SUBTYPE HELPER 134 | // 135 | 136 | namespace internal { 137 | 138 | 139 | template 140 | class AnyWord_ : public AnyWord { 141 | protected: 142 | friend class AnyValue; 143 | AnyWord_ (Dont) : AnyWord (Dont::Initialize) {} 144 | 145 | public: 146 | explicit AnyWord_ (char const * cstr, Engine * engine = nullptr) : 147 | AnyWord (cstr, F, nullptr, engine) 148 | { 149 | } 150 | 151 | explicit AnyWord_ (char const * cstr, AnyContext & context) : 152 | AnyWord (cstr, F, &context, nullptr) 153 | { 154 | } 155 | 156 | explicit AnyWord_ (std::string const & str, Engine * engine = nullptr) : 157 | AnyWord (str.c_str(), F, nullptr, engine) 158 | { 159 | } 160 | 161 | explicit AnyWord_ (std::string const & str, AnyContext & context) : 162 | AnyWord (str.c_str(), F, &context, nullptr) 163 | { 164 | } 165 | 166 | #if REN_CLASSLIB_QT == 1 167 | explicit AnyWord_ (QString const & str, Engine * engine = nullptr) : 168 | AnyWord (str, F, nullptr, engine) 169 | { 170 | } 171 | explicit AnyWord_ (QString const & str, AnyContext & context) : 172 | AnyWord (str, F, &context, nullptr) 173 | { 174 | } 175 | 176 | #endif 177 | 178 | template 179 | explicit AnyWord_ ( 180 | internal::AnyWord_ const & other 181 | ) : 182 | AnyWord (other, F) 183 | { 184 | } 185 | }; 186 | 187 | } // end namespace internal 188 | 189 | 190 | 191 | // 192 | // CONCRETE WORD TYPES 193 | // 194 | 195 | // 196 | // For why these are classes and not typedefs: 197 | // 198 | // https://github.com/hostilefork/rencpp/issues/49 199 | // 200 | 201 | class Word 202 | : public internal::AnyWord_ 203 | { 204 | protected: 205 | static bool isValid(REBVAL const * cell); 206 | 207 | public: 208 | friend class AnyValue; 209 | using AnyWord_::AnyWord_; 210 | 211 | public: 212 | template 213 | inline optional operator()(Ts &&... args) const { 214 | return apply(std::forward(args)...); 215 | } 216 | }; 217 | 218 | 219 | class SetWord 220 | : public internal::AnyWord_ 221 | { 222 | protected: 223 | static bool isValid(REBVAL const * cell); 224 | 225 | public: 226 | friend class AnyValue; 227 | using AnyWord_::AnyWord_; 228 | 229 | public: 230 | template 231 | inline AnyValue operator()(Ts &&... args) const { 232 | // An expression like `x: (...)` cannot evaluate to not being set, 233 | // because it would generate an error. 234 | return *apply(std::forward(args)...); 235 | } 236 | }; 237 | 238 | 239 | class GetWord 240 | : public internal::AnyWord_ 241 | { 242 | protected: 243 | static bool isValid(REBVAL const * cell); 244 | 245 | public: 246 | friend class AnyValue; 247 | using AnyWord_::AnyWord_; 248 | 249 | // A get-word! does not take any parameters, but it's nice to have a 250 | // shorthand for treating it something like a zero-parameter function 251 | public: 252 | #ifdef REN_RUNTIME 253 | inline optional operator()() const { 254 | return apply(); 255 | } 256 | #endif 257 | }; 258 | 259 | 260 | class LitWord 261 | : public internal::AnyWord_ 262 | { 263 | protected: 264 | static bool isValid(REBVAL const * cell); 265 | 266 | public: 267 | friend class AnyValue; 268 | using AnyWord_::AnyWord_; 269 | }; 270 | 271 | 272 | // REFINEMENT! is targeted for being subsumed into PATH!, with an optimization 273 | // that allows for 1-element paths to fit inside a REBSER with no data 274 | // allocation. They look like paths and should act like them! 275 | 276 | class Refinement 277 | : public internal::AnyWord_ 278 | { 279 | protected: 280 | static bool isValid(REBVAL const * cell); 281 | 282 | public: 283 | friend class AnyValue; 284 | using AnyWord_::AnyWord_; 285 | }; 286 | 287 | } // end namespace ren 288 | 289 | #endif 290 | -------------------------------------------------------------------------------- /src/arrays.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/value.hpp" 4 | #include "rencpp/arrays.hpp" 5 | #include "rencpp/context.hpp" 6 | 7 | #include "rencpp/rebol.hpp" 8 | 9 | #include "common.hpp" 10 | 11 | 12 | namespace ren { 13 | 14 | // 15 | // TYPE DETECTION 16 | // 17 | 18 | bool Block::isValid(REBVAL const * cell) { 19 | return IS_BLOCK(cell); 20 | } 21 | 22 | bool Group::isValid(REBVAL const * cell) { 23 | return IS_GROUP(cell); 24 | } 25 | 26 | bool Path::isValid(REBVAL const * cell) { 27 | return IS_PATH(cell); 28 | } 29 | 30 | bool GetPath::isValid(REBVAL const * cell) { 31 | return IS_GET_PATH(cell); 32 | } 33 | 34 | bool SetPath::isValid(REBVAL const * cell) { 35 | return IS_SET_PATH(cell); 36 | } 37 | 38 | bool LitPath::isValid(REBVAL const * cell) { 39 | return IS_LIT_PATH(cell); 40 | } 41 | 42 | bool AnyArray::isValid(REBVAL const * cell) { 43 | return IS_BLOCK(cell) 44 | || IS_GROUP(cell) 45 | || IS_PATH(cell) 46 | || IS_SET_PATH(cell) 47 | || IS_GET_PATH(cell) 48 | || IS_LIT_PATH(cell); 49 | } 50 | 51 | 52 | // 53 | // TYPE HEADER INITIALIZATION 54 | // 55 | 56 | void AnyArray::initBlock(REBVAL *cell) { 57 | VAL_RESET_HEADER(cell, REB_BLOCK); 58 | } 59 | 60 | void AnyArray::initGroup(REBVAL *cell) { 61 | VAL_RESET_HEADER(cell, REB_GROUP); 62 | } 63 | 64 | void AnyArray::initPath(REBVAL *cell) { 65 | VAL_RESET_HEADER(cell, REB_PATH); 66 | } 67 | 68 | void AnyArray::initGetPath(REBVAL *cell) { 69 | VAL_RESET_HEADER(cell, REB_GET_PATH); 70 | } 71 | 72 | void AnyArray::initSetPath(REBVAL *cell) { 73 | VAL_RESET_HEADER(cell, REB_SET_PATH); 74 | } 75 | 76 | void AnyArray::initLitPath(REBVAL *cell) { 77 | VAL_RESET_HEADER(cell, REB_LIT_PATH); 78 | } 79 | 80 | 81 | // 82 | // BLOCK CONSTRUCTION 83 | // 84 | 85 | 86 | AnyArray::AnyArray ( 87 | internal::Loadable const loadables[], 88 | size_t numLoadables, 89 | internal::CellFunction cellfun, 90 | AnyContext const * contextPtr, 91 | Engine * engine 92 | ) : 93 | AnyArray (Dont::Initialize) 94 | { 95 | (*cellfun)(this->cell); 96 | 97 | AnyContext context = contextPtr 98 | ? *contextPtr 99 | : AnyContext::current(engine); 100 | 101 | constructOrApplyInitialize( 102 | context.getEngine(), 103 | &context, 104 | nullptr, // no applicand 105 | loadables, 106 | numLoadables, 107 | this, // Do construct 108 | nullptr // Don't apply 109 | ); 110 | } 111 | 112 | 113 | // TBD: Finish version where you can use values directly as an array 114 | /* 115 | AnyArray::AnyArray ( 116 | AnyValue const values[], 117 | size_t numValues, 118 | internal::CellFunction cellfun, 119 | AnyContext const * contextPtr, 120 | Engine * engine 121 | ) : 122 | AnyArray (Dont::Initialize) 123 | { 124 | (*cellfun)(this->cell); 125 | 126 | 127 | AnyContext context = contextPtr 128 | ? *contextPtr 129 | : AnyContext::current(engine); 130 | 131 | constructOrApplyInitialize( 132 | context.getEngine(), 133 | &context, 134 | nullptr, // no applicand 135 | loadables, 136 | numLoadables, 137 | this, // Do construct 138 | nullptr // Don't apply 139 | ); 140 | } 141 | */ 142 | 143 | 144 | 145 | } // end namespace ren 146 | -------------------------------------------------------------------------------- /src/atoms.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/value.hpp" 4 | #include "rencpp/atoms.hpp" 5 | #include "rencpp/engine.hpp" 6 | 7 | #include "common.hpp" 8 | 9 | 10 | namespace ren { 11 | 12 | bool Atom::isValid(REBVAL const * cell) { 13 | // Will be more efficient when atom makes it formally into the 14 | // Rebol base typesets. 15 | // 16 | // !!! Review handling of void. It is not considered an atom, correct? 17 | // 18 | return ( 19 | IS_BLANK(cell) 20 | || IS_LOGIC(cell) 21 | || IS_CHAR(cell) 22 | || IS_INTEGER(cell) 23 | || IS_DECIMAL(cell) 24 | || IS_DATE(cell) 25 | ); 26 | } 27 | 28 | 29 | 30 | // 31 | // BLANK 32 | // 33 | 34 | bool Blank::isValid(REBVAL const * cell) { 35 | return IS_BLANK(cell); 36 | } 37 | 38 | AnyValue::AnyValue (blank_t, Engine * engine) noexcept : 39 | AnyValue (Dont::Initialize) 40 | { 41 | Init_Blank(cell); 42 | 43 | // !!! Should some types not need an engine field? 44 | if (engine == nullptr) 45 | engine = &Engine::runFinder(); 46 | 47 | finishInit(engine->getHandle()); 48 | } 49 | 50 | 51 | 52 | // 53 | // LOGIC 54 | // 55 | 56 | bool Logic::isValid(REBVAL const * cell) { 57 | return IS_LOGIC(cell); 58 | } 59 | 60 | bool AnyValue::isTruthy() const { 61 | return IS_TRUTHY(cell); 62 | } 63 | 64 | bool AnyValue::isFalsey() const { 65 | return IS_FALSEY(cell); 66 | } 67 | 68 | AnyValue::AnyValue (bool someBool, Engine * engine) noexcept : 69 | AnyValue (Dont::Initialize) 70 | { 71 | Init_Logic(cell, someBool ? TRUE : FALSE); 72 | 73 | // !!! Should some types not need an engine field? 74 | if (engine == nullptr) 75 | engine = &Engine::runFinder(); 76 | 77 | finishInit(engine->getHandle()); 78 | } 79 | 80 | Logic::operator bool() const { 81 | return VAL_LOGIC(cell); 82 | } 83 | 84 | 85 | 86 | // 87 | // CHARACTER 88 | // 89 | 90 | bool Character::isValid(REBVAL const * cell) { 91 | return IS_CHAR(cell); 92 | } 93 | 94 | AnyValue::AnyValue (char c, Engine * engine) : 95 | AnyValue (Dont::Initialize) 96 | { 97 | if (c < 0) 98 | throw std::runtime_error("Non-ASCII char passed to AnyValue::AnyValue()"); 99 | 100 | Init_Char(cell, static_cast(c)); 101 | 102 | // !!! Should some types not need an engine field? 103 | if (engine == nullptr) 104 | engine = &Engine::runFinder(); 105 | 106 | finishInit(engine->getHandle()); 107 | } 108 | 109 | AnyValue::AnyValue (wchar_t wc, Engine * engine) noexcept : 110 | AnyValue (Dont::Initialize) 111 | { 112 | Init_Char(cell, wc); 113 | 114 | // !!! Should some types not need an engine field? 115 | if (engine == nullptr) 116 | engine = &Engine::runFinder(); 117 | 118 | finishInit(engine->getHandle()); 119 | } 120 | 121 | Character::operator char () const { 122 | REBUNI uni = VAL_CHAR(cell); 123 | if (uni > 127) 124 | throw std::runtime_error("Non-ASCII codepoint cast to char"); 125 | return static_cast(uni); 126 | } 127 | 128 | 129 | Character::operator wchar_t () const { 130 | REBUNI uni = VAL_CHAR(cell); 131 | // will throw in Red for "astral plane" unicode codepoints 132 | return static_cast(uni); 133 | } 134 | 135 | 136 | unsigned long Character::codepoint() const { 137 | REBUNI uni = VAL_CHAR(cell); 138 | // will probably not throw in Red, either 139 | return uni; 140 | } 141 | 142 | 143 | #if REN_CLASSLIB_QT 144 | Character::operator QChar () const { 145 | REBUNI uni = VAL_CHAR(cell); 146 | return QChar(uni); 147 | } 148 | #endif 149 | 150 | 151 | 152 | // 153 | // INTEGER 154 | // 155 | 156 | bool Integer::isValid(REBVAL const * cell) { 157 | return IS_INTEGER(cell); 158 | } 159 | 160 | AnyValue::AnyValue (int someInt, Engine * engine) noexcept : 161 | AnyValue (Dont::Initialize) 162 | { 163 | Init_Integer(cell, someInt); 164 | 165 | // !!! Should some types not need an engine field? 166 | if (engine == nullptr) 167 | engine = &Engine::runFinder(); 168 | 169 | finishInit(engine->getHandle()); 170 | } 171 | 172 | Integer::operator int() const { 173 | // !!! How to correctly support 64-bit coercions? Throw if out of range? 174 | int i = VAL_INT32(cell); 175 | return i; 176 | } 177 | 178 | 179 | 180 | // 181 | // FLOAT 182 | // 183 | 184 | bool Float::isValid(REBVAL const * cell) { 185 | return IS_DECIMAL(cell); 186 | } 187 | 188 | AnyValue::AnyValue (double someDouble, Engine * engine) noexcept : 189 | AnyValue (Dont::Initialize) 190 | { 191 | Init_Decimal(cell, someDouble); 192 | 193 | // !!! Should some types not need an engine field? 194 | if (engine == nullptr) 195 | engine = &Engine::runFinder(); 196 | 197 | finishInit(engine->getHandle()); 198 | } 199 | 200 | Float::operator double() const { 201 | return VAL_DECIMAL(cell); 202 | } 203 | 204 | 205 | 206 | // 207 | // DATE 208 | // 209 | 210 | bool Date::isValid(REBVAL const * cell) { 211 | return IS_DATE(cell); 212 | } 213 | 214 | 215 | } // end namespace ren 216 | -------------------------------------------------------------------------------- /src/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RENCPP_REBOL_COMMON_HPP 2 | #define RENCPP_REBOL_COMMON_HPP 3 | 4 | #ifdef _WIN32 5 | // 6 | // On Windows it is required to include , and defining the 7 | // _WIN32_WINNT constant to 0x0501 specifies the minimum targeted version 8 | // is Windows XP. This is the earliest platform API still supported by 9 | // Visual Studio 2015: 10 | // 11 | // https://msdn.microsoft.com/en-us/library/6sehtctf.aspx 12 | // 13 | // R3-Alpha used 0x0500, indicating a minimum target of Windows 2000. No 14 | // Windows-XP-specific dependencies were added in Ren-C, but the version 15 | // was bumped to avoid compilation errors in the common case. 16 | // 17 | // !!! Note that %sys-core.h includes as well if building 18 | // for windows. The redundant inclusion should not create a problem. 19 | // (So better to do the inclusion just to test that it doesn't.) 20 | // 21 | #undef _WIN32_WINNT 22 | #define _WIN32_WINNT 0x0501 23 | #include 24 | 25 | // Put any dependencies that include here 26 | // 27 | /* #include "..." */ 28 | /* #include "..." */ 29 | 30 | // Undefine the Windows version of IS_ERROR to avoid compiler warning 31 | // when Rebol redefines it. (Rebol defines IS_XXX for all datatypes.) 32 | // 33 | #undef IS_ERROR 34 | #undef max 35 | #undef min 36 | #else 37 | #include // needed for SIGINT, SIGTERM, SIGHUP 38 | #endif 39 | 40 | #include "rebol/src/include/sys-core.h" 41 | 42 | 43 | // !!! This functionality will likely be added to make APPLY work in a more 44 | // general fashion (and not just on functions). 45 | 46 | extern REBOOL Generalized_Apply_Throws( 47 | REBVAL *out, 48 | const REBVAL *applicand, 49 | REBARR *args, 50 | REBSPC *specifier 51 | ); 52 | 53 | #endif // RENCPP_REBOL_COMMON_HPP 54 | -------------------------------------------------------------------------------- /src/context.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // context.cpp 3 | // This file is part of RenCpp 4 | // Copyright (C) 2015-2018 HostileFork.com 5 | // 6 | // Licensed under the Boost License, Version 1.0 (the "License") 7 | // 8 | // http://www.boost.org/LICENSE_1_0.txt 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | // implied. See the License for the specific language governing 14 | // permissions and limitations under the License. 15 | // 16 | // See http://rencpp.hostilefork.com for more information on this project 17 | // 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "rencpp/value.hpp" 24 | #include "rencpp/engine.hpp" 25 | #include "rencpp/runtime.hpp" 26 | #include "rencpp/context.hpp" 27 | 28 | #include "common.hpp" 29 | 30 | 31 | namespace ren { 32 | 33 | AnyContext::Finder AnyContext::finder; 34 | 35 | 36 | 37 | AnyContext AnyContext::lookup(const char * name, Engine * engine) 38 | { 39 | if (engine == nullptr) 40 | engine = &Engine::runFinder(); 41 | 42 | AnyContext result (Dont::Initialize); 43 | 44 | if (::RenFindContext(result->cell, engine->handle, name) != REN_SUCCESS) 45 | throw std::runtime_error ("Couldn't find named context"); 46 | 47 | result->finishInit(engine->handle); 48 | return result; 49 | } 50 | 51 | 52 | 53 | AnyContext AnyContext::current(Engine * engine) { 54 | if (finder == nullptr) { 55 | finder = [] (Engine * engine) -> AnyContext & { 56 | if (engine == nullptr) 57 | engine = &Engine::runFinder(); 58 | 59 | static AnyContext user = lookup("USER", engine); 60 | return user; 61 | }; 62 | } 63 | return finder(engine); 64 | } 65 | 66 | 67 | // 68 | // TYPE DETECTION 69 | // 70 | 71 | bool AnyContext::isValid(REBVAL const * cell) { 72 | return IS_OBJECT(cell); 73 | } 74 | 75 | 76 | 77 | // 78 | // CONSTRUCTION 79 | // 80 | 81 | AnyContext::AnyContext ( 82 | internal::Loadable const loadables[], 83 | size_t numLoadables, 84 | internal::CellFunction cellfun, 85 | AnyContext const * contextPtr, 86 | Engine * engine 87 | ) : 88 | AnyValue (Dont::Initialize) 89 | { 90 | (*cellfun)(this->cell); 91 | 92 | // Here, a null context pointer means null. No finder is invoked. 93 | 94 | RenEngineHandle realEngine = contextPtr ? contextPtr->getEngine() : 95 | (engine ? engine->getHandle() : Engine::runFinder().getHandle()); 96 | 97 | constructOrApplyInitialize( 98 | realEngine, 99 | contextPtr, 100 | nullptr, // no applicand 101 | loadables, 102 | numLoadables, 103 | this, // Do construct 104 | nullptr // Don't apply 105 | ); 106 | } 107 | 108 | 109 | // TBD: Finish version where you can use values directly as an array 110 | /* 111 | 112 | AnyContext::AnyContext ( 113 | AnyValue const values[], 114 | size_t numValues, 115 | internal::CellFunction cellfun, 116 | AnyContext const * contextPtr, 117 | Engine * engine 118 | ) : 119 | AnyValue (Dont::Initialize) 120 | { 121 | (*cellfun)(this->cell); 122 | 123 | // Here, a null context pointer means null. No finder is invoked. 124 | 125 | RenEngineHandle realEngine = contextPtr ? contextPtr->getEngine() : 126 | (engine ? engine->getHandle() : Engine::runFinder().getHandle()); 127 | 128 | constructOrApplyInitialize( 129 | realEngine, 130 | nullptr, // no applicand 131 | loadables, 132 | numLoadables, 133 | this, // Do construct 134 | nullptr // Don't apply 135 | ); 136 | } 137 | 138 | */ 139 | 140 | // 141 | // TYPE HEADER INITIALIZATION 142 | // 143 | 144 | void AnyContext::initObject(REBVAL *cell) { 145 | VAL_RESET_HEADER(cell, REB_OBJECT); 146 | } 147 | 148 | void AnyContext::initError(REBVAL *cell) { 149 | VAL_RESET_HEADER(cell, REB_ERROR); 150 | } 151 | 152 | 153 | } // end namespace ren 154 | -------------------------------------------------------------------------------- /src/engine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // engine.cpp 3 | // This file is part of RenCpp 4 | // Copyright (C) 2015-2018 HostileFork.com 5 | // 6 | // Licensed under the Boost License, Version 1.0 (the "License") 7 | // 8 | // http://www.boost.org/LICENSE_1_0.txt 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | // implied. See the License for the specific language governing 14 | // permissions and limitations under the License. 15 | // 16 | // See http://rencpp.hostilefork.com for more information on this project 17 | // 18 | 19 | #include "rencpp/engine.hpp" 20 | #include "rencpp/ren.hpp" 21 | 22 | 23 | namespace ren { 24 | 25 | Engine::Finder Engine::finder; 26 | 27 | 28 | std::ostream & Engine::setOutputStream(std::ostream & os) { 29 | auto temp = osPtr; 30 | osPtr = &os; 31 | return *temp; 32 | } 33 | 34 | 35 | std::istream & Engine::setInputStream(std::istream & is) { 36 | auto temp = isPtr; 37 | isPtr = &is; 38 | return *temp; 39 | } 40 | 41 | 42 | std::ostream & Engine::getOutputStream() { 43 | return *osPtr; 44 | } 45 | 46 | 47 | std::istream & Engine::getInputStream() { 48 | return *isPtr; 49 | } 50 | 51 | 52 | optional Engine::evaluate( 53 | std::initializer_list> loadables, 54 | Engine & engine 55 | ) { 56 | return runtime.evaluate( 57 | loadables.begin(), 58 | loadables.size(), 59 | nullptr, 60 | &engine 61 | ); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/errors.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/value.hpp" 4 | #include "rencpp/error.hpp" 5 | #include "rencpp/engine.hpp" 6 | 7 | #include "common.hpp" 8 | 9 | 10 | namespace ren { 11 | 12 | // 13 | // TYPE DETECTION AND INITIALIZATION 14 | // 15 | 16 | bool Error::isValid(REBVAL const * cell) { 17 | return IS_ERROR(cell); 18 | } 19 | 20 | 21 | 22 | // 23 | // CONSTRUCTION 24 | // 25 | 26 | Error::Error (const char * msg, Engine * engine) : 27 | AnyContext_ (Dont::Initialize) 28 | { 29 | VAL_RESET_HEADER(cell, REB_ERROR); 30 | 31 | if (engine == nullptr) 32 | engine = &Engine::runFinder(); 33 | 34 | std::string array ( 35 | "#[error! [code: _ type: 'User id: 'message message: " 36 | ); 37 | 38 | array += '{'; 39 | array += msg; 40 | array += '}'; 41 | 42 | // the shim could adjust the where and say what function threw it? 43 | // file/line number optional? 44 | 45 | array += "]]"; 46 | 47 | internal::Loadable loadable = array.data(); 48 | 49 | constructOrApplyInitialize( 50 | engine->getHandle(), 51 | nullptr, // no context 52 | nullptr, // no applicand 53 | &loadable, 54 | 1, 55 | this, // Do construct 56 | nullptr // Don't apply 57 | ); 58 | } 59 | 60 | } // end namespace ren 61 | -------------------------------------------------------------------------------- /src/helpers.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // helpers.cpp 3 | // This file is part of RenCpp 4 | // Copyright (C) 2015-2018 HostileFork.com 5 | // 6 | // Licensed under the Boost License, Version 1.0 (the "License") 7 | // 8 | // http://www.boost.org/LICENSE_1_0.txt 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | // implied. See the License for the specific language governing 14 | // permissions and limitations under the License. 15 | // 16 | // See http://rencpp.hostilefork.com for more information on this project 17 | // 18 | 19 | #include "rencpp/helpers.hpp" 20 | 21 | namespace ren { 22 | 23 | Printer print; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/hooks.cpp: -------------------------------------------------------------------------------- 1 | #ifndef NDEBUG 2 | #include 3 | #include 4 | #endif 5 | #include 6 | #include 7 | 8 | #include "rencpp/rebol.hpp" 9 | 10 | // !!! hooks should not be throwing exceptions; still some in threadinit 11 | #include "rencpp/error.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "common.hpp" 20 | 21 | 22 | namespace ren { 23 | 24 | namespace internal { 25 | 26 | std::mutex refcountMutex; 27 | ren::AnyValue * head; 28 | 29 | class RebolHooks { 30 | 31 | private: 32 | RebolEngineHandle theEngine; // currently only support one "Engine" 33 | REBSER * allocatedContexts; 34 | 35 | 36 | public: 37 | RebolHooks () : 38 | theEngine (REBOL_ENGINE_HANDLE_INVALID), 39 | allocatedContexts (nullptr) 40 | { 41 | } 42 | 43 | 44 | // 45 | // ENGINE ALLOCATION AND FREEING 46 | // 47 | 48 | RenResult AllocEngine(RebolEngineHandle * engineOut) { 49 | if (!REBOL_IS_ENGINE_HANDLE_INVALID(theEngine)) 50 | throw std::runtime_error( 51 | "Rebol does not have Engine memory isolation at this" 52 | " point in time, and no VM sandboxing has been added" 53 | " in the binding. So only one engine may be allocated" 54 | ); 55 | 56 | theEngine.data = 1020; 57 | 58 | runtime.lazyInitializeIfNecessary(); 59 | 60 | *engineOut = theEngine; 61 | 62 | return REN_SUCCESS; 63 | } 64 | 65 | 66 | RenResult FreeEngine(RebolEngineHandle engine) { 67 | 68 | assert(engine.data == 1020); 69 | 70 | if (REBOL_IS_ENGINE_HANDLE_INVALID(engine)) 71 | return REN_BAD_ENGINE_HANDLE; 72 | 73 | theEngine = REBOL_ENGINE_HANDLE_INVALID; 74 | 75 | return REN_SUCCESS; 76 | } 77 | 78 | 79 | 80 | // 81 | // CONTEXT FINDING 82 | // 83 | 84 | 85 | RenResult FindContext( 86 | REBVAL *out, 87 | RebolEngineHandle engine, 88 | char const * name 89 | ) { 90 | assert(!REBOL_IS_ENGINE_HANDLE_INVALID(theEngine)); 91 | assert(engine.data == theEngine.data); 92 | 93 | REBCTX * context = nullptr; 94 | if (strcmp(name, "USER") == 0) 95 | context = VAL_CONTEXT(Get_System(SYS_CONTEXTS, CTX_USER)); 96 | else if (strcmp(name, "LIB") == 0) 97 | context = VAL_CONTEXT(Get_System(SYS_CONTEXTS, CTX_LIB)); 98 | if (strcmp(name, "SYS") == 0) 99 | context = VAL_CONTEXT(Get_System(SYS_CONTEXTS, CTX_SYS)); 100 | 101 | // don't expose CTX_ROOT? 102 | 103 | if (context) { 104 | Init_Object(out, context); 105 | return REN_SUCCESS; 106 | } 107 | 108 | return REN_ERROR_NO_SUCH_CONTEXT; 109 | } 110 | 111 | 112 | RenResult FormAsUtf8( 113 | RebolEngineHandle engine, 114 | REBVAL const * value, 115 | unsigned char * buffer, 116 | size_t bufSize, 117 | size_t * numBytesOut 118 | ) { 119 | assert(engine.data == 1020); 120 | 121 | // First we mold with the "FORM" settings and get a STRING! 122 | // series out of that. 123 | 124 | DECLARE_MOLD (mo); 125 | 126 | Push_Mold(mo); 127 | Form_Value(mo, const_cast(value)); 128 | 129 | REBSER * utf8_series = Pop_Molded_UTF8(mo); 130 | 131 | // Okay that should be the UTF8 data. Let's copy it into the buffer 132 | // the caller sent us. 133 | 134 | size_t len = SER_LEN(utf8_series); 135 | *numBytesOut = static_cast(len); 136 | 137 | RenResult result; 138 | if (len > bufSize) { 139 | len = bufSize; 140 | // should copy portion of buffer in anyway 141 | result = REN_BUFFER_TOO_SMALL; 142 | } 143 | else { 144 | result = REN_SUCCESS; 145 | } 146 | 147 | // Used to use std::copy, but MSVC complains unless you use their 148 | // non-standard checked_array_iterator<>: 149 | // 150 | // https://stackoverflow.com/q/25716841/ 151 | // 152 | memcpy(buffer, SER_HEAD(REBYTE, utf8_series), len); 153 | 154 | Free_Series(utf8_series); 155 | 156 | return result; 157 | } 158 | 159 | ~RebolHooks () { 160 | // The runtime may be shutdown already, so don't do anything here 161 | // using REBVALs or REBSERs. Put that in engine shutdown. 162 | } 163 | }; 164 | 165 | RebolHooks hooks; 166 | 167 | } // end namespace internal 168 | 169 | } // end namespace ren 170 | 171 | 172 | RenResult RenAllocEngine(RebolEngineHandle * engineOut) { 173 | return ren::internal::hooks.AllocEngine(engineOut); 174 | } 175 | 176 | 177 | RenResult RenFreeEngine(RebolEngineHandle engine) { 178 | return ren::internal::hooks.FreeEngine(engine); 179 | } 180 | 181 | 182 | RenResult RenFindContext( 183 | REBVAL *out, 184 | RenEngineHandle engine, 185 | char const * name 186 | ) { 187 | return ren::internal::hooks.FindContext(out, engine, name); 188 | } 189 | 190 | RenResult RenFormAsUtf8( 191 | RenEngineHandle engine, 192 | REBVAL const * value, 193 | unsigned char * buffer, 194 | size_t bufSize, 195 | size_t * lengthOut 196 | ) { 197 | return ren::internal::hooks.FormAsUtf8( 198 | engine, value, buffer, bufSize, lengthOut 199 | ); 200 | } 201 | 202 | REBVAL *RL_Arg(void *frame, int index) 203 | { 204 | return FRM_ARG( 205 | reinterpret_cast(frame), 206 | static_cast(index) 207 | ); 208 | } 209 | 210 | void RL_Move(REBVAL *out, REBVAL const * v) 211 | { 212 | Move_Value(out, v); 213 | } -------------------------------------------------------------------------------- /src/image.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/value.hpp" 4 | #include "rencpp/image.hpp" 5 | #include "rencpp/engine.hpp" 6 | 7 | #include "common.hpp" 8 | 9 | 10 | namespace ren { 11 | 12 | // 13 | // IMAGE 14 | // 15 | 16 | bool Image::isValid(REBVAL const * cell) { 17 | return IS_IMAGE(cell); 18 | } 19 | 20 | #if REN_CLASSLIB_QT == 1 21 | 22 | Image::Image (QImage const & image, Engine * engine) { 23 | // need to convert if this isn't true 24 | assert(image.format() == QImage::Format_ARGB32); 25 | 26 | REBCNT width = static_cast(image.width()); 27 | REBCNT height = static_cast(image.height()); 28 | 29 | VAL_RESET_HEADER(cell, REB_IMAGE); 30 | REBSER * img = Make_Image(width, height, FALSE); 31 | 32 | // Was using std::copy, but MSVC complained unless you used a safe 33 | // iterator that is non standard. 34 | // 35 | memcpy(IMG_DATA(img), image.bits(), sizeof(char[4]) * width * height); 36 | 37 | Init_Image(cell, img); 38 | finishInit(engine->getHandle()); 39 | } 40 | 41 | 42 | Image::operator QImage () const { 43 | QImage result { 44 | VAL_IMAGE_DATA(cell), 45 | static_cast(VAL_IMAGE_WIDE(cell)), 46 | static_cast(VAL_IMAGE_HIGH(cell)), 47 | QImage::Format_ARGB32 48 | }; 49 | 50 | return result; 51 | } 52 | 53 | #endif 54 | 55 | } // end namespace ren 56 | -------------------------------------------------------------------------------- /src/series.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rencpp/value.hpp" 5 | #include "rencpp/atoms.hpp" 6 | #include "rencpp/series.hpp" 7 | #include "rencpp/arrays.hpp" // For Path evaluation in operator[] 8 | 9 | #include "common.hpp" 10 | 11 | 12 | namespace ren { 13 | 14 | // 15 | // TYPE DETECTION 16 | // 17 | 18 | 19 | bool AnySeries::isValid(REBVAL const * cell) { 20 | return ANY_SERIES(cell); 21 | } 22 | 23 | 24 | 25 | // 26 | // ITERATION 27 | // 28 | 29 | 30 | void ren::internal::AnySeries_::operator++() { 31 | cell->payload.any_series.index++; 32 | } 33 | 34 | 35 | void ren::internal::AnySeries_::operator--() { 36 | cell->payload.any_series.index--; 37 | } 38 | 39 | 40 | void ren::internal::AnySeries_::operator++(int) { 41 | ++*this; 42 | } 43 | 44 | 45 | void ren::internal::AnySeries_::operator--(int) { 46 | --*this; 47 | } 48 | 49 | 50 | AnyValue ren::internal::AnySeries_::operator*() const { 51 | AnyValue result {Dont::Initialize}; 52 | 53 | if (0 == VAL_LEN_AT(cell)) { 54 | Init_Void(result.cell); 55 | } 56 | else if (ANY_STRING(cell)) { 57 | // from str_to_char in Rebol source 58 | Init_Char( 59 | cell, 60 | GET_ANY_CHAR(VAL_SERIES(cell), VAL_INDEX(cell)) 61 | ); 62 | } else if (GET_SER_FLAG(VAL_SERIES(cell), SERIES_FLAG_ARRAY)) { 63 | Derelativize( 64 | result.cell, 65 | ARR_AT(VAL_ARRAY(cell), VAL_INDEX(cell)), VAL_SPECIFIER(cell) 66 | ); 67 | } else { 68 | // Binary and such, would return an integer 69 | UNREACHABLE_CODE(); 70 | } 71 | result.finishInit(origin); 72 | return result; 73 | } 74 | 75 | 76 | AnyValue ren::internal::AnySeries_::operator->() const { 77 | return *(*this); 78 | } 79 | 80 | 81 | void ren::internal::AnySeries_::head() { 82 | cell->payload.any_series.index = 0; 83 | } 84 | 85 | 86 | void ren::internal::AnySeries_::tail() { 87 | cell->payload.any_series.index = VAL_LEN_HEAD(cell); 88 | } 89 | 90 | 91 | size_t AnySeries::length() const { 92 | return VAL_LEN_AT(cell); 93 | } 94 | 95 | 96 | AnyValue AnySeries::operator[](AnyValue const & picker) const { 97 | // 98 | // See notes on semantic questions of operator[] here, and why we go with 99 | // "whatever GET-PATH! selection does", e.g. PICK. 100 | // 101 | // https://github.com/hostilefork/rencpp/issues/64 102 | // 103 | REBVAL *picked = rebDo( 104 | BLANK_VALUE, // superflous blank not needed in UTF-8 Everywhere branch 105 | rebEval(NAT_VALUE(pick_p)), 106 | cell, 107 | picker.cell, 108 | END 109 | ); 110 | 111 | AnyValue result = AnyValue::fromCell_(picked, origin); 112 | rebFree(picked); 113 | return result; 114 | } 115 | 116 | 117 | } // end namespace ren 118 | -------------------------------------------------------------------------------- /src/stdio.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // This file is modeled after Rebol's dev-stdio.c 3 | // 4 | // The Rebol "host kit" has a special handler for dealing with standard IO 5 | // that is separate from the interaction with the files. Because the C++ 6 | // iostream model already has the cin and cout implemented, we take out the 7 | // handling and just use that; adding the ability to hook into it providing 8 | // a custom ostream and istream... 9 | // 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #if !defined TO_WINDOWS 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "rencpp/rebol.hpp" 26 | #include "rencpp/engine.hpp" 27 | 28 | #include "common.hpp" 29 | 30 | 31 | #define SF_DEV_NULL 31 // local flag to mark NULL device 32 | 33 | 34 | extern REBDEV *Devices[]; 35 | 36 | 37 | 38 | 39 | 40 | /*********************************************************************** 41 | ** 42 | */ static DEVICE_CMD Quit_IO(REBREQ *dr) 43 | /* 44 | ***********************************************************************/ 45 | { 46 | REBDEV *dev = (REBDEV*)dr; // just to keep compiler happy above 47 | 48 | dev->flags &= ~cast(unsigned int, RDF_OPEN); 49 | return DR_DONE; 50 | } 51 | 52 | 53 | /*********************************************************************** 54 | ** 55 | */ static DEVICE_CMD Open_IO(REBREQ *req) 56 | /* 57 | ***********************************************************************/ 58 | { 59 | REBDEV *dev; 60 | 61 | dev = Devices[req->device]; 62 | 63 | // Avoid opening the console twice (compare dev and req flags): 64 | if (dev->flags & RDF_OPEN) { 65 | // Device was opened earlier as null, so req must have that flag: 66 | if (dev->flags & SF_DEV_NULL) 67 | req->modes |= RDM_NULL; 68 | req->flags |= RRF_OPEN; 69 | return DR_DONE; // Do not do it again 70 | } 71 | 72 | if (req->modes & RDM_NULL) 73 | dev->flags|= SF_DEV_NULL; 74 | 75 | req->flags |= RRF_OPEN; 76 | dev->flags |= RDF_OPEN; 77 | 78 | return DR_DONE; 79 | } 80 | 81 | 82 | /*********************************************************************** 83 | ** 84 | */ static DEVICE_CMD Close_IO(REBREQ *req) 85 | /* 86 | ***********************************************************************/ 87 | { 88 | REBDEV *dev = Devices[req->device]; 89 | 90 | req->flags &= ~cast(unsigned int, RRF_OPEN); 91 | 92 | return DR_DONE; 93 | } 94 | 95 | 96 | /*********************************************************************** 97 | ** 98 | */ static DEVICE_CMD Write_IO(REBREQ *req) 99 | /* 100 | ** Low level "raw" standard output function. 101 | ** 102 | ** Allowed to restrict the write to a max OS buffer size. 103 | ** 104 | ** Returns the number of chars written. 105 | ** 106 | ***********************************************************************/ 107 | { 108 | if (req->modes & RDM_NULL) { 109 | req->actual = req->length; 110 | return DR_DONE; 111 | } 112 | 113 | std::ostream & os = ren::Engine::runFinder().getOutputStream(); 114 | 115 | os.write( 116 | reinterpret_cast(req->common.data), 117 | static_cast(req->length) // Clang build needs cast 118 | ); 119 | 120 | // knowing about a partial write would require using tellp() and comparing 121 | // which is both unreliable and not available on stdout anyway 122 | // 123 | // http://stackoverflow.com/a/14238640/211160 124 | 125 | if (!os) { 126 | req->error = 1020; 127 | return DR_ERROR; 128 | } 129 | 130 | // For now we flush on every write. It is inefficient, but it's not 131 | // clear what would be done about it otherwise if you are trying to read 132 | // the output in a console. Perhaps an iostream class can be 133 | // self-flushing based on a timer, so if it hasn't been flushed for a 134 | // second it will? 135 | 136 | //if (GET_FLAG(req->flags, RRF_FLUSH)) { 137 | os.flush(); 138 | //} 139 | 140 | // old code could theoretically tell you when you had partial output; 141 | // that's not really part of the ostream interface for write. What 142 | // could you do about partial output to stdout anyway? 143 | 144 | req->actual = req->length; 145 | 146 | return DR_DONE; 147 | } 148 | 149 | 150 | /*********************************************************************** 151 | ** 152 | */ static DEVICE_CMD Read_IO(REBREQ *req) 153 | /* 154 | ** Low level "raw" standard input function. 155 | ** 156 | ** The request buffer must be long enough to hold result. 157 | ** 158 | ** Result is NOT terminated (the actual field has length.) 159 | ** 160 | ***********************************************************************/ 161 | { 162 | u32 length = req->length; 163 | 164 | if (req->modes & RDM_NULL) { 165 | req->common.data[0] = 0; 166 | return DR_DONE; 167 | } 168 | 169 | req->actual = 0; 170 | 171 | std::istream & is = ren::Engine::runFinder().getInputStream(); 172 | 173 | // There is a std::string equivalent for getline that doesn't require 174 | // a buffer length, but we go with the version that takes a buffer 175 | // length for now. The only way to get the length of that is with 176 | // strlen, however. 177 | 178 | is.getline( 179 | reinterpret_cast(req->common.data), 180 | static_cast(req->length) // Clang needs this cast 181 | ); 182 | 183 | // C is *not* C++. Ren-C is a C codebase, and its defines and macros may 184 | // tread on C++ and that is how it is. Prohibiting the use of "fail" as 185 | // a method name is a liability, and Ren-C++ internally has the job of 186 | // insulating all the C madness from clients. Here's that at work. 187 | #undef fail 188 | if (is.fail()) { 189 | req->error = 1020; 190 | return DR_ERROR; 191 | } 192 | 193 | req->actual = LEN_BYTES(req->common.data); 194 | 195 | return DR_DONE; 196 | } 197 | 198 | 199 | /*********************************************************************** 200 | ** 201 | */ static DEVICE_CMD Open_Echo(REBREQ *) 202 | /* 203 | ** Open a file for low-level console echo (output). 204 | ** 205 | ***********************************************************************/ 206 | { 207 | throw std::runtime_error( 208 | "echo stdin and stdout to file not supported by binding" 209 | " in a direct fashion, you have to create a stream aggregator" 210 | " object that does it if you want that feature." 211 | ); 212 | 213 | return DR_DONE; 214 | } 215 | 216 | 217 | /*********************************************************************** 218 | ** 219 | ** Command Dispatch Table (RDC_ enum order) 220 | ** 221 | ***********************************************************************/ 222 | 223 | static DEVICE_CMD_FUNC Dev_Cmds[RDC_MAX] = 224 | { 225 | 0, // init 226 | Quit_IO, 227 | Open_IO, 228 | Close_IO, 229 | Read_IO, 230 | Write_IO, 231 | 0, // poll 232 | 0, // connect 233 | 0, // query 234 | 0, // modify 235 | Open_Echo, // CREATE used for opening echo file 236 | }; 237 | 238 | DEFINE_DEV(Dev_StdIO, "Standard IO", 1, Dev_Cmds, RDC_MAX, sizeof(REBREQ)); 239 | -------------------------------------------------------------------------------- /src/strings.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/value.hpp" 4 | #include "rencpp/strings.hpp" 5 | #include "rencpp/engine.hpp" 6 | 7 | #include "common.hpp" 8 | 9 | 10 | namespace ren { 11 | 12 | // 13 | // TYPE DETECTION 14 | // 15 | 16 | 17 | bool String::isValid(REBVAL const * cell) { 18 | return IS_STRING(cell); 19 | } 20 | 21 | bool Tag::isValid(REBVAL const * cell) { 22 | return IS_TAG(cell); 23 | } 24 | 25 | bool Filename::isValid(REBVAL const * cell) { 26 | return IS_FILE(cell); 27 | } 28 | 29 | bool AnyString::isValid(REBVAL const * cell) { 30 | return ANY_STRING(cell); 31 | } 32 | 33 | 34 | // 35 | // TYPE HEADER INITIALIZATION 36 | // 37 | 38 | void AnyString::initString(REBVAL *cell) { 39 | VAL_RESET_HEADER(cell, REB_STRING); 40 | } 41 | 42 | void AnyString::initTag(REBVAL *cell) { 43 | VAL_RESET_HEADER(cell, REB_TAG); 44 | } 45 | 46 | void AnyString::initFilename(REBVAL *cell) { 47 | VAL_RESET_HEADER(cell, REB_FILE); 48 | } 49 | 50 | 51 | 52 | // 53 | // CONSTRUCTION 54 | // 55 | 56 | AnyString::AnyString ( 57 | char const * spelling, 58 | internal::CellFunction cellfun, 59 | Engine * engine 60 | ) : 61 | AnySeries (Dont::Initialize) 62 | { 63 | (*cellfun)(cell); 64 | 65 | if (engine == nullptr) 66 | engine = &Engine::runFinder(); 67 | 68 | std::string source; 69 | 70 | if (hasType(*this)) { 71 | source += '{'; 72 | source += spelling; 73 | source += '}'; 74 | } 75 | else if (hasType(*this)) { 76 | source += '<'; 77 | source += spelling; 78 | source += '>'; 79 | } 80 | else if (hasType(*this)) { 81 | source += "%"; 82 | source += spelling; 83 | } 84 | else 85 | UNREACHABLE_CODE(); 86 | 87 | internal::Loadable loadable (source.data()); 88 | 89 | constructOrApplyInitialize( 90 | engine->getHandle(), 91 | nullptr, // no context 92 | nullptr, // no applicand 93 | &loadable, 94 | 1, 95 | this, // Do construct 96 | nullptr // Don't apply 97 | ); 98 | } 99 | 100 | 101 | AnyString::AnyString ( 102 | std::string const & spelling, 103 | internal::CellFunction cellfun, 104 | Engine * engine 105 | ) : 106 | AnyString (spelling.c_str(), cellfun, engine) 107 | { 108 | } 109 | 110 | 111 | 112 | 113 | #if REN_CLASSLIB_QT == 1 114 | 115 | AnyString::AnyString ( 116 | QString const & spelling, 117 | internal::CellFunction cellfun, 118 | Engine * engine 119 | ) 120 | : AnySeries(Dont::Initialize) 121 | { 122 | (*cellfun)(cell); 123 | 124 | QString source; 125 | 126 | // Note: wouldn't be able to return char * without intermediate 127 | // http://stackoverflow.com/questions/17936160/ 128 | 129 | if (hasType(*this)) { 130 | source += '{'; 131 | source += spelling; 132 | source += '}'; 133 | } 134 | else if (hasType(*this)) { 135 | source += '<'; 136 | source += spelling; 137 | source += '>'; 138 | } 139 | else 140 | UNREACHABLE_CODE(); 141 | 142 | QByteArray utf8 = source.toUtf8(); 143 | internal::Loadable loadable (utf8.data()); 144 | 145 | if (engine == nullptr) 146 | engine = &Engine::runFinder(); 147 | 148 | constructOrApplyInitialize( 149 | engine->getHandle(), 150 | nullptr, // no context 151 | nullptr, // no applicand 152 | &loadable, 153 | 1, 154 | this, // do construct 155 | nullptr // don't apply 156 | ); 157 | } 158 | 159 | #endif 160 | 161 | 162 | 163 | // 164 | // EXTRACTION 165 | // 166 | 167 | std::string AnyString::spellingOf_STD() const { 168 | std::string result = static_cast(*this); 169 | if (hasType(*this) /* or isUrl() or isEmail() or isFile() */) 170 | return result; 171 | if (hasType(*this)) { 172 | result.erase(0, 1); 173 | return result.erase(result.length() - 1, 1); 174 | } 175 | throw std::runtime_error {"Invalid String Type"}; 176 | } 177 | 178 | 179 | #if REN_CLASSLIB_QT 180 | 181 | QString AnyString::spellingOf_QT() const { 182 | QString result = static_cast(*this); 183 | if (hasType(*this) /* or isUrl() or isEmail() or isFile() */) 184 | return result; 185 | if (hasType(*this)) { 186 | assert(result.length() >= 2); 187 | return result.mid(1, result.length() - 2); 188 | } 189 | throw std::runtime_error {"Invalid String Type"}; 190 | } 191 | 192 | #endif 193 | 194 | 195 | } // end namespace ren 196 | -------------------------------------------------------------------------------- /src/words.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/value.hpp" 4 | #include "rencpp/words.hpp" 5 | #include "rencpp/context.hpp" 6 | 7 | #include "common.hpp" 8 | 9 | 10 | namespace ren { 11 | 12 | // 13 | // TYPE DETECTION 14 | // 15 | 16 | bool Word::isValid(const REBVAL *cell) { 17 | return IS_WORD(cell); 18 | } 19 | 20 | bool SetWord::isValid(const REBVAL *cell) { 21 | return IS_SET_WORD(cell); 22 | } 23 | 24 | bool GetWord::isValid(const REBVAL *cell) { 25 | return IS_GET_WORD(cell); 26 | } 27 | 28 | bool LitWord::isValid(const REBVAL *cell) { 29 | return IS_LIT_WORD(cell); 30 | } 31 | 32 | bool Refinement::isValid(const REBVAL *cell) { 33 | return IS_REFINEMENT(cell); 34 | } 35 | 36 | bool AnyWord::isValid(const REBVAL *cell) { 37 | return IS_WORD(cell) 38 | || IS_SET_WORD(cell) 39 | || IS_GET_WORD(cell) 40 | || IS_LIT_WORD(cell) 41 | || IS_REFINEMENT(cell) 42 | || IS_ISSUE(cell); 43 | } 44 | 45 | 46 | // 47 | // TYPE HEADER INITIALIZATION 48 | // 49 | 50 | void AnyWord::initWord(REBVAL *cell) { 51 | VAL_RESET_HEADER(cell, REB_WORD); 52 | } 53 | 54 | void AnyWord::initSetWord(REBVAL *cell) { 55 | VAL_RESET_HEADER(cell, REB_SET_WORD); 56 | } 57 | 58 | void AnyWord::initGetWord(REBVAL *cell) { 59 | VAL_RESET_HEADER(cell, REB_GET_WORD); 60 | } 61 | 62 | void AnyWord::initLitWord(REBVAL *cell) { 63 | VAL_RESET_HEADER(cell, REB_LIT_WORD); 64 | } 65 | 66 | void AnyWord::initRefinement(REBVAL *cell) { 67 | VAL_RESET_HEADER(cell, REB_REFINEMENT); 68 | } 69 | 70 | 71 | // 72 | // SPELLING 73 | // 74 | 75 | // 76 | // To get the "formed" version of the word, use to_string. That will include 77 | // the markup characters, so a GetWord of FOO will give back FOO: 78 | // 79 | // On the other hand, this returns just the "spelling" of the symbol, "FOO" 80 | // 81 | 82 | std::string AnyWord::spellingOf_STD() const { 83 | std::string result = to_string(*this); 84 | if (hasType(*this)) 85 | return result; 86 | if ( 87 | hasType(*this) 88 | || hasType(*this) 89 | || hasType(*this) 90 | ){ 91 | return result.erase(0, 1); 92 | } 93 | if (hasType(*this)) 94 | return result.erase(result.length() - 1, 1); 95 | throw std::runtime_error {"Invalid Word Type"}; 96 | } 97 | 98 | 99 | #if REN_CLASSLIB_QT 100 | QString AnyWord::spellingOf_QT() const { 101 | QString result = to_QString(*this); 102 | if (hasType(*this)) 103 | return result; 104 | if ( 105 | hasType(*this) 106 | || hasType(*this) 107 | || hasType(*this) 108 | ){ 109 | return result.right(result.length() - 1); 110 | } 111 | if (hasType(*this)) 112 | return result.left(result.length() - 1); 113 | throw std::runtime_error {"Invalid Word Type"}; 114 | } 115 | #endif 116 | 117 | 118 | 119 | // 120 | // CONSTRUCTION 121 | // 122 | 123 | AnyWord::AnyWord ( 124 | char const * spelling, 125 | internal::CellFunction cellfun, 126 | AnyContext const * contextPtr, 127 | Engine * engine 128 | ) : 129 | AnyValue (Dont::Initialize) 130 | { 131 | (*cellfun)(cell); 132 | 133 | std::string array; 134 | 135 | if (hasType(*this)) { 136 | array += spelling; 137 | } 138 | else if (hasType(*this)) { 139 | array += spelling; 140 | array += ':'; 141 | } 142 | else if (hasType(*this)) { 143 | array += ':'; 144 | array += spelling; 145 | } 146 | else if (hasType(*this)) { 147 | array += '\''; 148 | array += spelling; 149 | } 150 | else if (hasType(*this)) { 151 | array += '/'; 152 | array += spelling; 153 | } 154 | else 155 | UNREACHABLE_CODE(); 156 | 157 | internal::Loadable loadable = array.data(); 158 | 159 | AnyContext context = contextPtr 160 | ? *contextPtr 161 | : AnyContext::current(engine); 162 | 163 | constructOrApplyInitialize( 164 | context.getEngine(), 165 | &context, 166 | nullptr, // no applicand 167 | &loadable, 168 | 1, 169 | this, // do construct 170 | nullptr // don't apply 171 | ); 172 | 173 | assert(ANY_WORD(this->cell)); 174 | } 175 | 176 | 177 | 178 | #if REN_CLASSLIB_QT 179 | AnyWord::AnyWord ( 180 | QString const & spelling, 181 | internal::CellFunction cellfun, 182 | AnyContext const * contextPtr, 183 | Engine * engine 184 | ) : 185 | AnyValue (Dont::Initialize) 186 | { 187 | (*cellfun)(cell); 188 | 189 | QString source; 190 | 191 | if (hasType(*this)) { 192 | source += spelling; 193 | } 194 | else if (hasType(*this)) { 195 | source += spelling; 196 | source += ':'; 197 | } 198 | else if (hasType(*this)) { 199 | source += ':'; 200 | source += spelling; 201 | } 202 | else if (hasType(*this)) { 203 | source += '\''; 204 | source += spelling; 205 | } 206 | else if (hasType(*this)) { 207 | source += '/'; 208 | source += spelling; 209 | } 210 | else 211 | UNREACHABLE_CODE(); 212 | 213 | internal::Loadable loadable (source); 214 | 215 | AnyContext context = contextPtr 216 | ? *contextPtr 217 | : AnyContext::current(engine); 218 | 219 | constructOrApplyInitialize( 220 | context.getEngine(), 221 | &context, 222 | nullptr, // no applicand 223 | &loadable, 224 | 1, 225 | this, // do construct 226 | nullptr // don't apply 227 | ); 228 | 229 | assert(ANY_WORD(this->cell)); 230 | } 231 | #endif 232 | 233 | 234 | AnyWord::AnyWord (AnyWord const & other, internal::CellFunction cellfun) : 235 | AnyValue (Dont::Initialize) 236 | { 237 | // !!! There were changes where the header bits started to contain 238 | // information relevant to the binding. The original design for an 239 | // agnostic "cellfun" that would write headers for Rebol-or-Red was 240 | // such that was the place where the type was encoded. With the 241 | // implementations drifting apart, it's likely that this attempt at 242 | // code sharing won't last. 243 | // 244 | // Work around the lost binding issue here with a very temporary hack 245 | // to write the cell header, then extract the REB_XXX type, then 246 | // overwrite with the new cell, then put the type bits back. 247 | 248 | (*cellfun)(this->cell); 249 | 250 | enum Reb_Kind kind = VAL_TYPE(this->cell); 251 | RL_Move(this->cell, other.cell); 252 | VAL_SET_TYPE_BITS(this->cell, kind); 253 | 254 | finishInit(other.origin); 255 | } 256 | 257 | } // end namespace ren 258 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is an input file for the CMake makefile generator 2 | 3 | # See notes in root directory, where this is added via `add_subdirectory()` 4 | 5 | 6 | # 7 | # We set up tests to run with Catch test framework: 8 | # 9 | # https://github.com/catchorg/Catch2 10 | # 11 | # Catch itself is set up as an external, CMake-managed dependency in the root 12 | # ext/catch/ directory. This dependency is included in the main CMake file. A 13 | # CATCH_INCLUDE_DIR property is inherited, to allow us to include the single 14 | # Catch header file. 15 | # 16 | 17 | include_directories(${CATCH_INCLUDE_DIR}) 18 | 19 | # These rigid warnings are too strict for building with Catch. 20 | 21 | try_remove_cxx_flag("-Wctor-dtor-privacy") 22 | try_remove_cxx_flag("-Wredundant-decls") 23 | try_remove_cxx_flag("-Wswitch-default") 24 | 25 | 26 | # These tests do not require an evaluator, and simply test Ren values 27 | 28 | set( 29 | VALUE_TESTS 30 | 31 | isolated-test.cpp 32 | 33 | literals-test.cpp 34 | cast-test.cpp 35 | block-test.cpp 36 | assign-test.cpp 37 | form-test.cpp 38 | iterator-test.cpp 39 | ) 40 | 41 | 42 | # These tests should work with any evaluator, assuming there is one 43 | 44 | if(DEFINED RUNTIME) 45 | set( 46 | EVALUATOR_TESTS 47 | 48 | apply-test.cpp 49 | context-test.cpp 50 | function-test.cpp 51 | ) 52 | endif() 53 | 54 | 55 | # These tests can call methods on the runtime object that are specific to 56 | # the evaluator in use. 57 | 58 | if(RUNTIME STREQUAL "red") 59 | 60 | set(RUNTIME_TESTS red-test.cpp) 61 | 62 | elseif(RUNTIME STREQUAL "rebol") 63 | 64 | set(RUNTIME_TESTS rebol-test.cpp) 65 | 66 | endif() 67 | 68 | 69 | # Make one big executable... 70 | 71 | add_executable( 72 | test-rencpp 73 | 74 | main.cpp 75 | ${VALUE_TESTS} 76 | ${EVALUATOR_TESTS} 77 | ${RUNTIME_TESTS} 78 | ) 79 | 80 | target_link_libraries(test-rencpp RenCpp) 81 | 82 | add_test(run-test-rencpp test-rencpp) 83 | -------------------------------------------------------------------------------- /tests/apply-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rencpp/ren.hpp" 5 | 6 | using namespace ren; 7 | 8 | #include "catch.hpp" 9 | 10 | TEST_CASE("apply test", "[rebol] [apply]") 11 | { 12 | SECTION("set-word success") 13 | { 14 | AnyValue result = SetWord {"w"}(10); 15 | CHECK(hasType(result)); 16 | CHECK(static_cast(static_cast(result)) == 10); 17 | } 18 | 19 | SECTION("set-word failure") 20 | { 21 | CHECK_THROWS_AS( 22 | SetWord {"w"}(10, 20), 23 | evaluation_error 24 | ); 25 | } 26 | 27 | SECTION("blank failure") 28 | { 29 | // technical note: explicit blank(arg1, arg2...) is now illegal 30 | AnyValue value = blank; 31 | 32 | CHECK_THROWS_AS( 33 | value.apply(10), 34 | evaluation_error 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/assign-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/ren.hpp" 4 | 5 | using namespace ren; 6 | 7 | #include "catch.hpp" 8 | 9 | TEST_CASE("assign test", "[rebol] [assign]") 10 | { 11 | Integer someInt {10}; 12 | AnyValue someValue; 13 | 14 | someValue = someInt; 15 | 16 | Block someBlock {10, "foo"}; 17 | 18 | Block someOtherBlock {20, "bar"}; 19 | 20 | someBlock = someOtherBlock; 21 | 22 | CHECK(someBlock.isEqualTo(someOtherBlock)); 23 | } 24 | -------------------------------------------------------------------------------- /tests/block-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "rencpp/ren.hpp" 4 | 5 | using namespace ren; 6 | 7 | #include "catch.hpp" 8 | 9 | TEST_CASE("block test", "[rebol] [block]") 10 | { 11 | SECTION("empty") 12 | { 13 | Block block {}; 14 | CHECK(block.length() == 0); 15 | 16 | Block explicitEmpties {Block {}, Block {}, Block {}}; 17 | Block implicitEmpties {{}, {}, {}}; 18 | 19 | CHECK(explicitEmpties.isEqualTo(implicitEmpties)); 20 | } 21 | 22 | SECTION("singleton") { 23 | // This is a tricky case 24 | 25 | Block singleton {"foo"}; 26 | AnyValue singletonAsValue = singleton; 27 | 28 | static_cast(singletonAsValue); 29 | 30 | Block singletonInitializer {singleton}; 31 | } 32 | 33 | 34 | Block randomStuff {"blue", Block {true, 1020}, 3.04}; 35 | 36 | 37 | SECTION("nested") 38 | { 39 | // See discussion on: 40 | // 41 | // https://github.com/hostilefork/rencpp/issues/1 42 | 43 | Block blk { {1, true}, {false, 2} }; 44 | 45 | CHECK(hasType(blk)); 46 | CHECK(hasType(blk[1])); 47 | CHECK(hasType(blk[2])); 48 | 49 | Block blk1 = static_cast(blk[1]); 50 | Block blk2 = static_cast(blk[2]); 51 | CHECK(hasType(blk1[1])); 52 | CHECK(hasType(blk1[2])); 53 | CHECK(hasType(blk2[1])); 54 | CHECK(hasType(blk2[2])); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/cast-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rencpp/ren.hpp" 5 | 6 | using namespace ren; 7 | 8 | #include "catch.hpp" 9 | 10 | TEST_CASE("cast test", "[rebol] [cast]") 11 | { 12 | 13 | SECTION("integer cast") 14 | { 15 | AnyValue someIntAsValue = 10; 16 | CHECK(hasType(someIntAsValue)); 17 | 18 | Integer someInt = static_cast(someIntAsValue); 19 | 20 | CHECK(someIntAsValue.isEqualTo(someInt)); 21 | CHECK(static_cast(someInt) == 10); 22 | 23 | AnyValue someIntConstValue = someIntAsValue; 24 | Integer anotherInt = static_cast(someIntConstValue); 25 | } 26 | 27 | 28 | SECTION("integer to float cast") 29 | { 30 | AnyValue value = Integer {20}; 31 | 32 | // You can't treat the bits of an Integer as Float in the Rebol or 33 | // Red world...C++ *could* have a cast operator for it. See: 34 | // 35 | // https://github.com/hostilefork/rencpp/issues/7 36 | CHECK_THROWS_AS( 37 | static_cast(value), 38 | std::bad_cast 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/context-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "rencpp/ren.hpp" 6 | 7 | using namespace ren; 8 | 9 | #include "catch.hpp" 10 | 11 | TEST_CASE("context test", "[rebol] [context]") 12 | { 13 | AnyContext defaultContext = AnyContext::current(); 14 | 15 | Object contextOne {}; 16 | Object contextTwo {}; 17 | 18 | // We install a new "context finder" which uses an integer to indicate 19 | // which context is currently in effect. 20 | 21 | int contextNumber = 1; 22 | 23 | auto oldFinder = AnyContext::setFinder( 24 | [&](Engine *) -> AnyContext { 25 | if (contextNumber == 1) 26 | return contextOne; 27 | if (contextNumber == 2) 28 | return contextTwo; 29 | throw std::runtime_error("Invalid context number"); 30 | } 31 | ); 32 | 33 | // Use function apply notation on a SetWord to set x to 10 34 | 35 | SetWord set_x {"x"}; 36 | set_x(10); 37 | 38 | // now test it using runtime apply notation 39 | 40 | CHECK(runtime("x = 10")); 41 | 42 | // change the global state, so the runtime is operating in contextTwo 43 | // if no override provided. 44 | 45 | contextNumber = 2; 46 | 47 | // Here in context 2, the changes to x had no effect; x is unset 48 | 49 | CHECK(runtime("void? get/opt 'x")); 50 | 51 | // Let's use function apply notation to set x in contextTwo, this time 52 | // using a temporary SetWord... 53 | 54 | SetWord {"x"}(20); 55 | 56 | // Require that to have worked... 57 | 58 | CHECK(runtime("x = 20")); 59 | 60 | // even though our default is to run in the second context 61 | // at the moment, let's override it using an additional parameter 62 | // to the constructor...and write a new value into contextOne...this 63 | // time using the `auto` keyword to put the type on the right hand side 64 | 65 | auto y = SetWord {"y", contextOne}; 66 | y(30); 67 | 68 | // If we apply on a context, it will effectively run a bind/copy on 69 | // the argument so that it picks up bindings in that context. Be aware 70 | // it makes a deep copy of the argument to do so. 71 | 72 | CHECK(contextOne("y = 30")); 73 | 74 | // Now switch active contexts 75 | 76 | contextNumber = 1; 77 | 78 | // We notice that an unparameterized call now finds y in contextOne 79 | 80 | CHECK(runtime("y = 30")); 81 | 82 | // Restore the context finder to the previous one (default) so other 83 | // tests will work correctly 84 | 85 | AnyContext::setFinder(oldFinder); 86 | } 87 | -------------------------------------------------------------------------------- /tests/form-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "rencpp/ren.hpp" 6 | 7 | using namespace ren; 8 | 9 | #include "catch.hpp" 10 | 11 | TEST_CASE("form test", "[rebol] [form]") 12 | { 13 | CHECK(to_string(AnyValue {10}) == "10"); 14 | CHECK(to_string(AnyValue {1.5}) == "1.5"); 15 | CHECK(to_string(AnyValue {true}) == "true"); 16 | 17 | // The only type willing to implicitly cast to a std::string is 18 | // ren::String; all others must be explicit with to_string which 19 | // performs equivalently to TO-STRING (new behavior, formerly FORM) 20 | 21 | std::string converted = String {"Hello World"}; 22 | CHECK(converted == "Hello World"); 23 | 24 | CHECK(String {"Hello World"}.isEqualTo("Hello World")); 25 | 26 | // one Unicode test is better than zero unicode tests :-) 27 | // Smiley face: http://www.fileformat.info/info/unicode/char/263a/index.htm 28 | 29 | CHECK(String {"\n\t\xE2\x98\xBA"}.isEqualTo("\n\t\xE2\x98\xBA")); 30 | CHECK(String {"^/^-^(9786)"}.isEqualTo("\n\t\xE2\x98\xBA")); 31 | } 32 | -------------------------------------------------------------------------------- /tests/function-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rencpp/ren.hpp" 5 | 6 | using namespace ren; 7 | 8 | #include "catch.hpp" 9 | 10 | class Adder { 11 | private: 12 | int amount; 13 | public: 14 | Adder(int amount) : amount (amount) {} 15 | 16 | Integer operator()(Integer const & value) { 17 | return value + amount; 18 | } 19 | }; 20 | 21 | 22 | TEST_CASE("function test", "[rebol] [function]") 23 | { 24 | auto addFive = Function::construct( 25 | " {Demonstration of using an operator() overloaded class}" 26 | " value [integer!]", 27 | 28 | // This won't work, operator() may (in the general case) be a template 29 | // http://stackoverflow.com/a/8670836/211160 30 | /* Adder {5} */ 31 | 32 | // This works, but makes you repeat the type signature. That's life. 33 | std::function {Adder {5}} 34 | ); 35 | 36 | // Here we actually are using the behavior being complained about here 37 | // affecting assignment, by just using the function inline without any 38 | // call to an APPLY: 39 | // 40 | // http://stackoverflow.com/questions/27641809/ 41 | // 42 | 43 | CHECK(static_cast(*runtime("10 +", addFive, 100)) == 115); 44 | } 45 | -------------------------------------------------------------------------------- /tests/isolated-test.cpp: -------------------------------------------------------------------------------- 1 | // This file is here for throwing in temporary one-off tests 2 | // that are not meant to be added permanently to the test suite. 3 | // 4 | // If there are any tests in this file with this tag, then 5 | // Travis will run it in isolation from the other tests. 6 | // 7 | // Put the test with the appropriate runtime to prevent it from 8 | // causing unnecessary failures in the runtime you're not testing 9 | 10 | #include "rencpp/ren.hpp" 11 | 12 | using namespace ren; 13 | 14 | #include "catch.hpp" 15 | 16 | TEST_CASE("isolated rebol test", "[isolated] [rebol]") 17 | { 18 | } 19 | 20 | 21 | TEST_CASE("isolated red test", "[isolated] [red]") 22 | { 23 | } 24 | -------------------------------------------------------------------------------- /tests/iterator-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "rencpp/ren.hpp" 5 | 6 | using namespace ren; 7 | 8 | #include "catch.hpp" 9 | 10 | TEST_CASE("iteration tests", "[rebol] [iterator]") 11 | { 12 | SECTION("block") 13 | { 14 | Block blk {"1 2 3"}; 15 | 16 | auto it = blk.begin(); 17 | CHECK(it->isEqualTo(1)); 18 | CHECK(it == blk.begin()); 19 | CHECK(it != blk.end()); 20 | 21 | it++; 22 | CHECK((*it).isEqualTo(2)); 23 | CHECK(it != blk.begin()); 24 | CHECK(it != blk.end()); 25 | 26 | it++; 27 | CHECK(it->isEqualTo(3)); 28 | CHECK(it != blk.begin()); 29 | CHECK(it != blk.end()); 30 | 31 | it++; 32 | CHECK(it != blk.begin()); 33 | CHECK(it == blk.end()); 34 | } 35 | 36 | 37 | SECTION("ascii string iteration") 38 | { 39 | const char * renCstr = "Hello^/There\nWorld^/"; 40 | const char * cppCstr = "Hello\nThere\nWorld\n"; 41 | 42 | std::string s; 43 | for (auto c : String {renCstr}) 44 | s.push_back(static_cast(c)); 45 | 46 | int index = 0; 47 | for (auto c : s) { 48 | CHECK(cppCstr[index] == c); 49 | index++; 50 | } 51 | } 52 | 53 | 54 | SECTION("unicode string iteration") 55 | { 56 | const char * utf8Cstr = u8"MetÆducation\n"; 57 | 58 | std::wstring ws; 59 | for (auto wc : String{utf8Cstr}) 60 | ws.push_back(static_cast(wc)); 61 | 62 | // TBD: REQUIRE correct result beyond "compiles, doesn't crash" 63 | } 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /tests/literals-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "rencpp/ren.hpp" 6 | 7 | using namespace ren; 8 | 9 | #include "catch.hpp" 10 | 11 | TEST_CASE("literals construction", "[rebol] [literals]") 12 | { 13 | SECTION("default") 14 | { 15 | AnyValue value; 16 | CHECK(hasType(value)); 17 | } 18 | 19 | SECTION("blank") 20 | { 21 | AnyValue value = blank; 22 | CHECK(hasType(value)); 23 | } 24 | 25 | SECTION("logic") 26 | { 27 | AnyValue value = false; 28 | CHECK(hasType(value)); 29 | 30 | // https://github.com/hostilefork/rencpp/issues/24 31 | // No way ATM to test the "shouldn't compile" cases 32 | 33 | auto logical = [](Logic const &) {}; 34 | 35 | logical(true); // should work 36 | // logical("hello"); // shouldn't compile (!) 37 | // logical(15); // shouldn't compile (!) 38 | 39 | Logic temp1 = true; 40 | // Logic temp2 = "hello"; // shouldn't compile (!) 41 | // Logic temp3 = 15; // shouldn't compile (!) 42 | 43 | logical(Logic {true}); // should work 44 | logical(Logic {"hello"}); // this compiles, as it's "explicit" 45 | logical(Logic {15}); // also okay, as it's "explicit" 46 | } 47 | 48 | SECTION("integer") 49 | { 50 | AnyValue value = 1; 51 | CHECK(hasType(value)); 52 | } 53 | 54 | 55 | SECTION("float construction") 56 | { 57 | AnyValue value = 10.20; 58 | CHECK(hasType(value)); 59 | } 60 | 61 | 62 | SECTION("string construction") 63 | { 64 | String value {"Hello"}; 65 | CHECK(value.length() == 5); 66 | } 67 | 68 | 69 | SECTION("string construction error") 70 | { 71 | CHECK_THROWS_AS( 72 | runtime("{Hello"), 73 | load_error 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rencpp/ren.hpp" 3 | 4 | #define CATCH_CONFIG_RUNNER 5 | #include "catch.hpp" 6 | 7 | int main(int argc, char * argv[]) // char* const conflicts w/Rebol 8 | { 9 | Catch::Session session; // There must be exactly once instance 10 | 11 | // writing to session.configData() here sets defaults 12 | // this is the preferred way to set them 13 | 14 | int returnCode = session.applyCommandLine(argc, argv); 15 | if (returnCode != 0) // Indicates a command line error 16 | return returnCode; 17 | 18 | // Writing to session.configData() or session.Config() here 19 | // overrides command line args. 20 | // Only do this if you know you need to. 21 | 22 | return session.run(); 23 | } 24 | -------------------------------------------------------------------------------- /tests/rebol-test.cpp: -------------------------------------------------------------------------------- 1 | // We only do this if we've built for Rebol 2 | 3 | #include "rencpp/rebol.hpp" 4 | 5 | using namespace rebol; 6 | 7 | #include "catch.hpp" 8 | 9 | TEST_CASE("rebol test", "[rebol]") 10 | { 11 | runtime.doMagicOnlyRebolCanDo(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/red-test.cpp: -------------------------------------------------------------------------------- 1 | // We only do this if we've built for Red 2 | 3 | #include "rencpp/red.hpp" 4 | 5 | using namespace red; 6 | 7 | #include "catch.hpp" 8 | 9 | TEST_CASE("red test", "[red]") 10 | { 11 | runtime.doMagicOnlyRedCanDo(); 12 | } 13 | --------------------------------------------------------------------------------