diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000000..dc542b09cb --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,45 @@ +image: + - Visual Studio 2019 + #- Visual Studio 2017 + #- Visual Studio 2015 +environment: + matrix: + - NEON_ARCH: 64 + EXCLUDE_TESTS: 1 + - NEON_ARCH: 32 + - NEON_ARCH: 64 + #- WITH_SUBMODULES: 1 + # NEON_ARCH: 64 +#matrix: +# exclude: +# - image: Visual Studio 2015 +# - image: Visual Studio 2017 +# - image: Visual Studio 2019 +#install: +#- ps: >- +# if (-not (Test-Path C:\Python38\Lib\site-packages\scons-3.1.2)){ +# Start-FileDownload "https://downloads.sourceforge.net/project/scons/scons/3.1.2/scons-3.1.2.zip" -FileName "scons-3.1.2.zip" +# 7z x -y scons-3.1.2.zip +# } +build_script: +- cmd: set PATH=c:\python38;%PATH% +- cmd: copy c:\python38\python.exe c:\python38\python3.exe +#- cmd: python3 scons-3.1.2\setup.py install + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" if "%NEON_ARCH%"=="32" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" if "%NEON_ARCH%"=="64" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" if "%NEON_ARCH%"=="32" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat" + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" if "%NEON_ARCH%"=="64" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" if "%NEON_ARCH%"=="32" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" if "%NEON_ARCH%"=="64" call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" if "%NEON_ARCH%"=="64" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 + +- cmd: cmake . +- cmd: cmake --build . +- cmd: set PYTHONIOENCODING=utf8 +- cmd: if not "%EXCLUDE_TESTS%"=="1" ctest -C Debug --output-on-failure diff --git a/.builds/alpine-latest-cmake.yml b/.builds/alpine-latest-cmake.yml new file mode 100644 index 0000000000..a65495f77d --- /dev/null +++ b/.builds/alpine-latest-cmake.yml @@ -0,0 +1,29 @@ +image: alpine/latest +packages: +- cmake +- gmp-dev +- zlib-dev +- perl +# optional +#- openjdk11 (javac not found) +#- mono (not until mono supports musl?) +- nodejs +- rust +- go +sources: +- https://git.sr.ht/~ghewgill/neon-lang +triggers: +- action: email + condition: failure + to: ~ghewgill/neon-builds@lists.sr.ht +tasks: +- prep: | + cd neon-lang + git submodule deinit . +- build: | + cd neon-lang + cmake . + cmake --build . +- test: | + cd neon-lang + echo NOT RUNNING: ctest diff --git a/.builds/archlinux-cmake.yml b/.builds/archlinux-cmake.yml new file mode 100644 index 0000000000..4708c5c0bb --- /dev/null +++ b/.builds/archlinux-cmake.yml @@ -0,0 +1,29 @@ +image: archlinux +packages: +- cmake +- python3 +- ncurses +- gmp +# optional +- jdk-openjdk +- mono +- nodejs +- rust +#- go (can't find main module) +sources: +- https://git.sr.ht/~ghewgill/neon-lang +triggers: +- action: email + condition: failure + to: ~ghewgill/neon-builds@lists.sr.ht +tasks: +- prep: | + cd neon-lang + git submodule deinit . +- build: | + cd neon-lang + cmake . + cmake --build . +- test: | + cd neon-lang + echo NOT RUNNING: ctest diff --git a/.builds/debian-stable-cmake-all.yml b/.builds/debian-stable-cmake-all.yml new file mode 100644 index 0000000000..4e83633898 --- /dev/null +++ b/.builds/debian-stable-cmake-all.yml @@ -0,0 +1,33 @@ +image: debian/stable +packages: +- python3 +- cmake +- ncurses-dev +- libgmp-dev +- zlib1g-dev +# optional +- default-jdk +- mono-mcs +- nodejs +- rustc +- golang +sources: +- https://github.com/ghewgill/neon-lang +triggers: +- action: email + condition: failure + to: ~ghewgill/neon-builds@lists.sr.ht +tasks: +- prep: | + cd neon-lang + git clone https://github.com/ghewgill/neon-module-registry + for a in $(ls neon-module-registry | grep yaml); do + python3 tools/helium.py scripts/module-install.neon $(echo $a | sed -e 's/.yaml//'); + done +- build: | + cd neon-lang + cmake . + cmake --build . +- test: | + cd neon-lang + echo NOT RUNNING: ctest diff --git a/.builds/debian-stable-cmake.yml b/.builds/debian-stable-cmake.yml new file mode 100644 index 0000000000..659925e5ce --- /dev/null +++ b/.builds/debian-stable-cmake.yml @@ -0,0 +1,30 @@ +image: debian/stable +packages: +- python3 +- cmake +- ncurses-dev +- libgmp-dev +- zlib1g-dev +# optional +- default-jdk +- mono-mcs +- nodejs +- rustc +- golang +sources: +- https://git.sr.ht/~ghewgill/neon-lang +triggers: +- action: email + condition: failure + to: ~ghewgill/neon-builds@lists.sr.ht +tasks: +- prep: | + cd neon-lang + git submodule deinit . +- build: | + cd neon-lang + cmake . + cmake --build . +- test: | + cd neon-lang + echo NOT RUNNING: ctest diff --git a/.builds/fedora-latest-cmake.yml b/.builds/fedora-latest-cmake.yml new file mode 100644 index 0000000000..91775cf3a9 --- /dev/null +++ b/.builds/fedora-latest-cmake.yml @@ -0,0 +1,35 @@ +image: fedora/latest +packages: +- g++ +- cmake +- ncurses +- gmp-devel +- zlib-devel +# These perl modules are just for NaturalDocs +- perl-English +- perl-FindBin +- perl-File-Copy +- perl-Tie-RefHash +# optional +#- java-latest-openjdk (javac not working) +- mono-core +- nodejs +- rust +- go +sources: +- https://git.sr.ht/~ghewgill/neon-lang +triggers: +- action: email + condition: failure + to: ~ghewgill/neon-builds@lists.sr.ht +tasks: +- prep: | + cd neon-lang + git submodule deinit . +- build: | + cd neon-lang + cmake . + cmake --build . +- test: | + cd neon-lang + echo NOT RUNNING: ctest diff --git a/.builds/freebsd-latest-cmake.yml b/.builds/freebsd-latest-cmake.yml new file mode 100644 index 0000000000..f4269d604c --- /dev/null +++ b/.builds/freebsd-latest-cmake.yml @@ -0,0 +1,28 @@ +image: freebsd/latest +packages: +- cmake +- python3 +- gmp +# optional +- openjdk # version 1.7, needs 1.8 +- mono +- node +- rust +#- go (cannot find main module) +sources: +- https://git.sr.ht/~ghewgill/neon-lang +triggers: +- action: email + condition: failure + to: ~ghewgill/neon-builds@lists.sr.ht +tasks: +- prep: | + cd neon-lang + git submodule deinit . +- build: | + cd neon-lang + cmake . + cmake --build . +- test: | + cd neon-lang + echo NOT RUNNING: ctest diff --git a/.builds/openbsd-latest-cmake.yml b/.builds/openbsd-latest-cmake.yml new file mode 100644 index 0000000000..42cc6e5b7e --- /dev/null +++ b/.builds/openbsd-latest-cmake.yml @@ -0,0 +1,28 @@ +image: openbsd/latest +packages: +- cmake +- python3 +- gmpxx +# optional +- jdk +- mono +- node +- rust +- go +sources: +- https://git.sr.ht/~ghewgill/neon-lang +triggers: +- action: email + condition: failure + to: ~ghewgill/neon-builds@lists.sr.ht +tasks: +- prep: | + cd neon-lang + git submodule deinit . +- build: | + cd neon-lang + cmake . + cmake --build . +- test: | + cd neon-lang + echo NOT RUNNING: ctest diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..82dfdeed94 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,21 @@ +version: 2 +jobs: + build: + docker: + - image: debian:stretch + steps: + - run: + name: update packages + command: apt update + - run: + name: install packages + command: apt install -y python3 cmake make g++ libgmp-dev zlib1g-dev openjdk-8-jdk-headless mono-mcs nodejs rustc golang + - checkout + - run: + name: Build + command: cmake . && make + - run: + name: Test + command: ctest --output-on-failure + environment: + PYTHONIOENCODING: utf8 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000000..ee688c421e --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,15 @@ +name: macOS + +on: [push, pull_request] + +jobs: + build: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: prep + run: cmake . + - name: build + run: cmake --build . + - name: test + run: ctest --output-on-failure diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000000..acf344199d --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,15 @@ +name: Ubuntu + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: prep + run: cmake . + - name: build + run: cmake --build . + - name: test + run: ctest --output-on-failure diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000000..cd6408a061 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,23 @@ +name: Windows + +on: [push, pull_request] + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Setup csc.exe + uses: yoavain/Setup-CSC@v7 + - name: prep + run: | + where python + copy c:\hostedtoolcache\windows\Python\3.7.9\x64\python.exe c:\hostedtoolcache\windows\Python\3.7.9\x64\python3.exe + cmake . + shell: cmd + - name: build + run: cmake --build . + - name: test + run: ctest -C Debug --output-on-failure + env: + PYTHONIOENCODING: utf8 diff --git a/.gitignore b/.gitignore index a8935343c8..43ab15d6c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,59 @@ .sconsign.dblite +.sconf_temp/ +config.log *.o +*.os +*.obj +*.lib +*.exp +*.exe +*.dylib +*.dll +*.so *.gc* -simple -test_lexer -test_parser -test_interpreter -test_compiler +*.pyc +*.neonx +*.neond +*.class +lib/*.js +t/*.cpp +t/*.js +gen/ +bin/ +config.cache +test_grammar -external/IntelRDFPMathLib20U1/ +external/IntelRDFPMathLib20U2/ +external/gmp-6.1.2/ +external/utfcpp-master/ +external/hash-library/ +external/sqlite-amalgamation-3320300/ +external/zlib-1.2.8/ +external/minijson_writer-master/ +external/NaturalDocs/ +external/pyparsing-2.4.5/ +external/minizip11/ + +external/etc +external/include +external/lib +external/share +external/*.a + +# CMake ignores from https://github.com/github/gitignore/blob/master/CMake.gitignore +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + +# CMake outputs for MSVC +*.vcxproj +*.vcxproj.filters + +# CMake output for Ninja +*.ninja diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.neonpath b/.neonpath new file mode 100644 index 0000000000..c3af857904 --- /dev/null +++ b/.neonpath @@ -0,0 +1 @@ +lib/ diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 0000000000..fa2ac28fed --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,4 @@ +Authors ordered by first contribution. + +Greg Hewgill +Larry Frieson \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..34e2ad809c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,921 @@ +cmake_minimum_required(VERSION 3.2) +cmake_policy(SET CMP0054 NEW) +project(neon) + +enable_testing() + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY bin) + +find_program(JAVAC javac) +if (JAVAC) + execute_process( + COMMAND javac -version + OUTPUT_VARIABLE JAVAC_VERSION_OUT + ERROR_VARIABLE JAVAC_VERSION_ERR + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + ) + if ("${JAVAC_VERSION_OUT}" MATCHES "javac ([0-9]+\\.[0-9]+)") + set(JAVAC_VERSION "${CMAKE_MATCH_1}") + endif () + if ("${JAVAC_VERSION_ERR}" MATCHES "javac ([0-9]+\\.[0-9]+)") + set(JAVAC_VERSION "${CMAKE_MATCH_1}") + endif () + if ("${JAVAC_VERSION}") + if (NOT "${JAVAC_VERSION}" VERSION_LESS "1.8") + message("javac found: ${JAVAC}") + else () + message("javac found, but version ${JAVAC_VERSION} is < 1.8") + set(JAVAC "JAVAC-NOTFOUND") + endif () + else () + message("javac not found") + set(JAVAC "JAVAC-NOTFOUND") + endif () +else (JAVAC) + message("javac not found") +endif (JAVAC) +find_program(JAVA java) +if (NOT JAVA) + message("java not found") +endif (NOT JAVA) + +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") + find_program(CSC csc) + if (CSC) + message("csc found: ${CSC}") + else (CSC) + message("csc not found") + endif (CSC) +else () + find_program(MONO mono) + if (MONO) + message("mono found: ${MONO}") + else (MONO) + message("mono not found") + endif (MONO) + find_program(CSC mcs) + if (CSC) + message("mcs found: ${CSC}") + else (CSC) + message("mcs not found") + set(MONO "MONO-NOTFOUND") + endif (CSC) +endif () + +find_program(NODEJS NAMES nodejs node) +if (NODEJS) + message("nodejs found: ${NODEJS}") +else (NODEJS) + message("nodejs not found") +endif (NODEJS) + +find_program(RUSTC rustc) +if (RUSTC) + message("rustc found: ${RUSTC}") +else (RUSTC) + message("rustc not found") +endif (RUSTC) + +find_program(GO go) +if (GO) + message("go found: ${GO}") +else (GO) + message("go not found") +endif (GO) + +if (WIN32) + find_program(SCONS scons.bat) +else() + find_program(SCONS scons) +endif() +if (SCONS) + message("scons found: ${SCONS}") +else (SCONS) + message("scons not found") +endif (SCONS) + +execute_process( + COMMAND git describe --always --long --dirty + OUTPUT_VARIABLE GIT_DESCRIBE + OUTPUT_STRIP_TRAILING_WHITESPACE +) +set_property(DIRECTORY APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS + ${CMAKE_SOURCE_DIR}/.git/index +) +configure_file("src/version.cpp.in" "gen/version.cpp") + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + add_compile_options(/EHsc /W2 /WX /FS /wd4324) +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + add_compile_options(-Wall -Wextra -Werror -g) +endif () + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +find_package(GMP) +find_package(ZLIB) +add_subdirectory(external) + +if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") + include_directories(/usr/local/include) +endif () + +file(GLOB EXTENSION_DIRS "${CMAKE_CURRENT_LIST_DIR}/lib/*") +foreach (extension_dir ${EXTENSION_DIRS}) + if (NOT IS_DIRECTORY ${extension_dir}) + continue() + endif() + get_filename_component(libname ${extension_dir} NAME) + if (EXISTS "${extension_dir}/CMakeLists.txt") + add_custom_target(neon_${libname} ALL + COMMAND cmake . + COMMAND cmake --build . + WORKING_DIRECTORY ${extension_dir} + ) + message("building extension module ${libname} with cmake") + elseif (EXISTS "${extension_dir}/SConstruct" AND SCONS) + add_custom_target(neon_${libname} ALL + COMMAND ${SCONS} + WORKING_DIRECTORY ${extension_dir} + ) + message("building extension module ${libname} with scons") + elseif (EXISTS "${extension_dir}/configure") + add_custom_target(neon_${libname} ALL + COMMAND ./configure + COMMAND make + WORKING_DIRECTORY ${extension_dir} + ) + message("building extension module ${libname} with configure") + elseif (EXISTS "${extension_dir}/build" AND NOT WIN32) + add_custom_target(neon_${libname} ALL + COMMAND ./build + WORKING_DIRECTORY ${extension_dir} + ) + message("building extension module ${libname} with build") + elseif (EXISTS "${extension_dir}/build.cmd" AND WIN32) + add_custom_target(neon_${libname} ALL + COMMAND build.cmd + WORKING_DIRECTORY ${extension_dir} + ) + message("building extension module ${libname} with build.cmd") + else () + message("skipping extension module ${libname} because no usable build method found") + endif () +endforeach () + +set(RTL_NEON + lib/base.neon + lib/binary.neon + lib/complex.neon + lib/console.neon + lib/datetime.neon + lib/debugger.neon + lib/file.neon + lib/global.neon + lib/io.neon + lib/math.neon + lib/mmap.neon + lib/net.neon + lib/os.neon + lib/process.neon + lib/random.neon + lib/runtime.neon + lib/sqlite.neon + lib/string.neon + lib/struct.neon + lib/sys.neon + lib/textio.neon + lib/time.neon +) +if (WIN32) +else (WIN32) + set(RTL_NEON "${RTL_NEON};lib/posix.neon") +endif (WIN32) + +add_custom_command( + OUTPUT gen/thunks.inc gen/functions_compile.inc gen/functions_exec.inc gen/enums.inc gen/exceptions.inc + COMMAND python3 scripts/make_thunks.py ${RTL_NEON} + DEPENDS scripts/make_thunks.py + DEPENDS ${RTL_NEON} +) +set_source_files_properties( + src/ast.cpp + src/intrinsic.cpp + PROPERTIES OBJECT_DEPENDS gen/exceptions.inc +) + +add_custom_command( + OUTPUT gen/unicodedata.inc + COMMAND python3 tools/helium.py scripts/make_unicode.neon data/UnicodeData.txt >gen/unicodedata.inc + DEPENDS tools/helium.py + DEPENDS scripts/make_unicode.neon + DEPENDS data/UnicodeData.txt +) +set_source_files_properties( + src/lexer.cpp + PROPERTIES OBJECT_DEPENDS gen/unicodedata.inc +) + +if (JAVAC) + set(NEON_JVM + rtl/jvm/neon/Binary.java + rtl/jvm/neon/Datetime.java + rtl/jvm/neon/File.java + rtl/jvm/neon/Global.java + rtl/jvm/neon/Hash.java + rtl/jvm/neon/Io.java + rtl/jvm/neon/Math.java + rtl/jvm/neon/Os.java + rtl/jvm/neon/String.java + rtl/jvm/neon/Sys.java + rtl/jvm/neon/Textio.java + rtl/jvm/neon/Time.java + rtl/jvm/neon/type/Array.java + rtl/jvm/neon/type/NeonException.java + rtl/jvm/neon/type/Number.java + ) + string(REPLACE ".java" ".class" NEON_JVM_CLASSES "${NEON_JVM}") + foreach (java ${NEON_JVM}) + string(REPLACE ".java" ".class" class ${java}) + add_custom_command( + OUTPUT ${class} + COMMAND javac -cp rtl/jvm ${java} + DEPENDS ${java} + ) + endforeach () + add_custom_target(neon_jvm ALL + DEPENDS ${NEON_JVM_CLASSES} + ) + string(REPLACE "lib/global.neon" "" RTL_NEON_WITHOUT_GLOBAL "${RTL_NEON}") + string(REPLACE "lib/struct.neon" "" RTL_NEON_WITHOUT_GLOBAL "${RTL_NEON_WITHOUT_GLOBAL}") # TODO + string(REPLACE ".neon" ".class" RTL_CLASSES "${RTL_NEON_WITHOUT_GLOBAL}") + foreach (rtl_neon ${RTL_NEON_WITHOUT_GLOBAL}) + string(REPLACE ".neon" ".class" class ${rtl_neon}) + add_custom_command( + OUTPUT ${class} + COMMAND neonc -q -t jvm ${rtl_neon} + DEPENDS neonc ${rtl_neon} + ) + endforeach () + add_custom_target(neon_jvm_rtl ALL + DEPENDS ${RTL_CLASSES} + ) +endif (JAVAC) + +if (CSC) + set(NEON_CLI + rtl/cli/Global.cs + ) + # TODO: Use cmake C# support (3.8.2+) + add_custom_command( + OUTPUT t/Neon.dll + COMMAND ${CSC} /out:t/Neon.dll /target:library ${NEON_CLI} + DEPENDS ${NEON_CLI} + ) + add_custom_target(neon_cli ALL + DEPENDS t/Neon.dll + ) +endif (CSC) + +if (JAVAC) + add_subdirectory("exec/jnex") +endif (JAVAC) +add_subdirectory("exec/cnex") +if (CSC) + add_subdirectory("exec/csnex") +endif (CSC) +if (RUSTC) + add_subdirectory("exec/rsnex") +endif (RUSTC) +if (GO) + add_subdirectory("exec/gonex") +endif (GO) + +add_library(common STATIC + src/bytecode.cpp + src/disassembler.cpp + src/intrinsic.cpp + src/number.cpp + src/support.cpp + gen/version.cpp +) +target_include_directories(common + INTERFACE external/hash-library + INTERFACE external/minijson_writer-master + PUBLIC external/utfcpp-master/source + PRIVATE gen +) +target_link_libraries(common + bid + ${GMP_TARGET} + hash-library +) + +add_custom_command( + OUTPUT gen/rtl.inc + COMMAND python3 scripts/build_rtl_inc.py ${RTL_NEON} + DEPENDS scripts/build_rtl_inc.py + DEPENDS ${RTL_NEON} +) +add_library(compiler STATIC + src/analyzer.cpp + src/ast.cpp + src/compiler.cpp + src/debuginfo.cpp + src/lexer.cpp + src/parser.cpp + src/pt_dump.cpp + src/rtl_compile.cpp + src/sql.cpp + src/support_compiler.cpp + src/util.cpp + ${platform_compile} +) +set_source_files_properties( + src/support_compiler.cpp + PROPERTIES OBJECT_DEPENDS gen/rtl.inc +) +target_include_directories(compiler + PRIVATE gen +) +target_link_libraries(compiler + common +) + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + set(platform_executor + src/rtl_posix.cpp + lib/file_posix.cpp + lib/mmap_posix.cpp + lib/os_posix.cpp + lib/posix.cpp + lib/process_posix.cpp + lib/random_posix.cpp + lib/runtime_posix.cpp + lib/time_posix.cpp + lib/time_darwin.cpp + ) +elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") + set(platform_executor + src/rtl_posix.cpp + lib/file_posix.cpp + lib/mmap_posix.cpp + lib/os_posix.cpp + lib/posix.cpp + lib/process_posix.cpp + lib/random_posix.cpp + lib/runtime_posix.cpp + lib/time_posix.cpp + lib/time_linux.cpp + ) +elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set(platform_executor + src/rtl_win32.cpp + lib/file_win32.cpp + lib/mmap_win32.cpp + lib/os_win32.cpp + lib/process_win32.cpp + lib/random_win32.cpp + lib/runtime_win32.cpp + lib/time_win32.cpp + ) +endif () + +# Needed for USE_RTLX +#string(REPLACE "lib/global.neon" "" RTL_NEON_WITHOUT_GLOBAL "${RTL_NEON}") +#set(RTL_NEONX "") +#foreach (src ${RTL_NEON_WITHOUT_GLOBAL}) +# string(REPLACE ".neon" ".neonx" NEONX ${src}) +# add_custom_command( +# OUTPUT "${src}x" +# COMMAND neonc ${src} +# DEPENDS ${src} +# ) +# list(APPEND RTL_NEONX ${NEONX}) +#endforeach () +#add_custom_command( +# OUTPUT gen/rtlx.inc +# COMMAND python3 scripts/build_rtlx_inc.py ${RTL_NEONX} +# DEPENDS scripts/build_rtlx_inc.py +# DEPENDS ${RTL_NEONX} +#) + +add_library(executor STATIC + src/cell.cpp + src/exec.cpp + src/httpserver.cpp + src/object.cpp + src/rtl_exec.cpp + src/support_exec.cpp + lib/binary.cpp + lib/console.cpp + lib/datetime.cpp + lib/debugger.cpp + lib/global.cpp + lib/file.cpp + lib/io.cpp + lib/math.cpp + lib/net.cpp + lib/os.cpp + lib/random.cpp + lib/runtime.cpp + lib/sqlite.cpp + lib/string.cpp + lib/struct.cpp + lib/sys.cpp + lib/textio.cpp + lib/time.cpp + ${platform_executor} +) +#set_source_files_properties( +# src/support_exec.cpp +# PROPERTIES OBJECT_DEPENDS gen/rtlx.inc +#) +target_compile_options(executor PRIVATE) +set_source_files_properties( + src/exec.cpp + PROPERTIES OBJECT_DEPENDS gen/exceptions.inc +) +target_include_directories(executor + PRIVATE common + PRIVATE gen + PRIVATE src +) +target_link_libraries(executor + common + sqlite3 +) +if (WIN32) + target_link_libraries(executor wsock32) +endif (WIN32) +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + target_link_libraries(executor dl) +endif () +if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + target_link_libraries(executor pthread) +endif () + +add_executable(neonc + src/neonc.cpp + src/compiler_cli.cpp + src/compiler_cpp.cpp + src/compiler_js.cpp + src/compiler_jvm.cpp +) +target_include_directories(neonc + PRIVATE gen +) +target_link_libraries(neonc + compiler +) + +add_executable(neon + src/neon.cpp + src/repl.cpp +) +target_link_libraries(neon + compiler + executor +) + +add_executable(neonx + src/neonx.cpp + src/bundle.cpp +) +target_link_libraries(neonx + executor + minizip +) + +add_executable(neonstub + src/neonstub.cpp + src/bundle.cpp +) +target_link_libraries(neonstub + executor + minizip +) + +add_executable(neondis + src/neondis.cpp +) +target_link_libraries(neondis + compiler +) + +add_executable(neonbind + src/neonbind.cpp + src/support_exec.cpp +) +target_link_libraries(neonbind + common + minizip +) + +add_executable(fuzz_lexer + tests/fuzz_lexer.cpp +) +target_include_directories(fuzz_lexer PRIVATE + src +) +target_link_libraries(fuzz_lexer + compiler +) + +add_executable(fuzz_parser + tests/fuzz_parser.cpp +) +target_include_directories(fuzz_parser PRIVATE + src +) +target_link_libraries(fuzz_parser + compiler +) + +add_executable(perf_lexer + tests/perf_lexer.cpp +) +target_include_directories(perf_lexer PRIVATE + src +) +target_link_libraries(perf_lexer + compiler +) + +add_executable(test_lexer + tests/test_lexer.cpp +) +target_include_directories(test_lexer PRIVATE + src +) +target_link_libraries(test_lexer + compiler +) +add_test( + NAME test_lexer + COMMAND test_lexer +) +add_test( + NAME test_lexer_coverage + COMMAND test_lexer tests/lexer-coverage.neon +) + +add_executable(test_number + tests/test_number.cpp +) +target_include_directories(test_number PRIVATE + src +) +target_link_libraries(test_number + common +) +add_test( + NAME test_number + COMMAND test_number +) + +add_executable(test_parser + tests/test_parser.cpp +) +target_include_directories(test_parser PRIVATE + src +) +target_link_libraries(test_parser + compiler +) +add_test( + NAME test_parser + COMMAND test_parser tests/parser_coverage.neon +) + +add_executable(test_number_to_string + tests/test_number_to_string.cpp +) +target_include_directories(test_number_to_string PRIVATE + src +) +target_link_libraries(test_number_to_string + common +) +add_test( + NAME test_number_to_string + COMMAND test_number_to_string +) + +add_custom_command( + OUTPUT gen/errors.txt + COMMAND python3 tools/helium.py scripts/extract_errors.neon + DEPENDS src/lexer.cpp + DEPENDS src/parser.cpp + DEPENDS src/analyzer.cpp + DEPENDS src/sql.cpp + DEPENDS src/ast.cpp +) +add_custom_target(errors_txt ALL + DEPENDS gen/errors.txt +) + +add_custom_command( + OUTPUT contrib/grammar/neon.w3c.ebnf + COMMAND python3 tools/helium.py contrib/grammar/ebnf_w3c.neon contrib/grammar/neon.w3c.ebnf + DEPENDS tools/helium.py + DEPENDS contrib/grammar/ebnf_w3c.neon + DEPENDS contrib/grammar/neon.ebnf +) +add_custom_target(w3c_ebnf ALL + DEPENDS contrib/grammar/neon.w3c.ebnf +) + +function(add_tests TESTS TAG RUNNER EXCLUDEFILE) + set(EXCLUDE "") + if (EXCLUDEFILE) + file(STRINGS "${EXCLUDEFILE}" EXCLUDE_WITH_COMMENTS) + foreach (EXCL ${EXCLUDE_WITH_COMMENTS}) + string(REGEX REPLACE "[ ]*#.*" "" E "${EXCL}") + list(APPEND EXCLUDE ${E}) + endforeach() + endif (EXCLUDEFILE) + foreach (TEST ${TESTS}) + get_filename_component(T "${TEST}" NAME) + if (TAG) + if (NOT ";${EXCLUDE};" MATCHES ";${T};") + add_test( + NAME "${TAG}:${T}" + COMMAND python3 scripts/run_test.py --runner "${RUNNER}" ${TEST} + ) + endif() + else() + add_test( + NAME "${T}" + COMMAND python3 scripts/run_test.py --runner "${RUNNER}" ${TEST} + ) + endif() + endforeach() +endfunction(add_tests) + +file(GLOB COMPILE_TESTS t/compile-only/*.neon) +add_tests("${COMPILE_TESTS}" "compile" "$ -q" "") + +file(GLOB RUN_TESTS t/*.neon) +foreach (extension_dir ${EXTENSION_DIRS}) + file(GLOB extension_tests ${extension_dir}/t/*.neon) + foreach (extension_test ${extension_tests}) + list(APPEND RUN_TESTS ${extension_test}) + endforeach() +endforeach() +set(TESTS "") +set(TESTSX "") +foreach (TEST ${RUN_TESTS}) + get_filename_component(TEST_NAME "${TEST}" NAME) + string(REGEX MATCH "^(posix|win32)-" TEST_PLATFORM ${TEST_NAME}) + if (TEST_PLATFORM) + if (${TEST_PLATFORM} STREQUAL "posix-" AND NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "FreeBSD" OR ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "OpenBSD")) + continue() + endif() + if (${TEST_PLATFORM} STREQUAL "win32-" AND NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")) + continue() + endif() + endif (TEST_PLATFORM) + add_custom_command( + OUTPUT "${TEST}x" + COMMAND neonc -q ${TEST} + DEPENDS neonc ${TEST} + ) + list(APPEND TESTS ${TEST}) + list(APPEND TESTSX "${TEST}x") +endforeach () +add_custom_target(testx ALL + DEPENDS ${TESTSX} +) +add_tests("${TESTS}" "" $ "") +add_tests("${TESTS}" "helium" "python3 tools/helium.py" "tools/helium-exclude.txt") +if (NOT (WIN32 AND DEFINED ENV{GITHUB_ACTIONS})) + # TODO: Github Actions does not seem to be able to run the C++ compiler on Windows sensibly. + add_tests("${TESTS}" "cpp" "python3 scripts/run_cpp.py --neonc $" "scripts/cpp-exclude.txt") +endif () +if (NODEJS) + add_tests("${TESTS}" "js" "python3 scripts/run_js.py --neonc $" "scripts/js-exclude.txt") +endif (NODEJS) +if (JAVAC) + add_tests("${TESTS}" "jvm" "python3 scripts/run_jvm.py --neonc $ --java ${JAVA}" "scripts/jvm-exclude.txt") +endif (JAVAC) +if (CSC) + add_tests("${TESTS}" "cli" "python3 scripts/run_cli.py --neonc $" "scripts/cli-exclude.txt") +endif () +add_tests("${TESTS}" "nenex" "python3 exec/nenex/run_test.py --neon $" "exec/nenex/exclude.txt") +add_tests("${TESTS}" "pynex" "python3 exec/pynex/run_test.py" "exec/pynex/exclude.txt") +if (JAVAC) + add_tests("${TESTS}" "jnex" "python3 exec/jnex/run_test.py --java ${JAVA}" "exec/jnex/exclude.txt") +endif (JAVAC) +add_tests("${TESTS}" "cnex" "python3 exec/cnex/run_test.py --cnex $" "exec/cnex/exclude.txt") +if (CSC) + add_tests("${TESTS}" "csnex" "python3 exec/csnex/run_test.py" "exec/csnex/exclude.txt") +endif (CSC) +if (RUSTC) + add_tests("${TESTS}" "rsnex" "python3 exec/rsnex/run_test.py" "exec/rsnex/exclude.txt") +endif (RUSTC) +if (GO) + add_tests("${TESTS}" "gonex" "python3 exec/gonex/run_test.py" "exec/gonex/exclude.txt") +endif (GO) + +if (NOT WIN32) + # Disabled for WIN32 because hard coded paths (to bin/neon etc) are different + # paths on in WIN32 cmake builds. + file(GLOB INTEGRATION_TESTS t/integration/*.neon) + add_tests("${INTEGRATION_TESTS}" "integration" "$" "") +endif (NOT WIN32) + +file(GLOB TESTS t/todo/*.neon) +add_tests("${TESTS}" "" "$ -q" "") + +add_test( + NAME "import-optional:neonx" + COMMAND python3 scripts/test_import_optional.py $ $ +) + +# TODO: optional modules in nenex +#add_test( +# NAME "import-optional:nenex" +# COMMAND python3 scripts/test_import_optional.py $ $ exec/nenex/nenex.neon +#) + +# TODO: optional modules in pynex +#add_test( +# NAME "import-optional:pynex" +# COMMAND python3 scripts/test_import_optional.py $ python3 exec/pynex/pynex.py +#) + +# TODO: optional modules in jnex +#add_test( +# NAME "import-optional:jnex" +# COMMAND python3 scripts/test_import_optional.py $ java -cp exec/jnex/src org.neon_lang.jnex.Executor +#) + +# TODO: optional modules in cnex +#add_test( +# NAME "import-optional:cnex" +# COMMAND python3 scripts/test_import_optional.py $ $ +#) + +# TODO: optional modules in gonex +#add_test( +# NAME "import-optional:gonex" +# COMMAND python3 scripts/test_import_optional.py $ exec/gonex/gonex +#) + +add_test( + NAME "errors" + COMMAND python3 scripts/run_test.py --runner "$ -q" --errors t/errors +) + +file(GLOB TESTS_REPL t/repl_*.neon) +foreach (TEST ${TESTS_REPL}) + get_filename_component(T "${TEST}" NAME) + add_test( + NAME "repl:${T}" + COMMAND neon --repl-no-prompt --repl-stop-on-any-error --repl-input ${TEST} + ) +endforeach () + +file(GLOB_RECURSE SAMPLES neon/*.neon samples/*.neon tools/*.neon) +foreach (SAMPLE ${SAMPLES}) + execute_process( + COMMAND python3 tools/helium.py tools/imports.neon ${SAMPLE} + OUTPUT_VARIABLE IMPORTS_NL + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REPLACE "\n" ";" IMPORTS "${IMPORTS_NL}") + set(MISSING_IMPORT "") + foreach (IMPORT ${IMPORTS}) + if (NOT EXISTS "lib/${IMPORT}.neon" AND NOT EXISTS "lib/${IMPORT}/${IMPORT}.neon") + set(MISSING_IMPORT ${IMPORT}) + break() + endif() + endforeach() + if (NOT MISSING_IMPORT) + get_filename_component(X "${SAMPLE}" NAME) + add_test( + NAME "sample:${X}" + COMMAND neonc ${SAMPLE} + ) + # TODO: various language features still not understood by neon/parser + #add_test( + # NAME "sample.neon:${X}" + # COMMAND neon neon/parser.neon ${SAMPLE} + #) + else() + message("skipping ${SAMPLE} due to missing ${MISSING_IMPORT}") + endif() +endforeach() + +file(GLOB_RECURSE LIBS lib/*.neon) +add_test( + NAME test_grammar_libs + COMMAND python3 contrib/grammar/test-grammar.py ${LIBS} +) +add_test( + NAME test_grammar_tools + COMMAND python3 contrib/grammar/test-grammar.py tools/*.neon +) +add_test( + NAME test_grammar_samples + COMMAND python3 contrib/grammar/test-grammar.py ${SAMPLES} +) +add_test( + NAME test_grammar_tests + COMMAND python3 contrib/grammar/test-grammar.py t/*.neon +) +add_test( + NAME test_grammar_tests_n3 + COMMAND python3 contrib/grammar/test-grammar.py t/errors/N3*.neon +) +add_test( + NAME test_grammar_random + COMMAND python3 contrib/grammar/test-random.py --neonc $ +) + +# TODO: This fails because cmake does not seem to accept the # on a COMMAND line. +#add_custom_command( +# OUTPUT tmp/hello +# COMMAND echo '#!/usr/bin/env neon' | cat - samples/hello/hello.neon >tmp/hello && chmod +x tmp/hello +# DEPENDS samples/hello/hello.neon +#) +#add_custom_target( +# test_shebang ALL +# DEPENDS tmp/hello +#) +#add_test( +# NAME test_shebang +# COMMAND tmp/hello +#) + +add_test( + NAME "test_doc" + COMMAND python3 scripts/test_doc.py --neon $ --neonc $ +) + +add_custom_target(docs ALL + COMMAND perl external/NaturalDocs/NaturalDocs -i lib -o HTML gh-pages/html -p lib/nd.proj -ro -xi lib/compress/bzip2-1.0.6 -xi lib/compress/zlib-1.2.8 -xi lib/crypto/include -xi lib/crypto/libressl-2.2.4 -xi lib/fltk/fltk-1.3.4-1 -xi lib/hash/include -xi lib/hash/libressl-2.2.4 -xi lib/http/curl-7.41.0 -xi lib/http/libressl-2.2.4 -xi lib/http/zlib-1.2.8 -xi lib/http/include -xi lib/regex/pcre2-10.10 -xi lib/sdl/SDL2-2.0.3 +) +add_custom_target(docs_samples ALL + COMMAND perl external/NaturalDocs/NaturalDocs -i samples -o HTML gh-pages/samples -p samples/nd.proj -ro +) + +add_custom_command( + OUTPUT samples/hello/hello.neonx + COMMAND neonc samples/hello/hello.neon + DEPENDS neonc samples/hello/hello.neon +) +add_custom_target(hello_neb ALL + COMMAND neonbind -stub $ tmp/hello.neb samples/hello/hello.neonx + DEPENDS neonbind samples/hello/hello.neonx +) +add_test( + NAME hello_neb + COMMAND neonx tmp/hello.neb +) +add_custom_target(hello_exe ALL + COMMAND neonbind -stub $ -e tmp/hello.exe samples/hello/hello.neonx + DEPENDS neonbind neonstub samples/hello/hello.neonx +) +add_test( + NAME hello_exe + COMMAND tmp/hello.exe +) + +add_test( + NAME lexer_coverage_cpp + COMMAND python3 tests/compare_expected.py tests/lexer-coverage.expected $ tests/lexer-coverage.neon +) + +add_test( + NAME lexer_coverage_neon + COMMAND python3 tests/compare_expected.py tests/lexer-coverage.expected $ neon/lexer.neon tests/lexer-coverage.neon +) + +add_test( + NAME lexer_coverage_helium + COMMAND python3 tests/compare_expected.py tests/lexer-coverage.expected python3 tools/helium.py neon/lexer.neon tests/lexer-coverage.neon +) + +add_test( + NAME parser_coverage_cpp + COMMAND python3 tests/compare_expected.py tests/parser-coverage.expected $ tests/parser-coverage.neon +) + +add_test( + NAME parser_coverage_neon + COMMAND python3 tests/compare_expected.py tests/parser-coverage.expected $ neon/parser.neon tests/parser-coverage.neon +) + +#add_test( +# NAME parser_coverage_helium +# COMMAND python3 tests/compare_expected.py tests/parser-coverage.expected python3 tools/helium.py neon/parser.neon tests/parser-coverage.neon +#) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..adeedc2809 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright Neon Contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8ee34fd8fc..d3b4ff21fe 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -# Simple Language +# Neon Language -The primary goal of Simple is to find out whether a useful programming language can avoid some of the common pitfalls that beginners frequently encounter in other languages. Some of these common errors avoided by design are: +The primary goal of Neon is to find out whether a useful programming language can avoid some of the common pitfalls that beginners frequently encounter in other languages. Some of these common errors avoided by design are: * Floating point errors due to binary floating point * Writing `if (x = 0)` when `if (x == 0)` is intended +* Null pointer exceptions * Unintended empty loop with `while (condition);` * Forgetting to use the return value of a function @@ -21,13 +22,28 @@ This implementation is also intended to demonstrate the following concepts of a * Execution * Debugging -## Tutorial +## Prerequisites -See [Simple Tutorial](doc/tutorial.md) for an introduction to the features of Simple. +To build Neon, the following are required: -## Language Reference +- [Python 3.x](http://python.org) +- [cmake](https://cmake.org) +- A C++11 compiler (a modern `gcc`, `clang`, or Visual Studio 2013 or later) -See [Language Reference](doc/language.md) for the full definition of the Simple language. +Current master branch build status: +![Ubuntu](https://github.com/ghewgill/neon-lang/workflows/Ubuntu/badge.svg) +![macOS](https://github.com/ghewgill/neon-lang/workflows/macOS/badge.svg) +![Windows](https://github.com/ghewgill/neon-lang/workflows/Windows/badge.svg) +[![Appveyor](https://ci.appveyor.com/api/projects/status/github/ghewgill/neon-lang?branch=master&svg=true)](https://ci.appveyor.com/project/ghewgill/neon-lang) + +## Documentation + +See [Neon Documentation](http://neon-lang.dev/docs/) for full documentation including: + +- [Tutorial](http://neon-lang.dev/docs/tutorial.html) +- [Overview (for experienced programmers)](http://neon-lang.dev/docs/overview.html) +- [Language Reference](http://neon-lang.dev/docs/reference/index.html) +- [Standard Library](http://neon-lang.dev/docs/library.html) ## Common Errors @@ -37,8 +53,10 @@ The following types of programming errors have been identified as frequently occ * [Floating point errors due to binary floating point](#floating_point) * [Writing division expressions such as `5 / 2` and not expecting integer division](#integer_division) * [Writing `if (x = 0)` when `if (x == 0)` is intended](#assignment_equals) +* [Null pointer exceptions](#null_pointer) * [Unintended empty loop with `while (condition);`](#empty_loop) * [Writing `if a == b or c` (in Python) to test whether `a` is equal to either `b` or `c`](#logical_alternative) +* [Catching all exceptions](#catch_all) * [Accidentally shadowing outer scope variables with inner declaration](#shadow_variables) * [Returning a reference to a local variable (C++)](#return_reference) * [Partial reading of typed user input using Java `Scanner` or C `scanf('%c')`](#partial_input) @@ -62,7 +80,7 @@ False This happens because `0.2` cannot be repesented exactly in binary floating point. -To resolve this problem, Simple uses the [decimal64](https://en.wikipedia.org/wiki/Decimal64_floating-point_format) floating point type, which matches the base 10 that humans use to read and write numbers. +To resolve this problem, Neon uses the [decimal128](https://en.wikipedia.org/wiki/Decimal128_floating-point_format) floating point type, which matches the base 10 that humans use to read and write numbers. ### Writing division expressions such as `5 / 2` and not expecting integer division @@ -79,7 +97,7 @@ Beginners rightly assume that `c` will be `2.5` as the result of the division. However, the C language definition states that `/` will be *integer* division if both operands are integers. So, the result in `c` is `2`. -To resolve this problem, the only number type in Simple is decimal64 floating point (called `Number`). +To resolve this problem, the only number type in Neon is decimal128 floating point (called `Number`). In contexts such as array indexing where integers are expected, values are checked for the presence of a fractional part before trying to use them. @@ -89,7 +107,24 @@ In C and derived languages, `x = 0` is an *expression* with the result 0. Some compilers raise a warning if an expression like `(x = 0)` is used in a conditional statement, as it is not likely to be what is intended. However, this is not prohibited by the language. -To resolve this problem, the assignment operator in Simple is `:=` and assignment cannot appear within an expression. +To resolve this problem, the assignment operator in Neon is `:=` and assignment cannot appear within an expression. + + +### Null pointer exceptions + +In many common systems languages (eg. C, C++, Java, C#), a pointer may hold a "null" value which is a runtime error to dereference. Tony Hoare has called the introduction of the null reference in ALGOL his ["billion-dollar mistake"](https://en.wikipedia.org/wiki/Tony_Hoare). + +To resolve this problem, Neon introduces the idea of a "valid" pointer. A valid pointer is one that has been checked against `NIL` (the null reference) using a special form of the `IF` statement. The resulting valid pointer can be dereferenced without causing a null pointer exception. + + TYPE Node IS RECORD + value: String + END RECORD + + FUNCTION output(node: POINTER TO Node) + IF VALID node AS p THEN + print(p->value) + END IF + END FUNCTION ### Unintended empty loop with `while (condition);` @@ -100,16 +135,20 @@ In C and derived languages, sometimes a loop or conditional is mistakenly writte while (x < 5); { printf("%d\n", x); + x++; } ``` The trailing `;` on the `while` statement is in fact an empty loop body and the loop is an infinite loop. -To resolve this problem, Simple requires an explicitly terminated block in every compound statement: +To resolve this problem, Neon requires an explicitly terminated block in every compound statement: + + VAR x: Number := 0 - WHILE x < 5 - print(x) - END + WHILE x < 5 DO + print("\(x)") + INC x + END WHILE ### Writing `if a == b or c` (in Python) to test whether `a` is equal to either `b` or `c` @@ -123,7 +162,15 @@ if name == "Jack" or "Jill": This is valid because the `"Jill"` is automatically treated as a boolean expression (with value `True`) and combined with the `name == "Jack"` condition using the `or` operator. -To resolve this problem, values in Simple cannot be automatically converted from one type to another (in particular, not to Boolean). +To resolve this problem, values in Neon cannot be automatically converted from one type to another (in particular, not to Boolean). + + +### Catching all exceptions + +Languages with complex exception hierarchies (eg. C++, Java, C#, Python) allow the program to catch *all* types of exceptions using a construct such as `catch (...)` (C++) or `except:` (Python). +This generally has the unintended effect of masking exceptions that may not be among those expected by the programmer. + +Neon does not have an exception hierarchy, and the exception handling always uses explicitly named exceptions (there is no way to catch *all* types of exceptions). ### Accidentally shadowing outer scope variables with inner declaration @@ -141,7 +188,7 @@ void f() { This can lead to confusion due to unexpectedly using the wrong variable. -In Simple, it is an error for a declaration to shadow an outer declaration. +In Neon, it is an error for a declaration to shadow an outer declaration. ### Returning a reference to a local variable (C++) @@ -157,7 +204,7 @@ int &foo(int x) { This is *undefined behaviour* because the reference returns to memory that has been deallocated as soon as the function returns. -This is resolved in Simple by not having references. +This is resolved in Neon by not having references. ### Partial reading of typed user input using Java `Scanner` or C `scanf('%c')` @@ -167,7 +214,7 @@ However, when used with interactive terminal input, they do not return a value u This causes confusion when the user types more than what is expected and the remainder of what the user typed is held in the input buffer until the next time input is requested. At that time, the contents of the buffer are used without waiting for user input. -This is resolved in Simple by only providing line oriented input for text. +This is resolved in Neon by only providing line oriented input for text. ### Writing `^` to mean exponentiation in C or Java @@ -175,7 +222,7 @@ This is resolved in Simple by only providing line oriented input for text. Sometimes beginners expect the `^` operator to mean exponentiation (eg. `dist = sqrt(a^2 + b^2)`). However, `^` means bitwise XOR in many languages, which is not expected. -In Simple, `^` means exponentiation. +In Neon, `^` means exponentiation. ### Forgetting to use the return value of a function @@ -183,4 +230,4 @@ In Simple, `^` means exponentiation. Many programming languages permit a function that returns a value to be called without actually using the return value. This is a frequent source of bugs because the return value may indicate an error condition, which is then ignored. -In Simple, it is an error to call a function that returns a value without using that value in some way. +In Neon, it is an error to call a function that returns a value without using that value in some way. diff --git a/SConstruct b/SConstruct deleted file mode 100644 index 513c8c1ab2..0000000000 --- a/SConstruct +++ /dev/null @@ -1,95 +0,0 @@ -import os -import tarfile -from SCons.Script.SConscript import SConsEnvironment - -# Assume a UTF-8 capable terminal. -os.putenv("PYTHONIOENCODING", "UTF-8") - -coverage = ARGUMENTS.get("coverage", 0) -# This is needed on OS X because clang has a bug where this isn't included automatically. -coverage_lib = (["/Library/Developer/CommandLineTools/usr/lib/clang/6.0/lib/darwin/libclang_rt.profile_osx.a"] if coverage else []) - -env = Environment() - -env.Command("external/IntelRDFPMathLib20U1/LIBRARY/makefile.mak", "external/IntelRDFPMathLib20U1.tar.gz", lambda target, source, env: tarfile.open(source[0].path).extractall("external")) -libbid = env.Command("external/IntelRDFPMathLib20U1/LIBRARY/libbid.a", "external/IntelRDFPMathLib20U1/LIBRARY/makefile.mak", "cd external/IntelRDFPMathLib20U1/LIBRARY && make CC=gcc GLOBAL_RND=1 GLOBAL_FLAGS=1") - -env.Append(CPPPATH=[ - "external/IntelRDFPMathLib20U1/LIBRARY/src", -]) -env.Append(CXXFLAGS=[ - "-Wall", - "-Wextra", - "-Weffc++", - "-Werror", - "-pedantic", - "-Wno-c++11-extensions", - "-Wno-unused-parameter", - "-g", -]) -env.Append(LIBS=[libbid]) - -if coverage: - env.Append(CXXFLAGS=[ - "--coverage", "-O0", - ]) - -env.Program("simple", [ - "ast.cpp", - "bytecode.cpp", - "compiler.cpp", - "disassembler.cpp", - "exec.cpp", - "lexer.cpp", - "main.cpp", - "number.cpp", - "parser.cpp", - "util.cpp", -] + coverage_lib, -) - -env.Depends("number.h", libbid) - -def UnitTest(env, target, source, **kwargs): - t = env.Program(target, source, **kwargs) - # see the following for the reason why this lambda is necessary: - # http://stackoverflow.com/questions/8219743/scons-addpostaction-causes-dependency-check-error-work-around - env.AddPostAction(t, lambda *_, **__: os.system(t[0].abspath)) - env.Alias("test", t) - return t - -SConsEnvironment.UnitTest = UnitTest - -env.UnitTest("test_lexer", [ - "test_lexer.cpp", - "lexer.cpp", - "number.cpp", - "util.cpp", -] + coverage_lib, -) - -env.UnitTest("test_parser", [ - "test_parser.cpp", - "ast.cpp", - "compiler.cpp", - "parser.cpp", - "lexer.cpp", - "number.cpp", - "util.cpp", -] + coverage_lib, -) - -env.UnitTest("test_compiler", [ - "test_compiler.cpp", - "ast.cpp", - "bytecode.cpp", - "compiler.cpp", - "disassembler.cpp", - "lexer.cpp", - "number.cpp", - "parser.cpp", - "util.cpp", -] + coverage_lib, -) - -env.Command("dummy", ["simple", "run_test.py", Glob("t/*")], "python3 run_test.py t") diff --git a/ast.cpp b/ast.cpp deleted file mode 100644 index 9b6a76d94f..0000000000 --- a/ast.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "ast.h" - -#include -#include - -TypeNone *TYPE_NONE = new TypeNone(); -TypeBoolean *TYPE_BOOLEAN = new TypeBoolean(); -TypeNumber *TYPE_NUMBER = new TypeNumber(); -TypeString *TYPE_STRING = new TypeString(); - -void AstNode::dump(std::ostream &out, int depth) const -{ - out << std::string(depth*2, ' ') << text() << "\n"; - dumpsubnodes(out, depth); -} - -std::string ConstantBooleanExpression::text() const -{ - std::stringstream s; - s << "ConstantBooleanExpression(" << value << ")"; - return s.str(); -} - -std::string ConstantNumberExpression::text() const -{ - std::stringstream s; - s << "ConstantNumberExpression(" << number_to_string(value) << ")"; - return s.str(); -} - -std::string ConstantStringExpression::text() const -{ - std::stringstream s; - s << "ConstantStringExpression(" << value << ")"; - return s.str(); -} - -std::string FunctionCall::text() const -{ - std::stringstream s; - s << "FunctionCall(" << func->text() << ", ["; - for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { - s << (*i)->text(); - if (i+1 != args.end()) { - s << ", "; - } - } - s << "])"; - return s.str(); -} - -void CompoundStatement::dumpsubnodes(std::ostream &out, int depth) const -{ - for (std::vector::const_iterator i = statements.begin(); i != statements.end(); ++i) { - (*i)->dump(out, depth+1); - } -} - -const Name *Scope::lookupName(const std::string &name) const -{ - const Scope *s = this; - while (s != nullptr) { - auto n = s->names.find(name); - if (n != s->names.end()) { - n->second->referenced = true; - return n->second; - } - s = s->parent; - } - return nullptr; -} - -const Type *Function::makeFunctionType(const Type *returntype, const std::vector &args) -{ - std::vector argtypes; - for (auto a: args) { - argtypes.push_back(a->type); - } - return new TypeFunction(returntype, argtypes); -} - -Program::Program() - : scope(new Scope(nullptr)) -{ - scope->names["boolean"] = TYPE_BOOLEAN; - scope->names["number"] = TYPE_NUMBER; - scope->names["string"] = TYPE_STRING; - - static struct { - const char *name; - const Type *returntype; - const Type *args[10]; - } BuiltinFunctions[] = { - {"abs", TYPE_NUMBER, {TYPE_NUMBER}}, - {"concat", TYPE_STRING, {TYPE_STRING, TYPE_STRING}}, - {"print", TYPE_NONE, {TYPE_STRING}}, - {"str", TYPE_STRING, {TYPE_NUMBER}}, - {"strb", TYPE_STRING, {TYPE_BOOLEAN}}, - }; - for (auto f: BuiltinFunctions) { - std::vector args; - for (auto a: f.args) { - if (a == nullptr) { - break; - } - args.push_back(a); - } - scope->names[f.name] = new PredefinedFunction(f.name, new TypeFunction(f.returntype, args)); - } -} - -void Program::dumpsubnodes(std::ostream &out, int depth) const -{ - for (std::vector::const_iterator i = statements.begin(); i != statements.end(); ++i) { - (*i)->dump(out, depth+1); - } -} diff --git a/ast.h b/ast.h deleted file mode 100644 index e262ea8647..0000000000 --- a/ast.h +++ /dev/null @@ -1,646 +0,0 @@ -#ifndef AST_H -#define AST_H - -#include -#include -#include -#include - -#include "number.h" - -// Compiler -class Emitter; - -class AstNode { -public: - AstNode() {} - void dump(std::ostream &out, int depth = 0) const; - virtual std::string text() const = 0; - virtual void dumpsubnodes(std::ostream &out, int depth) const {} -private: - AstNode(const AstNode &); - AstNode &operator=(const AstNode &); -}; - -class Name; -class Type; - -class Scope { -public: - Scope(Scope *parent): parent(parent) {} - - virtual void predeclare(Emitter &emitter) const; - virtual void postdeclare(Emitter &emitter) const; - - const Name *lookupName(const std::string &name) const; - - Scope *const parent; - std::map names; - int count; -}; - -class Name: public AstNode { -public: - Name(const std::string &name, const Type *type): name(name), type(type), referenced(false) {} - const std::string name; - const Type *type; - bool referenced; - - virtual void predeclare(Emitter &emitter) {} - virtual void postdeclare(Emitter &emitter) {} -}; - -class Type: public Name { -public: - Type(const std::string &name): Name(name, nullptr) {} - virtual void generate_load(Emitter &emitter) const = 0; - virtual void generate_store(Emitter &emitter) const = 0; - virtual void generate_call(Emitter &emitter) const = 0; -}; - -class TypeNone: public Type { -public: - TypeNone(): Type("") {} - virtual void generate_load(Emitter &emitter) const {} - virtual void generate_store(Emitter &emitter) const {} - virtual void generate_call(Emitter &emitter) const {} - - virtual std::string text() const { return "TypeNone"; } -}; - -extern TypeNone *TYPE_NONE; - -class TypeBoolean: public Type { -public: - TypeBoolean(): Type("boolean") {} - virtual void generate_load(Emitter &emitter) const; - virtual void generate_store(Emitter &emitter) const; - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const { return "TypeBoolean"; } -}; - -extern TypeBoolean *TYPE_BOOLEAN; - -class TypeNumber: public Type { -public: - TypeNumber(): Type("number") {} - virtual void generate_load(Emitter &emitter) const; - virtual void generate_store(Emitter &emitter) const; - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const { return "TypeNumber"; } -}; - -extern TypeNumber *TYPE_NUMBER; - -class TypeString: public Type { -public: - TypeString(): Type("string") {} - virtual void generate_load(Emitter &emitter) const; - virtual void generate_store(Emitter &emitter) const; - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const { return "TypeString"; } -}; - -extern TypeString *TYPE_STRING; - -class TypeFunction: public Type { -public: - TypeFunction(const Type *returntype, const std::vector &args): Type("function"), returntype(returntype), args(args) {} - virtual void generate_load(Emitter &emitter) const; - virtual void generate_store(Emitter &emitter) const; - virtual void generate_call(Emitter &emitter) const; - - const Type *returntype; - const std::vector args; - - virtual std::string text() const { return "TypeFunction(...)"; } -}; - -class TypeArray: public Type { -public: - TypeArray(const Type *elementtype): Type("array"), elementtype(elementtype) {} - const Type *elementtype; - - virtual void generate_load(Emitter &emitter) const { assert(false); } - virtual void generate_store(Emitter &emitter) const { assert(false); } - virtual void generate_call(Emitter &emitter) const { assert(false); } - - virtual std::string text() const { return "TypeArray(" + elementtype->text() + ")"; } -}; - -class TypeDictionary: public Type { -public: - TypeDictionary(const Type *elementtype): Type("dictionary"), elementtype(elementtype) {} - const Type *elementtype; - - virtual void generate_load(Emitter &emitter) const { assert(false); } - virtual void generate_store(Emitter &emitter) const { assert(false); } - virtual void generate_call(Emitter &emitter) const { assert(false); } - - virtual std::string text() const { return "TypeDictionary(" + elementtype->text() + ")"; } -}; - -class TypeRecord: public Type { -public: - TypeRecord(const std::map > &fields): Type("record"), fields(fields) {} - const std::map > fields; - - virtual void generate_load(Emitter &emitter) const { assert(false); } - virtual void generate_store(Emitter &emitter) const { assert(false); } - virtual void generate_call(Emitter &emitter) const { assert(false); } - - virtual std::string text() const { return "TypeRecord(...)"; } -}; - -class Variable: public Name { -public: - Variable(const std::string &name, const Type *type): Name(name, type) {} - - virtual void generate_address(Emitter &emitter) const = 0; - virtual void generate_load(Emitter &emitter) const; - virtual void generate_store(Emitter &emitter) const; - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const { return "Variable(" + name + ", " + type->text() + ")"; } -}; - -class GlobalVariable: public Variable { -public: - GlobalVariable(const std::string &name, const Type *type): Variable(name, type), index() {} - int index; - - virtual void predeclare(Emitter &emitter); - virtual void generate_address(Emitter &emitter) const; - - virtual std::string text() const { return "GlobalVariable(" + name + ", " + type->text() + ")"; } -}; - -class LocalVariable: public Variable { -public: - LocalVariable(const std::string &name, const Type *type, Scope *scope): Variable(name, type), scope(scope), index() {} - Scope *scope; - int index; - - virtual void predeclare(Emitter &emitter); - virtual void generate_address(Emitter &emitter) const; - - virtual std::string text() const { return "LocalVariable(" + name + ", " + type->text() + ")"; } -}; - -class Expression: public AstNode { -public: - Expression(const Type *type): type(type) {} - - virtual void generate(Emitter &emitter) const = 0; - - const Type *type; -}; - -class ConstantBooleanExpression: public Expression { -public: - ConstantBooleanExpression(bool value): Expression(TYPE_BOOLEAN), value(value) {} - - const bool value; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const; -}; - -class ConstantNumberExpression: public Expression { -public: - ConstantNumberExpression(Number value): Expression(TYPE_NUMBER), value(value) {} - - const Number value; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const; -}; - -class ConstantStringExpression: public Expression { -public: - ConstantStringExpression(const std::string &value): Expression(TYPE_STRING), value(value) {} - - const std::string value; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const; -}; - -class UnaryMinusExpression: public Expression { -public: - UnaryMinusExpression(const Expression *value): Expression(value->type), value(value) { - assert(type == TYPE_NUMBER); - } - - const Expression *const value; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "UnaryMinusExpression(" + value->text() + ")"; - } -}; - -class LogicalNotExpression: public Expression { -public: - LogicalNotExpression(const Expression *value): Expression(value->type), value(value) { - assert(type == TYPE_BOOLEAN); - } - - const Expression *const value; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "LogicalNotExpression(" + value->text() + ")"; - } -}; - -class DisjunctionExpression: public Expression { -public: - DisjunctionExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_BOOLEAN); - assert(right->type == TYPE_BOOLEAN); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "DisjunctionExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class ConjunctionExpression: public Expression { -public: - ConjunctionExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_BOOLEAN); - assert(right->type == TYPE_BOOLEAN); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "ConjunctionExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class ComparisonExpression: public Expression { -public: - enum Comparison { - EQ, NE, LT, GT, LE, GE - }; - ComparisonExpression(const Expression *left, const Expression *right, Comparison comp): Expression(TYPE_BOOLEAN), left(left), right(right), comp(comp) {} - - const Expression *const left; - const Expression *const right; - const Comparison comp; -}; - -class NumericComparisonExpression: public ComparisonExpression { -public: - NumericComparisonExpression(const Expression *left, const Expression *right, Comparison comp): ComparisonExpression(left, right, comp) {} - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "NumericComparisonExpression(" + left->text() + std::to_string(comp) + right->text() + ")"; - } -}; - -class StringComparisonExpression: public ComparisonExpression { -public: - StringComparisonExpression(const Expression *left, const Expression *right, Comparison comp): ComparisonExpression(left, right, comp) {} - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "StringComparisonExpression(" + left->text() + std::to_string(comp) + right->text() + ")"; - } -}; - -class AdditionExpression: public Expression { -public: - AdditionExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_NUMBER); - assert(right->type == TYPE_NUMBER); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "AdditionExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class SubtractionExpression: public Expression { -public: - SubtractionExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_NUMBER); - assert(right->type == TYPE_NUMBER); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "SubtractionExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class MultiplicationExpression: public Expression { -public: - MultiplicationExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_NUMBER); - assert(right->type == TYPE_NUMBER); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "MultiplicationExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class DivisionExpression: public Expression { -public: - DivisionExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_NUMBER); - assert(right->type == TYPE_NUMBER); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "DivisionExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class ModuloExpression: public Expression { -public: - ModuloExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_NUMBER); - assert(right->type == TYPE_NUMBER); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "ModuloExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class ExponentiationExpression: public Expression { -public: - ExponentiationExpression(const Expression *left, const Expression *right): Expression(left->type), left(left), right(right) { - assert(left->type == TYPE_NUMBER); - assert(right->type == TYPE_NUMBER); - } - - const Expression *const left; - const Expression *const right; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "ExponentiationExpression(" + left->text() + "," + right->text() + ")"; - } -}; - -class VariableReference { -public: - VariableReference(const Type *type): type(type) {} - - const Type *type; - - virtual void generate_address(Emitter &emitter) const = 0; - virtual void generate_load(Emitter &emitter) const; - virtual void generate_store(Emitter &emitter) const; - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const = 0; -}; - -class ScalarVariableReference: public VariableReference { -public: - ScalarVariableReference(const Variable *var): VariableReference(var->type), var(var) {} - - const Variable *var; - - virtual void generate_address(Emitter &emitter) const; - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const { - return "ScalarVariableReference(" + var->text() + ")"; - } -}; - -class ArrayReference: public VariableReference { -public: - ArrayReference(const Type *type, const VariableReference *array, const Expression *index): VariableReference(type), array(array), index(index) {} - - const VariableReference *array; - const Expression *index; - - virtual void generate_address(Emitter &emitter) const; - - virtual std::string text() const { return "ArrayReference(" + array->text() + ", " + index->text() + ")"; } -}; - -class DictionaryReference: public VariableReference { -public: - DictionaryReference(const Type *type, const VariableReference *dict, const Expression *index): VariableReference(type), dict(dict), index(index) {} - - const VariableReference *dict; - const Expression *index; - - virtual void generate_address(Emitter &emitter) const; - - virtual std::string text() const { return "DictionaryReference(" + dict->text() + ", " + index->text() + ")"; } -}; - -class VariableExpression: public Expression { -public: - VariableExpression(const VariableReference *var): Expression(var->type), var(var) {} - - const VariableReference *var; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "VariableExpression(" + var->text() + ")"; - } -}; - -class FunctionCall: public Expression { -public: - FunctionCall(const VariableReference *func, const std::vector &args): Expression(dynamic_cast(func->type)->returntype), func(func), args(args) {} - - const VariableReference *const func; - const std::vector args; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const; -}; - -class Statement: public AstNode { -public: - virtual void generate(Emitter &emitter) const = 0; -}; - -class AssignmentStatement: public Statement { -public: - AssignmentStatement(const VariableReference *variable, const Expression *expr): variable(variable), expr(expr) { - assert(variable->type == expr->type); - } - - const VariableReference *const variable; - const Expression *const expr; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "AssignmentStatement(" + variable->text() + ", " + expr->text() + ")"; - } -}; - -class ExpressionStatement: public Statement { -public: - ExpressionStatement(const Expression *expr): expr(expr) {} - - const Expression *const expr; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "ExpressionStatement(" + expr->text() + ")"; - } -}; - -class ReturnStatement: public Statement { -public: - ReturnStatement(const Expression *expr): expr(expr) {} - - const Expression *const expr; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { return "ReturnStatement(" + expr->text() + ")"; } -}; - -class CompoundStatement: public Statement { -public: - CompoundStatement(const std::vector &statements): statements(statements) {} - - const std::vector statements; - - virtual void dumpsubnodes(std::ostream &out, int depth) const; -}; - -class IfStatement: public Statement { -public: - IfStatement(const Expression *condition, const std::vector &then_statements, const std::vector &else_statements): condition(condition), then_statements(then_statements), else_statements(else_statements) {} - - const Expression *condition; - const std::vector then_statements; - const std::vector else_statements; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "IfStatement(" + condition->text() + ")"; - } -}; - -class WhileStatement: public CompoundStatement { -public: - WhileStatement(const Expression *condition, const std::vector &statements): CompoundStatement(statements), condition(condition) {} - - const Expression *condition; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { - return "WhileStatement(" + condition->text() + ")"; - } -}; - -class Function: public Variable { -public: - Function(const std::string &name, const Type *returntype, Scope *scope, const std::vector &args): Variable(name, makeFunctionType(returntype, args)), scope(scope), args(args) { - for (auto v: args) { - scope->names[v->name] = v; - } - } - Scope *scope; - const std::vector args; - int entry_label; - - std::vector statements; - - static const Type *makeFunctionType(const Type *returntype, const std::vector &args); - - virtual void predeclare(Emitter &emitter); - virtual void postdeclare(Emitter &emitter); - virtual void generate_address(Emitter &emitter) const { assert(false); } - virtual void generate_load(Emitter &emitter) const { assert(false); } - virtual void generate_store(Emitter &emitter) const { assert(false); } - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const { return "Function(" + name + ", " + type->text() + ")"; } -}; - -class PredefinedFunction: public Variable { -public: - PredefinedFunction(const std::string &name, const Type *type): Variable(name, type) {} - int name_index; - - virtual void predeclare(Emitter &emitter); - virtual void generate_address(Emitter &emitter) const { assert(false); } - virtual void generate_load(Emitter &emitter) const { assert(false); } - virtual void generate_store(Emitter &emitter) const { assert(false); } - virtual void generate_call(Emitter &emitter) const; - - virtual std::string text() const { return "PredefinedFunction(" + name + ", " + type->text() + ")"; } -}; - -class Program: public AstNode { -public: - Program(); - - Scope *scope; - std::vector statements; - - virtual void generate(Emitter &emitter) const; - - virtual std::string text() const { return "Program"; } - virtual void dumpsubnodes(std::ostream &out, int depth) const; -}; - -#endif diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bytecode.cpp b/bytecode.cpp deleted file mode 100644 index 2335fbe69f..0000000000 --- a/bytecode.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "bytecode.h" - -Bytecode::Bytecode(const std::vector &obj) -{ - unsigned int strtablesize = (obj[0] << 8) | obj[1]; - strtable = getstrtable(obj.begin() + 2, obj.begin() + 2 + strtablesize); - code = bytecode(obj.begin() + 2 + strtablesize, obj.end()); -} - -std::vector Bytecode::getstrtable(bytecode::const_iterator start, bytecode::const_iterator end) -{ - std::vector r; - while (start != end) { - std::string s; - while (*start != 0) { - s.push_back(*start); - ++start; - } - r.push_back(s); - ++start; - } - return r; -} diff --git a/bytecode.h b/bytecode.h deleted file mode 100644 index 671d0ff7a5..0000000000 --- a/bytecode.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef BYTECODE_H -#define BYTECODE_H - -#include -#include - -class Bytecode { -public: - Bytecode(const std::vector &obj); - - typedef std::vector bytecode; - - std::vector strtable; - bytecode code; - -private: - std::vector getstrtable(bytecode::const_iterator start, bytecode::const_iterator end); -}; - -#endif diff --git a/cmake/FindGMP.cmake b/cmake/FindGMP.cmake new file mode 100644 index 0000000000..41b7100edc --- /dev/null +++ b/cmake/FindGMP.cmake @@ -0,0 +1,13 @@ +find_path(GMP_INCLUDE_DIRS NAMES gmpxx.h) +find_library(GMP_LIBRARY NAMES gmp libgmp) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GMP + DEFAULT_MSG + GMP_LIBRARY + GMP_INCLUDE_DIRS) +mark_as_advanced(GMP_INCLUDE_DIRS GMP_LIBRARY) +if (GMP_FOUND) + add_library(GMP::GMP UNKNOWN IMPORTED) + set_target_properties(GMP::GMP PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIRS}") + set_target_properties(GMP::GMP PROPERTIES IMPORTED_LOCATION "${GMP_LIBRARY}") +endif (GMP_FOUND) diff --git a/common/neonext.h b/common/neonext.h new file mode 100644 index 0000000000..b30f7b5632 --- /dev/null +++ b/common/neonext.h @@ -0,0 +1,122 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define Ne_EXPORT __declspec(dllexport) +#else +#define Ne_EXPORT +#endif + +#define Ne_SUCCESS 0 +#define Ne_EXCEPTION 1 + +struct Ne_ParameterList; +struct Ne_Cell; +struct Ne_Boolean; +struct Ne_Number; +struct Ne_String; +struct Ne_Bytes; +struct Ne_ArrayNumber; +struct Ne_ArrayString; +struct Ne_DictionaryNumber; +struct Ne_DictionaryString; + +struct Ne_MethodTable { + struct Ne_ParameterList *(*parameterlist_alloc)(int n); + void (*parameterlist_free)(struct Ne_ParameterList *params); + int (*parameterlist_get_size)(const struct Ne_ParameterList *list); + int (*parameterlist_check_types)(const struct Ne_ParameterList *list, const char *types); + const struct Ne_Cell *(*parameterlist_get_cell)(const struct Ne_ParameterList *list, int i); + struct Ne_Cell *(*parameterlist_set_cell)(struct Ne_ParameterList *list, int i); + + struct Ne_Cell *(*cell_alloc)(); + void (*cell_free)(struct Ne_Cell *cell); + void (*cell_copy)(struct Ne_Cell *dest, const struct Ne_Cell *src); + int (*cell_get_boolean)(const struct Ne_Cell *cell); + void (*cell_set_boolean)(struct Ne_Cell *cell, int value); + int (*cell_get_number_int)(const struct Ne_Cell *cell); + unsigned int (*cell_get_number_uint)(const struct Ne_Cell *cell); + void (*cell_set_number_int)(struct Ne_Cell *cell, int value); + void (*cell_set_number_uint)(struct Ne_Cell *cell, unsigned int value); + const char *(*cell_get_string)(const struct Ne_Cell *cell); + void (*cell_set_string)(struct Ne_Cell *cell, const char *value); + const unsigned char *(*cell_get_bytes)(const struct Ne_Cell *cell); + int (*cell_get_bytes_size)(const struct Ne_Cell *cell); + void (*cell_set_bytes)(struct Ne_Cell *cell, const unsigned char *value, int size); + void *(*cell_get_pointer)(const struct Ne_Cell *cell); + void (*cell_set_pointer)(struct Ne_Cell *cell, void *p); + int (*cell_get_array_size)(const struct Ne_Cell *cell); + void (*cell_array_clear)(struct Ne_Cell *cell); + const struct Ne_Cell *(*cell_get_array_cell)(const struct Ne_Cell *cell, int index); + struct Ne_Cell *(*cell_set_array_cell)(struct Ne_Cell *cell, int index); + int (*cell_get_dictionary_size)(const struct Ne_Cell *cell); + const char *(*cell_get_dictionary_key)(const struct Ne_Cell *cell, int n); + const struct Ne_Cell *(*cell_get_dictionary_cell)(const struct Ne_Cell *cell, const char *key); + struct Ne_Cell *(*cell_set_dictionary_cell)(struct Ne_Cell *cell, const char *key); + + void (*exec_callback)(const struct Ne_Cell *callback, const struct Ne_ParameterList *params, struct Ne_Cell *retval); + // TODO: Remove "code" and make "info" an Object when neonext gets object capability. + int (*raise_exception)(struct Ne_Cell *retval, const char *name, const char *info, int code); +}; + +struct Ne_Bytes { + const unsigned char *ptr; + int len; +}; + +Ne_EXPORT int Ne_INIT(const struct Ne_MethodTable *methodtable); +typedef int (*Ne_ExtensionFunction)(struct Ne_Cell *retval, struct Ne_ParameterList *in_params, struct Ne_ParameterList *out_params); + +#ifdef _MSC_VER +#pragma warning(disable: 4100) // unreferenced formal parameter +#pragma warning(disable: 4127) // conditional expression is constant +#endif + +#define Ne_FUNC(name) Ne_EXPORT int name(struct Ne_Cell *retval, struct Ne_ParameterList *in_params, struct Ne_ParameterList *out_params) + +#define Ne_IN_PARAM(i) Ne->parameterlist_get_cell(in_params, (i)) +#define Ne_OUT_PARAM(i) Ne->parameterlist_set_cell(out_params, (i)) + +#define Ne_PARAM_BOOL(i) Ne->cell_get_boolean(Ne_IN_PARAM(i)) +#define Ne_PARAM_INT(i) Ne->cell_get_number_int(Ne_IN_PARAM(i)) +#define Ne_PARAM_UINT(i) Ne->cell_get_number_uint(Ne_IN_PARAM(i)) +#define Ne_PARAM_STRING(i) Ne->cell_get_string(Ne_IN_PARAM(i)) +#define Ne_PARAM_BYTES(i) { Ne->cell_get_bytes(Ne->parameterlist_get_cell(in_params, (i))), Ne->cell_get_bytes_size(Ne->parameterlist_get_cell(in_params, (i))) } +#define Ne_PARAM_POINTER(type, i) (type *)(Ne->cell_get_pointer(Ne_IN_PARAM(i))) + +#define Ne_RETURN_BOOL(r) do { Ne->cell_set_boolean(retval, (r)); return Ne_SUCCESS; } while (0) +#define Ne_RETURN_INT(r) do { Ne->cell_set_number_int(retval, (r)); return Ne_SUCCESS; } while (0) +#define Ne_RETURN_UINT(r) do { Ne->cell_set_number_uint(retval, (r)); return Ne_SUCCESS; } while (0) +#define Ne_RETURN_STRING(r) do { Ne->cell_set_string(retval, (r)); return Ne_SUCCESS; } while (0) +#define Ne_RETURN_BYTES(r, n) do { Ne->cell_set_bytes(retval, (r), (n)); return Ne_SUCCESS; } while (0) +#define Ne_RETURN_POINTER(r) do { Ne->cell_set_pointer(retval, (r)); return Ne_SUCCESS; } while (0) + +#define Ne_RAISE_EXCEPTION(e, i, c) do { Ne->raise_exception(retval, (e), (i), (c)); return Ne_EXCEPTION; } while (0) + +#define Ne_CONST_INT(name, value) Ne_FUNC(name) { Ne_RETURN_INT(value); } + +#ifdef __cplusplus +} // extern "C" +#endif + +#ifdef __cplusplus + +#include + +extern const Ne_MethodTable *Ne; + +inline std::vector Ne_PARAM_ARRAY_INT(Ne_ParameterList *in_params, int i) +{ + const Ne_Cell *a = Ne->parameterlist_get_cell(in_params, (i)); + std::vector r; + int n = Ne->cell_get_array_size(a); + for (int j = 0; j < n; j++) { + r.push_back(Ne->cell_get_number_int(Ne->cell_get_array_cell(a, j))); + } + return r; +} + +#define Ne_RETURN_ARRAY_INT(r) do { for (size_t i = 0; i < r.size(); i++) { Ne->cell_set_number_int(Ne->cell_set_array_cell(retval, static_cast(i)), r[i]); } return Ne_SUCCESS; } while (0) + +#endif // __cplusplus diff --git a/compiler.cpp b/compiler.cpp deleted file mode 100644 index 7e90bd5ff3..0000000000 --- a/compiler.cpp +++ /dev/null @@ -1,509 +0,0 @@ -#include "compiler.h" - -#include - -#include "ast.h" -#include "opcode.h" - -class Emitter { - class Label { - friend class Emitter; - Label(): target(-1) {} - std::vector fixups; - int target; - }; -public: - void emit(unsigned char b); - void emit_int(int value); - void emit(unsigned char b, int value); - void emit(const std::vector &instr); - std::vector getObject(); - unsigned int global(const std::string &name); - unsigned int str(const std::string &s); - int next_function(); - Label &function_label(int index); - Label create_label(); - void emit_jump(unsigned char b, Label &label); - void jump_target(Label &label); -private: - std::vector code; - std::vector strings; - std::vector globals; - std::vector