Annotation
The branch of PicoScheme but modifyed for ESP32 MCU and idf toolschain. It was sucessfully used for The haptic feedback rifle for Oculus Quest with only 4MB version of ESP32 microcontroller.
A small, embeddable scheme interpreter in c++17. This project started as a test bed to evaluate the new std::variant class template as plain c-union replacement and to test shared, reference counting pointers for basic memory menagement. Considering that there are already very feature complete, small and efficient scheme implementations and to keep the implementation effort reasonable, at least most scheme functions from the old R4RS scheme specification are implemented. Postponed for now is a complete numeric tower, including rational numbers and arbitrary precision arithmetic, and more advanced features like call/cc and define-syntax macros, which I haven't fully understood so far. However integer, floating point and complex numbers, call/cc as simple escape procedure and old school lisp-style macros are implemented.
While a more formal approach, on how to implement a dynamically typed, functional language interpreter would definitely be advisable, I followed here the ad hoc learning by doing approach and read the sources of some publicly available scheme implementations first. Therefore I would like to thank the following authors and maintainers for releasing their work:
- minischeme really miniature scheme by Atsushi Moriwaki and Cat's Eye Technologies.
- TinyScheme by Dimitrios Souflis, Kevin Cozens, Jonathan S. Shapiro and many others.
- Jscheme very readable Java implementation by Peter Norvig.
- LispMe interesting SECD vm scheme by Fred Bayer with source code.
- Chibi-Scheme very feature complete R7RS scheme by Alex Shinn.
The source code successfully compiles with a fairly recent GNU g++ compiler and under MS-Windows with the Visual Studio Community Edition v15 c++ compiler or the MSYS2 GNU build tools.
Please make sure that either Visual Studio with Desktop development with C++ or the command-line Build Tools for Visual Studio is successfully installed. Additionally please install the Kitware CMake build generator software.
- Open a developer-console for Visual Studio 2017 and change into your prefered
working directory, for example
cd %home%
- Download the PicoScheme sources and change into the unpacked source directory
with
cd PicoScheme
- or optional in case Git version control is installed,
directly clone the PicoScheme github repository with
git clone https://github.com/arichel/PicoScheme.git
and change into the new PicoScheme source directoy. - Create a new build sub directory with
mkdir build
an type:cmake -H. -Bbuild
to generate a Visual Studio project file. - Change into the build directory and type
msbuild PicoScheme.sln
to build the static librarypicoscheme.lib
and test programpicoscm.exe
- Optionally copy a scheme resource file from
cp ../test/picoscmrc.scm Debug/.
into theDebug
orRelease
subdirectory. - Change into the
Debug
subdirectory and typepicoscm.exe
to start an interactive scheme read-eval-print loop (repl). Type(exit)
orCtrl+c
to quit the interpreter.
Please make sure that the MSYS2 software building platform is installed and a functional MSYS2 console is available.
- Open the msys2 console and optional update the package database with
pacman -Syu
then close the console, reopen and run againpacman -Su
- If you haven't already, please add the following packages:
Kitware CMake with
pacman -S cmake
- Optional if you like to clone the PicoScheme repository instead of
just downloading the sources, additionally install the Git version control
package with
pacman -S git
.
Now we are ready to clone or download and compile the PicoScheme sources:
- Change into your prefered installation directory and either unpack
the downloaded PicoScheme.zip folder or if Git is installed, clone with:
git clone https://github.com/arichel/PicoScheme.git
- Change into PicoScheme source directory and create a new build directory
mkdir build
- Generate a unix makefile in this build directory with:
cmake -H. -Bbuild
- Change into the
build
sub directory and typemake all
to build a staticlibpicoscm.a
library and apicoscm.exe
test program. - Optional copy a scheme startup file
cp ../test/picoscmrc.scm
into the build directory and type./picoscm.exe
to start the interpreter. Type(exit)
or Ctrl+d to quit the interpreter.
todo...
A scheme cell is derived from a std::variant type, as declared in types.hpp, as a union of all supported types:
using Cons = std::tuple</*car*/Cell, /*cdr*/Cell, /*gc-mark*/bool>;
using None = std::monostate;
using Nil = std::nullptr_t;
using Bool = bool;
using Char = wchar_t;
using StringPtr = std::shared_ptr<std::basic_string<Char>>;
using VectorPtr = std::shared_ptr<std::vector<Cell>>;
// ...
using Variant = std::variant <
/* Atom types: */
None, Nil, Intern, Bool, Char, Number,
/* Compound value types: */
Symbol, Procedure,
/* Pointer types: */
Cons*, StringPtr, VectorPtr, PortPtr, FunctionPtr, SymenvPtr
>;
struct Cell : Variant {
using base_type = Variant;
using Variant::Variant;
};
Derivation of structure Cell from Variant is necessary to forward declare a scheme Cons-cell as a std::tuple of two Cell types itself. Atomic types, like booleans, characters, numbers and symbols are directly stored as value types and compound types like strings, vectors or IO-ports are stored as shared pointers. Symbols and procedures (closures) are stored as a small handle class with internal pointer to the implementation class. This assures that the byte size of a scheme cell remains reasonable small. Currently, the largest value type are numbers with 16 bytes for a complex number consisting of two double floating point values and additional eight bytes for the variant type internals itself plus alignment padding. A scheme Cell structure has therefore a size of 32 bytes in total (16 bytes + 8 bytes variant internals + 8 padding bytes). Scheme Cons-cells are stored as plain c-pointers into the global cell store, which is basically a std::list container of Cons-cell pairs.
todo...
Drop it to the components folder of your ESP32 project, then enable exceptions with menuconfig. Also enable C++17 support for your project (see below)
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32)
The next samplecode shows how to initialize the scheme
pscm::Scheme scm;
pscm::Parser parser(scm);
scm.gc.logging(true);
using pscm::Intern, pscm::Cell, pscm::str, pscm::nil;
scm.function("greet", [cntr = 0](Scheme& scm, const SymenvPtr&, const std::vector<Cell>&) mutable -> Cell {
return scm.list(pscm::str("hello world"), pscm::num(cntr++));
});
The next samplecode shows how evaluate the cstring*
extern "C" void scheme_run(pscm::Scheme& scm, pscm::Parser& parser, char* cstr, int* res)
{
std::string str(cstr);
try {
pscm::SymenvPtr env = scm.getenv();
ISTRINGSTREAM stream(str);
Cell expr = parser.read(stream);
COUT << expr << endl;
Cell proc = scm.eval(env, expr);
COUT << proc << endl;
return;
} catch (std::bad_variant_access& e) {
cout << e.what() << endl;
} catch (std::invalid_argument& e) {
cout << e.what() << endl;
} catch (std::exception& e) {
cout << e.what() << endl;
}
}
Copyright (c) 2018 Arichel
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.