Merge remote-tracking branch 'origin/incoming' into stable
This commit is contained in:
commit
e74d1f06ec
|
@ -0,0 +1,6 @@
|
|||
Dockerfile
|
||||
.dockerignore
|
||||
docs
|
||||
examples
|
||||
Jenkinsfile
|
||||
cmake-build-*
|
160
CMakeLists.txt
160
CMakeLists.txt
|
@ -1,6 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.18)
|
||||
project(Yerbacon VERSION 0.0.1 LANGUAGES C CXX)
|
||||
set(EXENAME "ybcon")
|
||||
string(TOLOWER "${PROJECT_NAME}" LOWERCASE_PROJECT_NAME)
|
||||
|
||||
if (NOT DEFINED CODENAME)
|
||||
set(CODENAME "Unknown")
|
||||
|
@ -13,7 +14,8 @@ set(EXEDESC "Transpiler for the yerbacon language.")
|
|||
string(TIMESTAMP SHORT_BUILD_TIMESTAMP "%Y%m")
|
||||
string(SUBSTRING ${SHORT_BUILD_TIMESTAMP} 2 4 SHORT_BUILD_TIMESTAMP)
|
||||
string(ASCII 169 CopyrightCharacter)
|
||||
string(TIMESTAMP LEGALCOPYRIGHT "Copyright ${CopyrightCharacter} 2020-%Y. Available under the MPL-2.0 license.")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MPL-2.0")
|
||||
string(TIMESTAMP LEGAL-COPYRIGHT "Copyright ${CopyrightCharacter} 2020-%Y. Available under the ${CPACK_RPM_PACKAGE_LICENSE} license.")
|
||||
file(COPY "resources/${PROJECT_NAME}.ico" DESTINATION "processed")
|
||||
configure_file("resources/${PROJECT_NAME}.manifest" "processed/${PROJECT_NAME}.manifest" @ONLY)
|
||||
configure_file("resources/${PROJECT_NAME}.rc" "processed/${PROJECT_NAME}.rc" @ONLY)
|
||||
|
@ -21,20 +23,33 @@ configure_file("resources/${PROJECT_NAME}.rc" "processed/${PROJECT_NAME}.rc" @ON
|
|||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
if (NOT DEFINED CMAKE_UNITY_BUILD)
|
||||
set(CMAKE_UNITY_BUILD TRUE)
|
||||
endif()
|
||||
include(CheckPIESupported)
|
||||
check_pie_supported(LANGUAGES CXX)
|
||||
set(USER_DEFINED_PIE (DEFINED CMAKE_POSITION_INDEPENDENT_CODE))
|
||||
if (CMAKE_CXX_LINK_PIE_SUPPORTED)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
else()
|
||||
message(NOTICE "Position-Independent Code (PIC) is not supported by the current toolchain")
|
||||
if (NOT USER_DEFINED_PIE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
endif()
|
||||
elseif(USER_DEFINED_PIE AND CMAKE_POSITION_INDEPENDENT_CODE)
|
||||
message(NOTICE "-- Could NOT manually enable Position-Independent Code (PIC) because it is not supported by the current toolchain")
|
||||
endif()
|
||||
if (DEFINED CXX_TARGET)
|
||||
set(CMAKE_SYSTEM_PROCESSOR "${CXX_TARGET}")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "")
|
||||
set(CMAKE_SYSTEM_PROCESSOR "${CMAKE_HOST_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "-Wall")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-Os")
|
||||
set(CMAKE_COLOR_DIAGNOSTICS ON)
|
||||
set(MINIMAL_GNU "11.0")
|
||||
set(MINIMAL_CLANG "13.0")
|
||||
set(MINIMAL_MSVC "19.25")
|
||||
set(IS_GNU (${CMAKE_CXX_COMPILER_ID} STREQUAL GNU))
|
||||
set(IS_CLANG (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang))
|
||||
set(MINIMAL_CLANG "14.0.1")
|
||||
set(MINIMAL_MSVC "19.30")
|
||||
option(IGNORE_MINIMAL_COMPILER_VERSION "Whether or not to ignore the minimal compiler versions")
|
||||
set(IS_GNU (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
|
||||
set(IS_CLANG (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
|
||||
|
||||
option(NO_CCACHE "Disables CCache")
|
||||
if (NOT NO_CCACHE)
|
||||
|
@ -49,13 +64,30 @@ find_package(Threads)
|
|||
if (${IS_GNU} OR ${IS_CLANG})
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
if (Threads_FOUND AND NOT MINGW)
|
||||
include(FindOpenMP)
|
||||
if (OpenMP_CXX_FOUND)
|
||||
set(CMAKE_CXX_FLAGS "${OpenMP_CXX_FLAGS} ${CMAKE_CXX_FLAGS}")
|
||||
add_definitions(-D_GLIBCXX_PARALLEL)
|
||||
if (NOT ("${CMAKE_EXE_LINKER_FLAGS}" MATCHES -static))
|
||||
include(FindOpenMP)
|
||||
if (OpenMP_CXX_FOUND)
|
||||
set(CMAKE_CXX_FLAGS "${OpenMP_CXX_FLAGS} ${CMAKE_CXX_FLAGS}")
|
||||
add_definitions(-D_GLIBCXX_PARALLEL)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstrict-enums -pipe -fstack-protector-strong -fstack-clash-protection -funwind-tables -fasynchronous-unwind-tables -frtti -fexceptions")
|
||||
if (NOT DEFINED EMSCRIPTEN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong -fstack-clash-protection")
|
||||
else()
|
||||
if (NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Android") # See https://github.com/WebAssembly/binaryen/issues/4519
|
||||
set(IS_HOST_NOT_ANDROID 1)
|
||||
else()
|
||||
set(IS_HOST_NOT_ANDROID 0)
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "--closure 1 -sWASM=0 --memory-init-file 0 -sEXPORTED_FUNCTIONS=_main -sEXIT_RUNTIME=1 -sSINGLE_FILE=1 -sSAFE_HEAP=${IS_HOST_NOT_ANDROID} -sABORTING_MALLOC=0 -sJS_MATH=1 -sENVIRONMENT='web,webview,worker,node,shell' -sNODEJS_CATCH_EXIT=0 -sSTRICT=1 -sMINIMAL_RUNTIME=0 -sDISABLE_EXCEPTION_CATCHING=0 -sALLOW_MEMORY_GROWTH=1 -Wno-pthreads-mem-growth -sMEMORY_GROWTH_GEOMETRIC_STEP=0 -sDECLARE_ASM_MODULE_EXPORTS=0 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
if (CMAKE_USE_PTHREADS_INIT)
|
||||
set(CMAKE_CXX_FLAGS "-pthread ${CMAKE_CXX_FLAGS}") # Emscripten requires the -pthread flag
|
||||
set(CMAKE_EXE_LINKER_FLAGS "--closure-args=\"--compilation_level SIMPLE\" -sPROXY_TO_PTHREAD=1 ${CMAKE_EXE_LINKER_FLAGS}") # See https://github.com/emscripten-core/emscripten/issues/16706
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstrict-enums -pipe -funwind-tables -fasynchronous-unwind-tables -frtti -fexceptions")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffunction-sections -fdata-sections -fmerge-all-constants -ftree-vectorize -fno-math-errno")
|
||||
include(CheckCXXCompilerFlag)
|
||||
set(CF_PROTECTION "-fcf-protection")
|
||||
|
@ -65,37 +97,49 @@ if (${IS_GNU} OR ${IS_CLANG})
|
|||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE LANGUAGES CXX)
|
||||
if (NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE)
|
||||
set(USER_DEFINED_INTERPROCEDURAL_OPTIMIZATION_RELEASE NO)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE LANGUAGES CXX)
|
||||
else()
|
||||
set(USER_DEFINED_INTERPROCEDURAL_OPTIMIZATION_RELEASE YES)
|
||||
endif()
|
||||
|
||||
if (${IS_GNU})
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MINIMAL_GNU})
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MINIMAL_GNU} AND NOT ${IGNORE_MINIMAL_COMPILER_VERSION})
|
||||
message(FATAL_ERROR "G++ ${MINIMAL_GNU} or higher is required.")
|
||||
endif()
|
||||
if (Threads_FOUND)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ftree-parallelize-loops=2")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvect-cost-model=unlimited -foptimize-strlen -fsched-pressure -flive-range-shrinkage -fpredictive-commoning -ftree-partial-pre -fzero-call-used-regs=used-gpr-arg -fira-loop-pressure -ftree-loop-distribution -floop-interchange -fsplit-paths -fgcse-las -fgcse-sm -fipa-pta -fstdarg-opt -fivopts")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvect-cost-model=unlimited -foptimize-strlen -fsched-pressure -flive-range-shrinkage -fpredictive-commoning -ftree-partial-pre -fzero-call-used-regs=used-gpr-arg -fira-loop-pressure -ftree-loop-distribution -floop-interchange -fsplit-paths -fgcse-las -fgcse-sm -fipa-pta -fstdarg-opt -fivopts -fweb")
|
||||
if (${CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE})
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-fwhole-program ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-fwhole-program -flto-partition=none ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
endif()
|
||||
elseif(${IS_CLANG})
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MINIMAL_CLANG})
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MINIMAL_CLANG} AND NOT ${IGNORE_MINIMAL_COMPILER_VERSION})
|
||||
message(FATAL_ERROR "Clang ${MINIMAL_CLANG} or higher is required.")
|
||||
endif()
|
||||
if (${CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE} OR MINGW) # CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE is false on llvm-mingw toolchains even though it is supported
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto=full -fwhole-program-vtables")
|
||||
if (MINGW AND NOT USER_DEFINED_INTERPROCEDURAL_OPTIMIZATION_RELEASE) # CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE is false on llvm-mingw toolchains even though it is supported
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE YES)
|
||||
endif()
|
||||
if (CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto=thin")
|
||||
if (NOT DEFINED EMSCRIPTEN)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fwhole-program-vtables")
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-unqualified-std-cast-call -Wno-reinterpret-base-class")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fstrict-vtable-pointers")
|
||||
elseif(MSVC)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MINIMAL_MSVC})
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${MINIMAL_MSVC} AND NOT ${IGNORE_MINIMAL_COMPILER_VERSION})
|
||||
message(FATAL_ERROR "MSVC ${MINIMAL_MSVC} or higher is required")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR /W3 /Zc:__cplusplus /Zc:preprocessor /Zc:throwingNew /Zc:inline")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /permissive- /Zc:twoPhase- /EHsc /GR /W3 /Zc:__cplusplus /Zc:preprocessor /Zc:throwingNew /Zc:inline")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO")
|
||||
endif()
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPERCASE_BUILD_TYPE)
|
||||
if (UPPERCASE_BUILD_TYPE STREQUAL RELEASE)
|
||||
if (UPPERCASE_BUILD_TYPE STREQUAL "RELEASE")
|
||||
add_definitions(-DYBCON_FLAGS="${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
endif()
|
||||
add_definitions(-DYBCON_COMPILER="${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
|
@ -119,39 +163,77 @@ set(CPACK_VERBATIM_VARIABLES TRUE)
|
|||
string(TIMESTAMP TIME "%Y.%m+%d")
|
||||
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}-${TIME}")
|
||||
set(CPACK_PACKAGE_VENDOR "Contributor(s)")
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/Yerbacon.png")
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/${PROJECT_NAME}.png")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/misc/desc.txt")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${EXEDESC}")
|
||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt")
|
||||
set(CPACK_PACKAGE_CONTACT "Username404 <w.iron.zombie@gmail.com>")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Yerbacon ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${CMAKE_PROJECT_VERSION}-${TIME}")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${LOWERCASE_PROJECT_NAME}-${PROJECT_VERSION}-${TIME}.${CMAKE_SYSTEM_PROCESSOR}")
|
||||
|
||||
include_directories(${CMAKE_CURRENT_LIST_DIR})
|
||||
add_executable(${EXENAME} src/main.cpp ${CMAKE_CURRENT_BINARY_DIR}/processed/${PROJECT_NAME}.rc src/etc/filefuncs.cpp src/etc/lexer.cpp src/headers/lex.hpp src/headers/misc.hpp src/headers/parsing/ParseComponents.hpp src/headers/transpiler/Target.hpp src/headers/transpiler/implementations/Lua.hpp src/headers/transpiler/implementations/Js.hpp src/headers/transpiler/implementations/Py.hpp src/headers/parsing/Parser.hpp src/headers/arguments.hpp)
|
||||
add_executable(${EXENAME} src/main.cpp ${CMAKE_CURRENT_BINARY_DIR}/processed/${PROJECT_NAME}.rc src/etc/filefuncs.cpp src/etc/lexer.cpp src/headers/lex.hpp src/headers/misc.hpp src/headers/parsing/ParseComponents.hpp src/headers/transpiler/Target.hpp src/headers/transpiler/implementations/Lua.hpp src/headers/transpiler/implementations/Js.hpp src/headers/transpiler/implementations/Py.hpp src/headers/parsing/Parser.hpp src/headers/arguments.hpp src/headers/parsing/ReservedIdentifiers.hpp)
|
||||
target_compile_definitions(${EXENAME} PRIVATE YBCON_VERSION="${CODENAME} ${PROJECT_VERSION}")
|
||||
target_precompile_headers(${EXENAME} PRIVATE src/headers/Yerbacon.hpp)
|
||||
if (Threads_FOUND)
|
||||
target_link_libraries(${EXENAME} PRIVATE Threads::Threads)
|
||||
endif()
|
||||
|
||||
# lpkg = linux package, wpkg = windows package
|
||||
set(PNAME ${PROJECT_NAME}-${CODENAME}-${TIME})
|
||||
if (UNIX AND NOT (MINGW OR CMAKE_HOST_WIN32))
|
||||
option(NO_SELF_PACKER "Disables usage of a self-packer")
|
||||
if (NOT (UPPERCASE_BUILD_TYPE STREQUAL "DEBUG" OR UPPERCASE_BUILD_TYPE STREQUAL "RELWITHDEBINFO" OR NO_SELF_PACKER OR DEFINED EMSCRIPTEN))
|
||||
include(FindSelfPackers)
|
||||
if (SELF_PACKER_FOR_EXECUTABLE MATCHES upx) # UPX version d61edc9 or higher is required when using a cross-compiler to target the musl C library
|
||||
if (CMAKE_CXX_LINK_NO_PIE_SUPPORTED OR MINGW) # MINGW does not support PIE, yet the variable is set to NO.
|
||||
set(SELF_PACKER_FOR_EXECUTABLE_FLAGS ${SELF_PACKER_FOR_EXECUTABLE_FLAGS} --ultra-brute --best)
|
||||
if (USER_DEFINED_PIE AND CMAKE_POSITION_INDEPENDENT_CODE)
|
||||
message(NOTICE "-- Could NOT manually enable PIE (UPX is in use)")
|
||||
endif()
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE FALSE)
|
||||
set_target_properties(${EXENAME} PROPERTIES POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE})
|
||||
else()
|
||||
set(SELF_PACKER_FOR_EXECUTABLE "SELF_PACKER_FOR_EXECUTABLE-NOTFOUND")
|
||||
message(NOTICE "UPX usage has been disabled because position-dependent executables are unsupported by the current compiler")
|
||||
endif()
|
||||
endif()
|
||||
if (NOT SELF_PACKER_FOR_EXECUTABLE STREQUAL "SELF_PACKER_FOR_EXECUTABLE-NOTFOUND")
|
||||
add_custom_command(TARGET ${EXENAME} POST_BUILD COMMAND "${SELF_PACKER_FOR_EXECUTABLE}" ${SELF_PACKER_FOR_EXECUTABLE_FLAGS} $<TARGET_FILE:${EXENAME}> VERBATIM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
find_program(NPM_PRESENT npm)
|
||||
find_program(YARN_PRESENT yarn)
|
||||
if (YARN_PRESENT)
|
||||
set(NODE_PACKAGE_PROGRAM "${YARN_PRESENT}")
|
||||
elseif(NPM_PRESENT)
|
||||
set(NODE_PACKAGE_PROGRAM "${NPM_PRESENT}")
|
||||
endif()
|
||||
if (DEFINED NODE_PACKAGE_PROGRAM)
|
||||
configure_file("resources/package.json" "processed/package.json" @ONLY)
|
||||
file(COPY "${CMAKE_CURRENT_BINARY_DIR}/processed/package.json" DESTINATION ".")
|
||||
file(COPY "resources/.npmrc" DESTINATION ".")
|
||||
add_custom_target(vercel_pkg COMMAND ${NODE_PACKAGE_PROGRAM} install)
|
||||
add_dependencies(vercel_pkg ${EXENAME})
|
||||
# Warning: https://github.com/sbingner/ldid has to be on the PATH, before packaging, for the arm64 macOS executable to work
|
||||
add_custom_command(TARGET vercel_pkg POST_BUILD COMMAND node_modules/.bin/pkg --config processed/package.json -C Brotli $<TARGET_FILE:${EXENAME}> --no-bytecode --public-packages "\"*\"" --public --output "${EXENAME}-mac" VERBATIM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT (MINGW OR CMAKE_HOST_WIN32 OR EMSCRIPTEN))
|
||||
include(GNUInstallDirs)
|
||||
set(CMAKE_INSTALL_PREFIX "/usr")
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${PNAME}_lpkg")
|
||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scripts/postinst" "processed/postinst" @ONLY)
|
||||
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/processed/postinst")
|
||||
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/processed/postinst")
|
||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA ${CPACK_RPM_POST_INSTALL_SCRIPT_FILE})
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
|
||||
set(CPACK_DEBIAN_COMPRESSION_TYPE xz)
|
||||
set(CPACK_RPM_COMPRESSION_TYPE ${CPACK_DEBIAN_COMPRESSION_TYPE})
|
||||
set(CPACK_RPM_PACKAGE_AUTOREQ YES)
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MPL-2.0")
|
||||
set(CPACK_RPM_FILE_NAME "RPM-DEFAULT")
|
||||
set(CPACK_RPM_CHANGELOG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/changelog")
|
||||
if (CMAKE_VERSION STREQUAL "3.21.1")
|
||||
message(WARNING "CPack ${CMAKE_VERSION} has a bug which causes rpm scripts to be unusable; please update or downgrade")
|
||||
|
@ -200,14 +282,14 @@ elseif(MINGW OR MSVC)
|
|||
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CODENAME})
|
||||
set(CPACK_NSIS_INSTALL_ROOT "${CMAKE_INSTALL_PREFIX}")
|
||||
if (MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static -static-libstdc++ -static-libgcc ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
endif()
|
||||
set(CPACK_PACKAGE_FILE_NAME "${PNAME}_wpkg")
|
||||
set(CPACK_NSIS_COMPRESSOR "/SOLID lzma")
|
||||
set(CPACK_NSIS_MANIFEST_DPI_AWARE TRUE)
|
||||
set(CPACK_NSIS_PACKAGE_NAME "${PROJECT_NAME} ${CODENAME}-${PROJECT_VERSION}")
|
||||
set(CPACK_NSIS_WELCOME_TITLE_3LINES ON)
|
||||
set(CPACK_NSIS_URL_INFO_ABOUT "https://gits.username404.fr/Username404-59/Yerbacon")
|
||||
set(CPACK_NSIS_URL_INFO_ABOUT "https://gits.username404.fr/Username404-59/${PROJECT_NAME}")
|
||||
set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/${PROJECT_NAME}.ico")
|
||||
set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/${PROJECT_NAME}.ico")
|
||||
set(CPACK_NSIS_DEFINES "RequestExecutionLevel highest")
|
||||
|
@ -217,7 +299,7 @@ elseif(MINGW OR MSVC)
|
|||
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
|
||||
if (CMAKE_HOST_WIN32)
|
||||
# The two following variables require CMake 3.20 or higher
|
||||
set(CPACK_NSIS_BRANDING_TEXT "NSIS Installer for the yerbacon compiler")
|
||||
set(CPACK_NSIS_BRANDING_TEXT "NSIS Installer for ${PROJECT_NAME}")
|
||||
set(CPACK_NSIS_BRANDING_TEXT_TRIM_POSITION CENTER)
|
||||
endif()
|
||||
# CMake 3.22+ is required to ignore the NSIS license page
|
||||
|
@ -225,8 +307,4 @@ elseif(MINGW OR MSVC)
|
|||
set(CPACK_GENERATOR ZIP;NSIS)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${EXENAME}.exe DESTINATION bin)
|
||||
endif()
|
||||
if (NOT DEFINED CXX_TARGET)
|
||||
set(CXX_TARGET ${CMAKE_HOST_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}.${CXX_TARGET}")
|
||||
include(CPack)
|
|
@ -0,0 +1,23 @@
|
|||
FROM alpine:edge
|
||||
RUN apk add --no-cache gcc g++ musl-dev libc-dev cmake git samurai coreutils rpm dpkg dpkg-dev xz file
|
||||
|
||||
RUN mkdir -p /usr/src/yerbacon
|
||||
WORKDIR /usr/src/
|
||||
ARG BRANCH="devel4"
|
||||
ADD https://api.github.com/repos/upx/upx/git/refs/heads/$BRANCH version.json
|
||||
RUN git clone --quiet -n --branch $BRANCH https://github.com/upx/upx/
|
||||
ARG CMAKE_BUILD_TYPE="Release"
|
||||
WORKDIR ./upx
|
||||
RUN git checkout --quiet `git tag | sort -V | tail -1`
|
||||
RUN git submodule --quiet update --init
|
||||
RUN mkdir -p ./build/release
|
||||
WORKDIR ./build/release
|
||||
RUN cmake -G Ninja ../..
|
||||
RUN cmake --build . --parallel `nproc`
|
||||
|
||||
WORKDIR /usr/src/yerbacon
|
||||
COPY . ./
|
||||
RUN cmake -G Ninja -S . -B ./cmake-build-release -DCMAKE_EXE_LINKER_FLAGS="-static" -DSELF_PACKER_FOR_EXECUTABLE=/usr/src/upx/build/release/upx
|
||||
RUN cmake --build ./cmake-build-release --parallel `nproc`
|
||||
WORKDIR ./cmake-build-release
|
||||
RUN cpack
|
|
@ -1,29 +1,29 @@
|
|||
def buildTarget(String path, String rpmArch = 'noarch', String debArch = 'noarch', boolean isPackageArchDeb = true, String suffix = '') {
|
||||
final String packageArch = isPackageArchDeb ? debArch : rpmArch;
|
||||
final String system_name = !path.contains('mingw') ? sh(returnStdout: true, script: 'uname -s').trim() : 'Windows'
|
||||
// Note: CMake 3.20 or higher is needed
|
||||
cmakeBuild buildDir: "cmake-build-${packageArch}${suffix}", buildType: 'release', cleanBuild: true, installation: 'Latest',
|
||||
cmakeArgs: "--no-warn-unused-cli -DCMAKE_SYSTEM_NAME=\"${system_name}\" -DCMAKE_C_COMPILER=/usr/bin/${path}-gcc -DCMAKE_CXX_COMPILER=/usr/bin/${path}-g++ -DCMAKE_LINKER=/usr/bin/${path}-ld.gold -DCMAKE_AR=/usr/bin/${path}-ar -DCMAKE_RC_COMPILER=/usr/bin/${path}-windres -DCPACK_RPM_PACKAGE_ARCHITECTURE=${rpmArch} -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=${debArch} -DCXX_TARGET=${packageArch} -DCPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS=/usr/${path}/lib/ -DNO_CCACHE=ON",
|
||||
generator: fileExists('/usr/bin/ninja') ? 'Ninja' : 'Unix Makefiles'
|
||||
cmake arguments: "--build ./cmake-build-${packageArch}${suffix} --target ybcon", installation: 'Latest'
|
||||
sh "/usr/bin/${path}-strip -s ./cmake-build-${packageArch}${suffix}/ybcon*"
|
||||
cpack installation: 'Latest', workingDir: "cmake-build-${packageArch}${suffix}"
|
||||
}
|
||||
|
||||
final String windowsSuffix = '-windows'
|
||||
String cmake_generator() { return fileExists('/usr/bin/ninja') ? 'Ninja' : 'Unix Makefiles' }
|
||||
def clean_workspace() { cleanWs(cleanWhenNotBuilt: false, deleteDirs: true, disableDeferredWipeout: false, notFailBuild: true, skipWhenFailed: false) }
|
||||
boolean use_yarn_or_npm() { return fileExists('/usr/bin/yarn') || fileExists('/usr/bin/npm') }
|
||||
|
||||
/* Required Plugins:
|
||||
- CMake
|
||||
- Sidebar Link
|
||||
- Workspace Cleanup
|
||||
Required Compilers:
|
||||
- G++ cross-compiler for riscv64
|
||||
- Musl cross-compiling toolchains (archives ending in "-cross" from https://musl.cc/#binaries) for x86_64, i686, armel, armhf and aarch64, unzipped into /usr/, along with soft links from /usr/bin/ to the binaries
|
||||
- Emscripten Clang (https://github.com/emscripten-core/emsdk) in /usr/share/
|
||||
- MinGW32 G++/Clang (https://github.com/mstorsjo/llvm-mingw) for x86_64, i686, armhf and aarch64, (unzip the content of the folder in the tar.xz file) in /usr/, along with soft links from /usr/bin/ to the binaries
|
||||
Optional Tools:
|
||||
- Ninja
|
||||
- UPX (d61edc9 or higher)
|
||||
- Vercel PKG (https://github.com/vercel/pkg)
|
||||
- LDID (https://github.com/sbingner/ldid)
|
||||
*/
|
||||
|
||||
pipeline { // Multi-branch pipeline script for Yerbacon.
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
options {
|
||||
buildDiscarder(logRotator(numToKeepStr: '48', artifactNumToKeepStr: '96'))
|
||||
timeout(time: 8, unit: 'MINUTES')
|
||||
timeout(time: 25, unit: 'MINUTES')
|
||||
sidebarLinks([
|
||||
[displayName: 'Gitea Repository', iconFileName: '/userContent/Yerbacon.png', urlName: 'https://gits.username404.fr/Username404-59/Yerbacon/src/branch/' + env.BRANCH_NAME]
|
||||
])
|
||||
|
@ -33,39 +33,70 @@ pipeline { // Multi-branch pipeline script for Yerbacon.
|
|||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
echo "Building the ${env.BRANCH_NAME} branch.."
|
||||
buildTarget('x86_64-linux-gnu', 'x86_64', 'amd64')
|
||||
buildTarget('i686-linux-gnu', 'i386', 'i386')
|
||||
buildTarget('arm-linux-gnueabi', 'armv4l', 'armel')
|
||||
buildTarget('arm-linux-gnueabihf', 'armv7hl', 'armhf')
|
||||
buildTarget('aarch64-linux-gnu', 'aarch64', 'arm64', false)
|
||||
buildTarget('riscv64-linux-gnu', 'riscv64', 'riscv64')
|
||||
}
|
||||
}
|
||||
stage('Build for other platforms') {
|
||||
steps {
|
||||
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
|
||||
buildTarget('x86_64-w64-mingw32', 'x86_64', 'amd64', true, windowsSuffix)
|
||||
buildTarget('i686-w64-mingw32', 'i386', 'i386', true, windowsSuffix)
|
||||
buildTarget('armv7-w64-mingw32', 'armv7hl', 'armhf', true, windowsSuffix)
|
||||
buildTarget('aarch64-w64-mingw32', 'aarch64', 'arm64', false, windowsSuffix)
|
||||
matrix {
|
||||
agent any
|
||||
axes {
|
||||
axis {
|
||||
name 'TARGET'
|
||||
values(
|
||||
'x86_64-linux-musl (RPM=x86_64, DEB=amd64)',
|
||||
'i686-linux-musl (RPM=i386, DEB=i386)',
|
||||
'armel-linux-musleabi (RPM=armv4l, DEB=armel)',
|
||||
'armv7l-linux-musleabihf (RPM=armv7hl, DEB=armhf)',
|
||||
'aarch64-linux-musl (RPM=aarch64, DEB=arm64)',
|
||||
'riscv64-linux-gnu (RPM=riscv64, DEB=riscv64)',
|
||||
'x86_64-w64-mingw32 (RPM=x86_64, DEB=amd64)',
|
||||
'i686-w64-mingw32 (RPM=i386, DEB=i386)',
|
||||
'armv7-w64-mingw32 (RPM=armv7hl, DEB=armhf)',
|
||||
'aarch64-w64-mingw32 (RPM=aarch64, DEB=arm64)',
|
||||
'/usr/share/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Deploy') {
|
||||
steps {
|
||||
echo 'Deploying....'
|
||||
archiveArtifacts artifacts: 'cmake-build-*/*.deb, cmake-build-*/*.rpm, cmake-build-*/*.tar.gz, cmake-build-*/*.sh', fingerprint: false
|
||||
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
|
||||
archiveArtifacts artifacts: 'cmake-build-*/*wpkg.*.exe, cmake-build-*/*.zip'
|
||||
stages {
|
||||
stage('Compile') {
|
||||
steps {
|
||||
script {
|
||||
final boolean use_toolchain = "${TARGET}".endsWith('.cmake')
|
||||
final String path = use_toolchain ? "${TARGET}" : "${TARGET}".substring(0, "${TARGET}".indexOf(' ('))
|
||||
final String rpmArch = use_toolchain ? 'noarch' : "${TARGET}".substring("${TARGET}".indexOf('(RPM=') + 5, "${TARGET}".indexOf(','))
|
||||
final String debArch = use_toolchain ? 'noarch' : "${TARGET}".substring("${TARGET}".indexOf('DEB=') + 4, "${TARGET}".indexOf(')'))
|
||||
final String system_name = !path.contains('mingw') ? sh(returnStdout: true, script: 'uname -s').trim() : 'Windows'
|
||||
final String linker = "/usr/bin/${path}-ld"
|
||||
final boolean not_packer_compatible = path.contains('riscv') || ((path.contains('aarch64') || path.contains('arm')) && path.contains('mingw'))
|
||||
final String build_directory = "cmake-build-${use_toolchain ? path.substring(path.lastIndexOf('/') + 1, path.length() - 6) : path}".toLowerCase()
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
// Note: CMake 3.20 or higher is needed
|
||||
cmakeBuild buildDir: build_directory, buildType: 'release', cleanBuild: true, installation: 'Latest',
|
||||
cmakeArgs: "--no-warn-unused-cli ${use_toolchain ? "-DCMAKE_TOOLCHAIN_FILE=${path}" : "-DCMAKE_SYSTEM_NAME=\"${system_name}\" -DCXX_TARGET=\"${use_toolchain ? 'noarch' : debArch}\" -DCPACK_RPM_PACKAGE_ARCHITECTURE=${rpmArch} -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=${debArch} -DCMAKE_SYSTEM_PROCESSOR=\"${path.substring(0, path.indexOf('-'))}\" -DCMAKE_C_COMPILER=/usr/bin/${path}-gcc -DCMAKE_CXX_COMPILER=/usr/bin/${path}-g++ -DCMAKE_LINKER=${fileExists("${linker}.gold") ? "${linker}.gold" : linker} -DCMAKE_AR=/usr/bin/${path}-ar -DCMAKE_RC_COMPILER=/usr/bin/${path}-windres -DCMAKE_EXE_LINKER_FLAGS=-static"} -DNO_SELF_PACKER=${!(not_packer_compatible || use_toolchain) ? "OFF" : "ON"} -DIGNORE_MINIMAL_COMPILER_VERSION=ON -DNO_CCACHE=ON -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON${path.endsWith('armv7-w64-mingw32') ? ' -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=NO' : ''}",
|
||||
generator: cmake_generator()
|
||||
cmake arguments: "--build ./$build_directory --target ${!("${TARGET}".endsWith('Emscripten.cmake') && use_yarn_or_npm()) ? 'ybcon' : 'vercel_pkg'}", installation: 'Latest'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Package') {
|
||||
when { equals expected: true, actual: !"${TARGET}".endsWith('.cmake') }
|
||||
steps {
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
script {
|
||||
cpack installation: 'Latest', workingDir: "cmake-build-${"${TARGET}".substring(0, "${TARGET}".indexOf(' ('))}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Deploy') {
|
||||
steps {
|
||||
echo 'Deploying....'
|
||||
catchError(buildResult: "${TARGET}".endsWith('.cmake') ? 'FAILURE' : 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
archiveArtifacts artifacts: "cmake-build-*/*.deb, cmake-build-*/*.rpm, cmake-build-*/*.tar.gz, cmake-build-*/*.sh, cmake-build-*/*.js,${use_yarn_or_npm() ? 'cmake-build-*/*-mac-*,' : ''} cmake-build-*/*.*.exe, cmake-build-*/*.zip", fingerprint: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post { always { clean_workspace() } }
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
cleanWs(cleanWhenNotBuilt: false, deleteDirs: true, disableDeferredWipeout: false, notFailBuild: true, skipWhenFailed: false)
|
||||
}
|
||||
}
|
||||
post { always { clean_workspace() } }
|
||||
}
|
29
README.md
29
README.md
|
@ -1,17 +1,17 @@
|
|||
# Yerbacon
|
||||
# Yerbacon
|
||||
|
||||
[![Build Status](https://ci.username404.fr/buildStatus/icon?style=plastic)](https://ci.username404.fr/job/Yerbacon/job/stable/)
|
||||
<img title="Yer 🥓" align="left" src="/Username404-59/Yerbacon/raw/branch/stable/resources/Yerbacon.svg" alt="Yerbacon logo" width="192" height="192">
|
||||
|
||||
<img align="left" src="/Username404-59/Yerbacon/raw/branch/stable/resources/Yerbacon.svg" alt="Yerbacon logo" width="192" height="192">
|
||||
Aka Yer Bacon,
|
||||
|
||||
- #### A language that transpiles into lua, javascript (ES6) or python code.
|
||||
### ► A language that transpiles into another programming language, like Lua, JavaScript (ES6) or Python.
|
||||
```
|
||||
main #=> {
|
||||
main >> {
|
||||
print_line("Hello, World!")
|
||||
}
|
||||
main()
|
||||
```
|
||||
|
||||
[![Build Status](https://ci.username404.fr/buildStatus/icon?style=plastic)](https://ci.username404.fr/job/Yerbacon/job/stable/)
|
||||
|
||||
### Planned features:
|
||||
|
||||
- Type inference
|
||||
|
@ -23,14 +23,11 @@ main #=> {
|
|||
|
||||
## Build requirements
|
||||
|
||||
CMake 3.18 or higher is needed.
|
||||
1. CMake 3.18 *or higher*
|
||||
|
||||
### Linux target
|
||||
2. A compiler from the following list:
|
||||
- GCC 11+
|
||||
- Clang 14.0.1+ with llvm-gold
|
||||
- MSVC 19.30+
|
||||
|
||||
- GCC 11+ or Clang 13+ with llvm-gold
|
||||
|
||||
### Windows target
|
||||
|
||||
- MinGW32 (on debian you will need to use the `i686-w64-mingw32-gcc-posix` executable provided by the `gcc-mingw-w64-i686-posix-runtime` package)
|
||||
|
||||
MSVC could also be used instead if you are on Windows.
|
||||
Note that to build for Windows targets with a mingw toolchain on debian, you will need to use the `i686-w64-mingw32-gcc-posix` executable provided by the `gcc-mingw-w64-i686-posix-runtime` package.
|
|
@ -7,33 +7,23 @@ helloworld #= "Hello, World!"
|
|||
## 2 - (Anonymous) Functions
|
||||
You can make an anonymous functions using the following syntax:
|
||||
```
|
||||
getHelloWorld: ~String => {
|
||||
getHelloWorld: ~String #= {
|
||||
return "Hello, World!"
|
||||
}
|
||||
```
|
||||
The type (`~String` here, which basically means "an anonymous function that returns a String") can be omitted, see [#3](#3---types).
|
||||
|
||||
Note that you can make `getHelloWorld` final by replacing `=>` with `#=>`.
|
||||
|
||||
To define a **named** (and mandatorily final) function, replace `=>` with `>>`:
|
||||
To define a **named** (and mandatorily final) function, replace `#=` with `>>`:
|
||||
```
|
||||
getHelloWorld: String >> {
|
||||
return "Hello, World!"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.1 - Main
|
||||
Main can be a variable named `main` that contains an anonymous function, a function named `main` or a function that has the `as_main` attribute:
|
||||
```
|
||||
main #=> {}
|
||||
main >> {}
|
||||
as_main helloWorld >> {}
|
||||
```
|
||||
|
||||
### 2.2 - Function parameters
|
||||
### 2.1 - Function parameters
|
||||
Parameters can be added to an anonymous function by specifying the types as follows:
|
||||
```
|
||||
addIntToString (int, String) => { a, b;
|
||||
addIntToString: (int, String)~ #= { a, b;
|
||||
print_line(b + a)
|
||||
}
|
||||
```
|
||||
|
@ -45,7 +35,7 @@ addIntToString(a: int, b: String) => {
|
|||
}
|
||||
```
|
||||
|
||||
### 2.3 - Function calls
|
||||
### 2.2 - Function calls
|
||||
A function can be simply invoked like this if it has no parameters:
|
||||
```
|
||||
helloWorld()
|
||||
|
@ -67,7 +57,7 @@ Types are *inferred*, which means that specifying types of variables or returned
|
|||
|
||||
<sup>Note: While primitives types (`String`, `int`, `double`, `boolean`, `float`) will be transpiled to their equivalents for the target of the transpiler, this is not the case for other types.<sup>
|
||||
|
||||
Every variable has a static type by default; it is possible to make a **non-final** variable dynamic by adding `dyn`/`dynamic` to the attributes or making it the return type:
|
||||
Every variable has a static type by default at compile time; it is possible to make a **non-final** variable dynamic by adding `dyn`/`dynamic` to the attributes or making it the return type:
|
||||
```
|
||||
dyn helloWorld = 0 # helloWorld: dyn = 0 would also work
|
||||
helloWorld = "Hello, World!"
|
||||
|
@ -99,7 +89,5 @@ class Program {
|
|||
}
|
||||
}
|
||||
|
||||
main >> {
|
||||
Program("World")
|
||||
}
|
||||
program #= Program("World")
|
||||
```
|
|
@ -1,3 +1,5 @@
|
|||
main #= [] -> {
|
||||
main >> {
|
||||
print_line("Hello, World!")
|
||||
}
|
||||
|
||||
main()
|
|
@ -0,0 +1 @@
|
|||
package-lock=false
|
|
@ -14,7 +14,7 @@ BEGIN
|
|||
VALUE "FileDescription", "@EXEDESC@"
|
||||
VALUE "FileVersion", "@CMAKE_PROJECT_VERSION@.@SHORT_BUILD_TIMESTAMP@"
|
||||
VALUE "InternalName", "@EXENAME@"
|
||||
VALUE "LegalCopyright", "@LEGALCOPYRIGHT@"
|
||||
VALUE "LegalCopyright", "@LEGAL-COPYRIGHT@"
|
||||
VALUE "OriginalName", "@EXENAME@.exe"
|
||||
VALUE "ProductName", "@PROJECT_NAME@"
|
||||
VALUE "ProductVersion", "@CODENAME@ @CMAKE_PROJECT_VERSION@"
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "@EXENAME@",
|
||||
"bin": "@EXENAME@.js",
|
||||
"license": "@CPACK_RPM_PACKAGE_LICENSE@",
|
||||
"devDependencies": { "pkg": "^5.8.0" },
|
||||
"pkg": {
|
||||
"assets": "../@EXENAME@.worker.js",
|
||||
"targets": [
|
||||
"latest-macos-x64",
|
||||
"latest-macos-arm64"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
cd ./cmake-build-release/ && clear && ./ybcon --printresult ../examples/HelloWorld.ybcon
|
|
@ -8,7 +8,7 @@ _ybconAutoComplete() {
|
|||
COMPREPLY=()
|
||||
current="${COMP_WORDS[COMP_CWORD]}"
|
||||
previous="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
options='-h -p --help --parallel --target= --newlines= --printresult --version --buildInfo'
|
||||
options='-h -p -t --help --parallel --target= --newlines= --printresult --text --version --buildInfo'
|
||||
if [[ "${current}" == -* ]]; then
|
||||
YCompReply "$(compgen -W "$options" -- "$current")"
|
||||
return 0
|
||||
|
|
|
@ -7,4 +7,5 @@ complete -c ybcon -x -l buildInfo -d "Print informations about how the ybcon exe
|
|||
complete -c ybcon -l parallel -k -d "Transpile files in parallel mode"
|
||||
complete -c ybcon -l target -k -f -a 'lua js py' -d "Set the transpilation target"
|
||||
complete -c ybcon -l newlines -k -f -a 'on off' -d "Enable or disable new lines"
|
||||
complete -c ybcon -s p -l printresult -d "Enable printing the transpilation result to stdout"
|
||||
complete -c ybcon -s p -l printresult -d "Enable printing the transpilation result to stdout"
|
||||
complete -c ybcon -x -s t -l text -d "Transpile text provided after this argument (implies -p)"
|
|
@ -9,6 +9,7 @@ _ybcon() {
|
|||
--target='[Set the transpilation target]:language:(lua js py)' \
|
||||
--newlines='[Enable or disable new lines]:state:(on off)' \
|
||||
{-p,--printresult}'[Enable printing the transpilation result to stdout]' \
|
||||
{-t,--text}'[Transpile text provided after this argument (implies -p)]' \
|
||||
"*:$completeyfile"
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ defaultBinLocation="$scriptDir/../libexec/ybcon"
|
|||
|
||||
usage() {
|
||||
if [ "$1" = false ]; then echo "Invalid arguments, usage:"; fi
|
||||
echo "$EXENAME [--version] [--buildInfo] [-h|--help] [--parallel] [--target=<target>] [--newlines=on/off] [-p|--printresult] <file>"
|
||||
echo "$EXENAME [--version] [--buildInfo] [-h|--help] [--parallel] [--target=<target>] [--newlines=on/off] [-p|--printresult] [-t|--text] <file>"
|
||||
if [ "$1" = true ]; then
|
||||
echo " --version Print the version"
|
||||
echo " --buildInfo Print informations about how the ybcon executable was built"
|
||||
|
@ -20,6 +20,7 @@ usage() {
|
|||
echo " --target=<target> Set the transpilation target"
|
||||
echo " --newlines=on/off Enable or disable new lines"
|
||||
echo " -p or --printresult Enable printing the transpilation result to stdout"
|
||||
echo " -t or --text Transpile text provided after this argument (implies -p)"
|
||||
printf "\n"
|
||||
fi
|
||||
}
|
||||
|
@ -29,57 +30,51 @@ usageExit() {
|
|||
exit 0
|
||||
}; helpExit() { usage true; exit 0; }
|
||||
|
||||
args="";
|
||||
arguments="";
|
||||
run=false;
|
||||
|
||||
newArgs() {
|
||||
if [ "$args" = "" ]; then
|
||||
args="$args$1"
|
||||
else
|
||||
args="$args $1"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$#" != 0 ]; then
|
||||
if [ "$#" = 1 ]; then
|
||||
case "$1" in
|
||||
-h | --help )
|
||||
helpExit ;;
|
||||
--version | --buildInfo )
|
||||
run=true; args="$1" ;;
|
||||
*.ybcon )
|
||||
newArgs "$1"; run=true ;;
|
||||
--version | --buildInfo | *.ybcon )
|
||||
run=true ;;
|
||||
* )
|
||||
usageExit ;;
|
||||
esac
|
||||
else
|
||||
text_provided=false
|
||||
for it in "$@"
|
||||
do
|
||||
case "$it" in
|
||||
-p | --printresult | --parallel | --target=* | --newlines=on | --newlines=off )
|
||||
if test "${args#*$it}" = "$args"; then
|
||||
newArgs "$it"
|
||||
if [ "${arguments#*$it}" != "$arguments" ] && [ "${arguments%*-$it}" = "${arguments#*-$it}" ]; then
|
||||
usageExit
|
||||
fi ;;
|
||||
-t | --text )
|
||||
text_provided=true ;;
|
||||
* )
|
||||
if [ $text_provided = true ] || [ "${it%*.ybcon}" != "$it" ]; then
|
||||
run=true
|
||||
else
|
||||
usageExit
|
||||
fi ;;
|
||||
*.ybcon )
|
||||
newArgs "$it"
|
||||
run=true ;;
|
||||
* )
|
||||
usageExit ;;
|
||||
esac
|
||||
if [ "$arguments" != "" ]; then
|
||||
arguments="$arguments "
|
||||
fi
|
||||
arguments="$arguments$it"
|
||||
done
|
||||
if test "${args#*".ybcon"}" = "$args" ; then usageExit; fi
|
||||
if test $run = false ; then usageExit; fi
|
||||
fi
|
||||
else
|
||||
usage false
|
||||
fi
|
||||
|
||||
runIt() { eval "$1" " $args"; }
|
||||
|
||||
if [ "$run" = true ]; then
|
||||
if [ -f "$defaultBinLocation" ]; then
|
||||
runIt "$defaultBinLocation"
|
||||
exec "$defaultBinLocation" "$@"
|
||||
else
|
||||
echo "Yerbacon executable not found at $defaultBinLocation"
|
||||
fi
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <system_error>
|
||||
#include "../headers/Yerbacon.hpp"
|
||||
using namespace std;
|
||||
|
||||
string getFileContent(const string& file)
|
||||
|
|
|
@ -9,20 +9,26 @@ tok::type getIdentifierCharType(const char& Char) {
|
|||
else return UNEXPECTED;
|
||||
}
|
||||
|
||||
vector<tok> lex(const string& in)
|
||||
vector<tok> lex(const string_view& in)
|
||||
{
|
||||
vector<tok> resVal;
|
||||
unsigned long lineNumber = 1;
|
||||
for (unsigned int i = 0; i <= in.size(); ++i) {
|
||||
for (unsigned int i = 0; i < in.size(); ++i) {
|
||||
const char& current = in[i];
|
||||
|
||||
switch (current) {
|
||||
case LPAR: case LBRACE: case LBRACKET: {
|
||||
const auto reversedCharacter = static_cast<char>(tok::inverseLCharacter(current));
|
||||
if (find_corresponding(in.cbegin() + i + 1, in.cend(), current, reversedCharacter) != in.cend()) {
|
||||
goto insertToken;
|
||||
} else throw tok::LexerException(string("Missing \"") + reversedCharacter + "\" character", lineNumber);
|
||||
}
|
||||
case DIVIDE: if (in[i + 1] == current) i += 2; else goto insertToken;
|
||||
case TAG: {
|
||||
if (current == TAG && in[i + 1] == DEFINE) { // See the IDENTIFIER case in Parser.hpp
|
||||
goto insertToken;
|
||||
} else {
|
||||
while (not (in[i + 1] == EOF_ || in[i + 1] == '\n')) {
|
||||
while (not (i == in.size() || in[i + 1] == '\n')) {
|
||||
++i;
|
||||
}
|
||||
break;
|
||||
|
@ -42,13 +48,12 @@ vector<tok> lex(const string& in)
|
|||
break;
|
||||
} else goto insertToken;
|
||||
}
|
||||
[[unlikely]] case EOF_: --lineNumber;
|
||||
case DEFINE: case LPAR: case RPAR:
|
||||
case LBRACE: case RBRACE: case LBRACKET: case RBRACKET:
|
||||
case DEFINE: case RPAR: case COMMA:
|
||||
case RBRACE: case RBRACKET: case SEMICOLON:
|
||||
case PLUS: case HYPHEN: case LCOMP: case RCOMP:
|
||||
case DOT: case DOLLAR_SIGN: case SQUOTE:
|
||||
insertToken: resVal.emplace_back(static_cast<tok::type>(current), lineNumber);
|
||||
[[likely]] case ' ': case '\t': case '\r': case ';': break;
|
||||
[[likely]] case ' ': case '\t': case '\r': break;
|
||||
[[likely]] case '\n': ++lineNumber; break;
|
||||
default: {
|
||||
const tok::type type = getIdentifierCharType(current);
|
||||
|
|
|
@ -22,9 +22,6 @@
|
|||
#if not defined(__cpp_lib_concepts) || not defined(__cpp_lib_integer_comparison_functions)
|
||||
#error "The current standard library is incomplete"
|
||||
#endif
|
||||
#ifndef __cpp_using_enum
|
||||
#error "The "using enum" syntax is not supported by this compiler"
|
||||
#endif
|
||||
|
||||
#define token_expansion(X) #X
|
||||
#define make_string(X) token_expansion(X)
|
||||
|
@ -44,14 +41,14 @@ namespace Yerbacon {
|
|||
" Compiler: " YBCON_COMPILER "\n"
|
||||
" C++ standard: " make_string(__cplusplus);
|
||||
}
|
||||
static void exit(const std::initializer_list<const char*> reason) {
|
||||
static void fail(const std::initializer_list<const char*> reason) {
|
||||
std::for_each(reason.begin(), reason.end(), [](const char* current_string){
|
||||
std::cout << current_string;
|
||||
});
|
||||
std::cout << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
inline void exit(const char* reason) { exit({reason}); }
|
||||
inline void fail(const char* reason) { fail({reason}); }
|
||||
class Exception: public std::exception {
|
||||
std::string exceptionCause;
|
||||
public:
|
||||
|
@ -65,10 +62,6 @@ namespace Yerbacon {
|
|||
};
|
||||
}
|
||||
|
||||
#include <memory>
|
||||
template<typename T, typename U>
|
||||
constexpr T& pointerAs(const std::unique_ptr<U>& ptr) { return reinterpret_cast<T&>(*ptr); }
|
||||
|
||||
#undef YBCON_VERSION
|
||||
#undef YBCON_FLAGS
|
||||
#undef YBCON_COMPILER
|
||||
|
|
|
@ -5,34 +5,44 @@
|
|||
#include "Yerbacon.hpp"
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <concepts>
|
||||
|
||||
struct tok {
|
||||
typedef Yerbacon::Exception LexerException;
|
||||
enum type: const unsigned short {
|
||||
UNEXPECTED = std::numeric_limits<unsigned char>::max() + 1, IDENTIFIER, NUMBER, ALPHACHAR,
|
||||
EOF_ = '\0', DEFINE = '=', TAG = '#', DOLLAR_SIGN = '$', DOT = '.',
|
||||
DEFINE = '=', TAG = '#', DOLLAR_SIGN = '$', DOT = '.', COMMA = ',', SEMICOLON = ';',
|
||||
LPAR = '(', LBRACE = '{', LBRACKET = '[', RPAR = ')',
|
||||
RBRACE = '}', RBRACKET = ']',
|
||||
PLUS = '+', HYPHEN = '-', DIVIDE = '/',
|
||||
LCOMP = '>', RCOMP = '<',
|
||||
SQUOTE = '\'', ASTERISK = '*', STRING = '"',
|
||||
};
|
||||
const type toktype;
|
||||
const std::string toktext;
|
||||
const unsigned long line;
|
||||
tok& operator=(const tok& other) {
|
||||
if (this != &other) {
|
||||
const_cast<type&>(toktype) = other.toktype;
|
||||
const_cast<std::string&>(toktext) = other.toktext;
|
||||
const_cast<unsigned long&>(line) = other.line;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
tok(): toktype(UNEXPECTED), toktext(), line(0) {};
|
||||
tok(const type& Type, std::string_view Text, const decltype(line)& line = 0): toktype(Type), toktext(Text), line(line) {}
|
||||
explicit tok(const type& Type, const decltype(line)& line = 0): tok(Type, std::string(1, Type), line) {};
|
||||
static auto inverseLCharacter(const unsigned char& character) {
|
||||
return static_cast<unsigned char>(((character + 2) - (character == tok::LPAR)));
|
||||
};
|
||||
type toktype;
|
||||
std::string toktext;
|
||||
unsigned long line;
|
||||
tok() = delete;
|
||||
tok(const type& Type, std::string_view Text, const decltype(line)& line = 1): toktype(Type), toktext(Text), line(line) {}
|
||||
explicit tok(const type& Type, const decltype(line)& line = 1): tok(Type, std::string(1, Type), line) {};
|
||||
operator char() const { return static_cast<char>(toktype); }
|
||||
friend std::ostream& operator<<(std::ostream& output, const tok& it) { return output << it.toktext; }
|
||||
};
|
||||
std::vector<tok> lex(const std::string& in);
|
||||
|
||||
auto find_corresponding(std::input_iterator auto begin, std::input_iterator auto end, const unsigned char open, const unsigned char close) {
|
||||
unsigned short occurrences = 1;
|
||||
return std::find_if(begin, end, [&open, &close, &occurrences](const char& it){
|
||||
if (it == open) {
|
||||
++occurrences;
|
||||
} else if (it == close) {
|
||||
return --occurrences == 0;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<tok> lex(const std::string_view& in);
|
||||
|
||||
#endif //YERBACON_TEST_H
|
|
@ -6,6 +6,6 @@ void outputFileContent(const string& file, string_view content);
|
|||
|
||||
#include "lex.hpp"
|
||||
#include "parsing/Parser.hpp"
|
||||
inline auto parseString(const string& toParse) { return Parser::parseVector(lex(toParse)); }
|
||||
inline auto parseString(const string_view& toParse) { return Parser::parse(lex(toParse)); }
|
||||
|
||||
#endif //YERBACON_MISC_HPP
|
|
@ -9,6 +9,7 @@
|
|||
#include <memory>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include "ReservedIdentifiers.hpp"
|
||||
using namespace std;
|
||||
|
||||
#include "../lex.hpp"
|
||||
|
@ -19,66 +20,63 @@ struct ParseComponent {
|
|||
[[nodiscard]] inline const type_info& getId() const { return typeid(*this); }
|
||||
virtual ~ParseComponent() = default;
|
||||
};
|
||||
|
||||
namespace StandardComponents {
|
||||
struct NamedIdentifier: ParseComponent {
|
||||
const string name;
|
||||
explicit NamedIdentifier(string_view nameText): name(nameText) {}
|
||||
};
|
||||
struct Define: NamedIdentifier {
|
||||
const bool final;
|
||||
explicit Define(const bool& isFinal, string_view nameText): NamedIdentifier(nameText), final(isFinal) {}
|
||||
explicit Define(string_view nameText): Define(false, nameText) {}
|
||||
};
|
||||
struct Reference: NamedIdentifier {
|
||||
using NamedIdentifier::NamedIdentifier;
|
||||
};
|
||||
|
||||
struct Class: NamedIdentifier {
|
||||
using NamedIdentifier::NamedIdentifier;
|
||||
};
|
||||
namespace types {
|
||||
struct String: ParseComponent {
|
||||
const string content;
|
||||
explicit String(const char* string): content(string) {}
|
||||
};
|
||||
template<bool disallow_reserved>
|
||||
struct NamedIdentifier: virtual ParseComponent {
|
||||
struct identifier_reserved_exception: exception {};
|
||||
const string name;
|
||||
explicit NamedIdentifier(const string_view nameText): name(nameText) {
|
||||
if (disallow_reserved and reserved(name)) {
|
||||
throw identifier_reserved_exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
NamedIdentifier() = delete;
|
||||
};
|
||||
|
||||
typedef unique_ptr<ParseComponent> component_ptr;
|
||||
|
||||
#define IS_PARSECOMPONENT IS(ParseComponent)
|
||||
class ParseTree {
|
||||
mutable vector<unique_ptr<ParseComponent>> subComponents;
|
||||
#define IS_IDENTIFIER enable_if_t<is_base_of_v<NamedIdentifier<false>, T> or is_base_of_v<NamedIdentifier<true>, T>, T>
|
||||
class ParseTree: public virtual ParseComponent {
|
||||
mutable vector<component_ptr> subComponents;
|
||||
using array_type = decltype(subComponents);
|
||||
using iterator = array_type::iterator;
|
||||
using constant_iterator = array_type::const_iterator;
|
||||
protected:
|
||||
IS_PARSECOMPONENT
|
||||
void addComponent(const T& component) const
|
||||
{ subComponents.emplace_back(new T(component)); };
|
||||
void addComponent(const T& component) const {
|
||||
if constexpr(is_copy_constructible_v<T>) {
|
||||
subComponents.emplace_back(new T(component));
|
||||
} else {
|
||||
static_assert(is_move_constructible_v<T>, "T is not copy-constructible or move-constructible");
|
||||
subComponents.emplace_back(new T(move(const_cast<T&>(component))));
|
||||
}
|
||||
};
|
||||
IS_PARSECOMPONENT
|
||||
void addAllComponents(
|
||||
const initializer_list<T>& components
|
||||
) const {
|
||||
subComponents.reserve(components.size());
|
||||
subComponents.reserve(subComponents.size() + components.size());
|
||||
for (const T& current: components) addComponent<T>(current);
|
||||
}
|
||||
public:
|
||||
inline iterator begin() const noexcept { return subComponents.begin(); }
|
||||
inline size_t size() const { return subComponents.size(); }
|
||||
inline bool empty() const { return size() == 0; }
|
||||
inline iterator begin() noexcept { return subComponents.begin(); }
|
||||
inline constant_iterator cbegin() const noexcept { return subComponents.cbegin(); }
|
||||
inline iterator end() const noexcept { return subComponents.end(); }
|
||||
inline iterator end() noexcept { return subComponents.end(); }
|
||||
inline constant_iterator cend() const noexcept { return subComponents.cend(); }
|
||||
IS_PARSECOMPONENT
|
||||
vector<T*> findById() const {
|
||||
vector<T*> filteredComponents;
|
||||
for_each(cbegin(), cend(), [&filteredComponents](const unique_ptr<ParseComponent>& it) {
|
||||
for_each(cbegin(), cend(), [&filteredComponents](const component_ptr& it) {
|
||||
if (it->getId() == typeid(T)) {
|
||||
filteredComponents.push_back(reinterpret_cast<T*>(it.get()));
|
||||
filteredComponents.push_back(dynamic_cast<T*>(to_address(it)));
|
||||
}
|
||||
});
|
||||
return filteredComponents;
|
||||
}
|
||||
IS(StandardComponents::NamedIdentifier)
|
||||
optional<reference_wrapper<T>> findReferenceByName(const string& name) const {
|
||||
template<typename T>
|
||||
optional<reference_wrapper<IS_IDENTIFIER>> findReferenceByName(const string& name) const {
|
||||
const vector<T*> identifiers = findById<T>();
|
||||
for (T* identifier: identifiers) {
|
||||
if (identifier->getId() == typeid(T) && identifier->name == name) {
|
||||
|
@ -87,17 +85,61 @@ public:
|
|||
}
|
||||
return optional<reference_wrapper<T>>();
|
||||
};
|
||||
inline decltype(*subComponents.data()) operator[](const unsigned int& index) const { return subComponents[index]; }
|
||||
IS(StandardComponents::NamedIdentifier)
|
||||
inline auto operator[](const string& key) const { return findReferenceByName<T>(key); }
|
||||
inline size_t getCompCount() const { return subComponents.size(); }
|
||||
inline component_ptr& operator[](const unsigned int& index) const { return subComponents[index]; }
|
||||
inline component_ptr& at(const unsigned int& index) const { return subComponents.at(index); }
|
||||
template<typename T>
|
||||
inline auto operator[](const string& key) const { return findReferenceByName<IS_IDENTIFIER>(key); }
|
||||
IS_PARSECOMPONENT inline void add(const T& component) { addComponent<T>(component); }
|
||||
IS_PARSECOMPONENT inline void addAll(const initializer_list<T>& components) { addAllComponents<T>(components); }
|
||||
IS_PARSECOMPONENT inline ParseTree& operator<<(const T& component) { add(component); return *this; }
|
||||
ParseTree& operator<<(const string&);
|
||||
ParseTree(): subComponents() {};
|
||||
IS_PARSECOMPONENT constexpr explicit ParseTree(const T& element): ParseTree() { addComponent(element); }
|
||||
IS_PARSECOMPONENT constexpr ParseTree(const initializer_list<T>& elements): ParseTree() { addAllComponents(elements); }
|
||||
IS_PARSECOMPONENT inline explicit ParseTree(const T& element): ParseTree() { addComponent(element); }
|
||||
IS_PARSECOMPONENT inline ParseTree(const initializer_list<T>& elements): ParseTree() { addAllComponents(elements); }
|
||||
ParseTree& operator=(ParseTree&& parseTree) noexcept { subComponents = move(parseTree.subComponents); return *this; }
|
||||
ParseTree(ParseTree&& parseTree) noexcept: subComponents(move(parseTree.subComponents)) {}
|
||||
ParseTree(const ParseTree& parseTree) = delete;
|
||||
};
|
||||
#undef IS_PARSECOMPONENT
|
||||
|
||||
namespace StandardComponents {
|
||||
struct Define: NamedIdentifier<true> {
|
||||
const bool final;
|
||||
ParseTree content;
|
||||
explicit Define(const bool& isFinal, string_view nameText, ParseTree&& content): NamedIdentifier(nameText), final(isFinal), content(move(content)) {}
|
||||
explicit Define(string_view nameText, ParseTree&& content): Define(false, nameText, move(content)) {}
|
||||
Define(Define&& define) noexcept: Define(define.final, define.name, move(define.content)) {}
|
||||
};
|
||||
struct Reference: NamedIdentifier<false> {
|
||||
using NamedIdentifier::NamedIdentifier;
|
||||
};
|
||||
namespace types {
|
||||
struct Integer: ParseComponent {
|
||||
typedef uint_fast8_t precision_type;
|
||||
const long double value;
|
||||
const precision_type precision;
|
||||
Integer(const long double& value, const precision_type& precision): value(value), precision(precision) {}
|
||||
};
|
||||
struct String: ParseComponent {
|
||||
const string content;
|
||||
explicit String(string content_string): content(move(content_string)) {}
|
||||
};
|
||||
}
|
||||
struct Call: ParseTree, Reference {
|
||||
using Reference::Reference;
|
||||
};
|
||||
|
||||
struct Function: NamedIdentifier<true>, ParseTree {
|
||||
ParseTree parameters;
|
||||
using NamedIdentifier::NamedIdentifier;
|
||||
};
|
||||
struct Class: NamedIdentifier<true>, ParseTree {
|
||||
using NamedIdentifier::NamedIdentifier;
|
||||
};
|
||||
}
|
||||
|
||||
ParseTree& ParseTree::operator<<(const string& text) {
|
||||
return *this << StandardComponents::types::String(text);
|
||||
}
|
||||
|
||||
#endif //YERBACON_PARSECOMPONENTS_HPP
|
|
@ -5,6 +5,10 @@
|
|||
#include "ParseComponents.hpp"
|
||||
#include "../Yerbacon.hpp"
|
||||
#include <concepts>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
#include "ReservedIdentifiers.hpp"
|
||||
|
||||
namespace Parser {
|
||||
typedef Yerbacon::Exception ParsingException;
|
||||
|
@ -15,15 +19,38 @@ namespace Parser {
|
|||
const tok& token, const string& text,
|
||||
const bool& quoteTokenText = false
|
||||
) { error(token, text, token.line, quoteTokenText); }
|
||||
ParseTree parseVector(const vector<tok>& lexed) {
|
||||
ParseTree parseTree;
|
||||
|
||||
void filter_comma_list(vector<tok>& tokens) {
|
||||
if (tokens.size() >= 2) {
|
||||
for (auto iterator = tokens.begin(); iterator->toktype != tok::COMMA && iterator < tokens.end() - 1; ++iterator) {
|
||||
const auto nextIterator = iterator + 1;
|
||||
if (nextIterator->toktype == tok::COMMA) {
|
||||
tokens.erase(nextIterator);
|
||||
} else if (nextIterator->toktype != tok::DOT && iterator->toktype != tok::DOT) {
|
||||
throw ParsingException("Missing comma after \"" + iterator->toktext + '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vector<tok> filter_comma_list(input_iterator auto begin, input_iterator auto end) {
|
||||
vector tokens(begin, end);
|
||||
filter_comma_list(tokens);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
IS(ParseTree)
|
||||
inline T parse(const input_iterator auto&, const input_iterator auto&);
|
||||
|
||||
IS(ParseTree)
|
||||
T parse(const span<const tok>&& lexed) {
|
||||
T parseTree;
|
||||
using namespace StandardComponents;
|
||||
using enum tok::type;
|
||||
unsigned int i = 0;
|
||||
const auto nextAre = [&i, &lexed]<convertible_to<tok::type> T>(const initializer_list<T>& nextValues) -> bool {
|
||||
const auto nextAre = [&i, &lexed]<convertible_to<tok::type> Y>(const initializer_list<Y>& nextValues) -> bool {
|
||||
unsigned int j = 1;
|
||||
for (const T& nextValue: nextValues) {
|
||||
if (!cmp_greater(lexed.size() - i, nextValues.size()) || lexed[i + j].toktype != nextValue) {
|
||||
for (const Y& nextValue: nextValues) {
|
||||
if (cmp_less(lexed.size() - i, nextValues.size()) || lexed[i + j].toktype != nextValue) {
|
||||
return false;
|
||||
}
|
||||
++j;
|
||||
|
@ -31,39 +58,118 @@ namespace Parser {
|
|||
return true;
|
||||
};
|
||||
for (;i < lexed.size(); ++i) {
|
||||
const tok& current = lexed[i], next = (current.toktype != EOF_) ? lexed[i + 1] : current;
|
||||
const bool hasNext = (i + 1) < lexed.size();
|
||||
const tok& current = lexed[i], next = hasNext ? lexed[i + 1] : tok(UNEXPECTED, current.line);
|
||||
|
||||
switch (current.toktype) {
|
||||
case STRING: parseTree << types::String(current.toktext.data()); break;
|
||||
case IDENTIFIER: {
|
||||
if (current.toktext == "class" || current.toktext == "structure") {
|
||||
if (next.toktype == IDENTIFIER) {
|
||||
parseTree << Class(next.toktext); ++i;
|
||||
} else {
|
||||
const bool isNotBlank = (not (next.toktext.empty() or next.toktype == tok::EOF_));
|
||||
parsingError(next, isNotBlank ? " is not a valid class identifier" : "A class identifier is required", isNotBlank);
|
||||
try {
|
||||
switch (current.toktype) {
|
||||
case NUMBER: {
|
||||
long double v = stoul(current.toktext);
|
||||
if (i != 0 && lexed[i - 1].toktype == HYPHEN) v = -v;
|
||||
types::Integer::precision_type p = 0;
|
||||
if (nextAre({DOT, NUMBER})) {
|
||||
i += 2;
|
||||
const string& right = lexed[i].toktext;
|
||||
p = min(static_cast<int>(right.size()), numeric_limits<long double>::digits10);
|
||||
v += copysign(stold(right.substr(0, p)) / powl(deca::num, p), v);
|
||||
}
|
||||
} else {
|
||||
bool isFinalDefine = nextAre({TAG, DEFINE});
|
||||
if (isFinalDefine || next.toktype == DEFINE) {
|
||||
const optional previousDefinition = parseTree.findReferenceByName<Define>(current.toktext);
|
||||
if (previousDefinition.has_value()) {
|
||||
if (previousDefinition.value().get().final || isFinalDefine) {
|
||||
parsingError(current, previousDefinition->get().final ? " cannot be redefined as it is final" : " cannot be made final after it has been declared", true);
|
||||
}
|
||||
parseTree << types::Integer(v, p);
|
||||
break;
|
||||
}
|
||||
case STRING: parseTree << current.toktext; break;
|
||||
case IDENTIFIER: {
|
||||
using enum ReservedIdentifier;
|
||||
if (current.toktext == ID(CLASS) || current.toktext == ID(STRUCT)) {
|
||||
if (next.toktype == IDENTIFIER) {
|
||||
parseTree << Class(next.toktext); ++i;
|
||||
} else {
|
||||
parsingError(next, hasNext ? " is not a valid class identifier" : "A class identifier is required", hasNext);
|
||||
}
|
||||
parseTree << Define(isFinalDefine, current.toktext);
|
||||
i += 1 + isFinalDefine;
|
||||
} else {
|
||||
parseTree << Reference(current.toktext);
|
||||
unsigned int parametersDistance = 0;
|
||||
if (next.toktype == LPAR) {
|
||||
const auto closing = find_if(lexed.begin() + i, lexed.end(), [](const tok& token){ return token.toktype == RPAR; });
|
||||
parametersDistance = distance(lexed.begin() + i, closing);
|
||||
i += parametersDistance;
|
||||
}
|
||||
if (nextAre({LCOMP, LCOMP, LBRACE})) {
|
||||
Function function(current.toktext);
|
||||
if (parametersDistance > 2)
|
||||
function.parameters = parse(filter_comma_list(lexed.begin() + ((i + 2) - parametersDistance), lexed.begin() + i));
|
||||
parseTree << function;
|
||||
i += 2;
|
||||
break;
|
||||
} else i -= parametersDistance;
|
||||
|
||||
bool isFinalDefine = nextAre({TAG, DEFINE});
|
||||
if (isFinalDefine || next.toktype == DEFINE) {
|
||||
const optional previousDefinition = parseTree.template findReferenceByName<Define>(current.toktext);
|
||||
if (previousDefinition.has_value() && (previousDefinition.value().get().final || isFinalDefine))
|
||||
parsingError(current, previousDefinition->get().final ? " cannot be redefined as it is final" : " cannot be made final after it has been declared", true);
|
||||
const unsigned increment = 2 + isFinalDefine;
|
||||
const auto beginning = lexed.begin() + i + increment;
|
||||
const auto end = find_if(beginning, lexed.end(), [¤t](const tok& it){
|
||||
return it.toktype == SEMICOLON || it.line != current.line;
|
||||
});
|
||||
parseTree << Define(isFinalDefine, current.toktext, parse(beginning, end));
|
||||
i += 1 + isFinalDefine + distance(beginning, end);
|
||||
} else {
|
||||
const bool method = nextAre({DOT, IDENTIFIER, LPAR});
|
||||
const bool property = not method && nextAre({DOT, IDENTIFIER});
|
||||
const string name = property or method ? current.toktext + '.' + lexed[i + 2].toktext : current.toktext;
|
||||
if (method or next.toktype == LPAR) {
|
||||
parseTree << Call(name);
|
||||
} else if (property) {
|
||||
parseTree << Reference(name);
|
||||
}
|
||||
if (property or method) i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
case SEMICOLON: break;
|
||||
case LPAR: case LBRACE: case LBRACKET: {
|
||||
const auto closingCharacter = find_corresponding(lexed.begin() + i + 1, lexed.end(), current.toktype, tok::inverseLCharacter(current.toktype));
|
||||
vector<tok> subTokens(lexed.begin() + i + 1, closingCharacter);
|
||||
if (current.toktype == LPAR || current.toktype == LBRACKET) filter_comma_list(subTokens);
|
||||
if (not parseTree.empty()) {
|
||||
try {
|
||||
auto& previous = dynamic_cast<ParseTree&>(*parseTree.at(parseTree.size() - 1));
|
||||
if (find_if(reverse_iterator(lexed.begin() + i), lexed.rend(), [](const tok& token){
|
||||
return token.toktype != SEMICOLON;
|
||||
})->toktype != *closingCharacter) {
|
||||
previous = parse(subTokens);
|
||||
i = distance(lexed.begin(), closingCharacter);
|
||||
break;
|
||||
}
|
||||
} catch (const out_of_range&) {} catch (const bad_cast&) {}
|
||||
}
|
||||
}
|
||||
default: parsingError(current, " \u27F5 Unexpected character", true);
|
||||
}
|
||||
} catch (const NamedIdentifier<true>::identifier_reserved_exception&) {
|
||||
parsingError(current, " is a reserved identifier", true);
|
||||
}
|
||||
if (not parseTree.empty()) {
|
||||
const auto& last = parseTree.cend() - 1;
|
||||
const type_info& lastId = last->get()->getId();
|
||||
const auto* last_identifier = dynamic_cast<NamedIdentifier<true>*>(last->get());
|
||||
if (last_identifier != nullptr) {
|
||||
if (lastId != typeid(Define) and
|
||||
any_of(parseTree.cbegin(), last, [&last_identifier](const component_ptr& pointer){
|
||||
try {
|
||||
return dynamic_cast<NamedIdentifier<true>&>(*pointer).name == last_identifier->name;
|
||||
} catch (const bad_cast&) {
|
||||
return false;
|
||||
}
|
||||
}))
|
||||
{ parsingError(current, " has already been defined previously", true); }
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return parseTree;
|
||||
}
|
||||
template<derived_from<ParseTree> T>
|
||||
inline T parse(const input_iterator auto& begin, const input_iterator auto& end) { return parse<T>(span(begin, end)); }
|
||||
}
|
||||
|
||||
#endif //YERBACON_PARSER_HPP
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef YERBACON_RESERVEDIDENTIFIERS_HPP
|
||||
#define YERBACON_RESERVEDIDENTIFIERS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include "../Yerbacon.hpp"
|
||||
#include <string_view>
|
||||
|
||||
static constinit const array identifiers {
|
||||
"class", "structure", "print", "print_line"
|
||||
};
|
||||
|
||||
enum class ReservedIdentifier: size_t {
|
||||
CLASS, STRUCT, PRINT_FUNCTION, PRINT_LINE_FUNCTION
|
||||
};
|
||||
|
||||
inline const char* ID(const ReservedIdentifier& identifier) {
|
||||
return identifiers.at(reinterpret_cast<const size_t&>(identifier));
|
||||
}
|
||||
|
||||
bool reserved(const string_view words) {
|
||||
return find(identifiers.cbegin(), identifiers.cend(), words) != identifiers.cend();
|
||||
}
|
||||
|
||||
#endif //YERBACON_RESERVEDIDENTIFIERS_HPP
|
|
@ -9,6 +9,7 @@
|
|||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
#include <typeindex>
|
||||
#include <limits>
|
||||
#ifdef __GNUC__
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
@ -19,7 +20,7 @@ class Target {
|
|||
constexpr static const char* const interpolationString = "${";
|
||||
constexpr static const char* const interpolationCloseString = "}";
|
||||
protected:
|
||||
virtual constexpr bool supportsOneLine() { return true; };
|
||||
inline bool supportsOneLine() { return uniqueLineSeparator().has_value(); };
|
||||
std::stringstream output;
|
||||
inline void stringInterpolation(const char* multiline, const string& view) { stringInterpolation(view, multiline, multiline); }
|
||||
void stringInterpolation(string view, const char* openMultiline = "", const char* closeMultiline = "", const char* concatenationCharacters = "+") {
|
||||
|
@ -65,84 +66,112 @@ protected:
|
|||
} else output << openCharacters << view << closeCharacters;
|
||||
}
|
||||
typedef function<void (const ParseTree& parsedTree, unsigned int& index)> task;
|
||||
#define make_task_base(type, captures, function_body) make_pair(type_index(typeid(type)), [captures](const ParseTree& parsedTree, unsigned int& index) { const type& parseComponent = pointerAs<type>(parsedTree[index]); function_body })
|
||||
#define make_task(T, F) make_task_base(T, this, F)
|
||||
#define make_task_base(start, type, captures, function_body) make_pair(type_index(typeid(type)), [captures](const ParseTree& parsedTree, unsigned int& index) { start; function_body })
|
||||
#define make_task_base_R(T, C, F) make_task_base(const T& parseComponent = reinterpret_cast<T&>(*parsedTree[index]), T, C, F)
|
||||
#define make_task(T, F) make_task_base_R(T, this, F)
|
||||
#define make_task_noR(T, F) make_task_base(,T, this, F)
|
||||
#define make_nonlocal_task(T, F) make_task_base_R(T, , F)
|
||||
typedef unordered_map<type_index, task> unordered_task_map;
|
||||
typedef pair<const char*, const char*> print_functions_pair;
|
||||
virtual unordered_task_map getTaskMap() = 0;
|
||||
public:
|
||||
const unordered_task_map& getTaskMapInstance() {
|
||||
static unordered_task_map staticMap = getTaskMap();
|
||||
// Default / Shared tasks:
|
||||
staticMap.merge(unordered_task_map({
|
||||
make_task(StandardComponents::Reference,
|
||||
output << parseComponent.name;
|
||||
)
|
||||
}));
|
||||
return staticMap;
|
||||
};
|
||||
static shared_ptr<Target> forName(string_view name, bool newLines);
|
||||
const bool newLines;
|
||||
string transpileWithTree(const ParseTree& tree) {
|
||||
const unordered_task_map& taskMap = getTaskMapInstance();
|
||||
if (not newLines && !supportsOneLine()) {
|
||||
throw Yerbacon::Exception("--newlines=off is not supported by the current target");
|
||||
}
|
||||
output.str(string());
|
||||
for (unsigned int i = 0; i < tree.getCompCount(); ++i) {
|
||||
const unique_ptr<ParseComponent>& component = tree[i];
|
||||
const type_info& id = component->getId();
|
||||
try {
|
||||
const task& currentTask = taskMap.at(id);
|
||||
currentTask(tree, i);
|
||||
} catch (const out_of_range&) {
|
||||
throw Yerbacon::Exception(string(
|
||||
#ifndef __GNUC__
|
||||
id.name()
|
||||
#else
|
||||
abi::__cxa_demangle(id.name(), nullptr, nullptr, nullptr)
|
||||
#endif
|
||||
) += " is not supported by the current target");
|
||||
virtual print_functions_pair printFunctions() = 0;
|
||||
IS(ParseTree)
|
||||
void transpileTree(const T& parseTree, const unsigned short& indentationLevel = 0, const function<void(decltype(parseTree.cbegin())&)>&& postInsertionFunction = [](auto&){}) {
|
||||
if (not parseTree.empty()) {
|
||||
const auto added_size = indentationLevel * strlen(indentation);
|
||||
if (newLines) {
|
||||
separator.reserve(separator.size() + added_size);
|
||||
for (unsigned short level = 0; level < indentationLevel; ++level) {
|
||||
separator.append(indentation);
|
||||
}
|
||||
}
|
||||
unsigned int subIndex = 0;
|
||||
for (auto pointer_iterator = parseTree.cbegin(); pointer_iterator < parseTree.cend(); ++pointer_iterator) {
|
||||
const type_info& id = pointer_iterator->get()->getId();
|
||||
try {
|
||||
getTaskMapInstance().at(id)(parseTree, subIndex); ++subIndex;
|
||||
} catch (const out_of_range&) {
|
||||
throw Yerbacon::Exception(string(
|
||||
#ifndef __GNUC__
|
||||
id.name()
|
||||
#else
|
||||
abi::__cxa_demangle(id.name(), nullptr, nullptr, nullptr)
|
||||
#endif
|
||||
) += " is not supported by the current target");
|
||||
}
|
||||
postInsertionFunction(pointer_iterator);
|
||||
}
|
||||
if (newLines) separator.erase(separator.size() - added_size);
|
||||
}
|
||||
}
|
||||
IS(ParseTree)
|
||||
void separate_transpileTree(const T& parseTree, const unsigned short& indentationLevel = 0) {
|
||||
transpileTree(parseTree, indentationLevel, [this, &parseTree](const auto& iterator){
|
||||
if ((newLines || iterator + 1 == parseTree.cend()) && use_uniqueLineSeparator()) output << uniqueLineSeparator().value();
|
||||
if (iterator + 1 != parseTree.cend()) output << separator;
|
||||
});
|
||||
}
|
||||
IS(ParseTree)
|
||||
void separate_transpileTree(const T& parseTree, const string_view separation_characters) {
|
||||
transpileTree(parseTree, 0, [this, &parseTree, &separation_characters](const auto& iterator){
|
||||
if (iterator + 1 != parseTree.cend()) { output << separation_characters; }
|
||||
});
|
||||
}
|
||||
typedef optional<const char*> optional_string;
|
||||
virtual optional_string uniqueLineSeparator() { return ";"; };
|
||||
virtual bool use_uniqueLineSeparator() { return false; };
|
||||
const bool newLines;
|
||||
string separator;
|
||||
static constexpr const char* indentation = " ";
|
||||
public:
|
||||
unordered_task_map getTaskMapInstance() {
|
||||
unordered_task_map fullMap = getTaskMap();
|
||||
// Default / Shared tasks:
|
||||
fullMap.merge(unordered_task_map({
|
||||
make_task(ParseTree, transpileTree(parseComponent);),
|
||||
make_task(StandardComponents::Reference, output << parseComponent.name;),
|
||||
make_task(StandardComponents::Call,
|
||||
const auto print_functions = printFunctions();
|
||||
using enum ReservedIdentifier;
|
||||
output << ((parseComponent.name == ID(PRINT_FUNCTION)) ? print_functions.first : (parseComponent.name == ID(PRINT_LINE_FUNCTION)) ? print_functions.second : parseComponent.name) << '(';
|
||||
separate_transpileTree(parseComponent, ", ");
|
||||
output << ')';
|
||||
),
|
||||
make_task(StandardComponents::types::Integer, output << setprecision(parseComponent.precision) << fixed << parseComponent.value;)
|
||||
}));
|
||||
return fullMap;
|
||||
};
|
||||
static unique_ptr<Target> forName(string_view name, bool newLines);
|
||||
string transpileWithTree(const ParseTree& tree) {
|
||||
separator = newLines ? "\n" : (supportsOneLine() ? uniqueLineSeparator().value() : throw Yerbacon::Exception("--newlines=off is not supported by the current target"));
|
||||
output.str(string());
|
||||
separate_transpileTree(tree);
|
||||
return output.str() + '\n';
|
||||
};
|
||||
explicit Target(const bool newLines): newLines(newLines) {};
|
||||
explicit Target(const bool& newLines): output(), newLines(newLines), separator() {};
|
||||
Target() = delete;
|
||||
virtual ~Target() = default;
|
||||
};
|
||||
|
||||
#include "implementations/Lua.hpp"
|
||||
#include "implementations/Js.hpp"
|
||||
#include "implementations/Py.hpp"
|
||||
#include "implementations/GodotScript.hpp"
|
||||
|
||||
enum LANGUAGE: signed short { NONE = -1, LUA, JS, PY };
|
||||
constinit const array<string_view, 3> languages { ".lua", ".js", ".py" };
|
||||
|
||||
shared_ptr<Target> Target::forName(string_view name, const bool newLines = true) {
|
||||
LANGUAGE selected = NONE;
|
||||
for (unsigned int i = 0; i < languages.size(); ++i) {
|
||||
if (name == languages[i]) {
|
||||
selected = static_cast<LANGUAGE>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
shared_ptr<Target> target;
|
||||
#define ADDTARGET(X) target = shared_ptr<X>(new X(newLines))
|
||||
switch (selected) {
|
||||
#ifdef LUA_HPP
|
||||
case LUA: ADDTARGET(LUA_HPP); break;
|
||||
#endif
|
||||
#ifdef JS_HPP
|
||||
case JS: ADDTARGET(JS_HPP); break;
|
||||
#endif
|
||||
#ifdef PY_HPP
|
||||
case PY: ADDTARGET(PY_HPP); break;
|
||||
#endif
|
||||
case NONE:
|
||||
default: Yerbacon::exit({"\"", string(1, (char) toupper(name.at(1))).data(), name.substr(2).data(), "\" is not a valid target."});
|
||||
}
|
||||
unique_ptr<Target> Target::forName(string_view name, const bool newLines = true) {
|
||||
#define ADDTARGET(X, target_class) if (name == X) return unique_ptr<target_class>(new target_class(newLines))
|
||||
ADDTARGET("lua", LuaTarget);
|
||||
ADDTARGET("js", JsTarget);
|
||||
ADDTARGET("py", PyTarget);
|
||||
ADDTARGET("gd", GsTarget);
|
||||
#undef ADDTARGET
|
||||
#undef make_nonlocal_task
|
||||
#undef make_task_noR
|
||||
#undef make_task
|
||||
return target;
|
||||
#undef make_task_base_R
|
||||
#undef make_task_base
|
||||
Yerbacon::fail({"\"", string(1, (char) toupper(name.at(1))).data(), name.substr(2).data(), "\" is not a valid target."});
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif //YERBACON_TARGET_HPP
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef GODOTSCRIPT_HPP
|
||||
#define GODOTSCRIPT_HPP
|
||||
|
||||
struct GsTarget: Target {
|
||||
print_functions_pair printFunctions() final { return make_pair("printraw", "print"); }
|
||||
unordered_task_map getTaskMap() final {
|
||||
return {
|
||||
make_task(Define,
|
||||
output << (parseComponent.final ? "const " : "var ") << parseComponent.name << " = ";
|
||||
transpileTree(parseComponent.content);
|
||||
),
|
||||
make_task(types::String, stringInterpolation(R"(""")", parseComponent.content);),
|
||||
make_task(Function,
|
||||
output << "func " << parseComponent.name << '(';
|
||||
separate_transpileTree(parseComponent.parameters, ", ");
|
||||
output << "):" << separator << indentation;
|
||||
if (parseComponent.empty()) output << "pass";
|
||||
separate_transpileTree(parseComponent, 1);
|
||||
)
|
||||
};
|
||||
}
|
||||
using Target::Target;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,15 +1,26 @@
|
|||
#ifndef JS_HPP
|
||||
#define JS_HPP JsTarget
|
||||
#define JS_HPP
|
||||
using namespace StandardComponents;
|
||||
|
||||
struct JsTarget: Target {
|
||||
print_functions_pair printFunctions() final { return make_pair("util.write", "console.log"); }
|
||||
bool use_uniqueLineSeparator() final { return true; }
|
||||
unordered_task_map getTaskMap() final {
|
||||
return {
|
||||
make_task(Define,
|
||||
output << (parseComponent.final ? "const " : "let ") << parseComponent.name << " = ";
|
||||
output << "ah";
|
||||
transpileTree(parseComponent.content);
|
||||
),
|
||||
make_task(types::String, stringInterpolation(parseComponent.content);)
|
||||
make_task(types::String, stringInterpolation(parseComponent.content);),
|
||||
make_task(Function,
|
||||
output << "function " << parseComponent.name << '(';
|
||||
separate_transpileTree(parseComponent.parameters, ", ");
|
||||
output << ") {";
|
||||
if (newLines and not parseComponent.empty()) output << separator << indentation;
|
||||
separate_transpileTree(parseComponent, 1);
|
||||
if (newLines and not parseComponent.empty()) output << separator;
|
||||
output << '}';
|
||||
)
|
||||
};
|
||||
}
|
||||
using Target::Target;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#ifndef LUA_HPP
|
||||
#define LUA_HPP LuaTarget
|
||||
#define LUA_HPP
|
||||
using namespace StandardComponents;
|
||||
|
||||
struct LuaTarget: Target {
|
||||
print_functions_pair printFunctions() final { return make_pair("io.write", "print"); }
|
||||
optional_string uniqueLineSeparator() final { return " "; }
|
||||
unordered_task_map getTaskMap() final {
|
||||
return {
|
||||
make_task(Define,
|
||||
|
@ -10,8 +12,21 @@ struct LuaTarget: Target {
|
|||
output << parseComponent.name;
|
||||
if (parseComponent.final) output << " <const>"; // TODO Find an alternative to <const> for lua <5.4
|
||||
output << " = ";
|
||||
transpileTree(parseComponent.content);
|
||||
),
|
||||
make_task(types::String, stringInterpolation(parseComponent.content, "[[", "]]", "..");)
|
||||
make_task(types::String, stringInterpolation(parseComponent.content, "[[", "]]", "..");),
|
||||
make_task(Function,
|
||||
output << "function " << parseComponent.name << '(';
|
||||
separate_transpileTree(parseComponent.parameters, ", ");
|
||||
output << ')';
|
||||
if (not parseComponent.empty()) {
|
||||
output << separator;
|
||||
if (newLines) output << indentation;
|
||||
} else output << ' ';
|
||||
separate_transpileTree(parseComponent, 1);
|
||||
if (not parseComponent.empty()) output << separator;
|
||||
output << "end";
|
||||
)
|
||||
};
|
||||
}
|
||||
using Target::Target;
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
#ifndef PY_HPP
|
||||
#define PY_HPP PyTarget
|
||||
#define PY_HPP
|
||||
using namespace StandardComponents;
|
||||
|
||||
struct PyTarget: Target {
|
||||
bool supportsOneLine() final { return false; }
|
||||
print_functions_pair printFunctions() final { return make_pair("sys.stdout.write", "print"); }
|
||||
optional_string uniqueLineSeparator() final { return {}; }
|
||||
unordered_task_map getTaskMap() final {
|
||||
return {
|
||||
make_task(Define, output << parseComponent.name << " = ";),
|
||||
make_task(types::String, stringInterpolation(R"(""")", parseComponent.content);)
|
||||
make_task(Define, output << parseComponent.name << " = "; transpileTree(parseComponent.content);),
|
||||
make_task(types::String, stringInterpolation(R"(""")", parseComponent.content);),
|
||||
make_task(Function,
|
||||
output << "def " << parseComponent.name << '(';
|
||||
separate_transpileTree(parseComponent.parameters, ", ");
|
||||
output << "):" << separator << indentation;
|
||||
if (parseComponent.empty()) output << "pass";
|
||||
separate_transpileTree(parseComponent, 1);
|
||||
),
|
||||
};
|
||||
}
|
||||
using Target::Target;
|
||||
|
|
102
src/main.cpp
102
src/main.cpp
|
@ -1,5 +1,5 @@
|
|||
#include <iostream>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include "headers/Yerbacon.hpp"
|
||||
#include <future>
|
||||
#include <filesystem>
|
||||
|
@ -11,79 +11,71 @@ using namespace std;
|
|||
|
||||
int main(int argc, char* argv[]) {
|
||||
setlocale(LC_ALL, "");
|
||||
string target = ".lua";
|
||||
string target = "lua";
|
||||
bool printResult = false,
|
||||
parallel = false,
|
||||
newLines = true;
|
||||
set<string_view> files;
|
||||
newLines = true,
|
||||
text_provided = false;
|
||||
using unit_result = pair<string, optional<Yerbacon::Exception>>;
|
||||
using unit = future<unit_result>;
|
||||
map<string_view, unit> Units;
|
||||
for (signed int i = 1; i < argc; ++i)
|
||||
{
|
||||
const string_view currentArg (argv[i]);
|
||||
if (currentArg == ArgumentShort("printresult")) printResult = true;
|
||||
else if (currentArg == ArgumentAssignable("target")) {
|
||||
const string_view value = ArgumentAssignable::getValueFor(currentArg);
|
||||
if (not value.empty()) (target = '.') += value;
|
||||
else Yerbacon::exit("No target was provided.");
|
||||
const string_view currentArgument (argv[i]);
|
||||
if (currentArgument == ArgumentShort("printresult")) printResult = true;
|
||||
else if (currentArgument == ArgumentAssignable("target")) {
|
||||
const string_view value = ArgumentAssignable::getValueFor(currentArgument);
|
||||
if (not value.empty()) target = value;
|
||||
else Yerbacon::fail("No target was provided.");
|
||||
}
|
||||
else if (currentArg == Argument("parallel")) parallel = true;
|
||||
else if (currentArg == ArgumentAssignable("newlines")) {
|
||||
const string_view enabled = ArgumentAssignable::getValueFor(currentArg);
|
||||
else if (currentArgument == Argument("parallel")) parallel = true;
|
||||
else if (currentArgument == ArgumentAssignable("newlines")) {
|
||||
const string_view enabled = ArgumentAssignable::getValueFor(currentArgument);
|
||||
if (enabled == "off") {
|
||||
newLines = false;
|
||||
} else if (enabled == "on") {
|
||||
newLines = true;
|
||||
} else goto invalid_argument;
|
||||
}
|
||||
else if (currentArg.ends_with(".ybcon")) files.insert(currentArg);
|
||||
else {
|
||||
if (argc == 2) {
|
||||
if (currentArg == Argument("version")) {
|
||||
cout << Yerbacon::getVersion();
|
||||
} else if (currentArg == Argument("buildInfo")) {
|
||||
cout << Yerbacon::getBuildInfo();
|
||||
} else goto invalid_argument;
|
||||
cout << '\n'; exit(EXIT_SUCCESS);
|
||||
} else invalid_argument: Yerbacon::exit({"\"", currentArg.data(), "\" is not a valid argument."});
|
||||
}
|
||||
}
|
||||
const auto currentTarget = Target::forName(target, newLines);
|
||||
const auto compile = [&target, ¤tTarget](string_view name) -> string {
|
||||
string transpiledString = currentTarget->transpileWithTree(parseString(getFileContent(name.data())));
|
||||
name.remove_suffix(6);
|
||||
string outputFile;
|
||||
(outputFile = name).append(target);
|
||||
outputFileContent(outputFile, transpiledString);
|
||||
return transpiledString;
|
||||
};
|
||||
int8_t exit_code = EXIT_SUCCESS;
|
||||
if (!files.empty()) {
|
||||
using unit = future<pair<string, optional<Yerbacon::Exception>>>;
|
||||
vector<unit> Units(files.size());
|
||||
const launch& Policy = not parallel ? launch::deferred : launch::async;
|
||||
transform(files.cbegin(), files.cend(), Units.begin(), [&Policy, &compile](const string_view& fileName){
|
||||
return async(Policy, [&fileName, &compile]() {
|
||||
pair<string, optional<Yerbacon::Exception>> resultingPair;
|
||||
else if (currentArgument == ArgumentShort("text")) printResult = text_provided = true;
|
||||
else if (text_provided || currentArgument.ends_with(".ybcon"))
|
||||
Units.insert_or_assign(currentArgument, async(not parallel ? launch::deferred : launch::async, [currentArgument, &text_provided, &target, &newLines]() {
|
||||
unit_result resultingPair;
|
||||
try {
|
||||
resultingPair.first = compile(fileName);
|
||||
resultingPair.first = Target::forName(target, newLines)->transpileWithTree(
|
||||
parseString(text_provided ? currentArgument : getFileContent(currentArgument.data()))
|
||||
);
|
||||
if (not text_provided) outputFileContent(string(currentArgument.substr(0, currentArgument.size() - 6)) + '.' + target, resultingPair.first);
|
||||
} catch (const Yerbacon::Exception& error) {
|
||||
size_t lastSlash = 0;
|
||||
const size_t position1 = fileName.find_last_of('/');
|
||||
const size_t position1 = currentArgument.find_last_of('/');
|
||||
if (cmp_not_equal(position1, string_view::npos)) lastSlash = position1;
|
||||
if constexpr(filesystem::path::preferred_separator != '/') {
|
||||
const size_t position2 = fileName.find_last_of(filesystem::path::preferred_separator);
|
||||
const size_t position2 = currentArgument.find_last_of(filesystem::path::preferred_separator);
|
||||
if (cmp_not_equal(position2, string_view::npos)) {
|
||||
lastSlash = max(lastSlash, position2);
|
||||
}
|
||||
}
|
||||
resultingPair.first = fileName.substr(lastSlash + 1);
|
||||
resultingPair.first = currentArgument.substr(lastSlash + 1);
|
||||
resultingPair.second.emplace(error);
|
||||
}
|
||||
return resultingPair;
|
||||
});
|
||||
});
|
||||
}));
|
||||
else {
|
||||
if (argc == 2) {
|
||||
if (currentArgument == Argument("version")) {
|
||||
cout << Yerbacon::getVersion();
|
||||
} else if (currentArgument == Argument("buildInfo")) {
|
||||
cout << Yerbacon::getBuildInfo();
|
||||
} else goto invalid_argument;
|
||||
cout << '\n'; exit(EXIT_SUCCESS);
|
||||
} else invalid_argument: Yerbacon::fail({"\"", currentArgument.data(), "\" is not a valid argument."});
|
||||
}
|
||||
}
|
||||
if (!Units.empty()) {
|
||||
if (printResult) cout << "~~~~[Yerbacon compilation result]~~~~\n\n";
|
||||
if (any_of(Units.rbegin(), Units.rend(), [&printResult](unit& currentFuture) -> bool {
|
||||
const pair result = currentFuture.get();
|
||||
for (auto entry_iterator = Units.rbegin(); entry_iterator != Units.rend(); ++entry_iterator) {
|
||||
const pair result = entry_iterator->second.get();
|
||||
const bool is_exception = result.second.has_value();
|
||||
if (not is_exception) {
|
||||
if (printResult && !(result.first.empty() || all_of(result.first.begin(), result.first.end(), [](const char& character){
|
||||
|
@ -92,13 +84,11 @@ int main(int argc, char* argv[]) {
|
|||
cout << result.first << '\n';
|
||||
}
|
||||
} else {
|
||||
cout << "Compilation of " << result.first << " has failed with the following error:" << endl;
|
||||
cout << "Compilation"; if (not text_provided) cout << " of " << result.first; cout << " has failed with the following error:" << endl;
|
||||
cerr << result.second.value().what() << '\n';
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return is_exception;
|
||||
})) {
|
||||
exit_code = EXIT_FAILURE;
|
||||
}
|
||||
} else cout << "No valid file provided.\n";
|
||||
return exit_code;
|
||||
} else cout << (!text_provided ? "No valid file provided" : "No text was provided") << ".\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue