├── .clang-format ├── .gitattributes ├── .github ├── actions │ ├── macos-ffi-leech │ │ └── action.yml │ ├── macos-ffi │ │ └── action.yml │ ├── macos-fmt-leech │ │ └── action.yml │ ├── macos-fmt │ │ └── action.yml │ ├── macos-icu-leech │ │ └── action.yml │ ├── macos-icu │ │ └── action.yml │ ├── macos-lightning-leech │ │ └── action.yml │ ├── macos-lightning │ │ └── action.yml │ ├── macos-prepare │ │ └── action.yml │ ├── macos-tar │ │ └── action.yml │ ├── ubuntu-lightning │ │ └── action.yml │ ├── ubuntu-prepare │ │ └── action.yml │ ├── ubuntu-tar │ │ └── action.yml │ └── windows-prepare │ │ └── action.yml └── workflows │ ├── macos-arm64-leech.yml │ ├── macos-arm64.yml │ ├── main.yml │ └── ubuntu-x86_64.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.foss ├── CMakeLists.static ├── CMakeLists.txt ├── CMakeLists.vendor ├── LICENSE.md ├── NOTES.md ├── PITFALLS.md ├── PREBUILT.md ├── README.md ├── VERSION.md ├── bugs ├── bug0-fixed.eg ├── bug1-fixed.eg ├── bug2-fixed.eg ├── bug3-fixed.eg ├── bug4-fixed.eg ├── bug5-fixed.eg └── bug6-fixed.eg ├── contrib ├── assets │ ├── Makefile │ ├── README.md │ ├── egel-black-transparent-256x256.png │ ├── egel-black-transparent-48x48.png │ ├── egel-black-transparent.svg │ ├── egel-black-white-256x256.png │ ├── egel-black-white-48x48.png │ ├── egel-black-white.svg │ ├── egel-logo.svg │ ├── egel-white-black-256x256.png │ ├── egel-white-black-48x48.png │ ├── egel-white-black.svg │ ├── egel-white-transparent-256x256.png │ ├── egel-white-transparent-48x48.png │ └── egel-white-transparent.svg ├── cmake │ └── modules │ │ ├── FindFmt.cmake │ │ └── FindICU.cmake ├── ffi │ ├── README.md │ └── src │ │ ├── Makefile │ │ ├── ffi.hpp │ │ └── test.cpp ├── scripts │ ├── tarpackage.sh │ └── version.sh └── vim │ ├── ftdetect │ └── egel.vim │ └── syntax │ └── egel.vim ├── examples ├── 100doors.eg ├── 3or5.eg ├── 99bottles.eg ├── abject.eg ├── ackermann.eg ├── assembler.eg ├── async.eg ├── bintrees.eg ├── booleans.eg ├── captcha-a.eg ├── captcha-b.eg ├── colist.eg ├── command.sh ├── concurrent.eg ├── cps.eg ├── cycle.eg ├── dispatch.eg ├── dup.eg ├── effect.eg ├── empty.eg ├── environment.eg ├── exception.eg ├── featureA.eg ├── featureB.eg ├── featureC.eg ├── featureD.eg ├── featureE.eg ├── featureF.eg ├── featureG.eg ├── featureH.eg ├── fib.eg ├── fib.egg ├── fib_memo.eg ├── fix.eg ├── fizzbuzz.eg ├── format.eg ├── frp.eg ├── genfib.eg ├── hailstone.eg ├── hanoi.eg ├── harper.eg ├── helloworld.eg ├── hexdump.eg ├── ieee754.eg ├── knuthshuffle.eg ├── lambda2sk.eg ├── lambool.eg ├── lazybinop.eg ├── life.eg ├── mandelbrot.eg ├── million.eg ├── monad.eg ├── multistring.eg ├── nqueens.eg ├── par.eg ├── parser.eg ├── proc.eg ├── ref.eg ├── reversalgame.eg ├── reverse_string.eg ├── serialize.eg ├── shebang.eg ├── sieve.eg ├── sieveK.eg ├── slides.eg ├── sort.eg ├── stall.eg ├── stategame.eg ├── stress.eg ├── time.eg ├── tinydb.eg ├── wart.eg └── yes.eg ├── include ├── calculate.eg ├── generator.eg ├── map.eg ├── prelude.eg └── search.eg ├── lib ├── pqueue │ └── pqueue.cpp └── random │ └── random.cpp ├── man ├── README.md ├── egel.1 ├── egel.1.html └── egel.1.md ├── optional ├── README.md ├── erpc │ ├── CMakeLists.txt │ ├── README.md │ ├── examples │ │ ├── client0.eg │ │ ├── client1.eg │ │ ├── client2.eg │ │ ├── client3.eg │ │ ├── client4.eg │ │ ├── client_import.eg │ │ ├── client_large.eg │ │ └── server.eg │ ├── lib │ │ ├── CMakeLists.txt │ │ └── egel_rpc.cpp │ └── proto │ │ ├── CMakeLists.txt │ │ └── egel.proto └── python │ ├── CMakeLists.txt │ ├── README.md │ ├── examples │ ├── ack.py │ ├── fib.py │ ├── test0.eg │ └── wxhello.eg │ └── src │ ├── python.cpp │ └── utils.hpp ├── src ├── ast.hpp ├── builtin_async.hpp ├── builtin_dict.hpp ├── builtin_eval.hpp ├── builtin_ffi.hpp ├── builtin_fs.hpp ├── builtin_math.hpp ├── builtin_os.hpp ├── builtin_process.hpp ├── builtin_regex.hpp ├── builtin_runtime.hpp ├── builtin_string.hpp ├── builtin_system.hpp ├── builtin_time.hpp ├── bytecode.hpp ├── constants.hpp ├── desugar.hpp ├── egel.cpp ├── emit.hpp ├── environment.hpp ├── error.hpp ├── eval.hpp ├── lexical.hpp ├── lift.hpp ├── lightning.hpp ├── machine.hpp ├── modules.hpp ├── operators.hpp ├── position.hpp ├── reader.hpp ├── runtime.hpp ├── semantical.hpp ├── serialize.hpp ├── syntactical.hpp └── transform.hpp ├── tests ├── concat.eg ├── ffi │ ├── test.c │ ├── test.eg │ └── test.sh ├── hello.eg ├── huge.eg ├── large.py ├── leak.eg ├── maplist.eg ├── maptest.eg ├── million.eg ├── tailfac.eg └── unicode.eg └── vendor ├── README.md └── makeall.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.eg linguist-language=Python 2 | *.egg linguist-language=Markdown 3 | -------------------------------------------------------------------------------- /.github/actions/macos-ffi-leech/action.yml: -------------------------------------------------------------------------------- 1 | name: FFI leech 2 | 3 | description: steal FFI from brew 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout FFI 10 | run: | 11 | echo "WARNING: stealing libffi from brew!" 12 | echo "This is not recommended, despite brew," 13 | echo "MacOS is _not_ a Linux and build from" 14 | echo "sources is preferred." 15 | echo "However, libffi fails to compile on the" 16 | echo "MacOS runner." 17 | brew install libffi 18 | shell: bash 19 | 20 | - name: Install FFI 21 | run: | 22 | mkdir -p ${{github.workspace}}/vendor/local 23 | mkdir -p ${{github.workspace}}/vendor/local/include 24 | mkdir -p ${{github.workspace}}/vendor/local/lib 25 | echo "copying into vendor/local" 26 | cp -r /opt/homebrew/opt/libffi/include/* ${{github.workspace}}/vendor/local/include 27 | cp -r /opt/homebrew/opt/libffi/lib/* ${{github.workspace}}/vendor/local/lib 28 | echo "vendor/local/include" 29 | ls -la ${{github.workspace}}/vendor/local/include 30 | echo "vendor/local/lib" 31 | ls -la ${{github.workspace}}/vendor/local/lib 32 | shell: bash 33 | -------------------------------------------------------------------------------- /.github/actions/macos-ffi/action.yml: -------------------------------------------------------------------------------- 1 | name: FFI 2 | 3 | description: Build and Install FFI 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout FFI source 10 | run: | 11 | git submodule update --init --recursive ./vendor/libffi 12 | pushd vendor/libffi 13 | git checkout v3.4.2 14 | popd 15 | shell: bash 16 | 17 | - name: Configure FFI 18 | run: | 19 | export CFLAGS="-fPIC" 20 | export CXXFLAGS="-fPIC" 21 | export PATH="/opt/homebrew/opt/libtool/libexec/gnubin:$PATH" 22 | export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH" 23 | pushd vendor/libffi 24 | ls -la 25 | ls -la /opt/homebrew/opt/libtool/libexec/gnubin 26 | echo "path: " $PATH 27 | #./autogen.sh 28 | autoreconf -v -i 29 | ./configure 30 | popd 31 | shell: bash 32 | 33 | - name: Compile FFI 34 | run: | 35 | export CFLAGS="-fPIC" 36 | export CXXFLAGS="-fPIC" 37 | export PATH="/opt/homebrew/opt/libtool/libexec/gnubin:$PATH" 38 | export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH" 39 | pushd vendor/libffi 40 | make 41 | popd 42 | shell: bash 43 | 44 | - name: Install FFI 45 | #if: always() 46 | run: | 47 | export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH" 48 | pushd vendor/libffi 49 | make install DESTDIR="../local" 50 | popd 51 | shell: bash 52 | -------------------------------------------------------------------------------- /.github/actions/macos-fmt-leech/action.yml: -------------------------------------------------------------------------------- 1 | name: FMT leech 2 | 3 | description: steal FMT from brew 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout FMT 10 | run: | 11 | echo "WARNING: stealing FMT from brew!" 12 | echo "This is not recommended, despite brew," 13 | echo "MacOS is _not_ a Linux and build from" 14 | echo "sources is preferred." 15 | echo "However, libffi fails to compile on the" 16 | echo "MacOS runner." 17 | brew install fmt 18 | shell: bash 19 | 20 | - name: Install FMT 21 | run: | 22 | mkdir -p ${{github.workspace}}/vendor/local 23 | mkdir -p ${{github.workspace}}/vendor/local/include 24 | mkdir -p ${{github.workspace}}/vendor/local/lib 25 | echo "copying into vendor/local" 26 | cp -r /opt/homebrew/opt/fmt/include/* ${{github.workspace}}/vendor/local/include 27 | cp -r /opt/homebrew/opt/fmt/lib/* ${{github.workspace}}/vendor/local/lib 28 | echo "vendor/local/include" 29 | ls -la ${{github.workspace}}/vendor/local/include 30 | echo "vendor/local/lib" 31 | ls -la ${{github.workspace}}/vendor/local/lib 32 | shell: bash 33 | -------------------------------------------------------------------------------- /.github/actions/macos-fmt/action.yml: -------------------------------------------------------------------------------- 1 | name: FMT 2 | 3 | description: Build and Install FMT 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout FMT source 10 | run: | 11 | git submodule update --init --recursive ./vendor/fmt 12 | shell: bash 13 | 14 | - name: Configure FMT 15 | run: | 16 | export CFLAGS="-fPIC" 17 | export CXXFLAGS="-fPIC" 18 | pushd vendor/fmt 19 | ls -la 20 | mkdir build 21 | cd build 22 | cmake -DCMAKE_INSTALL_PREFIX="../../local" .. 23 | popd 24 | shell: bash 25 | 26 | - name: Compile FMT 27 | run: | 28 | export CFLAGS="-fPIC" 29 | export CXXFLAGS="-fPIC" 30 | pushd vendor/fmt/build 31 | make 32 | popd 33 | shell: bash 34 | 35 | - name: Install FMT 36 | run: | 37 | pushd vendor/fmt/build 38 | make install 39 | popd 40 | shell: bash 41 | -------------------------------------------------------------------------------- /.github/actions/macos-icu-leech/action.yml: -------------------------------------------------------------------------------- 1 | name: ICU4C leech 2 | 3 | description: steal ICU4C from brew 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout ICU4C 10 | run: | 11 | echo "WARNING: stealing ICU4C from brew!" 12 | echo "This is not recommended, despite brew," 13 | echo "MacOS is _not_ a Linux and build from" 14 | echo "sources is preferred." 15 | echo "However, libffi fails to compile on the" 16 | echo "MacOS runner." 17 | brew install icu4c 18 | shell: bash 19 | 20 | - name: Install ICU4C 21 | run: | 22 | mkdir -p ${{github.workspace}}/vendor/local 23 | mkdir -p ${{github.workspace}}/vendor/local/include 24 | mkdir -p ${{github.workspace}}/vendor/local/lib 25 | echo "copying into vendor/local" 26 | cp -r /opt/homebrew/opt/icu4c/include/* ${{github.workspace}}/vendor/local/include 27 | cp -r /opt/homebrew/opt/icu4c/lib/* ${{github.workspace}}/vendor/local/lib 28 | echo "vendor/local/include" 29 | ls -la ${{github.workspace}}/vendor/local/include 30 | echo "vendor/local/lib" 31 | ls -la ${{github.workspace}}/vendor/local/lib 32 | shell: bash 33 | -------------------------------------------------------------------------------- /.github/actions/macos-icu/action.yml: -------------------------------------------------------------------------------- 1 | name: ICU4C 2 | 3 | description: Build and Install ICU4C 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout ICU source 10 | run: | 11 | git submodule update --init --recursive ./vendor/icu 12 | shell: bash 13 | 14 | - name: Configure ICU 15 | run: | 16 | export CFLAGS="-fPIC" 17 | export CXXFLAGS="-fPIC" 18 | pushd vendor/icu/icu4c/source 19 | ls -la 20 | chmod +x runConfigureICU configure install-sh 21 | ./runConfigureICU MacOSX --enable-static --prefix=$(realpath "../../../local") 22 | popd 23 | shell: bash 24 | 25 | - name: Compile ICU 26 | run: | 27 | export CFLAGS="-fPIC" 28 | export CXXFLAGS="-fPIC" 29 | pushd vendor/icu/icu4c/source 30 | export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH" 31 | make 32 | popd 33 | shell: bash 34 | 35 | - name: Install ICU 36 | run: | 37 | pushd vendor/icu/icu4c/source 38 | export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH" 39 | make install PREFIX="../../../local" 40 | popd 41 | shell: bash 42 | -------------------------------------------------------------------------------- /.github/actions/macos-lightning-leech/action.yml: -------------------------------------------------------------------------------- 1 | name: Lightning leech 2 | 3 | description: steal Lightning from brew 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout Lightning 10 | run: | 11 | echo "WARNING: stealing lightning from brew!" 12 | echo "This is not recommended, despite brew," 13 | echo "MacOS is _not_ a Linux and build from" 14 | echo "sources is preferred." 15 | echo "However, libffi fails to compile on the" 16 | echo "MacOS runner." 17 | brew install lightning 18 | shell: bash 19 | 20 | - name: Install Lightning 21 | run: | 22 | mkdir -p ${{github.workspace}}/vendor/local 23 | mkdir -p ${{github.workspace}}/vendor/local/include 24 | mkdir -p ${{github.workspace}}/vendor/local/lib 25 | echo "copying into vendor/local" 26 | cp -r /opt/homebrew/opt/lightning/include/* ${{github.workspace}}/vendor/local/include 27 | cp -r /opt/homebrew/opt/lightning/lib/* ${{github.workspace}}/vendor/local/lib 28 | echo "vendor/local/include" 29 | ls -la ${{github.workspace}}/vendor/local/include 30 | echo "vendor/local/lib" 31 | ls -la ${{github.workspace}}/vendor/local/lib 32 | shell: bash 33 | -------------------------------------------------------------------------------- /.github/actions/macos-lightning/action.yml: -------------------------------------------------------------------------------- 1 | name: GNU Lightning 2 | 3 | description: Build and Install GNU Lightning 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout GNU Lightning source 10 | run: | 11 | git submodule update --init --recursive ./vendor/lightning 12 | shell: bash 13 | 14 | - name: Configure GNU Lightning 15 | run: | 16 | export CFLAGS="-fPIC" 17 | export CXXFLAGS="-fPIC" 18 | pushd vendor/lightning 19 | ls -la 20 | ./bootstrap 21 | ./configure --prefix=$(realpath "../local") 22 | popd 23 | shell: bash 24 | 25 | - name: Compile GNU Lightning 26 | run: | 27 | export CFLAGS="-fPIC" 28 | export CXXFLAGS="-fPIC" 29 | pushd vendor/lightning 30 | make 31 | popd 32 | shell: bash 33 | 34 | - name: Install GNU Lightning 35 | run: | 36 | pushd vendor/lightning 37 | make install 38 | popd 39 | shell: bash 40 | -------------------------------------------------------------------------------- /.github/actions/macos-prepare/action.yml: -------------------------------------------------------------------------------- 1 | name: Prepare MacOS 2 | 3 | description: Prepare MacOS 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Install dependencies 10 | run: | 11 | brew update 12 | brew install gcc make autoconf automake libtool texinfo 13 | shell: bash 14 | 15 | - name: Report 16 | run: | 17 | echo "MacOS brew prepared" 18 | shell: bash 19 | -------------------------------------------------------------------------------- /.github/actions/macos-tar/action.yml: -------------------------------------------------------------------------------- 1 | name: MacOS Tar Artefact 2 | 3 | description: Create an MacOS Tar Artefact 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Determine tar package name 10 | id: tarname 11 | run: | 12 | egelversion=`contrib/scripts/version.sh src/egel.cpp` 13 | echo "TARNAME=egel-macos-$egelversion" >> $GITHUB_OUTPUT 14 | shell: bash 15 | 16 | - name: Set library paths 17 | run: | 18 | install_name_tool -change /opt/homebrew/opt/libffi/lib/libffi.8.dylib @executable_path/libffi.8.dylib ${{github.workspace}}/build/egel 19 | install_name_tool -change /opt/homebrew/opt/lightning/lib/liblightning.2.dylib @executable_path/liblightning.2.dylib ${{github.workspace}}/build/egel 20 | install_name_tool -change /opt/homebrew/opt/fmt/lib/libfmt.11.dylib @executable_path/libfmt.11.dylib ${{github.workspace}}/build/egel 21 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicuuc.74.dylib @executable_path/libicuuc.74.dylib ${{github.workspace}}/build/egel 22 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicudata.74.dylib @executable_path/libicudata.74.dylib ${{github.workspace}}/build/egel 23 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicuio.74.dylib @executable_path/libicuio.74.dylib ${{github.workspace}}/build/egel 24 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicui18n.74.dylib @executable_path/libicui18n.74.dylib ${{github.workspace}}/build/egel 25 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicutu.74.dylib @executable_path/libicutu.74.dylib ${{github.workspace}}/build/egel 26 | shell: bash 27 | 28 | - name: Create tar package 29 | run: | 30 | tarname=${{ steps.tarname.outputs.TARNAME }} 31 | echo "creating: $tarname" 32 | lib0=`find vendor/local/lib -name "liblightning.?.dylib" -print -quit` 33 | lib1=`find vendor/local/lib -name "libfmt.??.dylib" -print -quit` 34 | lib2=`find vendor/local/lib -name "libffi.?.dylib" -print -quit` 35 | lib3=`find vendor/local/lib -name "libicui18n.??.dylib" -print -quit` 36 | lib4=`find vendor/local/lib -name "libicuio.??.dylib" -print -quit` 37 | lib5=`find vendor/local/lib -name "libicutu.??.dylib" -print -quit` 38 | lib6=`find vendor/local/lib -name "libicudata.??.dylib" -print -quit` 39 | lib7=`find vendor/local/lib -name "libicuuc.??.dylib" -print -quit` 40 | contrib/scripts/tarpackage.sh $tarname . $lib0 $lib1 $lib2 $lib3 $lib4 $lib5 $lib6 $lib7 41 | shell: bash 42 | 43 | - name: List tar package 44 | run: | 45 | tarname=${{ steps.tarname.outputs.TARNAME }} 46 | echo "contents of created $tarname" 47 | tar -tf $tarname.tgz 48 | shell: bash 49 | 50 | - name: Upload artefact 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: ${{ steps.tarname.outputs.TARNAME }} 54 | path: ${{ steps.tarname.outputs.TARNAME }}.tgz 55 | 56 | -------------------------------------------------------------------------------- /.github/actions/ubuntu-lightning/action.yml: -------------------------------------------------------------------------------- 1 | name: GNU Lightning 2 | 3 | description: Build and Install GNU Lightning 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Checkout GNU Lightning source 10 | run: | 11 | git submodule update --init --recursive ./vendor/lightning 12 | shell: bash 13 | 14 | - name: Configure GNU Lightning 15 | run: | 16 | pushd vendor/lightning 17 | ls -la 18 | ./bootstrap 19 | ./configure 20 | popd 21 | shell: bash 22 | 23 | - name: Compile GNU Lightning 24 | run: | 25 | pushd vendor/lightning 26 | make 27 | popd 28 | shell: bash 29 | 30 | - name: Install GNU Lightning 31 | run: | 32 | pushd vendor/lightning 33 | sudo make install 34 | popd 35 | shell: bash 36 | -------------------------------------------------------------------------------- /.github/actions/ubuntu-prepare/action.yml: -------------------------------------------------------------------------------- 1 | name: Prepare Ubuntu 2 | 3 | description: Prepare Ubuntu 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Install dependencies 10 | run: | 11 | sudo apt-get update 12 | sudo apt-get install -y build-essential autoconf automake libtool texinfo 13 | shell: bash 14 | 15 | - name: Checkout code 16 | run: | 17 | sudo apt-get install -y libicu-dev 18 | sudo apt-get install -y libfmt-dev 19 | sudo apt-get install -y libffi-dev 20 | shell: bash 21 | 22 | - name: Report 23 | run: | 24 | echo "Ubuntu prepared" 25 | shell: bash 26 | -------------------------------------------------------------------------------- /.github/actions/ubuntu-tar/action.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu Tar Artefact 2 | 3 | description: Create an Ubuntu Tar Artefact 4 | 5 | runs: 6 | using: "composite" 7 | 8 | steps: 9 | - name: Determine tar package name 10 | id: tarname 11 | run: | 12 | egelversion=`contrib/scripts/version.sh src/egel.cpp` 13 | echo "TARNAME=egel-ubuntu-$egelversion" >> $GITHUB_OUTPUT 14 | shell: bash 15 | 16 | - name: Create tar package 17 | run: | 18 | tarname=${{ steps.tarname.outputs.TARNAME }} 19 | echo "creating: $tarname" 20 | lib0=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "liblightning.so.?" -print -quit` 21 | lib1=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "libfmt.so.?" -print -quit` 22 | lib2=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "libffi.so.?" -print -quit` 23 | lib3=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "libicui18n.so.??" -print -quit` 24 | lib4=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "libicuio.so.??" -print -quit` 25 | lib5=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "libicutu.so.??" -print -quit` 26 | lib6=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "libicudata.so.??" -print -quit` 27 | lib7=`find /usr/lib /usr/lib64/ /usr/local/lib /usr/local/lib64 -name "libicuuc.so.??" -print -quit` 28 | contrib/scripts/tarpackage.sh $tarname . $lib0 $lib1 $lib2 $lib3 $lib4 $lib5 $lib6 $lib7 29 | shell: bash 30 | 31 | - name: List tar package 32 | run: | 33 | tarname=${{ steps.tarname.outputs.TARNAME }} 34 | echo "contents of created $tarname" 35 | tar -tf $tarname.tgz 36 | shell: bash 37 | 38 | - name: Upload artefact 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: ${{ steps.tarname.outputs.TARNAME }} 42 | path: ${{ steps.tarname.outputs.TARNAME }}.tgz 43 | 44 | -------------------------------------------------------------------------------- /.github/actions/windows-prepare/action.yml: -------------------------------------------------------------------------------- 1 | name: Windows Prepare 2 | 3 | description: Window Prepare 4 | 5 | runs: 6 | using: "composite" 7 | steps: 8 | - name: Checkout code 9 | uses: actions/checkout@v3 10 | 11 | - name: Download Cygwin installer 12 | run: curl -LO https://cygwin.com/setup-x86_64.exe 13 | shell: pwsh 14 | 15 | - name: Install Cygwin and required packages 16 | run: | 17 | .\setup-x86_64.exe -q -P gcc-g++,make,cmake,git 18 | shell: pwsh 19 | 20 | - name: Add Cygwin to PATH 21 | run: | 22 | echo "C:\\cygwin64\\bin" >> $env:GITHUB_PATH 23 | shell: pwsh 24 | 25 | - name: Report install 26 | run: | 27 | bash -c "echo Cygwin Installed" 28 | shell: bash 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/macos-arm64-leech.yml: -------------------------------------------------------------------------------- 1 | name: Build MacOS by Leeching from Brew 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | 7 | env: 8 | BUILD_TYPE: Release 9 | 10 | jobs: 11 | build: 12 | runs-on: macos-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Print env 18 | run: | 19 | echo github.event.action: ${{ github.event.action }} 20 | echo github.event_name: ${{ github.event_name }} 21 | echo 22 | echo github.workspace: ${{ github.workspace }} 23 | echo env.BUILD_TYPE: ${{ env.BUILD_TYPE }} 24 | shell: bash 25 | 26 | - name: Fail 27 | run: | 28 | echo "Fail when testing other builds" 29 | #exit 1 30 | shell: bash 31 | 32 | - name: Prepare 33 | uses: ./.github/actions/macos-prepare 34 | 35 | - name: Make local dir 36 | run: | 37 | mkdir vendor/local 38 | shell: bash 39 | 40 | - name: Build FFI static lib 41 | uses: ./.github/actions/macos-ffi-leech 42 | 43 | - name: Build GNU Lightning static lib 44 | uses: ./.github/actions/macos-lightning-leech 45 | 46 | - name: Build FMT static lib 47 | uses: ./.github/actions/macos-fmt-leech 48 | 49 | - name: Build ICU static lib 50 | uses: ./.github/actions/macos-icu-leech 51 | 52 | - name: Link vendor cmake 53 | run: | 54 | rm -rf CMakeLists.txt 55 | ln -s CMakeLists.vendor CMakeLists.txt 56 | shell: bash 57 | 58 | - name: Configure CMake 59 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 60 | shell: bash 61 | 62 | - name: Build 63 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 64 | shell: bash 65 | 66 | - name: Test 67 | working-directory: ${{github.workspace}}/build 68 | run: ./egel -v 69 | shell: bash 70 | 71 | - name: Create tar 72 | uses: ./.github/actions/macos-tar 73 | -------------------------------------------------------------------------------- /.github/workflows/macos-arm64.yml: -------------------------------------------------------------------------------- 1 | name: Build MacOS 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | 7 | env: 8 | BUILD_TYPE: Release 9 | 10 | jobs: 11 | build: 12 | runs-on: macos-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Print env 18 | run: | 19 | echo github.event.action: ${{ github.event.action }} 20 | echo github.event_name: ${{ github.event_name }} 21 | echo 22 | echo github.workspace: ${{ github.workspace }} 23 | echo env.BUILD_TYPE: ${{ env.BUILD_TYPE }} 24 | shell: bash 25 | 26 | - name: Fail 27 | run: | 28 | echo "Fail when testing other builds" 29 | #exit 1 30 | shell: bash 31 | 32 | - name: Prepare 33 | uses: ./.github/actions/macos-prepare 34 | 35 | - name: Make local dir 36 | run: | 37 | mkdir vendor/local 38 | shell: bash 39 | 40 | - name: Build FFI static lib 41 | uses: ./.github/actions/macos-ffi 42 | 43 | - name: Build GNU Lightning static lib 44 | uses: ./.github/actions/macos-lightning 45 | 46 | - name: Build FMT static lib 47 | uses: ./.github/actions/macos-fmt 48 | 49 | - name: Build ICU static lib 50 | uses: ./.github/actions/macos-icu 51 | 52 | - name: Link vendor cmake 53 | run: | 54 | rm -rf CMakeLists.txt 55 | ln -s CMakeLists.vendor CMakeLists.txt 56 | shell: bash 57 | 58 | - name: Configure CMake 59 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 60 | shell: bash 61 | 62 | - name: Build 63 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 64 | shell: bash 65 | 66 | - name: Test 67 | working-directory: ${{github.workspace}}/build 68 | run: ./egel -v 69 | shell: bash 70 | 71 | - name: Create tar 72 | uses: ./.github/actions/macos-tar 73 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main Workflow 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-ubuntu-x86_64: 8 | uses: ./.github/workflows/ubuntu-x86_64.yml 9 | 10 | build-macos-arm64: 11 | uses: ./.github/workflows/macos-arm64-leech.yml 12 | 13 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu-x86_64.yml: -------------------------------------------------------------------------------- 1 | name: Build Ubuntu-x86_64 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | 7 | env: 8 | BUILD_TYPE: Release 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Print env 18 | run: | 19 | echo github.event.action: ${{ github.event.action }} 20 | echo github.event_name: ${{ github.event_name }} 21 | echo 22 | echo github.workspace: ${{ github.workspace }} 23 | echo env.BUILD_TYPE: ${{ env.BUILD_TYPE }} 24 | shell: bash 25 | 26 | - name: Fail 27 | run: | 28 | echo "Fail when testing other builds" 29 | #exit 1 30 | shell: bash 31 | 32 | - name: Checkout code 33 | uses: ./.github/actions/ubuntu-prepare 34 | 35 | - name: Install GNU lightning 36 | uses: ./.github/actions/ubuntu-lightning 37 | 38 | - name: Configure CMake 39 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 40 | shell: bash 41 | 42 | - name: Build 43 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 44 | 45 | - name: Test 46 | working-directory: ${{github.workspace}}/build 47 | run: ./egel -v 48 | shell: bash 49 | 50 | - name: Create Ubuntu artefact 51 | uses: ./.github/actions/ubuntu-tar 52 | 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled headers or modules 8 | *.gch 9 | *.pch 10 | *.ifc 11 | *.pcm.d 12 | *.pcm.ii 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Logs 29 | *.log 30 | *.trs 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | 37 | # VI swap files 38 | *.swp 39 | 40 | # Don't follow the main executable, archives, and dynlibs 41 | src/egel 42 | src/libegel.a 43 | *.ego 44 | 45 | # Don't follow generated test cases 46 | tests/large.eg 47 | 48 | # Don't follow cmake build/debug 49 | _build/* 50 | build/* 51 | _debug/* 52 | debug/* 53 | optional/*/build/* 54 | optional/*/_build/* 55 | optional/*/debug/* 56 | optional/*/_debug/* 57 | 58 | # ignore macos fs 59 | .DS_Store 60 | 61 | # ignore vscode 62 | #.vscode/* 63 | optional/*/.vscode/* 64 | 65 | # ignore pycache 66 | python/examples/__pycache__/* 67 | 68 | # c++20 provisional stuff 69 | src/gcm.cache/* 70 | 71 | +* 72 | 73 | *.o 74 | *.lo 75 | *.la 76 | 77 | .libs/ 78 | .deps/ 79 | */.libs/ 80 | */.deps/ 81 | 82 | vendor/local/* 83 | 84 | # libffi stuff 85 | vendor/libffi/* 86 | 87 | # GNU lightning stuff 88 | vendor/lightning-2.2.2/build-aux 89 | vendor/lightning-2.2.2/*/Makefile 90 | vendor/lightning-2.2.2/*/*/Makefile 91 | vendor/lightning-2.2.2/doc 92 | vendor/lightning-2.2.2/autom4te.cache 93 | vendor/lightning-2.2.2/aclocal.m4 94 | vendor/lightning-2.2.2/depcomp 95 | vendor/lightning-2.2.2/INSTALL 96 | vendor/lightning-2.2.2/Makefile 97 | vendor/lightning-2.2.2/Makefile.in 98 | vendor/lightning-2.2.2/config.guess 99 | vendor/lightning-2.2.2/config.h 100 | vendor/lightning-2.2.2/config.h.in 101 | vendor/lightning-2.2.2/config.log 102 | vendor/lightning-2.2.2/config.status 103 | vendor/lightning-2.2.2/config.sub 104 | vendor/lightning-2.2.2/configure 105 | vendor/lightning-2.2.2/install-sh 106 | vendor/lightning-2.2.2/libtool 107 | vendor/lightning-2.2.2/lightning-*.tar.* 108 | vendor/lightning-2.2.2/ltmain.sh 109 | vendor/lightning-2.2.2/missing 110 | vendor/lightning-2.2.2/size 111 | vendor/lightning-2.2.2/stamp-h1 112 | vendor/lightning-2.2.2/test-driver 113 | 114 | vendor/lightning-2.2.2/m4/libtool.m4 115 | vendor/lightning-2.2.2/m4/lt~obsolete.m4 116 | vendor/lightning-2.2.2/m4/ltoptions.m4 117 | vendor/lightning-2.2.2/m4/ltsugar.m4 118 | vendor/lightning-2.2.2/m4/ltversion.m4 119 | 120 | vendor/lightning-2.2.2/lightning.pc 121 | vendor/lightning-2.2.2/include/lightning.h 122 | 123 | vendor/lightning-2.2.2/build-aux/ 124 | 125 | vendor/lightning-2.2.2/check 126 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/icu"] 2 | path = vendor/icu 3 | url = https://github.com/unicode-org/icu.git 4 | [submodule "vendor/lightning"] 5 | path = vendor/lightning 6 | url = https://git.savannah.gnu.org/git/lightning.git 7 | branch = lightning-2.2.2 8 | [submodule "vendor/fmt"] 9 | path = vendor/fmt 10 | url = https://github.com/fmtlib/fmt.git 11 | [submodule "vendor/libffi"] 12 | path = vendor/libffi 13 | url = https://github.com/libffi/libffi.git 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMakeLists.foss -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Marco Devillers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 | OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | Notes about this interpreter 2 | ============================ 3 | 4 | This is a hobby project, not the next big thing. The experiment is to 5 | see how far one can push an eager combinator rewriting implementation in 6 | idiomatic C++. Everything about the interpreter is experimental, 7 | and the result will be a very, and I mean very, slow interpreter. 8 | 9 | The hope is that it will become a versatile tool for mathematics and 10 | small domain specific language development. 11 | 12 | My personal interest is mobile code. I hope to implement in such a way 13 | that code can be passed around in a data center such that complex 14 | calculations can be done with minimal traffic and start-up times. 15 | 16 | If you are looking for a language to add to some product, I recommend 17 | either a Lisp, Lua, or Python. 18 | 19 | ## More about the interpreter 20 | 21 | A number of design decision were made: 22 | 23 | * Egel is a term rewriter that evaluates by trampolining the root 24 | of a tree, or more general, a directed acyclic graph (DAG). 25 | A garbage collector isn't implemented, Egel piggybacks on C++ 26 | smart pointers and relies on that the runtime objects form 27 | a DAG at any point during evaluation. 28 | 29 | Egel is therefor a _very_ slow term rewriter, even slower 30 | than I initially expected. It's about an order of where 31 | I want it to be, and then still it will be very slow 32 | in comparison to most Lisps or Python. 33 | 34 | At some point I will be looking at a drop-in replacement 35 | for the most used smart pointer, the reference counted 36 | shared pointer, to get more performance. 37 | 38 | * When C++ modules came around I jumped on them, only to find 39 | out that clang and gcc support were severely lacking. 40 | As a stopgap solution Egel is distributed with mostly 41 | header-only sources. I fully intend to remove that 42 | stopgap when the three major compilers support modules. 43 | 44 | * With four dependencies, and every dependency supported by 45 | either an os dynamic library, a user-compiled dynamic 46 | library, or statically linked in, there are pow(3,4) = 81 47 | manners of building egel. 48 | 49 | On foss operating systems (Linux, BSD) the four dependencies 50 | are usually shipped by the package manager with the exception 51 | of Ubuntu/Debian that doesn't ship GNU Lightning. 52 | 53 | * On BSD the prefered manner of shipping egel is as a 54 | simple application that relies on the other four 55 | dependencies being installed. 56 | 57 | * On Linux that situation is similar with the exception 58 | of Ubuntu/Debian. I expect users to _compile and install_ 59 | GNU lightning prior to installing the egel interpreter 60 | 61 | * On Macos and Windows, the prefered manner of shipping 62 | is with the four dependencies as separate dynamic 63 | libraries compiled by the user. 64 | -------------------------------------------------------------------------------- /PITFALLS.md: -------------------------------------------------------------------------------- 1 | # Common Pifalls when Programming in Egel 2 | 3 | Egel is a minimalist language, reading the man page should suffice to 4 | start programming in Egel. 5 | 6 | However, I expect people to encounter the following common pitfalls. 7 | 8 | ## Wrong usage of patterns 9 | 10 | The following two patterns are very different. The first defines a 11 | function that takes two arguments, the latter defines a function that 12 | takes one argument, that one argument being the composite of two terms. 13 | 14 | ``` 15 | [X Y -> ... ] [(X Y) -> ... ] 16 | ``` 17 | 18 | ## Forget to open namespace in interactie mode 19 | 20 | In interactive mode the prelude is loaded and the System and List 21 | namespaces are opened. For the other namespace use: 22 | 23 | ``` 24 | >> using String;; using Math 25 | ``` 26 | 27 | ## No short-circuited boolean operators 28 | 29 | The `&&` and `||` combinators take functions as their second argument. 30 | Use in the following manner: 31 | 32 | ``` 33 | true || [_ -> false && [_ -> true]] 34 | ``` 35 | 36 | ## Passing variadic functions 37 | 38 | The following will not have the effect that one would assume it has. 39 | 40 | ``` 41 | map print {"hello", 3.14} 42 | ``` 43 | 44 | Print is a variadic function and Egel has eager semantics. Given that 45 | `print` is variadic it will print nothing and reduce to `none` after 46 | which that will be applied to the constants in the list. 47 | 48 | The following is safe, however. 49 | 50 | ``` 51 | map [X -> print X] {"hello", 3.14} 52 | ``` 53 | 54 | ## Math operators only work on floats 55 | 56 | Despite Egel being a dynamic language, the combinators defined in the 57 | Math namespace exclusively work on floats. Look in the prelude or cast 58 | with `to_float` and `to_int` combinators. 59 | 60 | ## Overlapping usings 61 | 62 | When two namespaces `A` and `B` both define `foo` and both namespaces are 63 | opened, `foo` will be redeclared and give a runtime error. Handle with 64 | care. 65 | 66 | ``` 67 | namespace A ( data foo ) namespace B ( data foo ) using A using B 68 | ``` 69 | -------------------------------------------------------------------------------- /PREBUILT.md: -------------------------------------------------------------------------------- 1 | # Prebuilt releases 2 | 3 | Prebuilt releases from Github are supplied for your convenience. 4 | 5 | * Ubuntu releases come with GNU Lightning, ICU, FMT, and FFI. 6 | 7 | Assuming bash as the shell and Egel installed in ~/.egel it is 8 | recommended to add the following two lines to .bashrc. 9 | 10 | ``` 11 | alias egel="LD_LIBRARY_PATH=~/.egel ~/.egel/egel" export 12 | EGEL_PATH=.:$HOME/.egel 13 | ``` 14 | 15 | 16 | * MacOS and Windows releases come with the four dependencies as 17 | dynamic libraries. 18 | 19 | MacOS runs with some elaborate security mechanism for trusted 20 | apps these days and the application and dynamic libraries are 21 | _not_ signed with a developer certificate. 22 | 23 | I was able to run the interpreter on MacOS by signing the 24 | the binaries and removing the quarantine attribute. 25 | 26 | For signing, make a local certificate and use codesign 27 | 28 | ``` 29 | codesign --force --deep --sign "Local Development" 30 | 31 | ``` 32 | 33 | To remove the quarantine attribute use xattr. 34 | 35 | ``` 36 | xattr -d com.apple.quarantine egel lib* 37 | ``` 38 | 39 | * A BSD release is planned for the future. 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Egel Language 2 | ================= 3 | 4 | Egel is an untyped concurrent functional scripting language based on eager 5 | combinator rewriting with a concise but remarkably powerful syntax. 6 | 7 | Installation 8 | ------------ 9 | 10 | This interpreter is being developed on Linux/MacOS/BSD and uses icu4c for 11 | Unicode support, fmt for formatting, ffi as a foreign function interface, 12 | and GNU lightning as a ahead-of-time backend. 13 | 14 | The interpreter can be compiled with a current C++ compiler and employs 15 | cmake to build. In general, you will need root access to build egel. 16 | 17 | There are roughly two manners in which operating systems and package 18 | managers provide C++ libraries. 19 | 20 | 1. The open source model (various Unixes and BSDs) where C++ libraries are 21 | compiled and dissimated by the operating system distributor. Use your 22 | package manager to install libicu, libffi, fmt, and GNU lightning. 23 | 24 | Warning: exceptionally, Ubuntu/Debian doesn't ship with GNU Lightning. 25 | You are supposed to _compile and install_ that package prior to 26 | compiling the interpreter. 27 | 28 | 2. The vendor based model (MacOS and Windows) where C++ libraries are 29 | usually not provided since they are brittle to link against, and where 30 | one usually compiles these libraries from scratch and statically links 31 | them in or distributes them with the application. 32 | Links to the vendors are provided as git submodules in the 33 | vendor directory and, you're on your own here, you'll need to download 34 | and compile those libraries. There's a separate README.md in the vendor 35 | directory that should help somewhat. 36 | 37 | CMake files are provided for both models, select the one you want to use 38 | and rename either to `CMakeLists.txt`. 39 | 40 | A static build cmake script based of the vendor model is also provided. 41 | 42 | After that the interpreter is made with `cmake` in the standard manner. Run 43 | the following commands on a Linux system. 44 | 45 | ``` 46 | mkdir build 47 | cd build 48 | cmake .. 49 | make 50 | ``` 51 | 52 | note: for older GCC you sometimes need to uncomment the 53 | `stdc++fs` rule. 54 | 55 | That should give you an interpreter named `egel` 56 | and a number of dynamically loadable Egel object files in the 57 | `build` directory. 58 | 59 | For a system-wide install run `make install` after a build 60 | as root. 61 | 62 | (MacOS dyld doesn't look in /usr/local/lib anymore, set the path.) 63 | 64 | On some systems the `EGEL_PATH` environment variable needs to be set. 65 | See the man page for further information on that. 66 | 67 | If you don't want to do that, please note that you only need the interpreter 68 | named `egel` and the prelude in the `include` directory for simple tasks. 69 | 70 | Cmake generated makefiles allow for a local install with the command: 71 | 72 | ``` 73 | make DESTDIR=~ install 74 | ``` 75 | 76 | In this case Egel components will be installed into `~/usr/local` directories 77 | and you might refer to those components by adding the following commands 78 | to your shell resource file, though the specific syntax may differ. 79 | 80 | ``` 81 | export PATH=~/usr/local/bin:$PATH 82 | export EGEL_PATH=.:~/usr/local/lib/egel 83 | ``` 84 | 85 | Usage 86 | ----- 87 | 88 | There's a [manual](https://egel-lang.github.io/egel.1.html) page you can 89 | consult, it should be installed, or read the following short 90 | [introduction](http://egel.readthedocs.io/) to the interpreter on 91 | the internet. 92 | 93 | For a list of builtin combinators look 94 | [here](https://github.com/egel-lang/egel-gen/blob/main/combs.md). 95 | 96 | Also 97 | ---- 98 | 99 | The interpreter doesn't provide command line editing, you might 100 | want to wrap it with the command `alias egel="rlwrap egel"`. 101 | 102 | The interpreter allocates lots of short-lived objects. If you want 103 | a bit of extra speed, it might pay off to switch the allocator. 104 | 105 | I use `jemalloc` on Linux by setting 106 | ``LD_PRELOAD=`jemalloc-config --libdir`/libjemalloc.so.`jemalloc-config --revision``. 107 | 108 | MacOS additional remarks 109 | ------------------------ 110 | 111 | Newer MacOS versions have a more stringent security model, and also 112 | you might not have HomeBrew installed for the prebuilt. 113 | 114 | In that case, run any of the following commands to fit your needs. 115 | 116 | To bypass the quarantine: 117 | 118 | sudo xattr -rd com.apple.quarantine ./egel 119 | sudo xattr -rd com.apple.quarantine *.ego 120 | sudo xattr -rd com.apple.quarantine *.dylib 121 | 122 | To relabel dynlibs to the local supplied files: 123 | 124 | xcode-select --install 125 | install_name_tool -change /opt/homebrew/opt/ffi/lib/libffi.8.dylib @executable_path/libffi.8.dylib ./egel 126 | install_name_tool -change /opt/homebrew/opt/lightning/lib/liblightning.2.dylib @executable_path/liblightning.2.dylib ./egel 127 | install_name_tool -change /opt/homebrew/opt/fmt/lib/libfmt.11.dylib @executable_path/libfmt.11.dylib ./egel 128 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicuuc.74.dylib @executable_path/libicuuc.74.dylib ./egel 129 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicudata.74.dylib @executable_path/libicudata.74.dylib ./egel 130 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicuio.74.dylib @executable_path/libicuio.74.dylib ./egel 131 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicui18n.74.dylib @executable_path/libicui18n.74.dylib ./egel 132 | install_name_tool -change /opt/homebrew/opt/icu4c/lib/libicutu.74.dylib @executable_path/libicutu.74.dylib ./egel 133 | -------------------------------------------------------------------------------- /VERSION.md: -------------------------------------------------------------------------------- 1 | VERSION 2 | ======= 3 | 4 | A list of all major changes between versions of the interpreter. 5 | 6 | ## Pending goals 7 | 8 | + replace all class preamble macros with templates 9 | + vararg function definitions 10 | + move to C++20 (modules, modern syntax) 11 | when libfmt makes it into llvm and gcc? 12 | + integer move instruction in bytecode 13 | + generators to prelude, generator for dict 14 | + vs code syntax highlighting 15 | + add local combinator definitions (`where`) 16 | + add backtick `` `f`` as a shorthand for `quote "f"` 17 | + dynamic dispatch 18 | + cleanup internals regarding combinator declarations 19 | + generate C++ from .eg file (module compilation) 20 | + target MSVC 21 | 22 | ## The bleeding, cutting edge 23 | 24 | + '.md' files added as extension to appease github 25 | + conditions are now 'rewrite on false' instead of 'rewrite on true' 26 | 27 | ## v0.1.14 - the devil fetch ye ragamuffin rapscallions; ye are all asleep 28 | 29 | + namespace rework 30 | + lambda multiple matches 31 | + module rework 32 | + complex numbers 33 | + docstrings and help 34 | + exposed the tokenizer 35 | 36 | ## v0.1.12 - in every Lisp lives a confused hedgehog 37 | 38 | + stratify float printing/lexing 39 | + drop client/server for netcat 40 | + blank slate mode 41 | + rewrite assembler 42 | + github actions 43 | + default submodules to latest stable version 44 | 45 | ## v0.1.11 - no kink shaming the couch 46 | 47 | + printf 48 | + static cmake build 49 | + double register size (u32) 50 | + hardcoded larger stack usage 51 | + remove cascading free bug at expense of performance 52 | + non-recursive print 53 | + time and date 54 | + print float with fmt 55 | + ffi 56 | 57 | ## v0.1.10 - egel, let's go! 58 | 59 | + only reduce redexes 60 | + simplified try/catch handling with combinators 61 | + added egg files, support for literate programming 62 | + added a GNU lightning back-end 63 | + dict is now a builtin 64 | + revert $ precedence since $ is bitwise or 65 | + added a stall combinator 66 | 67 | ## v0.1.9 - great conversation, chatgpt 68 | 69 | + drop par for async/await 70 | + async tasks 71 | + egel namespace in c++ source 72 | + utilities to vm 73 | + more macro removal 74 | + egel remote procedure calls 75 | 76 | ## v0.1.8 - who cares it's slow? 77 | 78 | + removed internal macros with c++ methods 79 | + removed some utf32 bugs and code smell 80 | + do syntax 81 | + BADARGS should report the arguments 82 | + reintroduction of monadic min 83 | 84 | ## v0.1.7 -- fine, keep your house Stroustrup 85 | 86 | * dis and asm should work again 87 | * introspection 88 | * add a proper data segment to combinators 89 | 90 | ## v0.1.6 -- rolling on 91 | 92 | * internal cleaning 93 | * get rid of spurious macros 94 | + get rid of .clone() for class::create() 95 | * dictionary 96 | * tinydb 97 | * small internal tweaks to array creation 98 | * generators library 99 | 100 | ## v0.1.5 -- advent of adventures 101 | 102 | + map abstract datatype 103 | + add `{X0,X1|XX}` pretty list syntax 104 | + serialization primitives 105 | 106 | ## v0.1.4 -- snakes! everywhere! 107 | 108 | + add a Python bridge 109 | + refactor code to have most (external) calls work over machine 110 | + static coding combinators (runtime inspection) 111 | 112 | ## v0.1.3 -- roads half built 113 | 114 | + swap ':' and '::', rename 'nop' to 'none' 115 | + added multiline strings 116 | 117 | ## v0.1.2 -- smaller is better 118 | 119 | + calculate and search theories 120 | 121 | ## v0.1.1 -- fear is the mind killer 122 | 123 | + readme/license changes 124 | + compile on more Linux/MacOs/BSD architectures 125 | + fixed critical regression LL(n) quadratic behavior 126 | 127 | ## v0.1.0 -- corrupting the neighborhood 128 | 129 | + MacOS (clang, arm64) and Linux (gcc, arm64) 130 | + man page 131 | 132 | ## v0.0.7 -- the work of the devil 133 | 134 | + cmake 135 | 136 | ## v0.0.6 -- rationality was a mistake 137 | 138 | + added reference objects 139 | + added monadic minus pass 140 | + performance (abysmal) tweaks 141 | + format 142 | 143 | ## v0.0.5 -- social distancing sucks 144 | 145 | + added lazy ops 146 | + added multiple commands to line parsing 147 | + changed default behavior to throw exceptions on bad arguments 148 | + processes 149 | + asm and dis 150 | 151 | ## v0.0.4 -- life is worth living 152 | 153 | + text based basic input/output and TCP transport 154 | + 'eval' and 'blip' 155 | 156 | ## v0.0.3 -- it may all break down yet 157 | 158 | + bugfix on '(1)' -length one array- results 159 | + switch to '#' style comments 160 | + regular expression support 161 | + small performance tweaks 162 | + got rid of the K combinator solution for statements 163 | 164 | ## v0.0.2 -- why bother? 165 | 166 | + check for overflow on simple math operators 167 | + changed from dot to colon syntax to free up '.' as a combinator 168 | + added semicolons as syntactic sugar to the language 169 | 170 | ## v0.0.1 -- heap of dung 171 | 172 | + CHANGED TO STANDARD PATTERN MATCHING SYNTAX 173 | + provisional fix to always print a dot in floats 174 | + fixed a scope bug in let 175 | + added hexadecimal numbers and binary operators 176 | + fixed a bug with lexical scope and try/catch blocks 177 | 178 | ## v0.0.0 -- not even wrong 179 | 180 | This is the version where a plethora of bugs were squashed, technically 181 | most things are in place, but there are still a large number of loose 182 | ends. 183 | 184 | + symbolic rewriting 185 | + REPL and batch mode support 186 | + initial support for a prelude and basic libraries 187 | + experiments with mutable state and OO 188 | + concurrency 189 | + dynamic libraries 190 | + optional system-wide install 191 | 192 | ## v0.0 -- initial release 193 | 194 | The initial commit. Expect lots and lots of bugs! 195 | 196 | -------------------------------------------------------------------------------- /bugs/bug0-fixed.eg: -------------------------------------------------------------------------------- 1 | /* 2 | * A scoping error was found on the #proglangdesign channel of freenode. 3 | * 4 | * The next function resulted in a core dump. 5 | */ 6 | 7 | def f = [ Y -> (try [ X -> throw Y ] catch 0) 1 ] "scope" 8 | 9 | /* This was a bug in Egel's operational semantics where it was assumed 10 | * that try/catch blocks could be translated to bytecode directly. 11 | * Instead, a different -more indirect- approach was needed. The 12 | * semantics of a try/catch block now is: 13 | * 14 | * 1. Evaluate the catch expression and install that as a handler. 15 | * 2. Evaluate the try expression with that handler. 16 | * 17 | * This corresponds to a C++ approach where a handler is an 18 | * (evaluated) closure over the variables in its lexical scope. 19 | * 20 | * The new semantics have been 'tested' with a small number of cases. 21 | * It should work! 22 | */ 23 | 24 | def main = f 25 | -------------------------------------------------------------------------------- /bugs/bug1-fixed.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | 3 | using System 4 | 5 | def fac = [ 0 -> 1 | N -> N * fac (N - 1) ] 6 | 7 | def main = [ fac -> "fac" | X -> "other" ] fac 8 | -------------------------------------------------------------------------------- /bugs/bug2-fixed.eg: -------------------------------------------------------------------------------- 1 | # A new feature, 'flexible' scoping for exceptions was discovered and fixed. 2 | 3 | using System 4 | 5 | def f = try [ 0 -> throw 0 ] catch [ E -> "inner" ] 6 | 7 | def g = try f 0 catch [ E -> "outer" ] 8 | 9 | def main = 10 | (g, 11 | try f 0 catch [ E -> "outer" ], 12 | try (try [ 0 -> throw 0 ] catch [ E -> "inner" ]) 0 catch [ E -> "outer" ]) 13 | 14 | # reduced to ("inner", "inner", "outer"). Only the last answer is correct. 15 | # now reduces to ("outer", "outer", "outer") 16 | # 17 | # A short analysis is that the term 18 | # `try (try [ 0 -> throw 0 ] catch [ E -> "inner" ]) 0 catch [ E -> "outer" ]) 19 | # is just a fancy manner of writing `([ 0 -> throw 0 ] 0)` with some 20 | # handlers installed. 21 | # And there is no means of switching handlers half-way the reduction 22 | # of a thunk. 23 | # 24 | # Now the reduction of a try/catch block is forced by prepending `id` to it. 25 | -------------------------------------------------------------------------------- /bugs/bug3-fixed.eg: -------------------------------------------------------------------------------- 1 | 2 | # There's something fishy going on with emojis on macos. 3 | # This is a unicode error somewhere, the expression below 4 | # is accepted when a space is given _after_ the closing 5 | # quote in interactive mode. 6 | # This seems to have something to do with code points 7 | # above 24 bits. 8 | 9 | def main = "🦔" 10 | -------------------------------------------------------------------------------- /bugs/bug4-fixed.eg: -------------------------------------------------------------------------------- 1 | using System 2 | 3 | def main = 4 | deserialize (serialize '∀') 5 | -------------------------------------------------------------------------------- /bugs/bug5-fixed.eg: -------------------------------------------------------------------------------- 1 | # printing large terms makes egel run out of the c stack 2 | 3 | # this is fixable, it's probably right to move printing to egel code 4 | 5 | # solved with some stacks in C++ 6 | 7 | import "prelude.eg" 8 | 9 | using List 10 | 11 | def main = from_to 0 1000000 12 | -------------------------------------------------------------------------------- /bugs/bug6-fixed.eg: -------------------------------------------------------------------------------- 1 | # printing large terms makes egel run out of the c stack 2 | 3 | # when large objects go out of scope they might trigger a c stack overflow 4 | # there isn't much there can be done about this since this is due to 5 | # native c++ RAII behavior on shared pointers 6 | # a stopgap solution is to break these large structure down gracefully 7 | 8 | # solved with a stopgap solution at great performance cost 9 | 10 | 11 | import "prelude.eg" 12 | 13 | using List 14 | using System 15 | 16 | def main = from_to 0 1000000 |> [_ -> none] 17 | -------------------------------------------------------------------------------- /contrib/assets/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # source files and objects 3 | SOURCES= \ 4 | egel-black-transparent.svg \ 5 | egel-black-white.svg \ 6 | egel-white-black.svg \ 7 | egel-white-transparent.svg 8 | 9 | OBJECTS48=$(SOURCES:.svg=-48x48.png) 10 | OBJECTS256=$(SOURCES:.svg=-256x256.png) 11 | 12 | # targets 13 | all: $(OBJECTS48) $(OBJECTS256) 14 | 15 | $(OBJECTS48): %-48x48.png: %.svg 16 | @echo "creating " $@ " from " $^ 17 | @inkscape --export-filename=$@ -w 48 -h 48 $^ 18 | 19 | # @mogrify -quiet -strip $@ 20 | 21 | $(OBJECTS256): %-256x256.png: %.svg 22 | @echo "creating " $@ " from " $^ 23 | @inkscape --export-filename=$@ -w 256 -h 256 $^ 24 | 25 | # @mogrify -quiet -strip $@ 26 | 27 | clean: 28 | @rm -rf $(OBJECTS48) 29 | @rm -rf $(OBJECTS256) 30 | 31 | 32 | -------------------------------------------------------------------------------- /contrib/assets/README.md: -------------------------------------------------------------------------------- 1 | 2 | + Original svg icon from svgrepo.com, no author given 3 | + CC0 licence on all the assets in this directory 4 | -------------------------------------------------------------------------------- /contrib/assets/egel-black-transparent-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-black-transparent-256x256.png -------------------------------------------------------------------------------- /contrib/assets/egel-black-transparent-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-black-transparent-48x48.png -------------------------------------------------------------------------------- /contrib/assets/egel-black-white-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-black-white-256x256.png -------------------------------------------------------------------------------- /contrib/assets/egel-black-white-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-black-white-48x48.png -------------------------------------------------------------------------------- /contrib/assets/egel-white-black-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-white-black-256x256.png -------------------------------------------------------------------------------- /contrib/assets/egel-white-black-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-white-black-48x48.png -------------------------------------------------------------------------------- /contrib/assets/egel-white-transparent-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-white-transparent-256x256.png -------------------------------------------------------------------------------- /contrib/assets/egel-white-transparent-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/contrib/assets/egel-white-transparent-48x48.png -------------------------------------------------------------------------------- /contrib/cmake/modules/FindFmt.cmake: -------------------------------------------------------------------------------- 1 | find_path(fmt_INCLUDE_DIR NAMES fmt/format.h) 2 | 3 | if(fmt_INCLUDE_DIR) 4 | set(_fmt_version_file "${fmt_INCLUDE_DIR}/fmt/core.h") 5 | if(NOT EXISTS "${_fmt_version_file}") 6 | set(_fmt_version_file "${fmt_INCLUDE_DIR}/fmt/format.h") 7 | endif() 8 | if(EXISTS "${_fmt_version_file}") 9 | # parse "#define FMT_VERSION 40100" to 4.1.0 10 | file(STRINGS "${_fmt_version_file}" fmt_VERSION_LINE 11 | REGEX "^#define[ \t]+FMT_VERSION[ \t]+[0-9]+$") 12 | string(REGEX REPLACE "^#define[ \t]+FMT_VERSION[ \t]+([0-9]+)$" 13 | "\\1" fmt_VERSION "${fmt_VERSION_LINE}") 14 | foreach(ver "fmt_VERSION_PATCH" "fmt_VERSION_MINOR" "fmt_VERSION_MAJOR") 15 | math(EXPR ${ver} "${fmt_VERSION} % 100") 16 | math(EXPR fmt_VERSION "(${fmt_VERSION} - ${${ver}}) / 100") 17 | endforeach() 18 | set(fmt_VERSION 19 | "${fmt_VERSION_MAJOR}.${fmt_VERSION_MINOR}.${fmt_VERSION_PATCH}") 20 | endif() 21 | endif() 22 | 23 | find_library(fmt_LIBRARY NAMES fmt) 24 | 25 | include(FindPackageHandleStandardArgs) 26 | find_package_handle_standard_args(fmt 27 | REQUIRED_VARS fmt_INCLUDE_DIR fmt_LIBRARY 28 | VERSION_VAR fmt_VERSION) 29 | mark_as_advanced( 30 | fmt_INCLUDE_DIR 31 | fmt_LIBRARY 32 | fmt_VERSION_MAJOR 33 | fmt_VERSION_MINOR 34 | fmt_VERSION_PATCH 35 | fmt_VERSION_STRING) 36 | 37 | if(fmt_FOUND AND NOT (TARGET fmt::fmt)) 38 | add_library(fmt-header-only INTERFACE) 39 | set_target_properties(fmt-header-only PROPERTIES 40 | INTERFACE_INCLUDE_DIRECTORIES "${fmt_INCLUDE_DIR}" 41 | INTERFACE_COMPILE_DEFINITIONS FMT_HEADER_ONLY=1 42 | INTERFACE_COMPILE_FEATURES cxx_std_11) 43 | 44 | add_library(fmt UNKNOWN IMPORTED GLOBAL) 45 | set_target_properties(fmt PROPERTIES 46 | INTERFACE_INCLUDE_DIRECTORIES "${fmt_INCLUDE_DIR}" 47 | INTERFACE_COMPILE_FEATURES cxx_std_11 48 | IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" 49 | IMPORTED_LOCATION "${fmt_LIBRARY}") 50 | 51 | if(WITH_FMT_HEADER_ONLY) 52 | # please note, this is different from how upstream defines fmt::fmt. 53 | # in order to force 3rd party libraries to link against fmt-header-only if 54 | # WITH_FMT_HEADER_ONLY is ON, we have to point fmt::fmt to fmt-header-only 55 | # in this case. 56 | add_library(fmt::fmt ALIAS fmt-header-only) 57 | else() 58 | add_library(fmt::fmt ALIAS fmt) 59 | endif() 60 | 61 | endif() 62 | -------------------------------------------------------------------------------- /contrib/ffi/README.md: -------------------------------------------------------------------------------- 1 | # Egel interpreter C++ template FFI bindings 2 | 3 | The file `src/ffi.hpp` contains a number of templates for 4 | light-weight foreign function interface (FFI) bindings. 5 | 6 | Use at you own risk. 7 | -------------------------------------------------------------------------------- /contrib/ffi/src/Makefile: -------------------------------------------------------------------------------- 1 | # compiler and compile options 2 | CC=g++ --std=c++0x 3 | EGEL_SRC_DIR=../../../src/ 4 | LIBS= \ 5 | -ldl \ 6 | -licudata \ 7 | -licui18n \ 8 | -licuio \ 9 | -licule \ 10 | -liculx \ 11 | -licutest \ 12 | -licutu \ 13 | -licuuc \ 14 | -legel 15 | CFLAGS=-c -Wall -pedantic -I$(EGEL_SRC_DIR) 16 | LDFLAGS=$(LIBS) -L$(EGEL_SRC_DIR) 17 | 18 | # source files and objects 19 | SOURCES= \ 20 | test.cpp 21 | 22 | OBJECTS=$(SOURCES:.cpp=.o) 23 | 24 | # dynamic object 25 | SHARED=../test.ego 26 | 27 | # targets 28 | all: CC=g++ --std=c++0x 29 | all: $(SOURCES) $(SHARED) 30 | 31 | O3: CC=g++ --std=c++0x -O3 32 | O3: $(SOURCES) $(SHARED) 33 | 34 | gprof: CC=g++ -std=c++0x -O3 -pg 35 | gprof: $(SOURCES) $(SHARED) 36 | 37 | debug: CC=g++ -std=c++0x -g 38 | debug: $(SOURCES) $(SHARED) 39 | 40 | $(SHARED): $(OBJECTS) 41 | $(CC) -shared $(LDFLAGS) $(OBJECTS) -o $@ 42 | 43 | .cpp.o: 44 | $(CC) -fPIC $(CFLAGS) $< -o $@ 45 | 46 | clean: 47 | -rm -f $(OBJECTS) $(SHARED) gmon.out massive.out out 48 | -------------------------------------------------------------------------------- /contrib/ffi/src/test.cpp: -------------------------------------------------------------------------------- 1 | #include "../../../src/runtime.hpp" 2 | #include "ffi.hpp" 3 | 4 | int answer() { 5 | return 42; 6 | } 7 | 8 | int mem = 0; 9 | 10 | extern "C" std::vector egel_imports() { 11 | return std::vector(); 12 | } 13 | 14 | extern "C" std::vector egel_exports(VM* vm) { 15 | std::vector oo; 16 | 17 | oo.push_back(ffi0(vm, "Test", "answer", [](){ return (vm_int_t) answer(); } ).clone()); 18 | oo.push_back(ffi0(vm, "Test", "hello", [](){ return UnicodeString("hello world!"); } ).clone()); 19 | oo.push_back(ffi1(vm, "Test", "char", [](vm_int_t n){ return UnicodeString((UChar32) n); } ).clone()); 20 | 21 | oo.push_back(ffi0(vm, "Test", "mem", [](){ return (vm_ptr_t) &mem ; } ).clone()); 22 | oo.push_back(ffi1(vm, "Test", "peek", [](vm_ptr_t p){ return (vm_int_t) ( *((int*)p) ) ; } ).clone()); 23 | oo.push_back(ffi2(vm, "Test", "poke", [](vm_ptr_t p, vm_int_t n){ return (vm_int_t) ( *((int*)p) = (int) n ) ; } ).clone()); 24 | 25 | 26 | return oo; 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /contrib/scripts/tarpackage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -lt 2 ]; then 4 | echo "usage: $0 name dir [fn0..fn]" 5 | exit 1 6 | fi 7 | 8 | name="$1"; shift 9 | egeldir="$1"; shift 10 | extras=$@ 11 | 12 | tardir=$name 13 | tarfile=$name.tgz 14 | 15 | create_dir() { 16 | mkdir $1 17 | } 18 | 19 | remove_dir() { 20 | rm -rf $1 21 | } 22 | 23 | file_store() { 24 | dest="$1"; shift 25 | for src in $@ 26 | do 27 | cp $src $dest 28 | done 29 | } 30 | 31 | create_tar() { 32 | tar -czf $1 $tardir 33 | } 34 | 35 | echo "extras $extras" 36 | 37 | create_dir $tardir 38 | file_store $tardir $egeldir/*.md 39 | file_store $tardir $egeldir/build/egel $egeldir/build/*ego 40 | file_store $tardir $egeldir/include/*eg 41 | create_dir $tardir/examples 42 | file_store $tardir/examples $egeldir/examples/* 43 | file_store $tardir $egeldir/man/egel.1.md 44 | file_store $tardir $extras 45 | create_tar $tarfile 46 | remove_dir $tardir 47 | -------------------------------------------------------------------------------- /contrib/scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -lt 1 ]; then 4 | echo "usage: $0 egel.cpp" 5 | exit 1 6 | fi 7 | 8 | major=$(sed -nE 's/#define EXECUTABLE_VERSION_MAJOR[ \t]+\"([^\"]+)\"/\1/p' "$1") 9 | minor=$(sed -nE 's/#define EXECUTABLE_VERSION_MINOR[ \t]+\"([^\"]+)\"/\1/p' "$1") 10 | patch=$(sed -nE 's/#define EXECUTABLE_VERSION_PATCH[ \t]+\"([^\"]+)\"/\1/p' "$1") 11 | 12 | echo $major.$minor.$patch 13 | -------------------------------------------------------------------------------- /contrib/vim/ftdetect/egel.vim: -------------------------------------------------------------------------------- 1 | " egel filetype file 2 | au BufRead,BufNewFile *.{eg} set filetype=egel 3 | -------------------------------------------------------------------------------- /contrib/vim/syntax/egel.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: egel 3 | " Filenames: *.eg 4 | " Maintainer: Marco Devillers - marco(.)devillers(@)gmail(.)com 5 | " Credits: 6 | " Last Change: 7 | " Change History: 8 | 9 | if exists("b:current_syntax") 10 | finish 11 | endif 12 | 13 | syn case match 14 | 15 | syn keyword egelKeyword if then else let in try catch throw do 16 | syn keyword egelKeyword data def val 17 | syn keyword egelDirective import using 18 | syn keyword egelNamespace namespace 19 | 20 | syn keyword egelTodo contained TODO XXX 21 | 22 | syn match egelOperator "[-+]" 23 | 24 | syn match egelChar "'.'" 25 | syn region egelText start=+\"+ end=+\"+ oneline skip=+\"\"+ 26 | syn match egelNumber "\<\d\+[ij]\=\>" 27 | syn match egelFloat "\<\d\+\(\.\d+\)\=\([eE][-]\=\d\+\)\=[ij]\=\>" 28 | 29 | syn match egelTab "\t" 30 | 31 | syn match egelArrow "->" 32 | syn match egelSemicolon ";" 33 | syn match egelDot "\." 34 | syn match egelComma "," 35 | syn match egelBar "|" 36 | syn match egelQuestion "?" 37 | syn match egelSquare "[][]" 38 | syn match egelParen "[()]" 39 | 40 | syn match egelComment "#.*$" contains=egelTodo,egelTab 41 | 42 | syn region egelText matchgroup=egelTripleQuotes start=+[uU]\=\z('''\|"""\)+ end="\z1" keepend 43 | 44 | "syn match egelError "-\=\<\d\+\.\d\+\.[^*/\\^]" 45 | "syn match egelError "-\=\<\d\+\.\d\+[eEdD][-+]\=\d\+\.\([^*/\\^]\)" 46 | 47 | if !exists("did_egel_syntax_inits") 48 | let did_egel_syntax_inits = 1 49 | 50 | highlight default link egelArrow SpecialChar 51 | highlight default link egelSemicolon SpecialChar 52 | highlight default link egelDot SpecialChar 53 | highlight default link egelComma SpecialChar 54 | highlight default link egelBar SpecialChar 55 | highlight default link egelQuestion SpecialChar 56 | highlight default link egelSquare SpecialChar 57 | highlight default link egelParen SpecialChar 58 | 59 | highlight default link egelKeyword Keyword 60 | highlight default link egelDirective Include 61 | highlight default link egelNamespace Type 62 | highlight default link egelOperator Operator 63 | highlight default link egelTodo Todo 64 | highlight default link egelTab Todo 65 | highlight default link egelChar String 66 | highlight default link egelText String 67 | highlight default link egelNumber Number 68 | highlight default link egelFloat Float 69 | highlight default link egelError Error 70 | highlight default link egelComment Comment 71 | 72 | highlight default link egelIdentifier Identifier 73 | highlight default link egelTab Error 74 | 75 | endif 76 | 77 | let b:current_syntax = "egel" 78 | 79 | "EOF vim: ts=8 noet tw=100 sw=8 sts=0 80 | -------------------------------------------------------------------------------- /examples/100doors.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example 100 doors. 2 | # 3 | # See: http://rosettacode.org 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | using List 9 | 10 | data open, closed 11 | 12 | def toggle = 13 | [ (open N) -> closed N | (closed N) -> open N ] 14 | 15 | def doors = 16 | [ N -> map [ N -> closed N ] (from_to 1 N) ] 17 | 18 | def toggleK = 19 | [ K nil -> nil 20 | | K (cons (D N) DD) -> 21 | let DOOR = if (N%K) == 0 then toggle (D N) else D N in 22 | cons DOOR (toggleK K DD) ] 23 | 24 | def toggleEvery = 25 | [ nil DOORS -> DOORS 26 | | (cons K KK) DOORS -> toggleEvery KK (toggleK K DOORS) ] 27 | 28 | def run = 29 | [ N -> toggleEvery (from_to 1 N) (doors N) ] 30 | 31 | def main = run 100 32 | -------------------------------------------------------------------------------- /examples/3or5.eg: -------------------------------------------------------------------------------- 1 | # Euler project[1] task 1. Sum the elements of a list 1..1000 which are 2 | # dividable by either three or five. 3 | # 4 | # [1]: https://projecteuler.net/ 5 | 6 | import "prelude.eg" 7 | 8 | namespace ThreeOrFive ( 9 | 10 | using System 11 | using List 12 | 13 | def three_or_five = [X -> or ((X%3) == 0) ((X%5) == 0) ] 14 | 15 | def euler1 = 16 | [ N -> (sum . (filter three_or_five)) (from_to 1 N) ] 17 | 18 | ) 19 | 20 | using ThreeOrFive 21 | 22 | def main = euler1 1000 23 | 24 | -------------------------------------------------------------------------------- /examples/99bottles.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example 99 Bottles. 2 | # 3 | # See: http://rosettacode.org 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | 9 | def print_rhyme = 10 | [ 0 -> 11 | print "better go to the store, and buy some more\n" 12 | | N -> 13 | let _ = print N " bottles of beer on the wall\n" in 14 | let _ = print N " bottles of beer\n" in 15 | let _ = print "take one down, pass it around\n" in 16 | print_rhyme (N - 1) ] 17 | 18 | def main = print_rhyme 99 19 | -------------------------------------------------------------------------------- /examples/abject.eg: -------------------------------------------------------------------------------- 1 | # A collection of terms which are happily accepted by Egel, 2 | # not so much by a number of Hindley-Milner based type 3 | # checkers. 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | 9 | # `self` is applying a function to itself, this usually fails 10 | # the occurs check in a functional language 11 | 12 | # self-application is interesting since it's at the heart of 13 | # Turing's halting argument, i.e., self halt = [F->F F] halt 14 | # = halt halt doesn't have an implementation. 15 | 16 | def self = [F -> F F] 17 | 18 | # `ones` is a coalgebraic/lazy infinite list of ones. 19 | # 20 | # In Egel, that is achieved by deferring the evaluation of a term, 21 | # ones none = (1, ones), a pair of a head and a tail. 22 | 23 | def ones = \_ -> (1, ones) 24 | 25 | # in ghc that term gives: 26 | # GHCi, version 8.10.7: https://www.haskell.org/ghc/ :? for help 27 | # Prelude> ones = \_ -> (1, ones) 28 | # 29 | # :1:8: error: 30 | # • Occurs check: cannot construct the infinite type: 31 | # b ~ p0 -> (a0, b) 32 | # • In the expression: \ _ -> (1, ones) 33 | # In an equation for ‘ones’: ones = \ _ -> (1, ones) 34 | # • Relevant bindings include ones :: b 35 | 36 | 37 | # `tuple` can be exploited to blow up a Hindley-Milner type checker. 38 | # Since with every application the type term doubles, that can 39 | # quickly blow up exponentially. 40 | 41 | def t = \X -> (X, X) 42 | 43 | def term = (t . t . t . t . t) 0 44 | 45 | # Or, double the doubling, 46 | 47 | def boom = 48 | let F0 = \X -> (X, X) in 49 | let F1 = \X -> F0 (F0 X) in 50 | let F2 = \X -> F1 (F1 X) in 51 | let F3 = \X -> F2 (F2 X) in 52 | let F4 = \X -> F3 (F3 X) in 53 | let F5 = \X -> F4 (F4 X) in 54 | F5 0 55 | 56 | # or 57 | 58 | def boom2 = 59 | let T = (\F -> F . F) in let D = (\X -> (X,X)) in (T.T.T.T) D 0 60 | 61 | # A similar trick, the following term has type int but a HM 62 | # inferencer often takes a long time to agree. 63 | # (see "An analysis of ML typability" by Kfoury, Tiuryn and Urzyczyn) 64 | 65 | def type_int = 66 | let F1 = \K A B -> K A B in 67 | let F2 = \K -> K F1 F1 in 68 | let F3 = \K -> K F2 F2 in 69 | let F4 = \K -> K F3 F3 in 70 | let F5 = \K -> K F4 F4 in 71 | F5 F4 F3 F2 F1 (+) 2 2 72 | 73 | # `tmap` applies a function to both arguments. It will be given # the 74 | # constrained type `(a->b) -> (a,a) -> (b,b)` but who wants that? 75 | 76 | def tmap = \F (X, Y) -> (F X, F Y) 77 | 78 | # A rank-N type looks like: 79 | # 80 | # applyBoth 81 | # :: forall a. forall b. (forall x. x -> x) -> (a, b) -> (a, b) 82 | # applyBoth = 83 | # forall a. forall b. -- [1] 84 | # \(f :: forall x. x -> x) -> -- [2] 85 | # \((k, h) :: (a, b)) -> -- [3] 86 | # (f @a k, f @b h) -- [4] 87 | # https://www.parsonsmatt.org/2021/11/30/rank_n_types_via_lambda_calculus.html 88 | # 89 | # Of course, a tuple is a small container so this holds for all 90 | # containers. 91 | 92 | -------------------------------------------------------------------------------- /examples/ackermann.eg: -------------------------------------------------------------------------------- 1 | # In computability theory, the Ackermann function, named after 2 | # Wilhelm Ackermann, is one of the simplest and earliest-discovered 3 | # examples of a total computable function that is not primitive 4 | # recursive. All primitive recursive functions are total and 5 | # computable, but the Ackermann function illustrates that not all 6 | # total computable functions are primitive recursive. 7 | # -- Wikipedia, the free encyclopedia 8 | # https://en.wikipedia.org/wiki/Ackermann_function 9 | # 10 | # This function grows rapidly, even for small inputs. 11 | 12 | using System 13 | 14 | def ackermann = 15 | [ 0 N -> N + 1 16 | | M 0 -> ackermann (M - 1) 1 17 | | M N -> ackermann (M - 1) (ackermann M (N - 1)) ] 18 | 19 | def main = ackermann 3 4 20 | -------------------------------------------------------------------------------- /examples/assembler.eg: -------------------------------------------------------------------------------- 1 | # showcase assembler and disassembler routines 2 | 3 | using System 4 | 5 | def f = [X -> X] 6 | 7 | def main = 8 | let CODE = dis f in 9 | print CODE; 10 | asm CODE 0 11 | -------------------------------------------------------------------------------- /examples/async.eg: -------------------------------------------------------------------------------- 1 | # Egel supports asynchronous tasks 2 | # async f - start task f 3 | # await f - block pending a result 4 | # wait_for f n - wait for n milliseconds or f to complete 5 | # is_valid f - inspect whether f reduced 6 | 7 | import "prelude.eg" 8 | 9 | using System 10 | using List 11 | 12 | def poll = 13 | [ F -> print "waiting\n"; if wait_for F 500 then await F else poll F ] 14 | 15 | def main = 16 | let F = async [_ -> from_to 1 1000000 |> sum ] in 17 | poll F 18 | -------------------------------------------------------------------------------- /examples/bintrees.eg: -------------------------------------------------------------------------------- 1 | # The binary trees benchmark. Allocate lots and 2 | # lots of small and big binary trees. 3 | # 4 | # This 'standard' micro benchmark runs in about 5 | # one and half hour on my laptop. That in 6 | # comparison to OCaml and GHC which run in 7 | # about 10 and 25 seconds, respectively. But 8 | # with employing four cores. 9 | # 10 | # The Computer Language Benchmarks Game 11 | # http://benchmarksgame.alioth.debian.org/ 12 | # 13 | # user$ time egel bintrees.eg 21 14 | # stretch tree of depth 22 check: 8388607 15 | # 2097152 trees of depth 4 check: 65011712 16 | # 524288 trees of depth 6 check: 66584576 17 | # 131072 trees of depth 8 check: 66977792 18 | # 32768 trees of depth 10 check: 67076096 19 | # 8192 trees of depth 12 check: 67100672 20 | # 2048 trees of depth 14 check: 67106816 21 | # 512 trees of depth 16 check: 67108352 22 | # 128 trees of depth 18 check: 67108736 23 | # 32 trees of depth 20 check: 67108832 24 | # long lived tree of depth 21 check: 4194303 25 | # 26 | # real 91m12.224s 27 | # user 90m59.900s 28 | # sys 0m0.797s 29 | 30 | import "prelude.eg" 31 | 32 | using System 33 | using List 34 | 35 | data node, leaf 36 | 37 | def make = 38 | [ 0 -> leaf 39 | | N -> node (make (N - 1)) (make (N - 1)) ] 40 | 41 | def check = 42 | [ (node L R) -> 1 + (check L) + (check R) 43 | | T -> 1 ] 44 | 45 | def minN = 4 46 | 47 | def max = 48 | [ X Y -> if X < Y then Y else X ] 49 | 50 | def make_stretch = 51 | [ MAXN -> 52 | let STRETCHN = MAXN + 1 in 53 | let C = check (make STRETCHN) in 54 | print "stretch tree of depth " STRETCHN "\t check: " C "\n" ] 55 | 56 | def sum_trees = 57 | [ D 0 T -> T 58 | | D N T -> let A = check (make D) in sum_trees D (N - 1) (T + A) ] 59 | 60 | def make_trees = 61 | [ D M -> 62 | let N = 1 << (M - D + minN) in 63 | if D <= M then 64 | # let _ = print "testing: " N " trees of depth " D "\n" in 65 | let CHECK = sum_trees D N 0 in 66 | cons (N, D, CHECK) (make_trees (D+2) M) 67 | else nil ] 68 | 69 | def print_trees = 70 | [ (cons (N, D, CHECK) TT) -> 71 | print N "\ttrees of depth " D "\t check: " CHECK "\n"; 72 | print_trees TT 73 | | _ -> none ] 74 | 75 | def main = 76 | let N = [ X:text -> to_int X | X -> throw "bintrees " ] (arg 2) in 77 | let MAXN = max (minN + 2) N in 78 | let _ = make_stretch MAXN in 79 | let TT = make_trees minN MAXN in 80 | let _ = print_trees TT in 81 | let LONG = make MAXN in 82 | print "long lived tree of depth " MAXN "\t check: " (check LONG) "\n" 83 | 84 | -------------------------------------------------------------------------------- /examples/booleans.eg: -------------------------------------------------------------------------------- 1 | # Lambda encoding of booleans. 2 | 3 | # true 4 | def t = [ X Y -> X ] 5 | 6 | # false 7 | def f = [ X Y -> Y ] 8 | 9 | # not 10 | def n = [ P X Y -> P Y X ] 11 | 12 | # and 13 | def a = [ P Q -> P Q P ] 14 | 15 | # xor 16 | def x = [ P Q -> P (n Q) Q ] 17 | 18 | def main = x t f 19 | -------------------------------------------------------------------------------- /examples/captcha-a.eg: -------------------------------------------------------------------------------- 1 | # This is the solution to the first problem "captcha" of Advent of Code. 2 | # 3 | # See: http://adventofcode.com 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | using String (to_chars) 9 | using List 10 | 11 | def input = "29917128875332952564321392569634257121244516819997569284938677239676779378822158323549832814412597817651244117851771257438674567254146559419528411463781241159837576747416543451994579655175322397355255587935456185669334559882554936642122347526466965746273596321419312386992922582836979771421518356285534285825212798113159911272923448284681544657616654285632235958355867722479252256292311384799669645293812691169936746744856227797779513997329663235176153745581296191298956836998758194274865327383988992499115472925731787228592624911829221985925935268785757854569131538763133427434848767475989173579655375125972435359317237712667658828722623837448758528395981635746922144957695238318954845799697142491972626942976788997427135797297649149849739186827185775786254552866371729489943881272817466129271912247236569141713377483469323737384967871876982476485658337183881519295728697121462266226452265259877781881868585356333494916519693683238733823362353424927852348119426673294798416314637799636344448941782774113142925315947664869341363354235389597893211532745789957591898692253157726576488811769461354938575527273474399545366389515353657644736458182565245181653996192644851687269744491856672563885457872883368415631469696994757636288575816146927747179133188841148212825453859269643736199836818121559198563122442483528316837885842696283932779475955796132242682934853291737434482287486978566652161245555856779844813283979453489221189332412315117573259531352875384444264457373153263878999332444178577127433891164266387721116357278222665798584824336957648454426665495982221179382794158366894875864761266695773155813823291684611617853255857774422185987921219618596814446229556938354417164971795294741898631698578989231245376826359179266783767935932788845143542293569863998773276365886375624694329228686284863341465994571635379257258559894197638117333711626435669415976255967412994139131385751822134927578932521461677534945328228131973291962134523589491173343648964449149716696761218423314765168285342711137126239639867897341514131244859826663281981251614843274762372382114258543828157464392" 12 | 13 | def char2int = 14 | [ '0' -> 0 | '1' -> 1 | '2' -> 2 | '3' -> 3 | '4' -> 4 15 | | '5' -> 5 | '6' -> 6 | '7' -> 7 | '8' -> 8 | '9' -> 9 ] 16 | 17 | def captcha = [ XX -> 18 | let XX = map char2int (to_chars XX) in 19 | let XX = zip XX ((tail XX) ++ XX) in 20 | let XX = filter (uncurry (==)) XX in 21 | let XX = map fst XX in 22 | foldl (+) 0 XX ] 23 | 24 | def main = captcha input 25 | 26 | -------------------------------------------------------------------------------- /examples/captcha-b.eg: -------------------------------------------------------------------------------- 1 | # This is the solution to the second problem "captcha" of Advent of Code. 2 | # 3 | # See: http://adventofcode.com 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | using String (to_chars) 9 | using List 10 | 11 | def input = "29917128875332952564321392569634257121244516819997569284938677239676779378822158323549832814412597817651244117851771257438674567254146559419528411463781241159837576747416543451994579655175322397355255587935456185669334559882554936642122347526466965746273596321419312386992922582836979771421518356285534285825212798113159911272923448284681544657616654285632235958355867722479252256292311384799669645293812691169936746744856227797779513997329663235176153745581296191298956836998758194274865327383988992499115472925731787228592624911829221985925935268785757854569131538763133427434848767475989173579655375125972435359317237712667658828722623837448758528395981635746922144957695238318954845799697142491972626942976788997427135797297649149849739186827185775786254552866371729489943881272817466129271912247236569141713377483469323737384967871876982476485658337183881519295728697121462266226452265259877781881868585356333494916519693683238733823362353424927852348119426673294798416314637799636344448941782774113142925315947664869341363354235389597893211532745789957591898692253157726576488811769461354938575527273474399545366389515353657644736458182565245181653996192644851687269744491856672563885457872883368415631469696994757636288575816146927747179133188841148212825453859269643736199836818121559198563122442483528316837885842696283932779475955796132242682934853291737434482287486978566652161245555856779844813283979453489221189332412315117573259531352875384444264457373153263878999332444178577127433891164266387721116357278222665798584824336957648454426665495982221179382794158366894875864761266695773155813823291684611617853255857774422185987921219618596814446229556938354417164971795294741898631698578989231245376826359179266783767935932788845143542293569863998773276365886375624694329228686284863341465994571635379257258559894197638117333711626435669415976255967412994139131385751822134927578932521461677534945328228131973291962134523589491173343648964449149716696761218423314765168285342711137126239639867897341514131244859826663281981251614843274762372382114258543828157464392" 12 | 13 | def char2int = 14 | [ '0' -> 0 | '1' -> 1 | '2' -> 2 | '3' -> 3 | '4' -> 4 15 | | '5' -> 5 | '6' -> 6 | '7' -> 7 | '8' -> 8 | '9' -> 9 ] 16 | 17 | def combine = [ XX -> 18 | let L = (List::length XX)/2 in 19 | let YY = drop L XX ++ take L XX in 20 | zip XX YY ] 21 | 22 | def captcha = [ XX -> 23 | let XX = map char2int (to_chars XX) in 24 | let XX = combine XX in 25 | let XX = filter (uncurry (==)) XX in 26 | let XX = map fst XX in 27 | foldl (+) 0 XX ] 28 | 29 | def main = captcha input 30 | -------------------------------------------------------------------------------- /examples/colist.eg: -------------------------------------------------------------------------------- 1 | # Colists example 2 | # 3 | 4 | import "prelude.eg" 5 | 6 | using System 7 | using List 8 | 9 | def ones = [ _ -> cons 1 ones ] 10 | 11 | def cohead = [ LL -> [ (cons H TT) -> H ] (LL none) ] 12 | 13 | def cotail = [ LL -> [ (cons H TT) -> TT ] (LL none) ] 14 | 15 | def comap = [ F LL _ -> [ nil -> nil | (cons H TT) -> cons (F H) (comap F TT) ] (LL none) ] 16 | 17 | def main = 18 | let TWOS = comap [ X -> X + 1 ] ones in (cohead (cotail (cotail TWOS))) 19 | -------------------------------------------------------------------------------- /examples/command.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | egel -e 'using String (chr);; print (chr 0x1f994) "\n"' 4 | -------------------------------------------------------------------------------- /examples/concurrent.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example Concurrent Programming. 2 | # 3 | # See: http://rosettacode.org 4 | # 5 | 6 | 7 | import "prelude.eg" 8 | 9 | using System 10 | 11 | def main = 12 | let X = async [_ -> print "enjoy\n"] in 13 | let Y = async [_ -> print "rosetta\n"] in 14 | let Z = async [_ -> print "code\n"] in 15 | (await X, await Y, await Z); none 16 | -------------------------------------------------------------------------------- /examples/cps.eg: -------------------------------------------------------------------------------- 1 | 2 | # 'Double' CPS translation of lambda terms with exceptions. 3 | # 4 | # CPS[x] k h = k x 5 | # CPS[\x.e] k h = k (\x,k',h'.CPS[e] k' h') 6 | # CPS[e1 e2] k h = CPS[e1](\f.CPS[e2] (\v.f v k h) h) h 7 | # CPS[try e1 catch x.e2] k h = CPS[e1] k (\x.CPS[e2] k h) 8 | # CPS[raise e] k h = CPS[e] h h 9 | # 10 | # (Taken from course CS 152, Harvard School of Eng. and App. Science. Lec. 12) 11 | 12 | import "prelude.eg" 13 | 14 | using System 15 | using List 16 | 17 | def fix = [ F -> F [ X -> (fix F) X ] ] 18 | 19 | data const, var, app, lam, raise, tr 20 | 21 | def subs = 22 | [ X E (const Y) -> const Y 23 | | X E (var Y) -> if X == Y then E else var Y 24 | | X E (app E0 E1) -> app (subs X E E0) (subs X E E1) 25 | | X E (lam Y E0) -> if X == Y then lam Y E0 else lam Y (subs X E E0) 26 | | X E (raise E0) -> raise (subs X E E0) 27 | | X E (tr E0 Y E1) -> if X == Y then tr E0 Y E1 else tr E0 Y (subs X E E1) ] 28 | 29 | def fv = 30 | [ (const X) -> {} 31 | | (var X) -> {X} 32 | | (app E0 E1) -> (fv E0) ++ (fv E1) 33 | | (lam X E) -> filter [ V -> not (V == X) ] (fv E) 34 | | (raise E) -> fv E 35 | | (tr E0 X E1) -> (fv E0) ++ (fv (lam X E1)) ] 36 | 37 | def free = 38 | [ V FV -> all [ V0 -> not (V == V0) ] FV ] 39 | 40 | def fresh = 41 | let F = fix [ F V FV -> if free V FV then V else F (V + 1) FV ] in 42 | [ FV -> F 0 FV ] 43 | 44 | def cps = 45 | [ (const X) -> let FV = fv (const X) in 46 | let K = fresh FV in 47 | let H = fresh ({K} ++ FV) in 48 | lam K (lam H (app (var K) (const X))) 49 | | (var X) -> let FV = fv (var X) in 50 | let K = fresh FV in 51 | let H = fresh ({K} ++ FV) in 52 | lam K (lam H (app (var K) (var X))) 53 | | (app E0 E1) -> let FV = fv (app E0 E1) in 54 | let K = fresh FV in 55 | let H = fresh ({K} ++ FV) in 56 | let F = fresh ({K,H} ++ FV) in 57 | let V = fresh ({K,H,F} ++ FV) in 58 | let C0 = cps E0 in let C1 = cps E1 in 59 | lam K (lam H (app (app C0 (lam F (app (app C1 (lam V (app (app (app (var F) (var V)) (var K)) (var H)))) (var H)))) (var H))) 60 | | (lam X E) -> let FV = fv (lam X E) in 61 | let K = fresh FV in 62 | let K0 = fresh ({K} ++ FV) in 63 | let H = fresh ({K, K0} ++ FV) in 64 | let H0 = fresh ({K, K0, H} ++ FV) in 65 | let C = cps E in 66 | lam K (lam H (app (var K) (lam X (lam K0 (lam H0 (app (app C (var K0)) (var H0))))))) 67 | | (raise E) -> let FV = fv (raise E) in 68 | let K = fresh FV in 69 | let H = fresh ({K} ++ FV) in 70 | let C = cps E in 71 | lam K (lam H (app (app C (var H)) (var H))) 72 | | (tr E0 X E1) -> let FV = fv (app (app (var X) E0) E1) in 73 | let K = fresh FV in 74 | let H = fresh ({K} ++ FV) in 75 | let C0 = cps E0 in let C1 = cps E1 in 76 | lam K (lam H (app (app C0 (var K)) (lam X (app (app C1 (var K)) (var H))))) 77 | ] 78 | 79 | def eval = 80 | [ (app (lam X E0) E1) -> eval (subs X (eval E1) E0) 81 | | (app E0 E1) -> eval (app (eval E0) E1) 82 | | X -> X ] 83 | 84 | 85 | def test0 = 86 | app (lam 0 (var 0)) (const 'a') 87 | 88 | def test1 = 89 | (tr (tr (raise (const 0)) 0 (raise (const "inner"))) 0 (raise (const "outer"))) 90 | 91 | def main = 92 | eval (app (app (cps test1) (lam 0 (var 0))) (lam 0 (var 0))) 93 | 94 | 95 | -------------------------------------------------------------------------------- /examples/cycle.eg: -------------------------------------------------------------------------------- 1 | # Egel supports variables though I didn't implement a nice syntax for 2 | # that. 3 | # 4 | # Variables can create cycles. For the moment, we don't do anything 5 | # about that though we may copy the argument once or fail to assign 6 | # given cycle detection. 7 | 8 | import "prelude.eg" 9 | 10 | using System 11 | 12 | def main = 13 | let Y = ref 0 in 14 | let Y = set_ref Y Y in 15 | Y 16 | -------------------------------------------------------------------------------- /examples/dispatch.eg: -------------------------------------------------------------------------------- 1 | # Just a showcase how one could do dynamic dispatch in Egel over a table. 2 | # 3 | # This is of course an extremely slow implementation over a lookup table 4 | # represented as a list. But moving the combinators to C++ would help. 5 | 6 | import "prelude.eg" 7 | 8 | using System 9 | using List 10 | 11 | namespace Dispatch ( 12 | 13 | val dispatch_table = ref {} 14 | 15 | def dispatch_register = 16 | [ TAG FUNC -> 17 | let TABLE = get_ref dispatch_table in 18 | let TABLE = cons (TAG FUNC) TABLE in 19 | set_ref dispatch_table TABLE ] 20 | 21 | def dispatch_findrec = 22 | [ TAG nil -> throw (format "dispatch for {} failed" TAG) 23 | | TAG0 (cons (TAG1 FUNC) TABLE) -> 24 | if TAG0 == TAG1 then FUNC [ _ -> dispatch_findrec TAG0 TABLE ] 25 | else dispatch_findrec TAG0 TABLE ] 26 | 27 | def dispatch_on = 28 | [ TAG -> let TABLE = get_ref dispatch_table in 29 | dispatch_findrec TAG TABLE ] 30 | ) 31 | 32 | def ++ = Dispatch::dispatch_on "++" 33 | 34 | # note that this makes use of variadic pattern matching. you either 35 | # match and apply on arguments, or continue with arguments undisturbed 36 | val plus_float_register = 37 | let FUNC = 38 | [ K EXPR0 EXPR1 -> 39 | if [ E:float -> true | E -> false ] EXPR0 then (+) EXPR0 EXPR1 else K none EXPR0 EXPR1 40 | | K -> K none ] 41 | in 42 | Dispatch::dispatch_register "++" FUNC 43 | 44 | val plus_text_register = 45 | let FUNC = 46 | [ K EXPR0 EXPR1 -> if [ E:text -> true | E -> false ] EXPR0 then (+) EXPR0 EXPR1 else K none EXPR0 EXPR1 47 | | K -> K none ] 48 | in 49 | Dispatch::dispatch_register "++" FUNC 50 | 51 | def main = 52 | print (1.0 ++ 2.0) "\n"; 53 | print ("hello " ++ "world") "\n"; 54 | print (1 ++ "failure") 55 | -------------------------------------------------------------------------------- /examples/dup.eg: -------------------------------------------------------------------------------- 1 | # Small explosion through dup example. 2 | 3 | using System 4 | 5 | def dup = [ F X -> F (F X) ] 6 | 7 | def inc = [ X -> X + 1 ] 8 | 9 | def main = 10 | dup dup dup dup inc 0 11 | -------------------------------------------------------------------------------- /examples/effect.eg: -------------------------------------------------------------------------------- 1 | # A small experiment with 'effects'. Below is the translation of a 2 | # program `tick + tick` where `tick` would be an effect. 3 | # 4 | # The trick is to factor out the effects with a continuation, and 5 | # use try/catch as a handler or control flow mechanism. 6 | # 7 | # I admit the end result looks gross, this is just to show that the 8 | # semantic model supports this. 9 | 10 | using System 11 | 12 | data tick 13 | 14 | def do_tick = [K -> throw (tick K)] 15 | 16 | def handle_tick = [N (tick K) -> try K N catch handle_tick (N+1)] 17 | 18 | def main = try do_tick [N -> do_tick ((+) N)] catch handle_tick 1 19 | -------------------------------------------------------------------------------- /examples/empty.eg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/egel-lang/egel/b5ca0961a6b51c9802f2a51f9fe0d3faa5154fd4/examples/empty.eg -------------------------------------------------------------------------------- /examples/environment.eg: -------------------------------------------------------------------------------- 1 | # Showcase arguments and environment variables. 2 | # 3 | import "prelude.eg" 4 | 5 | using System 6 | using List 7 | 8 | def prompt = 9 | get_env "EGEL_PS0" 10 | 11 | def main = 12 | (args, prompt) 13 | 14 | -------------------------------------------------------------------------------- /examples/exception.eg: -------------------------------------------------------------------------------- 1 | # Exceptions follow dynamic scope. 2 | 3 | def main = 4 | try let F = [Y -> throw "woot"] in 5 | let G = [H -> try H 1 catch [E -> 3]] in 6 | try G F catch [E -> 1] 7 | catch [E -> 2] 8 | -------------------------------------------------------------------------------- /examples/featureA.eg: -------------------------------------------------------------------------------- 1 | # FeatureA, exploit symbolic rewriting to see that a computation 2 | # failed. 3 | # 4 | # This is shown below. 'f' is a partially defined function which 5 | # only rewrites '0'; i.e., 'f 2' rewrites to 'f 2' since 'f' isn't 6 | # defined on '2'. You can subsequently catch that a computation 7 | # didn't reduce to an integer, and throw that expression as an 8 | # exception. 9 | # 10 | # This could be useful for, for instance, division by zero for 11 | # which no mathematical system seems to have a 'nice' solution. 12 | # I.e., '1/0' simply shouldn't rewrite, though it throws an 13 | # exception at the moment. 14 | 15 | using System 16 | 17 | def f = [ 0 -> 1 ] 18 | 19 | def main = [ X:int -> X | X -> throw X ] (f 2) 20 | -------------------------------------------------------------------------------- /examples/featureB.eg: -------------------------------------------------------------------------------- 1 | # FeatureB, exploit symbolic rewriting to rewrite a half evaluated 2 | # expression. 3 | # 4 | # The following main procedure rewrites in the following manner. 5 | # 6 | # main // main is rewritten 7 | # -> g (f 1) // f is evaluated but fails to rewrite 8 | # -> g (f 1) // g is evaluated 9 | # -> f 1 1 // f is evaluated 10 | # -> 1 11 | # 12 | # This makes the system non-confluent. I have no idea how this 13 | # feature might be exploited in a useful manner, but someone 14 | # is likely to come up with an idea. 15 | 16 | namespace Test ( 17 | 18 | def f = [ X Y -> X ] 19 | 20 | def g = [ (f X) -> f X X ] 21 | 22 | ) 23 | 24 | using Test 25 | 26 | def main = g (f 1) 27 | -------------------------------------------------------------------------------- /examples/featureC.eg: -------------------------------------------------------------------------------- 1 | # FeatureC, exploit symbolic rewriting to define variadic functions. 2 | # 3 | # Egel functions match patterns in a naive manner from first to last. 4 | # You can exploit that feature to implement variadic functions which 5 | # could be nice for, for example, default values. 6 | 7 | namespace Test ( 8 | using System 9 | 10 | def add = 11 | [ X Y -> X + Y 12 | | X -> X + 1 ] 13 | ) 14 | 15 | using Test 16 | 17 | def main = (add 7 8, add 5) 18 | -------------------------------------------------------------------------------- /examples/featureD.eg: -------------------------------------------------------------------------------- 1 | # FeatureD, since we have variadic functions, nullary lambda abstractions 2 | # are supported for orthogonality reasons. 3 | # 4 | # The example rewrites in the following manner: 5 | # f (f f) 6 | # -> f (f 0) 7 | # ->* f 1 8 | # ->* 2 9 | # 10 | # These are probably pretty much unsafe since they always allow a 11 | # combinator to rewrite but might turn out useful in cases. 12 | 13 | namespace Test ( 14 | using System 15 | 16 | def f = 17 | [ X -> X + 1 18 | | -> 0 ] 19 | ) 20 | 21 | using Test 22 | 23 | def main = f (f f) 24 | -------------------------------------------------------------------------------- /examples/featureE.eg: -------------------------------------------------------------------------------- 1 | # FeatureE, recursively eat function arguments. 2 | # 3 | # Another manner to deal with variadic functions. 4 | 5 | namespace Test ( 6 | using System 7 | 8 | def f = 9 | [ X Y -> f (X, Y) # combine two arguments, recurse 10 | | X -> X ] # if only one argument, return that 11 | ) 12 | 13 | using Test 14 | 15 | def main = f 1 2 3 4 16 | -------------------------------------------------------------------------------- /examples/featureF.eg: -------------------------------------------------------------------------------- 1 | # FeatureF, recursively eat function arguments to implement a sum. 2 | # 3 | # Reduces: sum 1 2 3 4 -> sum 3 3 4 -> sum 6 4 -> sum 10 -> 10 4 | 5 | namespace Test ( 6 | using System 7 | 8 | def sum = 9 | [ X Y -> sum (X + Y) 10 | | X -> X ] 11 | ) 12 | 13 | using Test 14 | 15 | def main = sum 1 2 3 4 16 | -------------------------------------------------------------------------------- /examples/featureG.eg: -------------------------------------------------------------------------------- 1 | # FeatureG, often, we can do without explicit tupling but simply 2 | # compose constants. 3 | 4 | import "prelude.eg" 5 | 6 | namespace Test ( 7 | using System 8 | 9 | def fac = 10 | [ 1 -> 1 11 | | N -> N * (fac (N - 1)) ] 12 | 13 | def combine_fac = 14 | [ X -> X (fac X) ] 15 | 16 | ) 17 | 18 | using System 19 | using List 20 | using Test 21 | 22 | def main = map combine_fac (from_to 1 5) 23 | -------------------------------------------------------------------------------- /examples/featureH.eg: -------------------------------------------------------------------------------- 1 | # FeatureH, not sure this is a feature but you do need 2 | # to think why this is correct behavior. 3 | # 4 | # Swapping composed constants. 5 | # 6 | # swap2 (1 2) = (1 2) 7 | # swap2 (1 [X->X]) = swap 1 8 | # swap2 [X->X] = swap 9 | 10 | import "prelude.eg" 11 | 12 | namespace Test ( 13 | using System 14 | 15 | def swap0 = [X Y -> Y X] 16 | def swap2 = swap0 . swap0 17 | ) 18 | 19 | using System 20 | using Test 21 | 22 | def main = (swap2 (1 2), swap2 (1 [X -> X]), swap2 [X -> X]) 23 | -------------------------------------------------------------------------------- /examples/fib.eg: -------------------------------------------------------------------------------- 1 | # The standard Fibonacci function defined in Egel. 2 | # 3 | # see: https://en.wikipedia.org/wiki/Fibonacci_number 4 | 5 | namespace Fibonacci ( 6 | using System 7 | 8 | def fib = 9 | [ 0 -> 1 10 | | 1 -> 1 11 | | N -> fib (N- 2) + fib (N- 1) ] 12 | ) 13 | 14 | using Fibonacci 15 | 16 | def main = fib 40 17 | -------------------------------------------------------------------------------- /examples/fib.egg: -------------------------------------------------------------------------------- 1 | # The standard Fibonacci function defined in an Egel egg 2 | 3 | You can read about this function [here](https://en.wikipedia.org/wiki/Fibonacci_number). 4 | 5 | Start of with defining the Fibonacci function as `fib` in its own namespace. 6 | 7 | ``` 8 | namespace Fibonacci ( 9 | using System 10 | 11 | def fib = 12 | [ 0 -> 1 13 | | 1 -> 1 14 | | N -> fib (N- 2) + fib (N- 1) ] 15 | ) 16 | ``` 17 | 18 | Pull all combinators defined in the Fibonnaci namespace into the global scope. 19 | 20 | ``` 21 | using Fibonacci 22 | ``` 23 | 24 | And now run it. 25 | 26 | ``` 27 | def main = fib 20 28 | ``` 29 | 30 | That's all! 31 | -------------------------------------------------------------------------------- /examples/fib_memo.eg: -------------------------------------------------------------------------------- 1 | # The standard Fibonacci function defined in Egel. 2 | # 3 | # see: https://en.wikipedia.org/wiki/Fibonacci_number 4 | 5 | import "prelude.eg" 6 | 7 | namespace Fibonacci ( 8 | using System, Dict 9 | 10 | def fib = 11 | [ D 0 -> 1 12 | | D 1 -> 1 13 | | D N -> memo D fib (N- 2) + memo D fib (N- 1) ] 14 | ) 15 | 16 | using Fibonacci 17 | 18 | def main = fib Dict::dict 40 19 | -------------------------------------------------------------------------------- /examples/fix.eg: -------------------------------------------------------------------------------- 1 | # Factorial expressed with an eager fixed point combinator. 2 | 3 | import "prelude.eg" 4 | 5 | using System 6 | 7 | # recursive eager fix 8 | def fix = [ F -> F [ X -> (fix F) X ] ] 9 | 10 | def fac0 = fix [FAC 0 -> 1 | FAC N -> N * (FAC (N- 1)) ] 11 | 12 | # non-recursive eager fix 13 | def fix2 = [F -> [X -> F [V -> (X X) V]] [X -> F [V -> (X X) V]]] 14 | 15 | def fac1 = fix2 [FAC 0 -> 1 | FAC N -> N * (FAC (N- 1)) ] 16 | 17 | # alternative to fix points 18 | def fac2 = [ F X -> F F X ] [ F 0 -> 1 | F N -> N * F F (N - 1) ] 19 | 20 | # test it 21 | def main = (fac0 5, fac1 5, fac2 5) 22 | 23 | -------------------------------------------------------------------------------- /examples/fizzbuzz.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example Fizz Buzz. 2 | # 3 | # See: http://rosettacode.org 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | 9 | def fizzbuzz = 10 | [ 100 -> print "100\n" 11 | | N -> 12 | if and ((N%3) == 0) ((N%5) == 0) then 13 | print "fizz buzz, "; fizzbuzz (N+1) 14 | else if (N%3) == 0 then 15 | print "fizz, "; fizzbuzz (N+1) 16 | else if (N%5) == 0 then 17 | print "buzz, "; fizzbuzz (N+1) 18 | else 19 | print N ", "; fizzbuzz (N+1) ] 20 | 21 | def main = fizzbuzz 1 22 | -------------------------------------------------------------------------------- /examples/format.eg: -------------------------------------------------------------------------------- 1 | # Showcase Egel's (C++20) format combinator 2 | 3 | using System 4 | 5 | def main = 6 | format "{} {} {:#06x} {:.2}" "Hello" "World" 1023 3.1415 7 | -------------------------------------------------------------------------------- /examples/frp.eg: -------------------------------------------------------------------------------- 1 | # Showcase Functional Reactive Programmers with simple 2 | # 'generator' functions. 3 | 4 | using System 5 | 6 | # every time you 'push the button' generate a 1 and the next function 7 | def ones = [ X -> (1, ones) ] 8 | 9 | # for every input, add 1 and generate the next function 10 | def plus = [ X -> (1+X, plus) ] 11 | 12 | # compose two generators sequentially 13 | def seq = [ F G X -> [ (X0, F0) -> [ (Y0, G0) -> (Y0, seq F0 G0) ] (G X0) ] (F X) ] 14 | 15 | # compose two generators parallel 16 | def par = [ F G (X,Y) -> [ (X0, F0) -> [ (Y0, G0) -> ((X0,Y0), par F0 G0) ] (G Y) ] (F X) ] 17 | 18 | # test it 19 | def main = seq ones plus "dummy" 20 | -------------------------------------------------------------------------------- /examples/genfib.eg: -------------------------------------------------------------------------------- 1 | # generator example of Fibonnaci 2 | 3 | import "prelude.eg" 4 | import "generator.eg" 5 | 6 | using System 7 | using Gen 8 | 9 | def fib = 10 | cons 1 [ _ -> cons 1 [ _ -> Gen::zip_with (+) fib (Gen::tail fib) ] ] 11 | 12 | def main = 13 | to_list (take 25 fib) 14 | 15 | -------------------------------------------------------------------------------- /examples/hailstone.eg: -------------------------------------------------------------------------------- 1 | # Hailstone numbers are a mathematical mystery. The following 2 | # function f(1) = 1, f(x) = x/2 iff x is even, f(x) = x*3 + 1 3 | # otherwise, has been proven to evaluate to 1 for an absurd amount of 4 | # numbers, but no proof of that property is known. 5 | # 6 | # The following program is taken from a programming contest. 7 | # 0. show in how many step hailstone(27) reduces to 1. 8 | # 1. show the first 4, and last 4, numbers of that sequence. 9 | # 2. determine the number with the longest path below 100.000. 10 | # 11 | # This program takes a long while to terminate. 12 | # 13 | # see: https://en.wikipedia.org/wiki/Collatz_conjecture 14 | 15 | import "prelude.eg" 16 | 17 | namespace Hailstone ( 18 | 19 | using System 20 | using List 21 | 22 | def even = [ N -> (N%2) == 0 ] 23 | 24 | def hailstone = 25 | [ 1 -> {1} 26 | | N -> if even N then cons N (hailstone (N/2)) 27 | else cons N (hailstone (N * 3 + 1)) ] 28 | 29 | def hailpair = 30 | [ N -> (N, length (hailstone N)) ] 31 | 32 | def hailmax = 33 | [ (N, NMAX) (M, MMAX) -> if (NMAX < MMAX) then (M, MMAX) else (N, NMAX) ] 34 | 35 | def largest = 36 | [ 1 -> (1, 1) 37 | | N -> 38 | let M0 = hailpair N in 39 | let M1 = largest (N - 1) in 40 | hailmax M0 M1 ] 41 | ) 42 | 43 | using System 44 | using List 45 | using Hailstone 46 | 47 | def task0 = let H27 = hailstone 27 in length H27 48 | 49 | def task1 = 50 | let H27 = hailstone 27 in 51 | let L = length H27 in 52 | (take 4 H27, drop (L - 4) H27) 53 | 54 | def task2 = largest 100000 55 | 56 | def main = (task0, task1, task2) 57 | 58 | -------------------------------------------------------------------------------- /examples/hanoi.eg: -------------------------------------------------------------------------------- 1 | # Towers of Hanoi example. 2 | # 3 | # Via numberphile [https://www.youtube.com/watch?v=8lhxIOAfDss]. 4 | 5 | using System 6 | 7 | def move = 8 | [ F T -> print (format "Move disc from {} to {}\n" F T) ] 9 | 10 | def hanoi = 11 | [ 0 F H T -> none 12 | | N F H T -> hanoi (N - 1) F T H; move F T; hanoi (N - 1) H F T ] 13 | 14 | def main = hanoi 4 "A" "B" "C" 15 | -------------------------------------------------------------------------------- /examples/harper.eg: -------------------------------------------------------------------------------- 1 | # A reducer for Harper's nor calculus[1]. 2 | # 3 | # A problem of Harper's semantics is that it relies on an optimal 4 | # strategy. 5 | # I.e., given `nor (fix e.E) (fix e'.E')` it's unclear what option to 6 | # reduce first. 7 | # 8 | # This reducer evaluates by 'exploding' a term, it will step both 9 | # branches of a nor, recursively. 10 | # 11 | # [1] https://www.youtube.com/watch?v=8cXl2Tfhy_Q 12 | 13 | using System 14 | 15 | data ff, tt, var, nor, fx 16 | 17 | def subs = 18 | [ X E (var Y) -> if X == Y then E else var Y 19 | | X E (nor E0 E1) -> nor (subs X E E0) (subs X E E1) 20 | | X E (fx Y E0) -> if (X == Y) then fx Y E0 else fx Y (subs X E E0) 21 | | X E Z -> Z ] 22 | 23 | def step = 24 | [ tt -> tt | ff -> ff 25 | | (nor _ tt) -> ff 26 | | (nor tt _) -> ff 27 | | (nor ff ff) -> tt 28 | | (nor E0 E1) -> nor (step E0) (step E1) 29 | | (fx X E0) -> subs X (fx X E0) E0 ] 30 | 31 | def eval = 32 | [ tt -> tt | ff -> ff | E -> eval (step E) ] 33 | 34 | def latch = [ R S -> fx 'x' (nor R (nor (var 'x') S)) ] 35 | 36 | def main = eval (latch ff tt) 37 | 38 | -------------------------------------------------------------------------------- /examples/helloworld.eg: -------------------------------------------------------------------------------- 1 | 2 | def main = "hello world" 3 | 4 | -------------------------------------------------------------------------------- /examples/hexdump.eg: -------------------------------------------------------------------------------- 1 | # A small hexdump thing to show byte input 2 | # 3 | import "prelude.eg" 4 | 5 | using System 6 | using OS 7 | using String 8 | using List (map) 9 | 10 | def fix = [ F -> F [ X -> (fix F) X ] ] 11 | 12 | def read_file = 13 | [ FN -> 14 | let CHAN = open_in FN in 15 | let LINES = fix [ F CHAN -> let B = read_byte CHAN in 16 | if eof CHAN then {} else {B|F CHAN} ] CHAN 17 | in close CHAN; LINES ] 18 | 19 | def hexit_to_string = 20 | [ 0 -> "0" | 1 -> "1" | 2 -> "2" | 3 -> "3" 21 | | 4 -> "4" | 5 -> "5" | 6 -> "6" | 7 -> "7" 22 | | 8 -> "8" | 9 -> "9" | 10 -> "a" | 11 -> "b" 23 | | 12 -> "c" | 13 -> "d" | 14 -> "e" | 15 -> "f" ] 24 | 25 | def int_to_hex = 26 | [ N -> "0x" + (hexit_to_string (N & 15)) + (hexit_to_string ((N >> 4) & 15)) ] 27 | 28 | def main = 29 | let T = read_file "hexdump.eg" in map int_to_hex T 30 | 31 | -------------------------------------------------------------------------------- /examples/ieee754.eg: -------------------------------------------------------------------------------- 1 | # Egel fully supports ieee754 2 | 3 | using System 4 | 5 | def main = 6 | 9007199254740993.0 + 1.0 7 | -------------------------------------------------------------------------------- /examples/knuthshuffle.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example Knuth Shuffle. 2 | # 3 | # See: http://rosettacode.org 4 | 5 | import "prelude.eg" 6 | import "random.ego" 7 | 8 | using System 9 | using List 10 | using Math 11 | 12 | def swap = 13 | [ (I J) XX -> insert I (nth J XX) (insert J (nth I XX) XX) ] 14 | 15 | def shuffle = 16 | [ XX -> 17 | let INDICES = reverse (from_to 0 ((length XX) - 1)) in 18 | let SWAPS = map [ I -> I (between 0 I) ] INDICES in 19 | foldr [I J -> swap I J] XX SWAPS ] 20 | 21 | def main = shuffle (from_to 1 9) 22 | -------------------------------------------------------------------------------- /examples/lambda2sk.eg: -------------------------------------------------------------------------------- 1 | # lambda to SK translator 2 | 3 | import "prelude.eg" 4 | 5 | using System 6 | using List 7 | 8 | data lam, app, var 9 | data s, k 10 | 11 | def fv = 12 | [ (var X) -> {X} 13 | | (app E0 E1) -> (fv E0) ++ (fv E1) 14 | | (lam X E) -> filter [ V -> not (V == X) ] (fv E) 15 | | _ -> {} ] 16 | 17 | def free = [ V E -> any [ V0 -> V == V0 ] (fv E) ] 18 | 19 | def translate = 20 | [ (var X) -> var X 21 | | (app E0 E1) -> app (translate E0) (translate E1) 22 | | (lam X E) -> 23 | [ false -> app k (translate E) 24 | | true -> [ (lam X0 (var X1)) -> [ true -> app (app s k) k ] (X0 == X1) 25 | | (lam X (lam Y E)) -> [ true -> translate (lam X (translate (lam Y E))) ] (free X E) 26 | | (lam X (app E0 E1))-> [ true -> app (app s (translate (lam X E0))) (translate (lam X E1)) ] 27 | (or (free X E0) (free X E1)) ] (lam X E) ] (free X E) 28 | | k -> k | s -> s ] 29 | 30 | def main = 31 | translate (lam 0 (app (lam 1 (app (var 1) (var 1))) (lam 1 (app (var 1) (var 1))))) 32 | -------------------------------------------------------------------------------- /examples/lambool.eg: -------------------------------------------------------------------------------- 1 | # lambda terms for booleans 2 | 3 | def true = [ T F -> T ] 4 | def false = [ T F -> F ] 5 | 6 | def if_ = [ P T F -> P T F ] 7 | 8 | def and = [ X Y -> if_ X Y false ] 9 | def or = [ X Y -> if_ X true Y ] 10 | def not = [ X -> if_ X false true ] 11 | 12 | def main = 13 | (and true false, not (not (or true false))) 14 | 15 | # (false, true) 16 | 17 | -------------------------------------------------------------------------------- /examples/lazybinop.eg: -------------------------------------------------------------------------------- 1 | # Showcase 'lazy' or 'short circuited' binary operators. 2 | # 3 | # `expr0 && expr1` is just shorthand for `expr0 && \_ -> expr1`. 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | 9 | def main = 10 | (false && "this never evals", true || "neither does this") 11 | -------------------------------------------------------------------------------- /examples/life.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example Conway's Life. 2 | # 3 | # See: http://rosettacode.org 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | using List 9 | 10 | def boardsize = 5 11 | 12 | def empty = [ X Y -> 0 ] 13 | 14 | def insert = 15 | [ X Y BOARD -> 16 | [ X0 Y0 -> if and (X0 == X) (Y0 == Y) then 1 17 | else BOARD X0 Y0 ] ] 18 | 19 | def coords = 20 | let R = from_to 0 (boardsize - 1) in 21 | [ XX YY -> map (\X -> map (\Y -> X Y) YY) XX ] R R 22 | 23 | def printcell = 24 | [ 0 -> print ". " 25 | | _ -> print "* " ] 26 | 27 | def printboard = 28 | [ BOARD -> 29 | foldl [_ XX -> map [(X Y) -> printcell (BOARD X Y)] XX; print "\n" ] none coords ] 30 | 31 | def count = 32 | [ BOARD X Y -> 33 | (BOARD (X - 1) (Y - 1)) + (BOARD (X) (Y - 1)) + (BOARD (X+1) (Y - 1)) + 34 | (BOARD (X - 1) Y) + (BOARD (X+1) Y) + 35 | (BOARD (X - 1) (Y+1)) + (BOARD (X) (Y+1)) + (BOARD (X+1) (Y+1)) ] 36 | 37 | def next = 38 | [ 0 N -> if N == 3 then 1 else 0 39 | | _ N -> if or (N == 2) (N == 3) then 1 else 0 ] 40 | 41 | def updateboard = 42 | [ BOARD -> 43 | let XX = map (\(X Y) -> X Y (BOARD X Y) (count BOARD X Y)) (flatten coords) in 44 | let YY = map (\(X Y C N) -> X Y (next C N)) XX in 45 | foldr [(X Y 0) BOARD -> BOARD | (X Y _) BOARD -> insert X Y BOARD ] empty YY ] 46 | 47 | def blinker = 48 | (insert 1 2) . (insert 2 2) . (insert 3 2) 49 | 50 | def main = 51 | let GEN0 = blinker empty in 52 | let GEN1 = updateboard GEN0 in 53 | let GEN2 = updateboard GEN1 in 54 | foldl [_ G -> print "generation:\n"; printboard G ] none {GEN0, GEN1, GEN2} 55 | -------------------------------------------------------------------------------- /examples/mandelbrot.eg: -------------------------------------------------------------------------------- 1 | # The Awesome Mandelbrot One-Liner by b_jonas (freenode). 2 | # 3 | 4 | import "prelude.eg" 5 | 6 | using Math 7 | using System 8 | 9 | def main = 10 | [H->H (-0.9);H (-0.7);H (-0.5);H (-0.3);H (-0.1);H 0.1;H 0.3;H 0.5;H 0.7;H 0.9][Y->[P->[H->H H""5][H B X->[true->H H(B+P X)(X- 1)|_->print B "\n"](-20[P->[0 0->" "|0 1->"."|1 0->"'"|1 1->":"](P Y)(P(Y+ 0.1))]([P X Y->P P 0 X Y 0.0 0.0][P S X Y U V->[true->P P(S+1)X Y(X+U*U-V*V)(Y+2.0*U*V)|_->[true->1|_->0]((abs U)+(abs V)<2.0)](S<11)](0.1*to_float X))]] 11 | 12 | -------------------------------------------------------------------------------- /examples/million.eg: -------------------------------------------------------------------------------- 1 | # Basic test of foldl for performance testing. 2 | # 3 | # Egel isn't very fast. This takes around 16 seconds on my 4 | # laptop. But it works. 5 | # 6 | # now 7 seconds on MacOS arm64 7 | 8 | @""" 9 | The million test is my goto microbenchmark for the interpreter's 10 | performance. The goal is to have this test run in 200ms, but 11 | at the moment the interpreter is one order off. 12 | """ 13 | 14 | 15 | import "prelude.eg" 16 | 17 | using System 18 | using List 19 | 20 | def main = 21 | @"calculate the sum of a million numbers" 22 | foldl (+) 0 (from_to 0 1000000) 23 | -------------------------------------------------------------------------------- /examples/monad.eg: -------------------------------------------------------------------------------- 1 | # A small state monad example. 2 | 3 | import "prelude.eg" 4 | 5 | namespace Calculate ( 6 | 7 | def return = [ A S -> (A,S) ] 8 | 9 | def >>== = [ F G S -> [(A,S) -> G A S] (F S) ] 10 | 11 | def put = [ X S -> (System::none, X) ] 12 | 13 | def get = [ S -> (S, S) ] 14 | 15 | def run = [ F S -> [(A,S) -> S] (F S) ] 16 | 17 | ) 18 | 19 | using Calculate 20 | using System 21 | 22 | def main = 23 | run (return 1 >>== \A -> put (A+3) >>== \_ -> get ) 0 24 | -------------------------------------------------------------------------------- /examples/multistring.eg: -------------------------------------------------------------------------------- 1 | # Strings can be defined over multiple lines. 2 | # 3 | # A multiline string starts with '"""\n' and ends with '"""', all 4 | # codepoints in between are the content of the string. 5 | 6 | def empty = """ 7 | """ 8 | 9 | def multi = """ 10 | this is a 11 | string 12 | over 13 | multiple 14 | lines 15 | """ 16 | 17 | def main = (empty, multi) 18 | -------------------------------------------------------------------------------- /examples/nqueens.eg: -------------------------------------------------------------------------------- 1 | # The N-Queens problem as translated from Haskell to Egel. 2 | # 3 | # I only have a rough idea how the following program computes its 4 | # result. This example shows that it often should be possible to copy 5 | # a simple Haskell program verbatim and have it run in egel. 6 | # 7 | # This program takes a mediocre time to run, mostly because it 8 | # cannot exploit lazy evaluation which is what the Haskell program 9 | # is optimized for. 10 | 11 | 12 | # Taken from Haskell code. 13 | # 14 | # queenPuzzle :: Int -> Int -> [[Int]] 15 | # queenPuzzle nRows nCols 16 | # | nRows <= 0 = [[]] 17 | # | otherwise = 18 | # foldr 19 | # (\solution a -> 20 | # a ++ 21 | # foldr 22 | # (\iCol b -> 23 | # if safe (nRows - 1) iCol solution 24 | # then b ++ [solution ++ [iCol]] 25 | # else b) 26 | # [] 27 | # [1 .. nCols]) 28 | # [] 29 | # (queenPuzzle (nRows - 1) nCols) 30 | # where 31 | # safe iRow iCol solution = 32 | # True `notElem` 33 | # zipWith 34 | # (\sc sr -> 35 | # (iCol == sc) || (sc + sr == iCol + iRow) || (sc - sr == iCol - iRow)) 36 | # solution 37 | # [0 .. iRow - 1] 38 | 39 | import "prelude.eg" 40 | 41 | namespace NQueens ( 42 | 43 | using System 44 | using List 45 | 46 | def nqueens = 47 | [ 0 NCOLS -> {{}} 48 | | NROWS NCOLS -> 49 | foldr 50 | [ SOLUTION A -> 51 | A ++ 52 | (foldr 53 | [ICOL B -> 54 | if safe (NROWS - 1) ICOL SOLUTION 55 | then B ++ ({SOLUTION ++ {ICOL}}) 56 | else B] 57 | nil 58 | (from_to 1 NCOLS)) ] 59 | nil 60 | (nqueens (NROWS - 1) NCOLS) ] 61 | 62 | def safe = 63 | [ IROW ICOL SOLUTION -> 64 | not_elem true 65 | (zip_with 66 | [SC SR -> 67 | or (ICOL == SC) 68 | (or (SC + SR == ICOL + IROW) 69 | (SC - SR == ICOL - IROW))] 70 | SOLUTION 71 | (from_to 0 (IROW - 1))) ] 72 | ) 73 | 74 | using List 75 | using NQueens 76 | 77 | def main = nqueens 8 8 78 | 79 | -------------------------------------------------------------------------------- /examples/par.eg: -------------------------------------------------------------------------------- 1 | # Egel's par construct. 2 | # 3 | # Dropped `par` for `async/await`. 4 | 5 | import "prelude.eg" 6 | 7 | namespace Fibonnaci ( 8 | using System 9 | 10 | def par = 11 | [ F G -> let X = async F in let Y = async G in (await X, await Y) ] 12 | 13 | def fib = 14 | [ 0 -> 0 15 | | 1 -> 1 16 | | N -> fib (N- 2) + fib (N- 1) ] 17 | 18 | def pfib = 19 | [ 0 -> 0 20 | | 1 -> 1 21 | | N -> [ (N0, N1) -> N0 + N1 ] (par [_ -> pfib (N - 1) ] [_ -> pfib (N - 2)]) ] 22 | 23 | ) 24 | 25 | using Fibonnaci 26 | using System 27 | 28 | def main = pfib 15 29 | -------------------------------------------------------------------------------- /examples/parser.eg: -------------------------------------------------------------------------------- 1 | # Searching is calculating and parsing is searching. 2 | # 3 | # A recursive descent parser example for a trivial language which 4 | # makes use of search combinators. 5 | # 6 | 7 | import "prelude.eg" 8 | import "search.eg" 9 | 10 | using System 11 | using List 12 | using Search 13 | using Regex (compile, look_at, look_match, replace) 14 | 15 | data number, plus, mul 16 | data lparen, rparen, white 17 | 18 | # lexing with regular expessions 19 | 20 | val regex_number = compile "[0-9]+" 21 | val regex_plus = compile "\\+" 22 | val regex_mul = compile "\\*" 23 | val regex_lparen = compile "\\(" 24 | val regex_rparen = compile "\\)" 25 | val regex_white = compile "[ \\n\\t]+" 26 | 27 | def lexeme_table = 28 | { (regex_number, [ S -> number (to_int S)] ), 29 | (regex_plus, [ S -> plus ]), 30 | (regex_mul, [ S -> mul ]), 31 | (regex_lparen, [ S -> lparen ]), 32 | (regex_rparen, [ S -> rparen ]), 33 | (regex_white, [ S -> white ]) } 34 | 35 | def lexify_front = 36 | [ nil S -> throw (format "lexer error at: {}" S) 37 | | (cons (PAT, A) AA) S -> 38 | if look_at PAT S then 39 | let L = look_match PAT S in 40 | let S = replace PAT "" S in (A L, S) 41 | else lexify_front AA S ] 42 | 43 | def lexify = 44 | [ TABLE "" -> nil 45 | | TABLE S -> 46 | let (L, S) = lexify_front TABLE S in 47 | let LL = lexify TABLE S in 48 | cons L LL ] 49 | 50 | # parsing 51 | 52 | def parse_lexeme = 53 | [ P nil -> fail nil 54 | | P (cons L LL) -> if P L then success L LL else fail LL ] 55 | 56 | def parse_number = 57 | parse_lexeme [ (number N) -> true | _ -> false ] <@> \(number N) -> N 58 | 59 | def parse_plus = 60 | parse_lexeme [ plus -> true | _ -> false ] 61 | 62 | def parse_mul = 63 | parse_lexeme [ mul -> true | _ -> false ] 64 | 65 | def parse_lparen = 66 | parse_lexeme [ lparen -> true | _ -> false ] 67 | 68 | def parse_rparen = 69 | parse_lexeme [ rparen -> true | _ -> false ] 70 | 71 | def parse_primary = 72 | parse_number <+> 73 | (parse_lparen <*> \_ -> parse_expression <*> \N -> parse_rparen <@> \_ -> N) 74 | 75 | def parse_expression = 76 | parse_primary \N0 -> ( 77 | (parse_plus <*> \_ -> parse_expression <@> \N1 -> N0 + N1) 78 | <+> 79 | (parse_mul <*> \_ -> parse_expression <@> \N1 -> N0 * N1) 80 | ) 81 | 82 | def parse = 83 | [ P -> search P 84 | [ T LL -> T ] 85 | [ F LL -> throw (format "parse failed with {} at {}" F LL) ] 86 | [ E LL -> throw (format "parse error {} at {}" E LL) ] 87 | ] 88 | 89 | # evaluate 90 | 91 | def evaluate = 92 | [ S -> 93 | let LL = lexify lexeme_table S in 94 | let LL = filter [ white -> false | _ -> true ] LL in 95 | let P = parse parse_expression LL in 96 | P ] 97 | 98 | def main = evaluate "(1 + 7) * 3" 99 | -------------------------------------------------------------------------------- /examples/proc.eg: -------------------------------------------------------------------------------- 1 | # Egel's process abstraction. 2 | # 3 | # For the moment, I only implemented a number of simplistic 4 | # combinators. 5 | # 6 | # A procces is a mealy machine which takes an input and 7 | # and results in an output and a continuation. 8 | # 9 | # `proc f` - create a process object from f 10 | # `send p m` - send message m to process p 11 | # `recv p` - receive a message from process p (blocks) 12 | # `halt p` - halt process p 13 | 14 | import "prelude.eg" 15 | 16 | using System 17 | 18 | def sum = [ X Y -> let Z = X + Y in (Z, sum Z) ] 19 | 20 | val p = proc (sum 0) 21 | 22 | def main = 23 | send p 2; 24 | send p 3; 25 | print "received: " (recv p) "\n"; 26 | print "received: " (recv p) "\n"; 27 | halt p 28 | -------------------------------------------------------------------------------- /examples/ref.eg: -------------------------------------------------------------------------------- 1 | # Egel supports references though I didn't implement a nice syntax for 2 | # that yet. 3 | # 4 | # `ref x` -- create a ref object from x 5 | # `setref r x` -- set r to x 6 | # `getref r` -- get contents of r 7 | # 8 | # References are unsafe since you can form cycles. 9 | # 10 | # References are not thread safe. 11 | 12 | using System 13 | 14 | val myglobal = ref 0 15 | 16 | def main = 17 | set_ref myglobal [ X -> X ]; 18 | get_ref myglobal "Hello" 19 | -------------------------------------------------------------------------------- /examples/reversalgame.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example Number Reversal Game. 2 | # 3 | # See: http://rosettacode.org 4 | 5 | import "prelude.eg" 6 | import "random.ego" 7 | 8 | using System 9 | using List 10 | using Math 11 | 12 | def swap = 13 | [ (I J) XX -> insert I (nth J XX) (insert J (nth I XX) XX) ] 14 | 15 | def shuffle = 16 | [ XX -> 17 | let INDICES = reverse (from_to 0 ((length XX) - 1)) in 18 | let SWAPS = map [ I -> I (between 0 I) ] INDICES in 19 | foldr [I J -> swap I J] XX SWAPS ] 20 | 21 | def prompt = 22 | [ XX TURN -> 23 | let _ = print TURN ". " in 24 | let _ = map [ X -> print X " " ] XX in 25 | let _ = print " : " in 26 | to_int get_line ] 27 | 28 | def game = 29 | [ GOAL SHUFFLE TURN -> 30 | if SHUFFLE == GOAL then 31 | let _ = print "the goal was " in 32 | let _ = map [ X -> print X " " ] GOAL in 33 | print "\nit took you " TURN " turns\n" 34 | else 35 | let N = prompt SHUFFLE TURN in 36 | let YY = (reverse (take N SHUFFLE)) ++ (drop N SHUFFLE) in 37 | game GOAL YY (TURN + 1) ] 38 | 39 | def main = 40 | let XX = from_to 1 9 in game XX (shuffle XX) 0 41 | -------------------------------------------------------------------------------- /examples/reverse_string.eg: -------------------------------------------------------------------------------- 1 | # Rosetta Code example Reverse String. 2 | 3 | import "prelude.eg" 4 | 5 | using System 6 | using String (to_chars, from_chars) 7 | using List 8 | 9 | def main = (from_chars . reverse . to_chars) "asdf" 10 | -------------------------------------------------------------------------------- /examples/serialize.eg: -------------------------------------------------------------------------------- 1 | # Serialization example 2 | 3 | using System 4 | 5 | def main = 6 | deserialize (serialize {none, 1, 3.14, '∀', "hello!"}) 7 | -------------------------------------------------------------------------------- /examples/shebang.eg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env egel 2 | 3 | def main = 4 | "hello world!" 5 | -------------------------------------------------------------------------------- /examples/sieve.eg: -------------------------------------------------------------------------------- 1 | # A prime sieve using streams. 2 | 3 | import "prelude.eg" 4 | 5 | using System 6 | 7 | def fix = [ F -> F [ X -> (fix F) X ] ] 8 | 9 | def nums = 10 | let NUMS = fix [ NUMS X _ -> (X, NUMS (X + 1)) ] in 11 | NUMS 2 12 | 13 | def filter = 14 | [ F S I -> let (O, S) = S I in 15 | if F O then (O, filter F S) else filter F S I ] 16 | 17 | def sieve = 18 | [ NN I -> 19 | let (N, NN) = NN I in 20 | (N, sieve (filter [ X -> not ((X % N) == 0) ] NN)) ] 21 | 22 | def print_all = 23 | [ S -> let (X, S) = S none in print X "\n"; print_all S ] 24 | 25 | def main = 26 | print_all (sieve nums) 27 | 28 | -------------------------------------------------------------------------------- /examples/sieveK.eg: -------------------------------------------------------------------------------- 1 | # A prime sieve through continuations/callbacks. 2 | # 3 | # Generators through three primitives: 4 | # yield n k gen - yield n to k and proceed with gen 5 | # step gen k - step generator gen then continue with k 6 | # run k gen - run consumer k on producer gen 7 | 8 | 9 | import "prelude.eg" 10 | 11 | using System 12 | 13 | def yield = [ N K GEN -> K N GEN ] 14 | 15 | def step = [ GEN K -> GEN K ] 16 | 17 | def run = [ K GEN -> GEN K ] 18 | 19 | def fix = [ F -> F [ X -> (fix F) X ] ] 20 | 21 | def nums = 22 | let NUMS = fix [ NUMS N K -> yield N K (NUMS (N+1)) ] in NUMS 2 23 | 24 | def filter = 25 | [ F GEN K -> step GEN 26 | [ N GEN -> if F N then yield N K (filter F GEN) else (filter F GEN K) ] ] 27 | 28 | def sieve = 29 | [ GEN K -> step GEN 30 | [ N GEN -> yield N K (sieve (filter [ X -> not (X % N == 0) ] GEN)) ] ] 31 | 32 | def print_all = 33 | [ N GEN -> print N "\n"; step GEN print_all ] 34 | 35 | def main = 36 | run print_all (sieve nums) 37 | -------------------------------------------------------------------------------- /examples/slides.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | 3 | namespace Slides ( 4 | using List 5 | using System 6 | 7 | def show_slide = 8 | [ N SS -> OS::exec (format "clear; cat {}" (nth N SS)) |> [X -> print X] ] 9 | 10 | def next_slide = 11 | [ N M -> 12 | [' ' -> (N+1) % M 13 | |'\n' -> (N+1) % M 14 | |'b' -> (((N - 1) % M) + M) % M 15 | |'q' -> none 16 | |_ -> N ] OS::get_key ] 17 | 18 | def show_slides = 19 | [ N M SS -> show_slide N SS; next_slide N M ||> [N -> show_slides N M SS] ] 20 | ) 21 | 22 | def main = 23 | let SS = System::iter 2 List::tail (System::args) in 24 | [ {} -> System::print "usage: slides fn0..fnn\n" 25 | | SS -> Slides::show_slides 0 (List::length SS) SS ] SS 26 | -------------------------------------------------------------------------------- /examples/sort.eg: -------------------------------------------------------------------------------- 1 | # Testing basic IO. 2 | # 3 | # This script reads standard input and prints it sorted to standard output. 4 | 5 | import "prelude.eg" 6 | 7 | using System 8 | using OS 9 | using List 10 | 11 | def input = 12 | let L = read_line stdin in 13 | if eof stdin then nil else cons L input 14 | 15 | def output = 16 | [ nil -> none 17 | | (cons L LL) -> let _ = print L "\n" in output LL ] 18 | 19 | def split = 20 | [ nil -> (nil, nil) 21 | | (cons X nil) -> (nil, cons X nil) 22 | | (cons X0 (cons X1 XX)) -> 23 | let (YY, ZZ) = split XX in (cons X0 YY, cons X1 ZZ) ] 24 | 25 | def merge = 26 | [ XX nil -> XX 27 | | nil YY -> YY 28 | | (cons X XX) (cons Y YY) -> 29 | if X < Y then cons X (merge XX (cons Y YY)) else cons Y (merge (cons X XX) YY) ] 30 | 31 | def sort = 32 | [ nil -> nil 33 | | (cons X nil) -> cons X nil 34 | | XX -> let (YY, ZZ) = split XX in merge (sort YY) (sort ZZ) ] 35 | 36 | def main = 37 | output (sort input) 38 | -------------------------------------------------------------------------------- /examples/stall.eg: -------------------------------------------------------------------------------- 1 | # The `stall` combinator defers evaluation to later. 2 | # 3 | # It has limited use but is needed to implement guards, since that 4 | # can be translated to a number of checks, which when fail returns 5 | # the defered term. 6 | 7 | import "prelude.eg" 8 | 9 | using System 10 | 11 | def inf = [X -> print "rewrite"; stall inf X] # do something 12 | 13 | def main = stall id 42 # rewrites to `id 42` 14 | -------------------------------------------------------------------------------- /examples/stategame.eg: -------------------------------------------------------------------------------- 1 | # A simple stategame showcasing calculations. 2 | # 3 | # The game is to produce a number from a string of {'a','b','c'}*. 4 | # The game state is a counter and whether the game is being played. 5 | # 6 | # Initially the game is off and the counter is 0. 7 | # 8 | # An 'a' increases the counter, a 'b' decreases the counter, a 'c' 9 | # toggles whether the game is being played. 10 | # 11 | # E.g., 12 | # 'ab' = 0 13 | # 'ca' = 1 14 | # 'cabca' = 0 15 | 16 | import "prelude.eg" 17 | import "calculate.eg" 18 | 19 | using System 20 | using String (to_chars) 21 | using List 22 | using Calculate 23 | 24 | 25 | # the state the game manipulates 26 | 27 | val game_state_start = (false, 0) 28 | 29 | def game_state_toggle = 30 | [ (ON, SCORE) -> (not ON, SCORE) ] 31 | 32 | def game_state_increase = 33 | [ (ON, SCORE) -> (ON, SCORE + 1) ] 34 | 35 | def game_state_decrease = 36 | [ (ON, SCORE) -> (ON, SCORE - 1) ] 37 | 38 | # state observers 39 | 40 | def get_score = 41 | [ (ON, SCORE) -> return SCORE (ON, SCORE) ] 42 | 43 | def get_on = 44 | [ (ON, SCORE) -> return ON (ON, SCORE) ] 45 | 46 | # the game 47 | 48 | def game_loop = 49 | [ nil -> 50 | get_score 51 | | (cons C CC) -> 52 | get_on <* \ON -> 53 | ([ 'a' -> if ON then skip <+ game_state_increase else skip 54 | | 'b' -> if ON then skip <+ game_state_decrease else skip 55 | | 'c' -> skip <+ game_state_toggle ] C) <* \_ -> game_loop CC ] 56 | 57 | 58 | def play_game = 59 | [ S -> 60 | let CC = to_chars S in 61 | run (game_loop CC) game_state_start ] 62 | 63 | def main = play_game "abcaabcbba" 64 | -------------------------------------------------------------------------------- /examples/stress.eg: -------------------------------------------------------------------------------- 1 | # Feed large terms into the parser and evaluate them. 2 | # The goal is to be able to do this for several million. 3 | 4 | import "prelude.eg" 5 | 6 | using System 7 | using List 8 | 9 | def main = 10 | from_to 0 100000 |> to_text |> eval |> sum 11 | -------------------------------------------------------------------------------- /examples/time.eg: -------------------------------------------------------------------------------- 1 | # WARNING: this abstraction turned out different than I expected. 2 | # Use at your own risk. 3 | 4 | import "prelude.eg" 5 | 6 | using System 7 | using Time 8 | 9 | val t0 = now (clock none) 10 | 11 | def main = 12 | print (format "{:%F %T %Z}\n" (t0 + hours 1)); 13 | print (format "{:%Y-%m-%d %H:%M:%S}\n" (local_time t0)); 14 | print (format "{:%Y-%m-%d %H:%M:%S}\n" (gm_time t0)); 15 | none 16 | -------------------------------------------------------------------------------- /examples/tinydb.eg: -------------------------------------------------------------------------------- 1 | # TinyDB: a file system object database 2 | 3 | # * a database is a directory 4 | # * a subdirectory is a table 5 | # * a file is a record, the filename is the key, the content is the 6 | # value 7 | # * a transaction is a computation which holds on to a lock 8 | 9 | # This is just me fooling around with serialization and not 10 | # intendet to be used. 11 | 12 | import "prelude.eg" 13 | 14 | namespace Tiny ( 15 | 16 | using System 17 | using List 18 | 19 | data database 20 | 21 | ## Tiny::db_tiny h p - tiny database object from a home and path 22 | def db_tiny = 23 | [ HOME PATH -> database HOME PATH ] 24 | 25 | ## Tiny::db_home db - retrieve the home location 26 | def db_home = 27 | [ (database HOME PATH) -> HOME ] 28 | 29 | ## Tiny::db_path db - retrieve the database name 30 | def db_path = 31 | [ (database HOME PATH) -> PATH ] 32 | 33 | ## Tiny::db_location db - path to database 34 | def db_location = 35 | [ DB -> OS::concat_with (db_home DB) (db_path DB) ] 36 | 37 | ## Tiny::db_location db table - path to a table 38 | def db_table_location = 39 | [ DB TABLE -> OS::concat_with (db_location DB) TABLE ] 40 | 41 | ## Tiny::db_exists db - does this database exist 42 | def db_exists = 43 | [ DB -> OS::is_directory (db_location DB) ] 44 | 45 | ## Tiny::db_create db - create the database 46 | def db_create = 47 | [ DB -> OS::create_directory (db_location DB) ] 48 | 49 | ## Tiny::db_list_tables db - list all tables in database 50 | def db_list_tables = 51 | [ DB -> 52 | let N = String::length (db_location DB) in 53 | let FF = OS::directory (db_location DB) in 54 | map (String::remove 0 (N+1)) FF ] 55 | 56 | ## Tiny::db_create_table db table - create a table in the database 57 | def db_create_table = 58 | [ DB TABLE -> 59 | let LOC = db_table_location DB TABLE in 60 | if OS::exists LOC then throw "table exists" 61 | else OS::create_directory LOC ] 62 | 63 | ## Tiny::db_drop_table db table - drop a table from the database 64 | def db_drop_table = 65 | [ DB TABLE -> 66 | OS::remove_all (db_table_location DB TABLE) ] 67 | 68 | ## Tiny::db_list_records db table - list all records from a table 69 | def db_list_records = 70 | [ DB TABLE -> 71 | let N = String::length (db_table_location DB TABLE) in 72 | let FF = OS::directory (db_table_location DB TABLE) in 73 | map OS::stem FF ] 74 | 75 | ## Tiny::db_read_table db table - read all table returning key/value pairs 76 | def db_read_table = 77 | [ DB TABLE -> 78 | let LOC = db_table_location DB TABLE in 79 | let KEYS = db_list_records DB TABLE in 80 | map [KEY -> (KEY, db_read_record DB TABLE KEY)] KEYS ] 81 | 82 | ## Tiny::db_record_location db table key - record location on the filesystem 83 | def db_record_location = 84 | [ DB TABLE KEY -> 85 | let R = OS::concat_with (db_table_location DB TABLE) KEY 86 | in OS::concat R ".k" ] 87 | 88 | ## Tiny::db_create_record db table key value - store a new key/value 89 | def db_create_record = 90 | [ DB TABLE KEY VALUE -> 91 | let FN = db_record_location DB TABLE KEY in 92 | if OS::exists FN then throw "record exists" 93 | else write_object FN VALUE ] 94 | 95 | ## Tiny::db_update_record db table key value - update a key/value 96 | def db_update_record = 97 | [ DB TABLE KEY VALUE -> 98 | let FN = db_record_location DB TABLE KEY in 99 | if OS::exists FN then 100 | remove_object FN; 101 | write_object FN VALUE 102 | else throw "record doesn't exist" ] 103 | 104 | ## Tiny::db_write_record db table key value - unconditionally create record 105 | def db_write_record = 106 | [ DB TABLE KEY VALUE -> 107 | let FN = db_record_location DB TABLE KEY in 108 | remove_object FN; 109 | write_object FN VALUE ] 110 | 111 | ## Tiny::db_write_record db table key - retrieve a record value 112 | def db_read_record = 113 | [ DB TABLE KEY -> 114 | let FN = db_record_location DB TABLE KEY in 115 | if OS::exists FN then read_object FN 116 | else throw "record doesn't exist" ] 117 | 118 | ## Tiny::db_remove_record db table key - remove a record by key 119 | def db_remove_record = 120 | [ DB TABLE KEY -> 121 | let FN = db_record_location DB TABLE KEY in 122 | remove_object FN ] 123 | 124 | ## Tiny::db_transaction db f - a transaction is a program holding on to a lock 125 | def db_transaction = 126 | [ DB F -> 127 | let LOCK = OS::flock (db_lock_location DB) in 128 | F LOCK ] 129 | 130 | def read_object = 131 | [ FN:text -> 132 | let CHAN = OS::open_in FN in 133 | let O = deserialize (OS::read_all CHAN) in 134 | OS::close CHAN; O ] 135 | 136 | def write_object = 137 | [ FN:text O -> 138 | let CHAN = OS::open_out FN in 139 | OS::write CHAN (serialize O); 140 | OS::close CHAN ] 141 | 142 | def remove_object = 143 | [ FN:text -> OS::remove_file FN ] 144 | 145 | def db_lock_location = 146 | [ DB -> 147 | try db_create_table DB "tinydb" catch [X -> X]; 148 | db_record_location DB "tinydb" "lock" ] 149 | 150 | ) 151 | 152 | 153 | -------------------------------------------------------------------------------- /examples/wart.eg: -------------------------------------------------------------------------------- 1 | # A 'wart' in the Egel interpreter is the lack of application nodes. 2 | # 3 | # Concretely, `[(X Y) -> Y X] ((1 2) 3)` doesn't reduce since 4 | # `(1 2) 3` equals `1 2 3` (without application nodes). 5 | # 6 | # This would be difficult to fix since what would 7 | # `[(X Y) Z -> .. ]` then mean.. 8 | # 9 | # So far, my only solution to that problem is to document it. 10 | 11 | def main = [(X Y) -> Y X] ((1 2) 3) 12 | -------------------------------------------------------------------------------- /examples/yes.eg: -------------------------------------------------------------------------------- 1 | # The 'yes' program in Egel. Used to measure throughput. 2 | 3 | using System 4 | 5 | def yes = let X = print "y\n" in yes 6 | 7 | def main = yes 8 | 9 | -------------------------------------------------------------------------------- /include/calculate.eg: -------------------------------------------------------------------------------- 1 | @""" 2 | Calculations are small abstractions where some computation is done 3 | modulo some state. 4 | 5 | All calculations are chained actions. An action is a function which 6 | gets as an arguments a state and returns a tuple of a result and a 7 | state. 8 | """ 9 | 10 | namespace Calculate ( 11 | 12 | using System 13 | 14 | def return = 15 | @"Calculate::return a - calculate further with value" 16 | [ A S -> (A, S) ] 17 | 18 | def chain = 19 | @"Calculate::chain f g - chain two calculations" 20 | [ F G S0 -> [(A, S1) -> G A S1] (F S0) ] 21 | 22 | def run = 23 | @"Calculate::run f s - run calculation on state" 24 | [ F S -> [(A, S) -> A] (F S) ] 25 | 26 | def <* = 27 | @"Calculate::<* f g - chain" 28 | chain 29 | 30 | def skip = 31 | @"Calculate::skip - return a none" 32 | return none 33 | 34 | def apply = 35 | @"Calculate::apply f g - apply a function to a calculation" 36 | [ F G -> chain F [A S -> (G A, S)] ] 37 | 38 | def modify = 39 | @"Calculate::modify f g - modify state" 40 | [ F G -> chain F [A S -> (A, G S)] ] 41 | 42 | def <@ = 43 | @"Calculate::<@ f g - apply" 44 | apply 45 | 46 | def <+ = 47 | @"Calculate::<+ f g - modify" 48 | modify 49 | 50 | ) 51 | -------------------------------------------------------------------------------- /include/generator.eg: -------------------------------------------------------------------------------- 1 | @"Generators model infinite list structures." 2 | 3 | import "prelude.eg" 4 | 5 | namespace Gen ( 6 | 7 | using System 8 | 9 | def to_list = 10 | @"Gen::to_list g - generator to list" 11 | [ nil -> nil 12 | | (cons X XX) -> cons X (to_list (XX none)) ] 13 | 14 | def from_list = 15 | @"Gen::from_list l - list to generator" 16 | [ nil -> nil 17 | | (cons X XX) -> cons X [ _ -> from_list XX ] ] 18 | 19 | def length = 20 | @"Gen::length l - length of a list" 21 | [ nil -> 0 22 | | (cons X XX) -> 1 + (length (XX none)) ] 23 | 24 | def foldl = 25 | @"Gen::foldl f z l - left fold on a list" 26 | [ F Z nil -> Z 27 | | F Z (cons X XX) -> foldl F (F Z X) (XX none) ] 28 | 29 | def foldr = 30 | @"Gen::foldr f z l - right fold on a list" 31 | [ F Z nil -> Z 32 | | F Z (cons X XX) -> F X (foldr F Z (XX none)) ] 33 | 34 | def head = 35 | @"Gen::head l - head of a list" 36 | [ (cons X XX) -> X ] 37 | 38 | def tail = 39 | @"Gen::tail l - tail of a list" 40 | [ (cons X XX) -> (XX none) ] 41 | 42 | def ++ = 43 | @"Gen::++ l0 l1 - concatenation of two lists" 44 | [ nil YY -> YY 45 | | (cons X XX) YY -> cons X [ _ -> (XX none) ++ YY ] ] 46 | 47 | def map = 48 | @"Gen::map f l - map a function over a list" 49 | [ F nil -> nil 50 | | F (cons X XX) -> let Y = F X in cons Y [_ -> (map F (XX none))] ] 51 | 52 | def reverse = 53 | @"Gen::reverse l - reverse a list" 54 | foldl (flip cons) nil 55 | 56 | def block = 57 | @"Gen::block n - list of number from lower to upper exclusive" 58 | [ 0 -> nil 59 | | N -> cons (N - 1) [ _ -> (block (N - 1)) ] ] 60 | 61 | def nth = 62 | @"Gen::nth n l - nth element of a list" 63 | [ 0 (cons X XX) -> X 64 | | N (cons X XX) -> nth (N - 1) (XX none) ] 65 | 66 | def insert = 67 | @"Gen::insert n x l - insert an element at given position" 68 | [ 0 X (cons Y YY) -> cons X YY 69 | | I X (cons Y YY) -> cons Y [_ -> (insert (I - 1) X (YY none)) ] ] 70 | 71 | def take = 72 | @"Gen::take n l - take the first elements of a list" 73 | [ 0 XX -> nil 74 | | N (cons X XX) -> cons X [_ -> (take (N - 1) (XX none)) ] ] 75 | 76 | def drop = 77 | @"Gen::drop n l - drop the first elements of a list" 78 | [ 0 XX -> XX 79 | | N (cons X XX) -> drop (N - 1) (XX none) ] 80 | 81 | def repeat = 82 | @"Gen::repeat n - infinite list of elements" 83 | [ N -> cons N [ _ -> repeat N ] ] 84 | 85 | def cycle = 86 | @"Gen::cycle l - infinite list of cycling list" 87 | [ nil -> nil 88 | | LL -> [ (cons X XX) -> cons X [_ -> (XX none) Gen::++ (cycle LL)] ] LL ] 89 | 90 | 91 | def from = 92 | @"Gen::from min - list of numbers from min " 93 | [ N -> cons N [ _ -> from (N+1) ] ] 94 | 95 | def from_to = 96 | @"Gen::from_to min max - list of numbers for min to max (exclusive)" 97 | [ X Y -> 98 | if X <= Y then cons X [ _ -> (from_to (X+1) Y) ] 99 | else nil ] 100 | 101 | def filter = 102 | @"Gen::filter p l - filter all members from a list which satisfy a predicate" 103 | [ P nil -> nil 104 | | P (cons X XX) -> if P X then cons X [ _ -> (filter P (XX none)) ] 105 | else filter P (XX none) ] 106 | 107 | def flatten = 108 | @"Gen::flatten ll - flatten a list of lists to a list" 109 | [ nil -> nil 110 | | (cons nil YY) -> flatten (YY none) 111 | | (cons (cons X XX) YY) -> cons X [ _ -> flatten (cons (XX none) YY) ] ] 112 | 113 | def zip = 114 | @"Gen::zip l0 l1 - zip to lists to a list of pairs" 115 | [ (cons X XX) (cons Y YY) -> cons (X,Y) [ _ -> zip (XX none) (YY none) ] 116 | | XX YY -> nil ] 117 | 118 | def zip_with = 119 | @"Gen::zip_with f l0 l1 - apply a function pairwise to members of two lists" 120 | [ Z (cons X XX) (cons Y YY) -> cons (Z X Y) [ _ -> zip_with Z (XX none) (YY none)] 121 | | Z XX YY -> nil ] 122 | 123 | def any = 124 | @"Gen::any p l - checks whether any element of a list satisfies a predicate" 125 | [ P nil -> false 126 | | P (cons B BB) -> if P B then true else any P (BB none) ] 127 | 128 | def all = 129 | @"Gen::all p l - checks whether all elements of a list satisfies a predicate" 130 | [ P nil -> true 131 | | P (cons B BB) -> if P B then all P (BB none) else false ] 132 | 133 | def elem = 134 | @"Gen::elem x l - membership test" 135 | [ X -> any ((==) X) ] 136 | 137 | def not_elem = 138 | @"Gen::not_elem x l - inverse membership test" 139 | [ X -> all ((/=) X) ] 140 | 141 | def from_lists = 142 | @"Gen::from_lists l - convert a list of lists to generator of generators" 143 | Gen::from_list . (List::map Gen::from_list) 144 | 145 | def to_lists = 146 | @"Gen::to_lists c - convert generator of generators to list of lists" 147 | (List::map Gen::to_list) . Gen::to_list 148 | 149 | def space = 150 | @"Gen::space - space, the final frontier" 151 | Gen::map [X -> Gen::map [ Y -> (X,Y) ] (Gen::from 0)] (Gen::from 0) 152 | 153 | def map_2d = 154 | @"Gen::map_2d - map on a space" 155 | [ F -> Gen::map [XX -> Gen::map F XX] ] 156 | 157 | def take_2d = 158 | @"Gen::take_2d - take a block " 159 | [ X Y B -> Gen::take X (Gen::map [L -> Gen::take Y L] B) ] 160 | 161 | def zip_2d = 162 | @"Gen::zip_2d" 163 | [ nil nil -> nil 164 | | (cons X XX) nil -> nil 165 | | nil (cons Y YY) -> nil 166 | | (cons X XX) (cons Y YY) -> cons (Gen::zip X Y) [_ -> Gen::zip_2d (XX none) (YY none)] ] 167 | 168 | def range = 169 | @"Gen::range l f - iterate over elements (reverse map" 170 | [ XX F -> Gen::map F XX ] 171 | 172 | def range2 = 173 | @"Gen::range2 l0 l1 f - iterate over elements of two lists" 174 | [ XG YG F -> Gen::flatten (Gen::range XG [X -> Gen::range YG [Y -> F X Y]]) ] 175 | 176 | def range3 = 177 | @"Gen::range3 l0 l1 l2 f - iterate over elements of three lists" 178 | [ XG YG ZG F -> Gen::flatten (Gen::range XG [X -> range2 YG ZG [Y Z -> F X Y Z]]) ] 179 | 180 | 181 | ) 182 | -------------------------------------------------------------------------------- /include/search.eg: -------------------------------------------------------------------------------- 1 | @"Searching is calculation over a state, minimalist theory." 2 | import "prelude.eg" 3 | import "calculate.eg" 4 | 5 | namespace Search ( 6 | 7 | using System 8 | using Calculate (return, <*) 9 | using List (++) 10 | 11 | 12 | data top, bot, cut 13 | 14 | def success = 15 | @"Search::success a - succeed with value a" 16 | [ S -> return (top S) ] 17 | 18 | def fail = 19 | @"Search::fail - fail an alternative" 20 | return (bot none) 21 | 22 | def raise = 23 | @"Search::raise - fail all alternatives" 24 | return (cut none) 25 | 26 | def message = 27 | @"Search::message m - fail or raise with message" 28 | [ P M -> P <* 29 | [ (bot _) -> return (bot M) 30 | | (cut _) -> return (cut M) 31 | | X -> return X ] ] 32 | 33 | def parallel = 34 | @"Search::parallel p q - try both alternatives" 35 | [ P0 P1 S0 -> 36 | [ (bot R1, S1) -> P1 S0 37 | | L -> L ] (P0 S0) ] 38 | 39 | def sequential = 40 | @"Search::sequential - try alternatives sequentially" 41 | [ P0 P1 -> P0 <* 42 | [ (top X) -> P1 X 43 | | X -> return X ] ] 44 | 45 | def serial = 46 | @"Search::serial p q - try alternative, then force the next" 47 | [ P0 P1 -> P0 <* 48 | [ (top X) -> P1 X <* [ (bot X) -> return (cut X) | X -> return X] 49 | | X -> return X ] ] 50 | 51 | def apply = 52 | @"Search::apply p f - apply to the argument being calculated" 53 | [ P F -> P <* 54 | [ (top X) -> return (top (F X)) 55 | | (bot M) -> return (bot M) 56 | | (cut M) -> return (cut M) ] ] 57 | 58 | def opt = 59 | @"Search::opt p v - optionally succeed with a value" 60 | [ P R0 S0 -> 61 | [ (bot R1, S1) -> (top R0, S0) 62 | | L -> L ] (P R0 S0) ] 63 | 64 | def serial_opt = 65 | @"Search::serial_opt p q - optionally succeed with value" 66 | [ P0 P1 -> P0 <* 67 | [ (top M) -> opt P1 M 68 | | L -> return L ] ] 69 | 70 | def <+> = 71 | @"Search::<+> p q - try both alternatives " 72 | parallel 73 | 74 | def <*> = 75 | @"Search::<-> p q - try composition of alternatives" 76 | sequential 77 | 78 | def <**> = 79 | @"Search::<**> p q - try alternative then force" 80 | serial 81 | 82 | def = 83 | @"Search:: p q - try alternative then optionally" 84 | serial_opt 85 | 86 | def <@> = 87 | @"Search::<@> p f - apply function to the result" 88 | apply 89 | 90 | def = 91 | @"Search:: p m - set the failure message" 92 | message 93 | 94 | def one = 95 | @"Search::one p - one time and return a singleton result" 96 | [ P -> P <@> [A -> {A} ] ] 97 | 98 | def plus = 99 | @"Search::plus p - one or more and return a list result" 100 | [ P -> 101 | (one P) \L0 -> 102 | (plus P) <@> \L1 -> L0 ++ L1 ] 103 | 104 | def star = 105 | @"Search::star p - zero or more and return a list result" 106 | [ P -> plus P <+> success nil ] 107 | 108 | def plus_sep = 109 | @"Search::plus_sep p s - one or more with separator" 110 | [ P Q -> 111 | one P \L0 -> 112 | Q <*> \U -> 113 | plus_sep P Q <@> \L1 -> 114 | L0 ++ L1 115 | ] 116 | 117 | def star_sep = 118 | @"Search::star_sep p s - zero or more with separator" 119 | [ P Q -> plus_sep P Q <+> success nil ] 120 | 121 | def search = 122 | @"Search::search p f t e s - search on state with three handlers" 123 | [ P F T E S -> 124 | let (A,B) = P S in 125 | [ (top X) -> F X B 126 | | (bot M) -> T M B 127 | | (cut M) -> E M B ] A 128 | ] 129 | 130 | ) 131 | -------------------------------------------------------------------------------- /lib/pqueue/pqueue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../../src/runtime.hpp" 11 | 12 | using namespace egel; 13 | 14 | struct Greater { 15 | bool operator()(const std::pair& p0, 16 | const std::pair& p1) const { 17 | CompareVMObjectPtr compare; 18 | auto c = compare(p0.first, p1.first); 19 | return c == 1; 20 | } 21 | }; 22 | 23 | typedef std::priority_queue, 24 | std::vector>, 25 | Greater> 26 | pqueue_t; 27 | 28 | class PQueue : public Opaque { 29 | public: 30 | OPAQUE_PREAMBLE(VM_SUB_EGO, PQueue, "System", "pqueue"); 31 | DOCSTRING("System::pqueue - a pqueue"); 32 | 33 | PQueue(VM* m, const pqueue_t& d) : PQueue(m) { 34 | _value = d; 35 | } 36 | 37 | PQueue(const PQueue& d) : PQueue(d.machine(), d.value()) { 38 | } 39 | 40 | static VMObjectPtr create(VM* m, const pqueue_t& d) { 41 | return VMObjectPtr(new PQueue(m, d)); 42 | } 43 | 44 | int compare(const VMObjectPtr& o) override { 45 | return -1; // XXX: for later 46 | } 47 | 48 | pqueue_t value() const { 49 | return _value; 50 | } 51 | 52 | size_t size() const { 53 | return _value.size(); 54 | } 55 | 56 | bool empty() const { 57 | return _value.size() == 0; 58 | } 59 | 60 | VMObjectPtr top() { 61 | auto p = _value.top(); 62 | 63 | VMObjectPtrs tt; 64 | tt.push_back(p.first); 65 | tt.push_back(p.second); 66 | 67 | return machine()->to_tuple(tt); 68 | } 69 | 70 | void pop() { 71 | _value.pop(); 72 | } 73 | 74 | void push(const VMObjectPtr& k, const VMObjectPtr& v) { 75 | _value.push(std::pair(k, v)); 76 | } 77 | 78 | protected: 79 | pqueue_t _value; 80 | }; 81 | 82 | class APQueue : public Medadic { 83 | public: 84 | MEDADIC_PREAMBLE(VM_SUB_EGO, APQueue, "System", "pqueue"); 85 | DOCSTRING("System::pqueue - create a pqueue object"); 86 | 87 | VMObjectPtr apply() const override { 88 | return PQueue::create(machine(), pqueue_t()); 89 | } 90 | }; 91 | 92 | class PQueueEmpty : public Monadic { 93 | public: 94 | MONADIC_PREAMBLE(VM_SUB_EGO, PQueueEmpty, "System", "pqueue_empty"); 95 | DOCSTRING("System::pqueue_has d k - check for key"); 96 | 97 | VMObjectPtr apply(const VMObjectPtr& arg0) const override { 98 | auto m = machine(); 99 | if (m->is_opaque(arg0) && m->symbol(arg0) == "System::pqueue") { 100 | auto d = PQueue::cast(arg0); 101 | return m->create_bool(d->empty()); 102 | } else { 103 | throw machine()->bad_args(this, arg0); 104 | } 105 | } 106 | }; 107 | 108 | class PQueueTop : public Monadic { 109 | public: 110 | MONADIC_PREAMBLE(VM_SUB_EGO, PQueueTop, "System", "pqueue_top"); 111 | DOCSTRING("System::pqueue_get d k - get a value by key"); 112 | 113 | VMObjectPtr apply(const VMObjectPtr& arg0) const override { 114 | auto m = machine(); 115 | if (m->is_opaque(arg0) && m->symbol(arg0) == "System::pqueue") { 116 | auto d = PQueue::cast(arg0); 117 | return d->top(); 118 | } else { 119 | throw machine()->bad_args(this, arg0); 120 | } 121 | } 122 | }; 123 | 124 | class PQueuePop : public Monadic { 125 | public: 126 | MONADIC_PREAMBLE(VM_SUB_EGO, PQueuePop, "System", "pqueue_pop"); 127 | DOCSTRING("System::pqueue_keys d - pqueue keys as list"); 128 | 129 | VMObjectPtr apply(const VMObjectPtr& arg0) const override { 130 | auto m = machine(); 131 | if (m->is_opaque(arg0) && m->symbol(arg0) == "System::pqueue") { 132 | auto d = PQueue::cast(arg0); 133 | d->pop(); 134 | return arg0; 135 | } else { 136 | throw machine()->bad_args(this, arg0); 137 | } 138 | } 139 | }; 140 | 141 | class PQueuePush : public Ternary { 142 | public: 143 | TERNARY_PREAMBLE(VM_SUB_EGO, PQueuePush, "System", "pqueue_push"); 144 | DOCSTRING("System::pqueue_set d k v - set a value by key"); 145 | 146 | VMObjectPtr apply(const VMObjectPtr& arg0, const VMObjectPtr& arg1, 147 | const VMObjectPtr& arg2) const override { 148 | auto m = machine(); 149 | if (m->is_opaque(arg0) && m->symbol(arg0) == "System::pqueue") { 150 | auto d = PQueue::cast(arg0); 151 | d->push(arg1, arg2); 152 | return arg0; 153 | } else { 154 | throw machine()->bad_args(this, arg0, arg1, arg2); 155 | } 156 | } 157 | }; 158 | 159 | class PQueueModule: public CModule { 160 | public: 161 | icu::UnicodeString name() const override { 162 | return "pqueue"; 163 | } 164 | 165 | icu::UnicodeString docstring() const override { 166 | return "The 'pqueue' module defines a priority queue abstraction."; 167 | } 168 | 169 | std::vector exports(VM *vm) override { 170 | std::vector oo; 171 | 172 | oo.push_back(APQueue::create(vm)); 173 | oo.push_back(PQueueEmpty::create(vm)); 174 | oo.push_back(PQueueTop::create(vm)); 175 | oo.push_back(PQueuePop::create(vm)); 176 | oo.push_back(PQueuePush::create(vm)); 177 | 178 | return oo; 179 | } 180 | }; 181 | 182 | extern "C" CModule* egel_module() { 183 | CModule* m = new PQueueModule(); 184 | return m; 185 | } 186 | 187 | -------------------------------------------------------------------------------- /lib/random/random.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../../src/runtime.hpp" 9 | 10 | using namespace egel; 11 | 12 | /** 13 | * Start of a simplistic uniform random prng library which synchronizes 14 | * on a thread-safe singleton. 15 | */ 16 | 17 | class random { 18 | private: 19 | random() { 20 | } 21 | 22 | public: 23 | static random& get() { 24 | static random instance; 25 | return instance; 26 | } 27 | 28 | vm_int_t between(const vm_int_t& min, const vm_int_t& max) { 29 | std::uniform_int_distribution distribution(min, max); 30 | std::lock_guard lock(_lock); 31 | return distribution(_generator); 32 | } 33 | 34 | private: 35 | std::mutex _lock; 36 | std::mt19937 _generator; 37 | }; 38 | 39 | class Random : public Dyadic { 40 | public: 41 | DYADIC_PREAMBLE(VM_SUB_EGO, Random, "Math", "between"); 42 | DOCSTRING("Math::between min max - a random number between min and max"); 43 | 44 | VMObjectPtr apply(const VMObjectPtr& arg0, 45 | const VMObjectPtr& arg1) const override { 46 | if ((machine()->is_integer(arg0)) && (machine()->is_integer(arg1))) { 47 | auto i0 = machine()->get_integer(arg0); 48 | auto i1 = machine()->get_integer(arg1); 49 | return machine()->create_integer(random::get().between(i0, i1)); 50 | } else { 51 | // XXX: extend once with two float values 52 | throw machine()->bad_args(this, arg0, arg1); 53 | } 54 | } 55 | }; 56 | 57 | class RandomModule: public CModule { 58 | public: 59 | icu::UnicodeString name() const override { 60 | return "random"; 61 | } 62 | 63 | icu::UnicodeString docstring() const override { 64 | return "The 'random' module defines randomization combinators. (Work in progress)"; 65 | } 66 | 67 | std::vector exports(VM *vm) override { 68 | std::vector oo; 69 | 70 | oo.push_back(Random::create(vm)); 71 | 72 | return oo; 73 | } 74 | }; 75 | 76 | extern "C" CModule* egel_module() { 77 | CModule* m = new RandomModule(); 78 | return m; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /man/README.md: -------------------------------------------------------------------------------- 1 | Egel interpreter manpage 2 | ======================== 3 | 4 | This directory holds the Egel interpreter man page. The source 5 | is **egel.1.md** and other files are created with **ronn** 6 | by the maintainer. 7 | -------------------------------------------------------------------------------- /man/egel.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn-NG/v0.10.1 2 | .\" http://github.com/apjanke/ronn-ng/tree/0.10.1 3 | .TH "EGEL" "1" "December 2024" "" 4 | .SH "NAME" 5 | \fBegel\fR \- an untyped functional scripting language 6 | .SH "SYNOPSIS" 7 | .TS 8 | allbox; 9 | \fBegel\fR [\- \-\-interact] [\-I \fIpath\fR \-\-include \fIpath\fR] [\fIfile\fR] 10 | .TE 11 | .TS 12 | allbox; 13 | \fBegel\fR [\-I \fIpath\fR \-\-include \fIpath\fR] \-e \fIcommand\fR 14 | .TE 15 | .TS 16 | allbox; 17 | \fBegel\fR [\-h \-\-help \-v \-\-version] 18 | .TE 19 | .SH "DESCRIPTION" 20 | Egel is an untyped concurrent functional scripting language based on eager combinator rewriting with a concise but remarkably powerful syntax\. 21 | .P 22 | Egel's basic functionality can be extended with your own modules written in C++\. Those modules are dynamically loaded\. 23 | .SH "OPTIONS" 24 | .TP 25 | \fB\-h\fR, \fB\-\-help\fR 26 | Prints brief usage information, may list debug options\. 27 | .TP 28 | \fB\-v\fR, \fB\-\-version\fR 29 | Prints the current version number\. 30 | .TP 31 | \fB\-\fR, \fB\-\-interact\fR 32 | Enter interactive mode unconditionally\. 33 | .TP 34 | \fB\-e\fR, \fB\-\-eval \fR 35 | Evaluate the given command\. 36 | .TP 37 | \fB\-I\fR, \fB\-\-include \fR 38 | Add an include path\. 39 | .SH "TUTORIAL" 40 | Egel is an expression language and the interpreter a symbolic evaluator\. 41 | .SS "Expressions" 42 | Egel code consist of expression which are evaluated eagerly\. 43 | .IP "\(bu" 4 44 | Basic primitives are integers, floats, complex, unicode characters, and unicode strings\. 45 | .IP 46 | \fB0 1 2\fR , \fB0\.0 3\.14 \-1\.2+3\.4j\fR , \fB'a'\fR \fB'∀'\fR , or \fB"Hello World!"\fR 47 | .IP "\(bu" 4 48 | All constants compose\. 49 | .IP 50 | \fB(0 1)\fR is just as legal as \fB(cons 'a' nil)\fR 51 | .IP "\(bu" 4 52 | Rewriting is done with the pattern\-matching anonymous abstraction, uppercase letters denote variables\. 53 | .IP 54 | \fB[ X \-> X ]\fR , \fB[ (cons HEAD TAIL) \-> HEAD ]\fR 55 | .IP 56 | The abstraction consists of a number of matches, it may be variadic without penalty\. 57 | .IP 58 | \fB[ X Y \-> 2 | X \-> 1 | \-> 0]\fR 59 | .IP 60 | A backslash \fB\e\fR starts a lambda abstraction\. 61 | .IP 62 | \fB\e(cons X XX) \-> X\fR 63 | .IP "\(bu" 4 64 | Patterns may match against tags\. 65 | .IP 66 | \fB[ I:int \-> "an int" | C:cons \-> "a cons" ]\fR 67 | .IP "\(bu" 4 68 | Let expressions assign values to intermediaries\. 69 | .IP 70 | \fBlet X = 1 + 2 in X * X\fR 71 | .IP "\(bu" 4 72 | A semicolon separates computations\. 73 | .IP 74 | \fBprint (1+2); "done"\fR 75 | .IP "\(bu" 4 76 | Exception handling is supported, any value may be thrown and caught\. 77 | .IP 78 | \fBtry 1 + throw "failure" catch [ EXC \-> print EXC ]\fR 79 | .IP "\(bu" 4 80 | The do notation composes chains of transformations\. 81 | .IP 82 | \fB(do ((+) 1) |> foldl (*) 1) (from_to 0 4)\fR 83 | .IP "\(bu" 4 84 | Parallell programming is achieved through the \fBasync/await\fR combinators\. Asynchronous programs start threads that return future objects\. 85 | .IP 86 | \fBlet F = async [ _ \-> fib 35 ] in await F\fR 87 | .IP "\(bu" 4 88 | Formatting strings is handled with the \fBformat\fR combinator, see \fIhttps://fmt\.dev/\fR\. 89 | .IP 90 | \fBprint (format "Hello {}" "world")\fR 91 | .IP "\(bu" 4 92 | The interpreter implements a term rewriter though has mutable references\. Cycles won't be garbage collected\. 93 | .IP 94 | \fBlet X = ref 0 in set_ref X 1; get_ref X\fR 95 | .IP "" 0 96 | .SS "Declarations" 97 | Declarations define combinators\. 98 | .IP "\(bu" 4 99 | A \fBdata\fR declaration introduces constants\. 100 | .IP 101 | \fBdata leaf, branch\fR 102 | .IP "\(bu" 4 103 | A \fBdef\fR declaration introduces a combinator\. 104 | .IP 105 | \fBdef fac = [0 \-> 1 | N \-> N * fac (N \- 1)]\fR 106 | .IP "\(bu" 4 107 | A \fBval\fR declaration introduces a combinator who's body is evaluated prior to it's definition\. 108 | .IP 109 | \fBval global = ref 3\.14\fR 110 | .IP "" 0 111 | .SS "Modules" 112 | A module is a series of combinator declarations possibly encapsulated in a namespace\. All combinators are named lowercase, there is some provisional support for unicode\. Modules may import each other\. The \fBmain\fR combinator of the top module drives all computation when present\. 113 | .P 114 | Tying it all together: 115 | .IP "" 4 116 | .nf 117 | # A Fibonacci implementation\. 118 | 119 | import "prelude\.eg" 120 | 121 | namespace Fibonacci ( 122 | using System 123 | 124 | def fib = 125 | [ 0 \-> 0 126 | | 1 \-> 1 127 | | N \-> fib (N\- 2) + fib (N \- 1) ] 128 | 129 | ) 130 | 131 | using System 132 | 133 | def main = Fibonacci::fib (3+2) 134 | .fi 135 | .IP "" 0 136 | .SH "EXAMPLES" 137 | There are three modes in which the interpreter is used: batch, interactive, or command mode\. 138 | .P 139 | In batch mode, just supply the top module with a \fBmain\fR combinator\. 140 | .IP "" 4 141 | .nf 142 | $ egel helloworld\.eg 143 | Hello world! 144 | .fi 145 | .IP "" 0 146 | .P 147 | The interpreter will start in interactive mode when invoked without a module argument\. 148 | .IP "" 4 149 | .nf 150 | $ egel 151 | > using System 152 | > 1 + 1 153 | 2 154 | .fi 155 | .IP "" 0 156 | .P 157 | Supply a command to use \fBegel \-e\fR as a simple calculator\. Double semicolons are separators\. 158 | .IP "" 4 159 | .nf 160 | $ egel fib\.eg \-e "using Fibonacci;; fib 3" 161 | 5 162 | .fi 163 | .IP "" 0 164 | .SH "FILES" 165 | The following files should be in the \fBEGEL_PATH\fR directory\. 166 | .TP 167 | \fBprelude\.eg\fR \fBcalculate\.eg\fR \fBsearch\.eg\fR 168 | The standard Egel prelude and additional scripts\. 169 | .TP 170 | \fBrandom\.ego\fR, etc\. 171 | Dynamically loaded libraries\. 172 | .SH "ENVIRONMENT" 173 | .TP 174 | \fBEGEL_PATH\fR 175 | The directories that are searched for inclusion\. 176 | .TP 177 | \fBEGEL_PS0\fR 178 | The prompt given by the interpreter in interactive mode\. 179 | .SH "BUGS" 180 | See GitHub Issues: \fIhttps://github\.com/egel\-lang/egel/issues\fR 181 | .SH "AUTHOR" 182 | MIT License (c) 2017 M\.C\.A\. (Marco) Devillers \fImarco\.devillers@gmail\.com\fR 183 | .SH "SEE ALSO" 184 | \fBc++(1)\fR 185 | -------------------------------------------------------------------------------- /man/egel.1.md: -------------------------------------------------------------------------------- 1 | EGEL(1) Version 0.1 | The Egel interpreter 2 | == 3 | 4 | ## NAME 5 | 6 | **egel** - an untyped functional scripting language 7 | 8 | ## SYNOPSIS 9 | 10 | `egel` [-|--interact] [-I |--include ] [] 11 | 12 | `egel` [-I |--include ] -e 13 | 14 | `egel` [-h|--help|-v|--version] 15 | 16 | ## DESCRIPTION 17 | 18 | Egel is an untyped concurrent functional scripting language based on 19 | eager combinator rewriting with a concise but remarkably powerful 20 | syntax. 21 | 22 | Egel's basic functionality can be extended with your own modules 23 | written in C++. Those modules are dynamically loaded. 24 | 25 | ## OPTIONS 26 | 27 | * `-h`, `--help`: 28 | Prints brief usage information, may list debug options. 29 | 30 | * `-v`, `--version`: 31 | Prints the current version number. 32 | 33 | * `-`, `--interact`: 34 | Enter interactive mode unconditionally. 35 | 36 | * `-e`, `--eval `: 37 | Evaluate the given command. 38 | 39 | * `-I`, `--include `: 40 | Add an include path. 41 | 42 | ## TUTORIAL 43 | 44 | Egel is an expression language and the interpreter a symbolic 45 | evaluator. 46 | 47 | ### Expressions 48 | 49 | Egel code consist of expression which are evaluated eagerly. 50 | 51 | * Basic primitives are integers, floats, complex, unicode characters, and 52 | unicode strings. 53 | 54 | `0 1 2` , `0.0 3.14 -1.2+3.4j` , `'a'` `'∀'` , or `"Hello World!"` 55 | 56 | * All constants compose. 57 | 58 | `(0 1)` is just as legal as `(cons 'a' nil)` 59 | 60 | * Rewriting is done with the pattern-matching anonymous abstraction, 61 | uppercase letters denote variables. 62 | 63 | `[ X -> X ]` , `[ (cons HEAD TAIL) -> HEAD ]` 64 | 65 | The abstraction consists of a number of matches, it may be variadic 66 | without penalty. 67 | 68 | `[ X Y -> 2 | X -> 1 | -> 0]` 69 | 70 | A backslash `\` starts a lambda abstraction. 71 | 72 | `\(cons X XX) -> X` 73 | 74 | * Patterns may match against tags. 75 | 76 | `[ I:int -> "an int" | C:cons -> "a cons" ]` 77 | 78 | * Let expressions assign values to intermediaries. 79 | 80 | `let X = 1 + 2 in X * X` 81 | 82 | * A semicolon separates computations. 83 | 84 | `print (1+2); "done"` 85 | 86 | * Exception handling is supported, any value may be thrown and 87 | caught. 88 | 89 | `try 1 + throw "failure" catch [ EXC -> print EXC ]` 90 | 91 | * The do notation composes chains of transformations. 92 | 93 | `(do ((+) 1) |> foldl (*) 1) (from_to 0 4)` 94 | 95 | * Parallell programming is achieved through the `async/await` 96 | combinators. 97 | Asynchronous programs start threads that return future objects. 98 | 99 | `let F = async [ _ -> fib 35 ] in await F` 100 | 101 | * Formatting strings is handled with the `format` combinator, see 102 | . 103 | 104 | `print (format "Hello {}" "world")` 105 | 106 | * The interpreter implements a term rewriter though has mutable 107 | references. 108 | Cycles won't be garbage collected. 109 | 110 | `let X = ref 0 in set_ref X 1; get_ref X` 111 | 112 | ### Declarations 113 | 114 | Declarations define combinators. 115 | 116 | * A `data` declaration introduces constants. 117 | 118 | `data leaf, branch` 119 | 120 | * A `def` declaration introduces a combinator. 121 | 122 | `def fac = [0 -> 1 | N -> N * fac (N - 1)]` 123 | 124 | * A `val` declaration introduces a combinator who's body is 125 | evaluated prior to it's definition. 126 | 127 | `val global = ref 3.14` 128 | 129 | ### Modules 130 | 131 | A module is a series of combinator declarations possibly encapsulated 132 | in a namespace. All combinators are named lowercase, there is some 133 | provisional support for unicode. Modules may import each other. The 134 | `main` combinator of the top module drives all computation when 135 | present. 136 | 137 | Tying it all together: 138 | 139 | ``` 140 | # A Fibonacci implementation. 141 | 142 | import "prelude.eg" 143 | 144 | namespace Fibonacci ( 145 | using System 146 | 147 | def fib = 148 | [ 0 -> 0 149 | | 1 -> 1 150 | | N -> fib (N- 2) + fib (N - 1) ] 151 | 152 | ) 153 | 154 | using System 155 | 156 | def main = Fibonacci::fib (3+2) 157 | ``` 158 | ## EXAMPLES 159 | 160 | There are three modes in which the interpreter is used: batch, 161 | interactive, or command mode. 162 | 163 | In batch mode, just supply the top module with a `main` combinator. 164 | 165 | $ egel helloworld.eg 166 | Hello world! 167 | 168 | The interpreter will start in interactive mode when invoked without a 169 | module argument. 170 | 171 | $ egel 172 | > using System 173 | > 1 + 1 174 | 2 175 | 176 | Supply a command to use `egel -e` as a simple calculator. Double 177 | semicolons are separators. 178 | 179 | $ egel fib.eg -e "using Fibonacci;; fib 3" 180 | 5 181 | 182 | ## FILES 183 | 184 | The following files should be in the `EGEL_PATH` directory. 185 | 186 | * `prelude.eg` `calculate.eg` `search.eg`: 187 | The standard Egel prelude and additional scripts. 188 | 189 | * `random.ego`, etc. : 190 | Dynamically loaded libraries. 191 | 192 | ## ENVIRONMENT 193 | 194 | * `EGEL_PATH`: 195 | The directories that are searched for inclusion. 196 | 197 | * `EGEL_PS0`: 198 | The prompt given by the interpreter in interactive mode. 199 | 200 | ## BUGS 201 | 202 | See GitHub Issues: 203 | 204 | ## AUTHOR 205 | 206 | MIT License (c) 2017 M.C.A. (Marco) Devillers 207 | 208 | 209 | ## SEE ALSO 210 | 211 | **c++(1)** 212 | -------------------------------------------------------------------------------- /optional/README.md: -------------------------------------------------------------------------------- 1 | Optional libraries for Egel 2 | --------------------------- 3 | 4 | These are the optional libraries for the Egel interpreter. They 5 | compile against an already installed runtime. 6 | 7 | At the moment, there are two packages: 8 | 9 | - erpc, remote procedure calls 10 | - python, python bindings 11 | 12 | -------------------------------------------------------------------------------- /optional/erpc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(egel-rpc LANGUAGES CXX) 3 | 4 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | add_subdirectory(lib) 8 | add_subdirectory(proto) 9 | -------------------------------------------------------------------------------- /optional/erpc/README.md: -------------------------------------------------------------------------------- 1 | # Egel Remote Procedure Calls 2 | 3 | Egel's intended usage is to be able to transparently shoot 4 | combinators (programs) over the internet. This module builds on top 5 | off grpc (Google's generic rpc implementation). 6 | 7 | Dependency packages 'grpc', 'openssl', and 'protobuf-c'. 8 | 9 | This compiles against an already installed egel distribution. 10 | 11 | Follow the usual cmake route to build the module. 12 | 13 | Note: on macos 14 | 15 | cmake .. -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/icu4c \ 16 | -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl 17 | -------------------------------------------------------------------------------- /optional/erpc/examples/client0.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | import "erpc.ego" 3 | 4 | using System 5 | using List 6 | 7 | def main = 8 | printf "{}\n" "connecting"; 9 | let C = rpc_client "localhost:50001" in 10 | printf "{}\n" "calling"; 11 | rpc_call C [_ -> none ] 12 | 13 | -------------------------------------------------------------------------------- /optional/erpc/examples/client1.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | import "erpc.ego" 3 | 4 | using System 5 | using List 6 | 7 | def main = 8 | let C = rpc_client "localhost:50001" in 9 | rpc_call C [_ -> 42 ] 10 | 11 | -------------------------------------------------------------------------------- /optional/erpc/examples/client2.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | import "erpc.ego" 3 | 4 | using System 5 | using List 6 | 7 | def main = 8 | let C = rpc_client "localhost:50001" in 9 | rpc_call C [_ -> [X -> X] ] 42 10 | 11 | -------------------------------------------------------------------------------- /optional/erpc/examples/client3.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | import "erpc.ego" 3 | 4 | using System 5 | using List 6 | 7 | def main = 8 | let C = rpc_client "localhost:50001" in 9 | rpc_call C [_ -> map [ _ -> print "hello\n" ] (from_to 1 10) ] 10 | 11 | -------------------------------------------------------------------------------- /optional/erpc/examples/client4.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | import "erpc.ego" 3 | 4 | using System 5 | using List 6 | 7 | def main = 8 | let C = rpc_client "localhost:50001" in 9 | rpc_call C [_ -> from_to 1 1000 ] |> sum 10 | 11 | -------------------------------------------------------------------------------- /optional/erpc/examples/client_import.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | import "erpc.ego" 3 | import "dictionary.eg" 4 | 5 | using System 6 | using List 7 | 8 | def some_list = 9 | from_to 1 10 |> map [X -> (X,X) ] 10 | |> Dict::from_list |> Dict::to_list 11 | 12 | def main = 13 | let C = rpc_client "localhost:50001" in 14 | rpc_import C "dict.ego"; 15 | rpc_call C [_ -> some_list ] 16 | 17 | -------------------------------------------------------------------------------- /optional/erpc/examples/client_large.eg: -------------------------------------------------------------------------------- 1 | # grpc on macos ignores increasing the message size 2 | # on linux this works 3 | 4 | import "prelude.eg" 5 | import "erpc.ego" 6 | 7 | using System 8 | using List 9 | 10 | def main = 11 | let C = rpc_client "localhost:50001" in 12 | rpc_call C [_ -> from_to 0 10000 ] |> sum 13 | 14 | -------------------------------------------------------------------------------- /optional/erpc/examples/server.eg: -------------------------------------------------------------------------------- 1 | import "erpc.ego" 2 | 3 | using System 4 | 5 | def main = rpc_server "localhost:50001" 6 | -------------------------------------------------------------------------------- /optional/erpc/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Dependencies 3 | # 4 | find_package(Threads) 5 | find_package(ICU 60.0 REQUIRED COMPONENTS data i18n io tu uc) 6 | include_directories(/usr/local/include) # compile against a local install 7 | 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 9 | 10 | # 11 | # Sources 12 | # 13 | set(SOURCES 14 | egel_rpc.cpp 15 | ) 16 | 17 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) 18 | 19 | # 20 | # Target 21 | # 22 | add_library(erpc SHARED ${SOURCES}) 23 | target_link_libraries(erpc 24 | PUBLIC 25 | ICU::uc ICU::i18n ICU::io 26 | PRIVATE 27 | egel-proto 28 | ) 29 | set_target_properties(erpc PROPERTIES PREFIX "" SUFFIX ".ego") 30 | 31 | # installation 32 | include(GNUInstallDirs) 33 | 34 | install(TARGETS erpc 35 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/egel" 36 | ) 37 | -------------------------------------------------------------------------------- /optional/erpc/proto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(gRPC CONFIG REQUIRED) 2 | find_package(Protobuf REQUIRED) 3 | find_package(Threads) 4 | find_package(ICU 60.0 REQUIRED COMPONENTS data i18n io tu uc) 5 | 6 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 7 | 8 | # 9 | # Protobuf/Grpc source files 10 | # 11 | set(PROTO_FILES 12 | egel.proto 13 | ) 14 | 15 | # 16 | # Add Library target with protobuf sources 17 | # 18 | add_library(egel-proto ${PROTO_FILES}) 19 | target_link_libraries(egel-proto 20 | PUBLIC 21 | protobuf::libprotobuf 22 | gRPC::grpc 23 | gRPC::grpc++ 24 | ) 25 | target_include_directories(egel-proto PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) 26 | 27 | # 28 | # Compile protobuf and grpc files in egel-proto target to cpp 29 | # 30 | get_target_property(grpc_cpp_plugin_location gRPC::grpc_cpp_plugin LOCATION) 31 | protobuf_generate(TARGET egel-proto LANGUAGE cpp) 32 | protobuf_generate(TARGET egel-proto LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${grpc_cpp_plugin_location}") 33 | -------------------------------------------------------------------------------- /optional/erpc/proto/egel.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package egel_rpc; 4 | 5 | service EgelRpc { 6 | rpc EgelCall(EgelText) returns (EgelResult) {} 7 | rpc EgelDependencies(EgelTexts) returns (EgelText) {} 8 | rpc EgelImport(EgelText) returns (EgelText) {} 9 | rpc EgelNodeInfo(EgelText) returns (EgelText) {} 10 | } 11 | 12 | message EgelText { 13 | string text = 1; 14 | } 15 | 16 | message EgelResult { 17 | string text = 1; 18 | bool exception = 2; 19 | } 20 | 21 | message EgelTexts { 22 | repeated string texts = 1; 23 | } 24 | -------------------------------------------------------------------------------- /optional/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # minimal cmake version 2 | cmake_minimum_required(VERSION 3.13) 3 | 4 | # fallback for other versions 5 | if(${CMAKE_VERSION} VERSION_LESS 3.18) 6 | cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) 7 | else() 8 | cmake_policy(VERSION 3.18) 9 | endif() 10 | 11 | # set the cxx compiler standard 12 | set(CMAKE_CXX_STANDARD 20) 13 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 14 | 15 | set(VERSION_MAJOR_REGEX "#define LIBRARY_VERSION_MAJOR[ \t]+\"(.+)\"") 16 | set(VERSION_MINOR_REGEX "#define LIBRARY_VERSION_MINOR[ \t]+\"(.+)\"") 17 | set(VERSION_PATCH_REGEX "#define LIBRARY_VERSION_PATCH[ \t]+\"(.+)\"") 18 | 19 | file(STRINGS "${CMAKE_SOURCE_DIR}/src/python.cpp" 20 | VERSION_MAJOR REGEX ${VERSION_MAJOR_REGEX}) 21 | file(STRINGS "${CMAKE_SOURCE_DIR}/src/python.cpp" 22 | VERSION_MINOR REGEX ${VERSION_MINOR_REGEX}) 23 | file(STRINGS "${CMAKE_SOURCE_DIR}/src/python.cpp" 24 | VERSION_PATCH REGEX ${VERSION_PATCH_REGEX}) 25 | 26 | string(REGEX REPLACE ${VERSION_MAJOR_REGEX} "\\1" VERSION_MAJOR "${VERSION_MAJOR}") 27 | string(REGEX REPLACE ${VERSION_MINOR_REGEX} "\\1" VERSION_MINOR "${VERSION_MINOR}") 28 | string(REGEX REPLACE ${VERSION_PATCH_REGEX} "\\1" VERSION_PATCH "${VERSION_PATCH}") 29 | 30 | set(PVERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) 31 | 32 | message("-- python bridge for egel ${PVERSION}") 33 | 34 | # add 'fallback' modules 35 | set(CMAKE_MODULE_PATH "../contrib/cmake/modules;${CMAKE_MODULE_PATH}") 36 | 37 | # the Python/Egel bridge 38 | project(PythonBridge DESCRIPTION "python bridge for egel" 39 | VERSION ${PVERSION} LANGUAGES CXX) 40 | 41 | # set the cxx compiler flags 42 | if(NOT CMAKE_BUILD_TYPE) 43 | set(CMAKE_BUILD_TYPE Release) 44 | endif() 45 | 46 | # compilers 47 | message("-- compiler: ${CMAKE_CXX_COMPILER_ID}") 48 | if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) 49 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic -Wno-unused-parameter -Wno-return-type-c-linkage") 50 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 51 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast") 52 | elseif(("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang")) 53 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic -Wno-unused-parameter -Wno-return-type-c-linkage") 54 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 55 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast") 56 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 57 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic -Wno-unused-parameter") 58 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 59 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 60 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") 61 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic -Wno-unused-parameter") 62 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 63 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 64 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 65 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic -Wno-unused-parameter") 66 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 67 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 68 | else() 69 | set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic -Wno-unused-parameter") 70 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 71 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 72 | endif() 73 | 74 | SET(CMAKE_CXX_FLAGS_PROFILE "-O3 -pg") 75 | SET(CMAKE_EXE_LINKER_FLAGS_PROFILE "-O3 -pg") 76 | SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "-O3 -pg") 77 | 78 | 79 | # for the moment, glob sources and headers 80 | file(GLOB HEADER_LIST CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/src/*.hpp") 81 | file(GLOB SOURCE_LIST CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/src/*.cpp") 82 | 83 | # check for ICU, Threads, fmt 84 | # ICU components: data, i18n, io, le, lx, test, tu and uc. 85 | find_package(ICU 60.0 REQUIRED COMPONENTS data i18n io tu uc) 86 | find_package(Threads REQUIRED) 87 | find_package(fmt 7.1 REQUIRED) 88 | find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter Development) 89 | 90 | message("-- Python3 include: ${Python3_INCLUDE_DIRS}") 91 | message("-- Python3 libraries: ${Python3_LIBRARIES}") 92 | include_directories(${Python3_INCLUDE_DIRS}) 93 | include_directories("${CMAKE_SOURCE_DIR}/src") 94 | include_directories(/usr/local/include) 95 | 96 | # python.ego 97 | file(GLOB PYTHON_EGO_LIST CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/src/*.cpp") 98 | add_library(python3 MODULE ${PYTHON_EGO_LIST}) 99 | target_link_libraries(python3 ICU::uc ICU::i18n ICU::io) 100 | target_link_libraries(python3 ${Python3_LIBRARIES}) 101 | set_target_properties(python3 PROPERTIES PREFIX "" SUFFIX ".ego") 102 | 103 | # installation 104 | include(GNUInstallDirs) 105 | 106 | install(TARGETS python3 107 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/egel" 108 | ) 109 | 110 | -------------------------------------------------------------------------------- /optional/python/README.md: -------------------------------------------------------------------------------- 1 | ## Python-Egel bridge 2 | 3 | The Python bridge is a separate module which allows the Egel 4 | interpreter to bridge to Python code. At some point, the reverse 5 | will also be added. 6 | 7 | This bridge is a separate package you can install independent of the 8 | interpreter. Compiling and installing follows the default cmake 9 | route. 10 | 11 | Both Egel and Python need to be installed on your system to compile 12 | the bridge. 13 | 14 | ``` 15 | user$ mkdir build 16 | user$ cd build 17 | user$ cmake .. 18 | user$ sudo make install 19 | ``` 20 | 21 | This bridge supports minimal functionality. Unfortunately, the 22 | current Python 3.10 seems buggy and unstable so this development 23 | is halted for a while. 24 | 25 | An example can be found in the `example` directory. 26 | 27 | -------------------------------------------------------------------------------- /optional/python/examples/ack.py: -------------------------------------------------------------------------------- 1 | 2 | def ack(m, n): 3 | if m == 0: 4 | return n + 1 5 | elif n == 0: 6 | return ack(m - 1, 1) 7 | else: 8 | return ack(m - 1, ack(m, n - 1)) 9 | 10 | print(ack(3,2)) 11 | -------------------------------------------------------------------------------- /optional/python/examples/fib.py: -------------------------------------------------------------------------------- 1 | 2 | def fib(n): 3 | if n == 0: 4 | return 1 5 | elif n == 1: 6 | return 1 7 | else: 8 | return fib(n-1) + fib(n-2) 9 | 10 | print(fib(10)) 11 | -------------------------------------------------------------------------------- /optional/python/examples/test0.eg: -------------------------------------------------------------------------------- 1 | # Egel-Python3 bridge tests 2 | 3 | import "prelude.eg" 4 | import "python3.ego" 5 | 6 | using System 7 | using Python 8 | using List 9 | 10 | val python3 = run none 11 | 12 | def one_on_one = 13 | [ E -> 14 | print "egel " E ", "; 15 | let O0 = to_object E in 16 | let O1 = from_object O0 in 17 | print "bridged " O1 ", "; 18 | let EQ = (E == O1) in 19 | print "eq " EQ ".\n"; 20 | EQ ] 21 | 22 | def test0 = 23 | map one_on_one {none, false, true, 7, 3.14, 'a', "text"} 24 | 25 | def tuple_map = 26 | [ F tuple -> tuple 27 | | F (tuple X) -> tuple (F X) 28 | | F (X, Y) -> (F X, F Y) 29 | | F (X, Y, Z) -> (F X, F Y, F Z) 30 | | F (X, Y, Z, A) -> (F X, F Y, F Y, F A) 31 | | F _ -> throw "tuple_map falure" ] 32 | 33 | def none_convert = 34 | [ X -> if is_none X then "none" else "not none" ] 35 | 36 | def none_test = 37 | [ X -> print "egel none test (" X ") result (" (none_convert (to_object X)) ")\n" ] 38 | 39 | def false_convert = 40 | [ X -> if is_false X then "false" else "not false" ] 41 | 42 | def false_test = 43 | [ X -> print "egel false test (" X ") result (" (false_convert (to_object X)) ")\n" ] 44 | 45 | def true_convert = 46 | [ X -> if is_true X then "true" else "not true" ] 47 | 48 | def true_test = 49 | [ X -> print "egel true test (" X ") result (" (true_convert (to_object X)) ")\n" ] 50 | 51 | def integer_convert = 52 | [ N -> if Python::is_integer N then from_integer N else "no integer" ] 53 | 54 | def integer_test = 55 | [ X -> print "egel integer test (" X ") result (" (integer_convert (to_object X)) ")\n" ] 56 | 57 | def float_convert = 58 | [ F -> if Python::is_float F then from_float F else "no float" ] 59 | 60 | def float_test = 61 | [ X -> print "egel float test (" X ") result (" (float_convert (to_object X)) ")\n" ] 62 | 63 | def text_convert = 64 | [ F -> if Python::is_text F then from_text F else "no text" ] 65 | 66 | def text_test = 67 | [ X -> print "egel text test (" X ") result (" (text_convert (to_object X)) ")\n" ] 68 | 69 | def test1 = 70 | none_test 0; none_test none; 71 | false_test 0; false_test false; 72 | true_test 0; true_test true; 73 | integer_test none; integer_test 7; 74 | float_test 7; float_test 3.14; 75 | text_test 7; text_test "a text"; 76 | none 77 | 78 | def tuple_on_tuple = 79 | [ E -> 80 | print "egel " E ", "; 81 | let O0 = to_tuple (tuple_map to_object E) in 82 | let O1 = tuple_map (from_object) (from_tuple O0) in 83 | print "bridged " O1 ", "; 84 | let EQ = (E == O1) in 85 | print "eq " EQ "\n"; 86 | EQ ] 87 | 88 | def list_on_list = 89 | [ E -> 90 | print "egel " E ", "; 91 | let O0 = to_list (map to_object E) in 92 | let O1 = map (from_object) (from_list O0) in 93 | print "bridged " O1 ", "; 94 | let EQ = (E == O1) in 95 | print "eq " EQ "\n"; 96 | EQ ] 97 | 98 | def set_on_set = 99 | [ E -> 100 | print "egel " E ", "; 101 | let O0 = to_set (map to_object E) in 102 | let O1 = map (from_object) (from_set O0) in 103 | print "bridged " O1 ", "; 104 | let EQ = (E == O1) in 105 | print "eq " EQ "\n"; 106 | EQ ] 107 | 108 | def dict_on_dict = 109 | [ E -> 110 | print "egel " E ", "; 111 | let O0 = to_dictionary (map [(K,V) -> (to_object K, to_object V)] E) in 112 | let O1 = map [(K,V) -> (from_object K, from_object V)] (from_dictionary O0) in 113 | print "bridged " O1 ", "; 114 | let EQ = (E == O1) in 115 | print "eq " EQ "\n"; 116 | EQ ] 117 | 118 | def test2 = 119 | map tuple_on_tuple {tuple, tuple 0, (true, 0), ("hello", none, "world")}; 120 | map list_on_list {nil, {0}, {true, 0}, {"hello", none, "world"}}; 121 | map set_on_set {nil, {0}, {true, 0}, {"hello", none, "world"}}; 122 | map dict_on_dict {nil, {(0, 2), (1, 5)}}; 123 | none 124 | 125 | def test3 = 126 | let O = to_list (map to_object {false, 0, "hello"}) in 127 | let D = directory O in 128 | print "egel dictionary " (map from_object (from_list O)) "\n" 129 | 130 | def line0 = """ 131 | print("hello world from Python!") 132 | """ 133 | 134 | def line1 = """ 135 | def factorial(n): 136 | if n == 0: 137 | return 1 138 | else: 139 | return n * factorial(n-1) 140 | """ 141 | 142 | def line2 = """ 143 | class Person: 144 | def __init__(self, name, age): 145 | self.name = name 146 | self.age = age 147 | 148 | def hello(self): 149 | print("hello my name is " + self.name) 150 | 151 | p1 = Person("John", 36) 152 | p1.hello() 153 | """ 154 | 155 | def test4 = 156 | map Python::eval {line0, line1, line2}; 157 | Python::eval_file "fib.py" 158 | 159 | def factorial = 160 | [ M -> 161 | let F = get_attribute M "factorial" in 162 | print "egel callable " (is_callable F) "\n"; 163 | let X = to_tuple (tuple (to_object 5)) in 164 | let Y = from_object (apply F X) in 165 | print "egel factorial(5) = " Y "\n" ] 166 | 167 | def person = 168 | [ M -> 169 | let P = get_attribute M "p1" in 170 | let H = get_attribute P "hello" in 171 | apply H (to_tuple tuple) ] 172 | 173 | def test5 = 174 | try 175 | let M = module_add "__main__" in 176 | let D = directory M in 177 | let L = map from_object (from_list D) in 178 | print "egel main directory " L "\n"; 179 | factorial M; 180 | person M; 181 | none 182 | catch [ PE -> throw (from_object PE) ]; 183 | none 184 | 185 | def test6 = 186 | Python::eval "import sys"; 187 | Python::eval "import os"; 188 | Python::eval "sys.path.append(\"./\")"; 189 | try 190 | let M = module_import "ack" 191 | in none 192 | catch [ PE -> throw (from_object PE) ]; 193 | none 194 | 195 | def main = 196 | test0; 197 | test1; 198 | test2; 199 | test3; 200 | test4; 201 | test5; 202 | #test6; 203 | none 204 | -------------------------------------------------------------------------------- /optional/python/examples/wxhello.eg: -------------------------------------------------------------------------------- 1 | # Egel-Python3 bridge tests 2 | 3 | import "prelude.eg" 4 | import "python3.ego" 5 | 6 | using System 7 | using Python 8 | using List 9 | 10 | val python3 = run none 11 | 12 | def import_wx = 13 | module_import "wx" 14 | 15 | def wx_app = 16 | [ WX -> 17 | let CLASS = get_attribute WX "App" in 18 | let APP = apply CLASS (to_tuple tuple) in 19 | APP ] 20 | 21 | def wx_app_mainloop = 22 | [ APP -> 23 | let LOOP = get_attribute APP "MainLoop" in 24 | apply LOOP (to_tuple tuple); 25 | none ] 26 | 27 | def wx_frame = 28 | [ WX -> 29 | let CLASS = get_attribute WX "Frame" in 30 | let DICT = to_dictionary {(to_object "title", to_object "Hello World!")} in 31 | let FRAME = call CLASS (to_tuple (tuple (to_object none))) DICT in 32 | FRAME ] 33 | 34 | def wx_frame_show = 35 | [ FRAME -> 36 | let SHOW = get_attribute FRAME "Show" in 37 | apply SHOW (to_tuple tuple); 38 | none ] 39 | 40 | def main = 41 | let WX = import_wx in 42 | let APP = wx_app WX in 43 | let FRAME = wx_frame WX in 44 | wx_frame_show FRAME; 45 | wx_app_mainloop APP 46 | 47 | -------------------------------------------------------------------------------- /src/builtin_async.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "runtime.hpp" 9 | 10 | /** 11 | * Egel's async tasks implementation. 12 | **/ 13 | 14 | namespace egel { 15 | 16 | // DOCSTRING("namespace System - async tasks support"); 17 | 18 | class Future : public Opaque { 19 | public: 20 | OPAQUE_PREAMBLE(VM_SUB_BUILTIN, Future, "System", "future"); 21 | 22 | DOCSTRING("System::future - opaque future object"); 23 | int compare(const VMObjectPtr &o) override { 24 | return -1; // XXX: fix this once 25 | } 26 | 27 | static VMReduceResult reduce0(VM *vm, const VMObjectPtr &o) { 28 | return vm->reduce(o); 29 | } 30 | 31 | void async(const VMObjectPtr &o) { 32 | VMObjectPtrs thunk; 33 | thunk.push_back(o); 34 | thunk.push_back(machine()->create_none()); 35 | auto app = machine()->create_array(thunk); 36 | 37 | _future = std::async(std::launch::async, reduce0, machine(), app); 38 | } 39 | 40 | VMReduceResult await() { 41 | return _future.get(); 42 | } 43 | 44 | bool wait_for(int n) { 45 | std::chrono::milliseconds ms(n); 46 | auto status = _future.wait_for(ms); 47 | return (status == std::future_status::ready); 48 | } 49 | 50 | bool valid() { 51 | return _future.valid(); 52 | } 53 | 54 | protected: 55 | std::future _future; 56 | }; 57 | 58 | class Async : public Monadic { 59 | public: 60 | MONADIC_PREAMBLE(VM_SUB_BUILTIN, Async, "System", "async"); 61 | 62 | DOCSTRING("System::async f - create a task"); 63 | VMObjectPtr apply(const VMObjectPtr &arg0) const override { 64 | auto vm = machine(); 65 | auto o = Future::create(vm); 66 | auto f = std::static_pointer_cast(o); 67 | f->async(arg0); 68 | return o; 69 | } 70 | }; 71 | 72 | class Await : public Monadic { 73 | public: 74 | MONADIC_PREAMBLE(VM_SUB_BUILTIN, Await, "System", "await"); 75 | 76 | DOCSTRING("System::await f - wait for async task"); 77 | VMObjectPtr apply(const VMObjectPtr &arg0) const override { 78 | if (Future::is_type(arg0)) { 79 | auto f = Future::cast(arg0); 80 | auto r = f->await(); 81 | if (r.exception) { 82 | throw r.result; 83 | } else { 84 | return r.result; 85 | } 86 | } else { 87 | throw machine()->bad_args(this, arg0); 88 | } 89 | } 90 | }; 91 | 92 | class WaitFor : public Dyadic { 93 | public: 94 | DYADIC_PREAMBLE(VM_SUB_BUILTIN, WaitFor, "System", "wait_for"); 95 | 96 | DOCSTRING( 97 | "System::wait_for f n - check whether future reduced during " 98 | "milliseconds"); 99 | VMObjectPtr apply(const VMObjectPtr &arg0, 100 | const VMObjectPtr &arg1) const override { 101 | if (Future::is_type(arg0) && (machine()->is_integer(arg1))) { 102 | auto f = Future::cast(arg0); 103 | auto n = machine()->get_integer(arg1); 104 | auto b = f->wait_for(n); 105 | return machine()->create_bool(b); 106 | } else { 107 | throw machine()->bad_args(this, arg0); 108 | } 109 | } 110 | }; 111 | 112 | class IsValid : public Monadic { 113 | public: 114 | MONADIC_PREAMBLE(VM_SUB_BUILTIN, IsValid, "System", "is_valid"); 115 | 116 | DOCSTRING("System::is_valid f - check whether future is reduced"); 117 | VMObjectPtr apply(const VMObjectPtr &arg0) const override { 118 | if (Future::is_type(arg0)) { 119 | auto f = Future::cast(arg0); 120 | auto b = f->valid(); 121 | return machine()->create_bool(b); 122 | } else { 123 | throw machine()->bad_args(this, arg0); 124 | } 125 | } 126 | }; 127 | 128 | class Sleep : public Monadic { 129 | public: 130 | MONADIC_PREAMBLE(VM_SUB_BUILTIN, Sleep, "System", "sleep"); 131 | 132 | DOCSTRING("System::sleep n - sleep for a number of milliseconds"); 133 | VMObjectPtr apply(const VMObjectPtr &arg0) const override { 134 | if (machine()->is_integer(arg0)) { 135 | auto n = machine()->get_integer(arg0); 136 | std::this_thread::sleep_for(std::chrono::milliseconds(n)); 137 | return machine()->create_none(); 138 | } else { 139 | throw machine()->bad_args(this, arg0); 140 | } 141 | } 142 | }; 143 | 144 | class AsyncModule : public CModule { 145 | public: 146 | virtual ~AsyncModule() { 147 | } 148 | 149 | icu::UnicodeString name() const override { 150 | return "async"; 151 | } 152 | 153 | icu::UnicodeString docstring() const override { 154 | return "The 'async' module defines concurrency combinators."; 155 | } 156 | 157 | std::vector exports(VM *vm) override { 158 | std::vector oo; 159 | 160 | // oo.push_back(Future::create(vm)); // XXX: I always forget whether 161 | // this 162 | // is needed 163 | oo.push_back(VMObjectStub::create(vm, "System::future")); 164 | oo.push_back(Async::create(vm)); 165 | oo.push_back(Await::create(vm)); 166 | oo.push_back(WaitFor::create(vm)); 167 | oo.push_back(IsValid::create(vm)); 168 | oo.push_back(Sleep::create(vm)); 169 | 170 | return oo; 171 | } 172 | }; 173 | 174 | } // namespace egel 175 | -------------------------------------------------------------------------------- /src/builtin_eval.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "runtime.hpp" 6 | 7 | namespace egel { 8 | 9 | class Evaluate : public Unary { 10 | public: 11 | UNARY_PREAMBLE(VM_SUB_BUILTIN, Evaluate, "System", "eval"); 12 | 13 | DOCSTRING("System::eval text - evaluatate the expression in `text`"); 14 | VMObjectPtr apply(const VMObjectPtr &arg0) const override { 15 | if (machine()->is_text(arg0)) { 16 | auto s = machine()->get_text(arg0); 17 | 18 | VMObjectPtr r = nullptr; 19 | VMObjectPtr e = nullptr; 20 | 21 | callback_t main = [&r](VM *vm, const VMObjectPtr &o) { r = o; }; 22 | callback_t exc = [&e](VM *vm, const VMObjectPtr &o) { e = o; }; 23 | 24 | try { 25 | machine()->eval_line(s, main, exc); 26 | } catch (Error &e) { 27 | auto s = e.message(); 28 | throw VMObjectText::create(s); 29 | } 30 | 31 | if (e != nullptr) { 32 | throw e; 33 | } else if (r != nullptr) { 34 | return r; 35 | } else { 36 | return nullptr; 37 | } 38 | } else { 39 | throw machine()->bad_args(this, arg0); 40 | } 41 | } 42 | 43 | UnicodeString result(const VMObjectPtr &o) { 44 | return o->to_text(); 45 | } 46 | }; 47 | 48 | class EvalModule : public CModule { 49 | public: 50 | virtual ~EvalModule() { 51 | } 52 | 53 | icu::UnicodeString name() const override { 54 | return "eval"; 55 | } 56 | 57 | icu::UnicodeString docstring() const override { 58 | return "The 'eval' module defines the eval combinator."; 59 | } 60 | 61 | std::vector exports(VM *vm) override { 62 | std::vector oo; 63 | oo.push_back(Evaluate::create(vm)); 64 | return oo; 65 | } 66 | }; 67 | 68 | } // namespace egel 69 | -------------------------------------------------------------------------------- /src/environment.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "error.hpp" 7 | #include "runtime.hpp" 8 | 9 | #include "unicode/regex.h" 10 | 11 | namespace egel { 12 | 13 | class Scope; 14 | using ScopePtr = std::shared_ptr; 15 | 16 | class Scope { 17 | public: 18 | Scope() : _outer(nullptr), _namespace("") { 19 | } 20 | 21 | Scope(ScopePtr scope) : _outer(scope), _namespace(scope->_namespace) { 22 | } 23 | 24 | static ScopePtr create() { 25 | return std::make_shared(); 26 | } 27 | 28 | static ScopePtr create(ScopePtr scope) { 29 | return std::make_shared(scope); 30 | } 31 | 32 | std::map map() { 33 | return _map; 34 | } 35 | 36 | ScopePtr outer() const { 37 | return _outer; 38 | } 39 | 40 | icu::UnicodeString get_namespace() const { 41 | return _namespace; 42 | } 43 | 44 | void extend_namespace(const icu::UnicodeString &s) { 45 | if (_namespace == "") { 46 | _namespace = s; 47 | } else { 48 | _namespace += "::" + s; 49 | } 50 | } 51 | 52 | void declare(const icu::UnicodeString &k, const icu::UnicodeString &v) { 53 | if (_map.count(k) > 0) { 54 | throw ErrorSemantical("redeclaration of " + k); 55 | } else { 56 | _map[k] = v; 57 | } 58 | } 59 | 60 | void redeclare(const icu::UnicodeString &k, 61 | const icu::UnicodeString &v) { 62 | _map[k] = v; 63 | } 64 | 65 | icu::UnicodeString get(const icu::UnicodeString &k) const { 66 | if (_map.count(k) > 0) { 67 | return _map.at(k); 68 | } else if (_outer != nullptr) { 69 | return _outer->get(k); 70 | } else { 71 | return ""; 72 | } 73 | } 74 | 75 | void render(std::ostream &os, int indent) const { 76 | os << "scope (" << std::endl; 77 | os << "namespace: " << _namespace << std::endl; 78 | for (const auto &kv:_map) { 79 | os << kv.first << " -> " << kv.second << std::endl; 80 | } 81 | if (_outer != nullptr) { 82 | os << "embedded in " << std::endl; 83 | _outer->render(os, indent+4); 84 | } 85 | os << ")" << std::endl; 86 | } 87 | 88 | void skip(std::ostream &os, int indent) const { 89 | for (int i = 0; i < indent; ++i) { 90 | os << " "; 91 | } 92 | } 93 | 94 | icu::UnicodeString to_text() { 95 | std::stringstream ss; 96 | render(ss, 0); 97 | icu::UnicodeString u(ss.str().c_str()); 98 | return u; 99 | } 100 | 101 | protected: 102 | std::map _map; 103 | ScopePtr _outer; 104 | icu::UnicodeString _namespace; 105 | }; 106 | 107 | // scope manipulation 108 | 109 | inline ScopePtr scope_global() { 110 | return Scope::create(); 111 | } 112 | 113 | inline ScopePtr enter_scope(ScopePtr& scope) { 114 | return Scope::create(scope); 115 | } 116 | 117 | inline ScopePtr leave_scope(ScopePtr& scope) { 118 | return scope->outer(); 119 | } 120 | 121 | inline ScopePtr get_global_scope(ScopePtr scope) { 122 | if (scope->outer() == nullptr) { 123 | return scope; 124 | } else { 125 | return get_global_scope(scope->outer()); 126 | } 127 | } 128 | 129 | inline icu::UnicodeString get_local(ScopePtr scope, const icu::UnicodeString &s) { 130 | return scope->get(s); 131 | } 132 | 133 | inline icu::UnicodeString get_global(ScopePtr scope, const icu::UnicodeString &s) { 134 | return get_local(get_global_scope(scope), s); 135 | } 136 | 137 | inline void declare_local(ScopePtr scope, const icu::UnicodeString &s) { 138 | scope->declare(s, s); 139 | } 140 | 141 | inline void declare_local(ScopePtr scope, const icu::UnicodeString &k, const icu::UnicodeString &v) { 142 | scope->declare(k, v); 143 | } 144 | 145 | inline void declare_global(ScopePtr scope, const icu::UnicodeString& s) { 146 | declare_local(get_global_scope(scope), s); 147 | } 148 | 149 | inline void redeclare_local(ScopePtr scope, const icu::UnicodeString& s) { 150 | scope->redeclare(s, s); 151 | } 152 | 153 | inline void redeclare_global(ScopePtr scope, const icu::UnicodeString& s) { 154 | redeclare_local(get_global_scope(scope), s); 155 | } 156 | 157 | inline void declare_rename(ScopePtr scope, const icu::UnicodeString& k, const icu::UnicodeString &v) { 158 | scope->declare(k, v); 159 | } 160 | 161 | inline std::vector get_namespace(ScopePtr scope, const icu::UnicodeString& ns) { 162 | auto map = get_global_scope(scope)->map(); 163 | std::vector dd; 164 | for (const auto &kv:map) { 165 | if (kv.first.startsWith(ns)) { 166 | dd.push_back(kv.first); 167 | } 168 | } 169 | return dd; 170 | } 171 | 172 | inline ScopePtr enter_namespace(ScopePtr scope, const icu::UnicodeString& s) { 173 | auto sc = enter_scope(scope); 174 | sc->extend_namespace(s); 175 | auto ns = sc->get_namespace(); 176 | auto dd = get_namespace(scope, ns); 177 | auto l = ns.countChar32() + 2; 178 | for (const auto &s:dd) { 179 | auto s0 = s; 180 | s0.removeBetween(0,l); 181 | declare_rename(sc, s0, s); 182 | } 183 | return sc; 184 | } 185 | 186 | inline ScopePtr leave_namespace(ScopePtr scope) { 187 | return leave_scope(scope); 188 | } 189 | 190 | inline void debug_scope(ScopePtr scope) { 191 | scope->render(std::cout, 0); 192 | } 193 | 194 | } // namespace egel 195 | -------------------------------------------------------------------------------- /src/error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "constants.hpp" 6 | #include "position.hpp" 7 | #include "runtime.hpp" 8 | #include "unicode/unistr.h" 9 | #include "unicode/ustdio.h" 10 | #include "unicode/ustream.h" 11 | 12 | namespace egel { 13 | 14 | enum error_tag_t { 15 | ERROR_IO, 16 | ERROR_LEXICAL, 17 | ERROR_SYNTACTICAL, 18 | ERROR_IDENTIFICATION, 19 | ERROR_SEMANTICAL, 20 | ERROR_INTERNAL, 21 | }; 22 | 23 | class Error : public std::exception { 24 | public: 25 | Error(error_tag_t t, const Position &p, const icu::UnicodeString &m) 26 | : _tag(t), _position(p), _message(m) { 27 | // useful for debugging, uncomment to get stack trace in debug mode 28 | // Error* e = (Error*) nullptr; 29 | // e->position(); 30 | } 31 | 32 | ~Error() { 33 | } 34 | 35 | Position position() const { 36 | return _position; 37 | } 38 | 39 | icu::UnicodeString message() const { 40 | return _message; 41 | } 42 | 43 | icu::UnicodeString error() const { 44 | icu::UnicodeString s = ""; 45 | if (position().resource() != "") { 46 | s += position().to_text() + ":"; 47 | } 48 | switch (_tag) { 49 | case ERROR_IO: 50 | s += STRING_IO; 51 | break; 52 | case ERROR_LEXICAL: 53 | s += STRING_LEXICAL; 54 | break; 55 | case ERROR_SYNTACTICAL: 56 | s += STRING_SYNTACTICAL; 57 | break; 58 | case ERROR_IDENTIFICATION: 59 | s += STRING_IDENTIFICATION; 60 | break; 61 | case ERROR_SEMANTICAL: 62 | s += STRING_SEMANTICAL; 63 | break; 64 | case ERROR_INTERNAL: 65 | s += STRING_INTERNAL; 66 | break; 67 | } 68 | s += ":" + message(); 69 | return s; 70 | } 71 | 72 | friend std::ostream &operator<<(std::ostream &o, const Error &e) { 73 | o << e.error(); 74 | return o; 75 | } 76 | 77 | private: 78 | error_tag_t _tag; 79 | Position _position; 80 | icu::UnicodeString _message; 81 | }; 82 | 83 | class ErrorIO : public Error { 84 | public: 85 | ErrorIO(const Position &p, const icu::UnicodeString &m) 86 | : Error(ERROR_IO, p, m) { 87 | } 88 | 89 | ErrorIO(const icu::UnicodeString &m) : Error(ERROR_IO, Position(), m) { 90 | } 91 | }; 92 | 93 | class ErrorLexical : public Error { 94 | public: 95 | ErrorLexical(const Position &p, const icu::UnicodeString &m) 96 | : Error(ERROR_LEXICAL, p, m) { 97 | } 98 | }; 99 | 100 | class ErrorSyntactical : public Error { 101 | public: 102 | ErrorSyntactical(const Position &p, const icu::UnicodeString &m) 103 | : Error(ERROR_SYNTACTICAL, p, m) { 104 | } 105 | }; 106 | 107 | class ErrorIdentification : public Error { 108 | public: 109 | ErrorIdentification(const Position &p, const icu::UnicodeString &m) 110 | : Error(ERROR_IDENTIFICATION, p, m) { 111 | } 112 | }; 113 | 114 | class ErrorSemantical : public Error { 115 | public: 116 | ErrorSemantical(const Position &p, const icu::UnicodeString &m) 117 | : Error(ERROR_SEMANTICAL, p, m) { 118 | } 119 | 120 | ErrorSemantical(const icu::UnicodeString &m) 121 | : Error(ERROR_SEMANTICAL, Position(), m) { 122 | } 123 | }; 124 | 125 | class ErrorInternal : public Error { 126 | public: 127 | ErrorInternal(const icu::UnicodeString &m) 128 | : Error(ERROR_INTERNAL, Position(), m) { 129 | } 130 | }; 131 | 132 | } // namespace egel 133 | -------------------------------------------------------------------------------- /src/operators.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "runtime.hpp" 4 | 5 | namespace egel { 6 | 7 | constexpr auto OPERATOR_BOTTOM = "="; 8 | 9 | inline int operator_compare(const icu::UnicodeString &o0, 10 | const icu::UnicodeString &o1); 11 | 12 | inline bool operator_is_infix(const icu::UnicodeString &o); 13 | inline bool operator_is_prefix(const icu::UnicodeString &o); 14 | inline bool operator_is_postfix(const icu::UnicodeString &o); 15 | 16 | inline bool operator_is_left_associative(const icu::UnicodeString &o); 17 | inline bool operator_is_right_associative(const icu::UnicodeString &o); 18 | inline bool operator_is_not_associative(const icu::UnicodeString &o); 19 | 20 | inline icu::UnicodeString operator_to_ascii(const icu::UnicodeString &o); 21 | 22 | constexpr auto LEFT_ASSOC = (1 << 0); 23 | constexpr auto RIGHT_ASSOC = (1 << 1); 24 | constexpr auto NONE_ASSOC = (1 << 2); 25 | constexpr auto PREFIX = (1 << 3); 26 | constexpr auto POSTFIX = (1 << 4); 27 | constexpr auto INFIX = (1 << 5); 28 | 29 | using attr_t = uint8_t; 30 | 31 | struct operator_t { 32 | UChar32 name; 33 | const char *translation; 34 | attr_t attribute; 35 | }; 36 | 37 | static operator_t operators[] = { 38 | {'=', "eq", NONE_ASSOC | INFIX}, 39 | {'!', "ex", NONE_ASSOC | PREFIX}, 40 | {'&', "am", LEFT_ASSOC | INFIX}, 41 | {'|', "br", LEFT_ASSOC | INFIX}, 42 | {'<', "lt", RIGHT_ASSOC | INFIX}, 43 | {'>', "gt", RIGHT_ASSOC | INFIX}, 44 | {'+', "pl", LEFT_ASSOC | PREFIX | INFIX}, 45 | {'-', "sb", LEFT_ASSOC | PREFIX | INFIX}, 46 | {'/', "dv", LEFT_ASSOC | INFIX}, 47 | {'*', "ml", LEFT_ASSOC | INFIX}, 48 | {'^', "pw", LEFT_ASSOC | INFIX}, 49 | {'#', "hs", RIGHT_ASSOC | INFIX}, 50 | {'%', "ct", RIGHT_ASSOC | INFIX}, 51 | {'~', "tl", NONE_ASSOC | PREFIX | INFIX}, 52 | {'$', "or", LEFT_ASSOC | INFIX}, 53 | {'@', "at", RIGHT_ASSOC | INFIX}, 54 | {'.', "dt", LEFT_ASSOC | INFIX}, 55 | }; 56 | 57 | constexpr auto OPERATORS_SIZE = (sizeof(operators) / sizeof(operator_t)); 58 | 59 | inline int operator_char_entry(const UChar32 &c) { 60 | for (size_t i = 0; i < OPERATORS_SIZE; i++) { 61 | if (operators[i].name == c) { 62 | return i; 63 | } 64 | } 65 | return -1; 66 | } 67 | 68 | inline icu::UnicodeString operator_char_translation(int i) { 69 | return icu::UnicodeString(operators[i].translation); 70 | } 71 | 72 | inline attr_t operator_char_attributes(int i) { 73 | return operators[i].attribute; 74 | } 75 | 76 | inline int operator_char_compare(const UChar32 c0, const UChar32 c1) { 77 | int i0 = operator_char_entry(c0); 78 | int i1 = operator_char_entry(c1); 79 | 80 | if ((i0 < 0) && (i1 < 0)) { // handle math and all other ops 81 | if (c0 < c1) { 82 | return -1; 83 | } else if (c1 < c0) { 84 | return 1; 85 | } else { 86 | return 0; 87 | } 88 | } 89 | 90 | if (i0 < i1) { 91 | return -1; 92 | } else if (i1 < i0) { 93 | return 1; 94 | } else { 95 | return 0; 96 | } 97 | } 98 | 99 | inline int operator_compare(const icu::UnicodeString &o0, 100 | const icu::UnicodeString &o1) { 101 | int l0 = o0.length(); 102 | int l1 = o1.length(); 103 | 104 | ASSERT(l0 > 0); 105 | ASSERT(l1 > 0); 106 | 107 | int32_t i0 = 0; 108 | int32_t i1 = 0; 109 | 110 | for (int i = 0; (i < l0) && (i < l1); i++) { 111 | int b = operator_char_compare(o0.char32At(i0), o1.char32At(i1)); 112 | if (b < 0) return -1; 113 | if (b > 0) return 1; 114 | i0 = o0.moveIndex32(i0, 1); 115 | i1 = o1.moveIndex32(i1, 1); 116 | }; 117 | 118 | if (l0 < l1) return -1; 119 | if (l0 > l1) return 1; 120 | 121 | return 0; 122 | } 123 | 124 | inline UChar32 operator_head_char(const icu::UnicodeString &o) { 125 | ASSERT(o.length() > 0); 126 | UChar32 c = o.char32At(0); 127 | return c; 128 | } 129 | 130 | inline bool operator_is_infix(const icu::UnicodeString &o) { 131 | UChar32 c = operator_head_char(o); 132 | int i = operator_char_entry(c); 133 | if (i < 0) { 134 | return false; 135 | } else { 136 | attr_t f = operator_char_attributes(i); 137 | return (f & INFIX) == INFIX; 138 | } 139 | } 140 | 141 | inline bool operator_is_prefix(const icu::UnicodeString &o) { 142 | UChar32 c = operator_head_char(o); 143 | int i = operator_char_entry(c); 144 | if (i < 0) { 145 | return false; 146 | } else { 147 | attr_t f = operator_char_attributes(i); 148 | return (f & PREFIX) == PREFIX; 149 | } 150 | } 151 | 152 | inline bool operator_is_postfix(const icu::UnicodeString &o) { 153 | UChar32 c = operator_head_char(o); 154 | int i = operator_char_entry(c); 155 | if (i < 0) { 156 | return false; 157 | } else { 158 | attr_t f = operator_char_attributes(i); 159 | return (f & POSTFIX) == POSTFIX; 160 | } 161 | } 162 | 163 | inline bool operator_is_left_associative(const icu::UnicodeString &o) { 164 | UChar32 c = operator_head_char(o); 165 | int i = operator_char_entry(c); 166 | if (i < 0) { 167 | return false; 168 | } else { 169 | attr_t f = operator_char_attributes(i); 170 | return (f & LEFT_ASSOC) == LEFT_ASSOC; 171 | } 172 | } 173 | 174 | inline bool operator_is_right_associative(const icu::UnicodeString &o) { 175 | UChar32 c = operator_head_char(o); 176 | int i = operator_char_entry(c); 177 | if (i < 0) { 178 | return false; 179 | } else { 180 | attr_t f = operator_char_attributes(i); 181 | return (f & RIGHT_ASSOC) == RIGHT_ASSOC; 182 | } 183 | } 184 | 185 | inline bool operator_is_not_associative(const icu::UnicodeString &o) { 186 | UChar32 c = operator_head_char(o); 187 | int i = operator_char_entry(c); 188 | if (i < 0) { 189 | return false; 190 | } else { 191 | attr_t f = operator_char_attributes(i); 192 | return (f & NONE_ASSOC) == NONE_ASSOC; 193 | } 194 | } 195 | 196 | inline icu::UnicodeString operator_to_ascii(const icu::UnicodeString &o) { 197 | // XXX 198 | return ""; 199 | } 200 | 201 | }; // namespace egel 202 | -------------------------------------------------------------------------------- /src/position.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "runtime.hpp" 7 | #include "unicode/unistr.h" 8 | #include "unicode/ustream.h" 9 | 10 | namespace egel { 11 | 12 | class Position { 13 | public: 14 | Position() { 15 | _resource = icu::UnicodeString(""); 16 | _row = 0; 17 | _column = 0; 18 | } 19 | 20 | Position(const icu::UnicodeString &resource, int32_t row, int32_t column) { 21 | _resource = resource; 22 | _row = row; 23 | _column = column; 24 | } 25 | 26 | ~Position() { 27 | } 28 | 29 | icu::UnicodeString resource() const { 30 | return _resource; 31 | } 32 | 33 | int32_t row() const { 34 | return _row; 35 | } 36 | 37 | int32_t column() const { 38 | return _column; 39 | } 40 | 41 | icu::UnicodeString to_text() { 42 | std::stringstream ss; 43 | ss << *this; 44 | icu::UnicodeString u(ss.str().c_str()); 45 | return u; 46 | } 47 | 48 | friend std::ostream &operator<<(std::ostream &os, const Position &p) { 49 | os << p.resource() << ":" << p.row() << ":" << p.column() << ""; 50 | return os; 51 | } 52 | 53 | private: 54 | icu::UnicodeString _resource; 55 | int32_t _row; 56 | int32_t _column; 57 | }; 58 | 59 | }; // namespace egel 60 | -------------------------------------------------------------------------------- /src/reader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "position.hpp" 6 | #include "runtime.hpp" 7 | #include "unicode/unistr.h" 8 | #include "unicode/ustream.h" 9 | 10 | namespace egel { 11 | 12 | class CharReader { 13 | public: 14 | virtual UChar32 look() = 0; 15 | virtual UChar32 look(int n) = 0; 16 | virtual void skip() = 0; 17 | virtual bool end() = 0; 18 | virtual bool eol() = 0; 19 | 20 | virtual void reset() = 0; 21 | 22 | virtual Position position() = 0; 23 | }; 24 | 25 | class StringCharReader : public CharReader { 26 | public: 27 | StringCharReader(const icu::UnicodeString &resource, 28 | const icu::UnicodeString &content) { 29 | _row = 1; 30 | _column = 1; 31 | _index = 0; 32 | _resource = resource; 33 | _content = content; 34 | fill_buffer(); 35 | } 36 | 37 | Position position() override { 38 | return Position(_resource, _row, _column); 39 | } 40 | 41 | icu::UnicodeString content() { 42 | return _content; 43 | } 44 | 45 | UChar32 look() override { 46 | if (end()) return '\0'; 47 | return _buffer[_index]; 48 | } 49 | 50 | UChar32 look(int n) override { // need LL(2) in the lexer for '-1', to be 51 | // decaprecated at some point 52 | return _buffer[_index + n]; 53 | } 54 | 55 | void skip() override { 56 | if (end()) return; 57 | UChar32 c = look(); 58 | _index++; 59 | switch (c) { 60 | // XXX: handle MSDOS like newlines 61 | case '\n': 62 | _column = 1; 63 | _row++; 64 | break; 65 | default: 66 | _column++; 67 | } 68 | } 69 | 70 | bool end() override { 71 | if (_index >= _length) return true; 72 | if (_buffer[_index] == 65535) 73 | return true; // make absolutely sure we always detect the end 74 | return false; 75 | } 76 | 77 | bool eol() override { 78 | if (end()) { 79 | return false; 80 | } else { 81 | return look() == '\n'; 82 | }; 83 | } 84 | 85 | void reset() override { 86 | _index = 0; 87 | _row = 1; 88 | _column = 1; 89 | } 90 | 91 | protected: 92 | void fill_buffer() { 93 | for (int i = 0; i < _content.length(); i = _content.moveIndex32(i, 1)) { 94 | auto c = _content.char32At(i); 95 | _buffer.push_back(c); 96 | } 97 | _length = _buffer.size(); 98 | } 99 | 100 | private: 101 | int32_t _row; 102 | int32_t _column; 103 | int32_t _index; 104 | int32_t _length; 105 | 106 | icu::UnicodeString _resource; 107 | icu::UnicodeString _content; 108 | std::vector _buffer; 109 | }; 110 | 111 | }; // namespace egel 112 | -------------------------------------------------------------------------------- /tests/concat.eg: -------------------------------------------------------------------------------- 1 | namespace List ( 2 | using System 3 | 4 | def concat = 5 | [ nil YY -> YY 6 | | (cons X XX) YY -> cons X (concat XX YY) ] 7 | 8 | def ++ = concat 9 | ) 10 | 11 | using List 12 | 13 | def main = {1,2} ++ {3,4,5} 14 | -------------------------------------------------------------------------------- /tests/ffi/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void id_void() { 5 | printf("id_void\n"); 6 | }; 7 | 8 | bool id_bool(bool b) { 9 | printf("id_bool: '%d'\n", b); 10 | return b; 11 | }; 12 | 13 | char id_char(char c) { 14 | printf("id_char: '%c'\n", c); 15 | return c; 16 | }; 17 | 18 | char id_byte(char b) { 19 | printf("id_byte: '%d'\n", b); 20 | return b; 21 | }; 22 | 23 | short id_short(short n) { 24 | printf("id_short: '%d'\n", n); 25 | return n; 26 | }; 27 | 28 | int id_int(int n) { 29 | printf("id_int: '%d'\n", n); 30 | return n; 31 | }; 32 | 33 | long id_long(long n) { 34 | printf("id_long: '%ld'\n", n); 35 | return n; 36 | }; 37 | 38 | long long id_long_long(long long n) { 39 | printf("id_long_long: '%lld'\n", n); 40 | return n; 41 | }; 42 | 43 | size_t id_size_t(size_t s) { 44 | printf("id_size_t: '%zu'\n", s); 45 | return s; 46 | }; 47 | 48 | ssize_t id_ssize_t(ssize_t s) { 49 | printf("id_ssize_t: '%zd'\n", s); 50 | return s; 51 | }; 52 | 53 | void* id_void_p(void* p) { 54 | printf("id_void_p: '%p'\n", p); 55 | return p; 56 | }; 57 | 58 | char* id_char_p(char* cc) { 59 | printf("id_char_p: '%s'\n", cc); 60 | return cc; 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /tests/ffi/test.eg: -------------------------------------------------------------------------------- 1 | import "prelude.eg" 2 | 3 | using System 4 | using FFI 5 | 6 | val lib = load_library "libtest.dylib" 7 | 8 | def func = [F -> function lib F] 9 | 10 | def test_void = call (func "id_void") c_void {} 11 | 12 | def test_char = [C -> call (func "id_char") c_char {c_char C}] 13 | 14 | def test_byte = [B -> call (func "id_byte") c_byte {c_byte B}] 15 | 16 | def test_ubyte = [B -> call (func "id_byte") c_ubyte {c_ubyte B}] 17 | 18 | def test_short = [B -> call (func "id_short") c_short {c_short B}] 19 | 20 | def test_int = [N -> call (func "id_int") c_int {c_int N}] 21 | 22 | def test_void_p = [N -> call (func "id_void_p") c_void_p {c_void_p N}] 23 | 24 | def test_char_p = [T -> call (func "id_char_p") c_char_p {c_char_p T}] 25 | 26 | def malloc_test = 27 | let P = malloc 16 in 28 | poke P 0 (c_int 12345); 29 | let V = peek P 0 c_int in 30 | free P; 31 | V 32 | 33 | def main = 34 | test_void; 35 | let TT = {(test_char, 'a'), (test_byte, -1), (test_ubyte, 255), (test_short, 65436), 36 | (test_int, 12345), (test_void_p, none), (test_void_p, 123456789), (test_char_p, none), (test_char_p, "hello world!")} in 37 | List::map [(F, X) -> let Y = F X in print (format "{} {} = {}\n" F X Y) ] TT; 38 | malloc_test 39 | -------------------------------------------------------------------------------- /tests/ffi/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "compiling c library" 4 | 5 | clang -c test.c -o test.o 6 | clang -shared -o libtest.dylib test.o 7 | -------------------------------------------------------------------------------- /tests/hello.eg: -------------------------------------------------------------------------------- 1 | 2 | def main = "Hello World!" 3 | -------------------------------------------------------------------------------- /tests/huge.eg: -------------------------------------------------------------------------------- 1 | # A small test on whether the language handles large data-structures. It 2 | # does but printing is implemented as a recursive C routine which 3 | # subsequently runs out of stack space. 4 | # 5 | # This concatenates about a million (2^20) singleton lists. 6 | 7 | namespace Test ( 8 | 9 | using System 10 | 11 | data nil, cons 12 | 13 | def concat = 14 | [ nil YY -> YY 15 | | (cons X XX) YY -> cons X (concat XX YY) ] 16 | 17 | def double = [ X -> concat X X ] 18 | 19 | def q = [ X -> double (double (double (double X))) ] 20 | 21 | def huge = q (q (q (q (q (cons 1 nil))))) 22 | 23 | def len = 24 | [ nil -> 0 25 | | (cons X XX) -> 1 + (len XX) ] 26 | ) 27 | 28 | using Test 29 | 30 | # def main = huge # XXX: bug 31 | 32 | def main = len huge 33 | 34 | -------------------------------------------------------------------------------- /tests/large.py: -------------------------------------------------------------------------------- 1 | 2 | def genmod(n): 3 | print("namespace Test%d (" % n) 4 | print(" data true, false") 5 | print("") 6 | print(" namespace F:G (") 7 | print(" def and =") 8 | print(" [ true true -> true | _ _ -> false ]") 9 | print(" )") 10 | print("") 11 | print(" namespace I:J (") 12 | print(" def or =") 13 | print(" [ false false -> true | _ _ -> true ]") 14 | print(" )") 15 | print("") 16 | print(" def not =") 17 | print(" [ false -> true | _ -> false ]") 18 | print("") 19 | print(")") 20 | 21 | for i in range(0, 2000): 22 | genmod(i) 23 | -------------------------------------------------------------------------------- /tests/leak.eg: -------------------------------------------------------------------------------- 1 | # Just a stress test to see how leaking behaves. 2 | # 3 | 4 | using System 5 | 6 | def leak_one_cycle = 7 | [ X -> setref X X ] (ref 0) 8 | 9 | def leak_more = 10 | [ 0 -> none | N -> leak_one_cycle; leak_more (N-1) ] 11 | 12 | def main = 13 | leak_more 1000000 14 | -------------------------------------------------------------------------------- /tests/maplist.eg: -------------------------------------------------------------------------------- 1 | namespace List ( 2 | using System 3 | 4 | def concat = 5 | [ nil YY -> YY 6 | | (cons X XX) YY -> cons X (concat XX YY) ] 7 | 8 | def ++ = concat 9 | 10 | def map = 11 | [ F nil -> nil 12 | | F (cons X XX) -> cons (F X) (map F XX) ] 13 | ) 14 | 15 | using System 16 | using List 17 | 18 | def main = map [X -> X*2] ({1,2} ++ {3,4,5}) 19 | -------------------------------------------------------------------------------- /tests/maptest.eg: -------------------------------------------------------------------------------- 1 | # showcase maps with several examples 2 | 3 | import "prelude.eg" 4 | import "map.eg" 5 | 6 | using System 7 | using Map 8 | 9 | 10 | def test_is_empty = 11 | [ M -> print "is_empty: " (is_empty M) "\n" ] 12 | 13 | def test_size = 14 | [ M S -> print "size = " (size M) " eq " ((size M) == S) "\n" ] 15 | 16 | # test empty 17 | 18 | def test_empty = 19 | test_is_empty empty; 20 | test_size empty 0 21 | 22 | # test singleton 23 | 24 | def singleton = 25 | [ K V -> insert K V empty] 26 | 27 | def test_nth = 28 | [ M K V -> print K " -> " (nth M K) " eq " ((nth M K) == V) "\n" ] 29 | 30 | def test_singleton = 31 | let S = singleton 0 1 in 32 | test_is_empty S; 33 | test_size S 1; 34 | test_nth S 0 1 35 | 36 | # create a list of tuples 37 | 38 | def inc_list = List::map [N -> (N,N+1)] (List::from_to 1 9) 39 | 40 | def inc_map = from_list inc_list 41 | 42 | def test_size0 = 43 | [ M -> (size M) == (List::length (to_list M)) ] 44 | 45 | def print_map = 46 | [ M -> print "map: " (to_list M) "\n" ] 47 | 48 | def test_map = 49 | let M = inc_map in 50 | print_map M; 51 | test_is_empty M; 52 | test_size M 9; 53 | test_nth M 1 2; 54 | test_nth M 3 4; 55 | test_nth M 9 10; 56 | print_map (delete 3 (delete 7 M)); 57 | print (List::map [ N -> nth M N ] (domain M)) "\n"; 58 | print (Map::foldr [ V E -> cons V E ] nil M) "\n"; 59 | none 60 | 61 | def main = 62 | test_empty; 63 | test_singleton; 64 | test_map 65 | -------------------------------------------------------------------------------- /tests/million.eg: -------------------------------------------------------------------------------- 1 | namespace List ( 2 | using System 3 | 4 | def len = 5 | [ nil -> 0 6 | | (cons X XX) -> 1 + (len XX) ] 7 | 8 | def block = 9 | [ 0 -> nil 10 | | N -> cons (N - 1) (block (N - 1)) ] 11 | ) 12 | 13 | using System 14 | using List 15 | 16 | def main = len (block 1000000) 17 | -------------------------------------------------------------------------------- /tests/tailfac.eg: -------------------------------------------------------------------------------- 1 | namespace FacTail ( 2 | 3 | using System 4 | 5 | def factail = 6 | [ 0 ACC -> ACC 7 | | N ACC -> factail (N - 1) (N*ACC) ] 8 | 9 | def fac = 10 | [ N -> factail N 1 ] 11 | 12 | ) 13 | 14 | using FacTail 15 | 16 | def main = fac 10 17 | 18 | -------------------------------------------------------------------------------- /tests/unicode.eg: -------------------------------------------------------------------------------- 1 | namespace Unicode ( 2 | 3 | def ↗ = [X -> X] 4 | 5 | def ∀ = [X -> X] 6 | 7 | ) 8 | 9 | using Unicode 10 | 11 | def main = ∀ 0 12 | -------------------------------------------------------------------------------- /vendor/README.md: -------------------------------------------------------------------------------- 1 | # Egel external static libraries 2 | 3 | Egel for a long while was developed on unix, where 4 | shared libraries are unique on a distribution version 5 | and that worked well. 6 | 7 | On other operating systems, c++ shared libraries are 8 | commonly not distributed with the system. That more or 9 | less forces the developer to build and compile used 10 | external libraries as part of a project. 11 | 12 | These submodules contain the external dependencies of 13 | the egel interpreter (icu4c, ffi, fmt, and gnu 14 | lightning). 15 | 16 | ICU4C is a big package and, together with fmt, will 17 | compile fine. 18 | 19 | A bash script, `makeall.sh`, is included for convenience. 20 | -------------------------------------------------------------------------------- /vendor/makeall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git submodule update --init --recursive 4 | 5 | # the vendor libraries are installed in vendor/local 6 | mkdir local 7 | 8 | # position independent code to make 9 | export CFLAGS="-fPIC" 10 | export CXXFLAGS="-fPIC" 11 | 12 | # libicu 13 | pushd icu/icu4c/source 14 | chmod +x runConfigureICU configure install-sh 15 | ./runConfigureICU Linux --enable-static --prefix=$(realpath "../../../local") 16 | #./runConfigureICU MacOSX --enable-static --prefix=$(realpath "../../../local") 17 | #./runConfigureICU FreeBSD --enable-static --prefix=$(realpath "../../../local") 18 | #./runConfigureICU Cygwin/MSCV --enable-static --prefix=$(realpath "../../../local") 19 | make --enable-static 20 | make install 21 | popd 22 | 23 | # libffi 24 | pushd libffi 25 | ./autogen.sh 26 | ./configure --prefix=$(realpath "../local") 27 | make 28 | make check 29 | make install 30 | popd 31 | 32 | # lightning, depends on libtools and texinfo 33 | pushd lightning 34 | ./bootstrap 35 | ./configure --prefix=$(realpath "../local") 36 | make 37 | make check 38 | make install 39 | popd 40 | 41 | # fmt 42 | pushd fmt 43 | mkdir build 44 | cd build 45 | cmake -DCMAKE_INSTALL_PREFIX="../../local" .. 46 | make 47 | make install 48 | popd 49 | 50 | --------------------------------------------------------------------------------