The Copper interpreted programming language / embeddable interpreter / virtual machine
This repository contains an interpreter that runs code of the Copper language - a statically-typed, simple syntax programming language. For more information, see the blog or the online documentation.
The language is described in Copper/docs/CopperLangGuide.html and on the Copper blog.
Copper is a statically-typed, simple syntax language revolving around the concept of function-objects. All variables are instantiated upon declaration and can contain only functions, but functions can return any object. There is no nil/null/void, so the default return from a function is an empty function. The language uses if/elif/else and loop structures.
Quick example:
fibonacci = {
ret([a=0 b=1]{
this.peek = +(this.a: this.b:)
this.a = this.b
this.b = this.peek
ret(this.peek:)
})
}
fibber = fibonacci()
loop {
a = fibber()
if ( gte(a: 100) ) {
stop
}
if ( equal(a: 13) ) {
skip
} elif ( equal(a:, 21) ) {
print("Still young!")
}
print("Value = ", a: "\n")
}
This is a tiny, light-weight, cross-platform interpreter/engine (roughly 300kb) providing only certain basic features essential to the language. The bare-bones engine includes functions for boolean comparisons and some for numbers and strings. An extension has been provided for useful numeric functions. Many features have been deliberately excluded from the engine to keep it small and also to allow users to have more control over the permitted flow of data. For example, basic strings are merely byte strands, but the user may create a separate object type to enforce Unicode.
The engine does not use a garbage collector. The language doesn't require one.
There is only one system dependency in the core code: climits. Other includes are primarily for printing and debugging but aren't necessary.
The code-base is written in cross-platform, standard-agnostic C++ and has been compiled successfully for C++98 and C++11 with GCC.
There are two layers of folders: The inner folder "Copper" contains the basic interpreter/engine itself (with basic printer and logger extensions). The outer folder includes this and the "ext" folder where you can store your extensions.
There are Premake files provided for making project files for the basic console application and debugging. You don't need them, but they may be helpful. A premake file is provided in the Copper folder. From Premake files, you can create a g++ make file or a Visual Studio file (2015 at highest; see the Premake documentation for other supported versions). The one inside the "Copper" folder can create make and VS project files for the basic interpreter. The one in the outer folder can create make and VS project files for the interpreter with all of the extensions provided in the "ext" folder. You may add your files without modifying the Premake file, but you must rerun the Premake file if you want to create the project files (make or VS) with these new files added (that is, if you don't want to manually add them yourself).
The Premake files are not necessary for the project as it is simply enough to include the necessary files manually.
The following files must be included for the basic interpreter (without the printer):
These files themselves have no dependencies, as opposed to the Printer and InStreamLogger files. In addition, there is a file called EngMsgToStr.h that contains useful string translations of the messages from the interpreter (and it is used in InStreamLogger.h).
The basic console application could be build with sh using a build.sh file containing:
g++ ./console.cpp src/Copper.h src/Copper.cpp src/utilList.h src/utilPipe.h src/Strings.h src/RRHash.h src/Strings.cpp stdlib/Printer.h stdlib/InStreamLogger.h -Wall -Wfatal-errors -O3 -o bin/Copper.exe
For debugging on ARM, you may need to add -funwind-tables to your gcc compiler flags.
Extensions for Copper are provided in the ext folder. These can be include in your main.cpp file along with Copper, and their functionality can be added to the engine by calling their respective addFunctionsToEngine(Engine&) functions.
The numbers extension provided ("basicmath.h") can optionally be compiled using the flag #define ENABLE_COPPER_INTEGER_BOUNDS_CHECKS for ensuring numbers don't exceed their cap and produce undefined behavior. Compiling without this flag creates a warning that the logger in the function is not used. This is not an error nor does it affect functionality, but if you are picky about having clean builds, you should use the aforementioned flag.
Optionally, you may define the compiler flag COMPILE_COPPER_FOR_C_PLUS_PLUS_11. All this does is change a few lines of code to use null_ptr and some functions to be deleted rather than set to empty functions. This should not effect functionality, and it is not C++11-specific. If you define this flag, you should use null_ptr for setters. If you do not define this flag, you should use the number 0 for setting pointers instead.
This project has been compiled with -Wall and -Wfatal-errors on GCC. A warning for an unused variable ("index") in the FFIServices has been silenced with a macro.
You need to include the core interpreter files, as given in section Project Structure and Building.
All interaction with the interpreter occurs through the small Cu::Engine API. For running a command-line-based version, the setup process is as follows:
For example:
Cu::Engine engine;
Cu::InStreamLogger insl;
engine.setLogger(&insl);
Cu::Printer printer;
engine.addForeignFunction(util::String("print"), &printer);
while( engine.run(insl) == EngineResult::Ok );
New functionality can be added through objects inheriting the class Cu::ForeignFunc, which are then passed to the engine via Engine::addForeignFunction(). This class contains a single method - "call()" - that is to return true if the "result" parameter is set.
The method "call()" is called whenever the name associated with the function (the string passed to Engine::addForeignFunction()) is called in Copper.
A number of helper classes have been provided for adding functions and class methods directly (so that there is no need to implement Cu::ForeignFunc).
The Copper Virtual Machine as given in the files included are copyright 2016-2020 Nicolaus Anderson. License details are provided in license.txt.